question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Render custom HTML markup inside pdf document

See original GitHub issue

Hi all, I am trying to render HTML inside the <View>/<Text> component while rendering the PDF inside the <PDFViewer> using a Template.

I’d like to render my custom HTML code shown below in my pdf

<p><span style="font-size: 24px;">Pre Interview Notes</span></p><p><br></p><p><strong style="font-size: 14px;"><u>This is a test Pre Interview Notes:</u></strong></p><p><br></p><p><em style="font-size: 14px;">The Guest requires a wheel chair for the show</em></p><p><br></p><p><br></p>

Environment

  • Browser [e.g. Chrome ]:
  • React-PDF version [1.6.8]:
  • React version [e.g. 16.8.6]:

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:23
  • Comments:20

github_iconTop GitHub Comments

24reactions
danomaticcommented, Aug 14, 2021
6reactions
tomgallaghercommented, Sep 10, 2020

@stevenbrown-85 it’s a bit more involved than a basic example but I can give you some pointers.

HTML and the mirror react-pdf code are both tree structures, so you need two different functions to walk/save the tree and then rebuild it. You’re looking at recursion to do this.

As you are using tinyMCE, you’re in luck as you can limit the html structures that can be in your tree, you don’t need to do every single HTML element. One thing to note is that tinyMCE onEditorChange event just provides a list of HTML elements, so you need to provide a ‘wrapper’ parent element for the below to work. I use a document fragment.

Look at this function: dom-to-json, which is where I started. The tree structure is preserved by using a childNodes array of sub-nodes. So you start with a document fragment with all the tinyMCE elements as childNodes.

Then you will need to write your own json-to-pdf function. Mine has an entry point that looks like this

export const JsonToPdfComponent = (input, bodyTextColorString) => {
    
    /*
        the object was created with following values 
        { 
            nodeType: <integer>, 
            nodeTagName: <string>, 
            nodeName: <string>, 
            nodeValue: <string>, 
            childNodes: <array> 
            attributes: <array of attributes arrays>
        }
    */
    
    //first we have an error handler which will result in blank elements in the pdf rather than crashes
    if (input === undefined) { console.error("JsonToPdfComponent: Undefined JSON Input"); return null; }
    //create the object, either parsing a JSON string or just using an existing javascript object
    let json = typeof input === 'string' ? JSON.parse(input) : input;
    //define the node type
    let nodeType = json.nodeType;
    //define the tag type
    let tagType = json.nodeTagName;
    //then the construction process is different depending on the type of node
    switch (nodeType) {
        //MOST OF THE WORK DONE ON ELEMENT_NODES
        case 1: 
            //then we need to create styled component views for each tag
            switch(tagType) {
                case 'h1':
                   //SEE EXAMPLE BELOW
                    return createH1ViewElement(json, bodyTextColorString);
                case 'h2':
                    return createH2ViewElement(json, bodyTextColorString);
                case 'h3':
                    return createH3ViewElement(json, bodyTextColorString);
                case 'h4':
                    return createH4ViewElement(json, bodyTextColorString);
                case 'h5':
                    return createH5ViewElement(json, bodyTextColorString);
                case 'h6':
                    return createH6ViewElement(json, bodyTextColorString);
                case 'strong':
                    return createStrongTextElement(json, bodyTextColorString);
                case 'em':
                    return createEmphasisTextElement(json, bodyTextColorString);
                case 'p':
                    return createParagraphTextElement(json, bodyTextColorString);
                case 'span':
                    return createSpanTextElement(json, bodyTextColorString);
                //we add a link tag only when the anchor tag has children, i.e. text
                case 'a':
                    return createLinkElement(json, bodyTextColorString);
                case 'img':
                    return createImageElement(json);
                //special processing for ordered and unordered list components
                case 'ol':
                    return createListComponent(json, bodyTextColorString, tagType);
                case 'ul':
                    return createListComponent(json, bodyTextColorString, tagType);
                //special processing for tables
                case 'table':
                    return createTableComponent(json, bodyTextColorString);
                //special processing for sup text
                case 'sup':
                    return createSupComponent(json, bodyTextColorString);
                default:
                    console.log(`No Processing for Tag ${tagType.toUpperCase()}`);
            }
            break;
        //TEXT_NODE - we can just create the simple text item
        case 3: 
            //this will return a null value if the text filtered for formatting characters has a length of zero
            return createTextComponent(json.nodeValue);
        default: 
            console.log("Skipping Node", json);
    }

};

Note that I do some intermediate processing, so there’s a crucial bit missing from this code, which is how to process the parent fragment element. You would do this by case 11, where you would, for example, create the react-pdf Page element using a routine like the following code sample and then all the childNodes accordingly.

To rebuild, you use React.createElement for each node, so for a simple h1 example:

import { View, Text } from '@react-pdf/renderer';

const createH1ViewElement = (node, bodyTextColorString) => {
    
    return React.createElement(
        //every html element should have its own view
        View,
        //add the special style props to the view, if you have any, from the JSON attributes array
        {
            //stops headers appearing at the bottom of the page
            minPresenceAhead: 1,
        },
        //then we pass in the children of the json object, as children of the React element
        React.createElement(
            Text,
            //add the special style props to the text element
            {
                //add the custom styles you have set for the pdf component
                style: {...dynamicStyles.styledHeader1, color: bodyTextColorString},
            },
            //then we pass in the children of the json object = recursion or the end of recursion
            node.childNodes.map(child => JsonToPdfComponent(child, bodyTextColorString))
        )
    );
    
};

That should get you started with basic text. Lists and tables are a bit tougher to get right.

Styles need to be converted from CSS to something understood by react-pdf. TinyMCE does most of the styling with span elements.

If you have all the default fonts from tinyMCE, they will need to be loaded from somewhere. Images are also a pain as they have CORS issues.

Hope that helps a bit.

There’s a big project potentially for react-pdf that converts any HTML string to pdf.

Tom

Read more comments on GitHub >

github_iconTop Results From Across the Web

Render custom HTML markup inside pdf document using ...
You will need to parse the HTML markup and re-create the output using the React-PDF components. I'm working on something similar now, except ......
Read more >
Render custom HTML markup inside pdf document using ...
Coding example for the question Render custom HTML markup inside pdf document using react-pdf/render library-Reactjs.
Read more >
Render HTML to PDF | Documents for PDF .NET Edition
GcPdf allows you to render HTML content to a PDF document easily. Learn how to render webpages, HTML strings or even URLs to...
Read more >
How to Create Highly Customized PDF from URL Web Page
Standard URL content to PDF rendering involves only document format conversion. Free edit PDF tools render source HTML code to PDF and don't...
Read more >
Custom PDF Rendering in JavaScript with Mozilla's PDF.Js
If your browser doesn't support Web Workers there's no need to worry as pdf.js contains all the code necessary to parse and render...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found