Transforming strings of HTML into objects in the DocX format

After developing a TypeScript script that transforms a JSON string into a Word Doc poster using Docx, I encountered a hurdle. Certain sections of the JSON may contain HTML tags, such as

<br/>, <i>, <p>
, and I need a way to pass the string in and return specific DocX objects like a Paragraph or a TextRun.

While I came across the Html-to-Docx package, it seems to require a complete HTML string to generate a file, limiting my control over the content displayed in the Word Doc. I'm hesitant to switch to another library like OpenXML and am wondering if there is a feature within DocX itself that can help me achieve this. Alternatively, should I manually parse the strings to identify and convert the
<br/>, <i>, <p>
tags into DocX objects? Any insights or suggestions on how to best approach this challenge would be greatly appreciated. Thank you.

Answer №1

In my pursuit of simplifying the HTML tag conversion process, I've developed a basic converter. While it may not cater to all scenarios, it can easily be expanded or enhanced by those who wish to do so. As it stands, this converter serves my specific needs quite effectively.

    const htmlTagMatch =
            content.match(/<\/?[^>]+(>|$)|[^<]+/g) || [];

    const paragraphStack = [];
    let currentParagraph = [];
    let currentBullet = null;
    let pCount = 0;

    let formattingStack = []; 
    htmlTagMatch.forEach((item, index) =>
    {
        if (item.startsWith('<') && item.endsWith('>')) {
            // Handling HTML tags
            var tag = item.toLowerCase();

            if (pCount % 2 == 0 && tag == '<p>') {
                tag = '</p>';
            }
            switch (tag) {
                case '<i>':
                case '<em>':
                    formattingStack.push('italics');
                    break;
                case '<b>':
                case '<strong>':
                    formattingStack.push('bold');
                    break;
                case '<ul>':
                    if (currentParagraph.length > 0) {
                        paragraphStack.push(
                            createParagraph(currentParagraph, currentBullet)
                        );
                    }
                    currentParagraph = [];
                    break;
                case '<li>':
                    if (currentParagraph.length > 0) {
                        paragraphStack.push(
                            createParagraph(currentParagraph, currentBullet)
                        );
                    }
                    currentBullet = { level: 0 };
                    currentParagraph = [];
                    break;
                case '</i>':
                case '</em>':
                case '</b>':
                case '</strong>':
                    formattingStack.pop();
                    break;
                case '</ul>':
                    if (currentParagraph.length > 0) {
                        paragraphStack.push(
                            createParagraph(currentParagraph, currentBullet)
                        );
                    }
                    currentBullet = null;
                    currentParagraph = [];
                    break;
                case '<br/>':
                case '<br>':
                    currentParagraph.push(new TextRun({ break: 1 }));
                    break;
                case '</p>':
                case '<p>':
                    pCount++;
                    if (htmlTagMatch[index + 1] == '<p>' && htmlTagMatch[index] == '</p>') {
                        break;
                    }
                    // Adding a line break
                    if (pCount == 1 && currentParagraph.length > 0) {
                        currentParagraph.push(new TextRun({ break: 2 }));
                    }
                    if (pCount != 1) {
                        currentParagraph.push(new TextRun({ break: 2 }));
                    }
                    break;
            }
        } else {
            // Handling plain text
            let textRun = new TextRun({ text: item });
                
            if (formattingStack.includes('italics')) {
                textRun = new TextRun({ text: item, italics: true });
            }
            if (formattingStack.includes('bold')) {
                textRun = new TextRun({ text: item, bold: true });
            }
                
            currentParagraph.push(textRun);
        }
    });

    // Adding the final paragraph to the paragraphStack
    if (currentParagraph.length > 0) {
        paragraphStack.push(createParagraph(currentParagraph, currentBullet));
    }

    return paragraphStack;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Transferring files and information using the Fetch API

I am currently working on a React application and I have defined the state of my application as shown below: const [book, setBook] = useState({ title: '', cover: {} numberPages: 0, resume: '', date: date, }); The & ...

Prevent the risk of revealing your LinkedIn API key within HTML code

For my website, I am looking to incorporate the Sign In With LinkedIn feature for user logins. The initial example snippet provided in the LinkedIn API docs is as follows: <script type="text/javascript" src="//platform.linkedin.com/in.js"> api_k ...

Is there a way to showcase just the month in one spot when presenting data using Angular 13?

Just starting out with Angular and facing a challenge in the Milestone section. There is a loop for displaying years and months, but I need to ensure that the month name is displayed only once for each specific year. Can someone provide me with a possible ...

Xpath-Utilizing a variable within an XPATH expression is necessary

Currently, I am utilizing Selenium-webdriver and facing an issue while trying to select an element based on its "text" using xpath. Since the value is subject to change, I am utilizing a variable. I am looking for a solution to resolve this problem or an a ...

What is the best way to create a loop with JSON data?

My challenge is to showcase json data using a loop. I have received the following results, but I am unsure of how to display them with a loop. [ {"latitude":"23.046100780353495","longitude":"72.56860542227514"}, {"latitude":"23.088427701737665"," ...

Changing the value of undefined properties in a JavaScript object

I am struggling to identify and replace null values with 0's in my object. I was able to access the correct member within the loop, but once it exited, the values were no longer assigned as 0. Working with objects in JavaScript is new to me, so I&apos ...

What is the best method for clearing all session cookies in my React application?

Currently, I am working on a project using React. I am trying to find a method to automatically logout the user by deleting all of the session cookies in my React application. However, I have not been able to come up with a solution yet. Is there a way to ...

Enhancing Page Content Dynamism: Making Elements Static

I have a div element that I want to align on the right side of the screen, but the problem is that it doesn't adjust its position as the content dynamically expands and exceeds the width of the page. Despite conducting extensive research, I haven&apos ...

A guide on how to retrieve images from a URL and save them using Blob in Angular 5

In my web application, I have a few links that lead to files with different content types - some are application/pdf and others are image/jpeg. When clicking on these links, the file should download or save based on their respective type. While downloadin ...

What is the best way to assign a default value in mat-select when using an ngFor loop?

Is there a different approach to setting a default value in mat-select within an *ngFor loop? I'm having trouble accessing the element in the array from the loop as desired. .ts file: persons: Person[] = .. //consist of Person with name and age .htm ...

Utilizing AngularJS: Conditionally rendering ngIf within an array of objects containing unique properties

Facing a challenge with the ngIf directive that I have been struggling to resolve despite trying various solutions. In my scenario, I have an array of objects, each containing different properties. The structure is as follows: object = [ { ...

How can I access and modify objects within a state array in reactJS when using the setState() method?

Upon reviewing my code, I came across the following declaration: constructor(props) { super(props); this.state = { productArray: [{ barcode: '', name: '' }], numberOfRecords: ...

React-Redux showing no errors despite non-functional Redux store

I'm currently facing an issue with integrating React-Redux into my React Native/Expo project. Despite not receiving any console error messages, the data from the Redux store is not displaying in the user interface. Below are some key files related to ...

Exploring ways to dynamically alter templates using the link function in Angular.js

Recently, I developed a directive called widget which functions similar to ng-view, but the template name is derived from an attribute. app.directive('widget', function() { return { restrict: 'EA', scope: true, ...

Determining the presence of generic K within generic M in Typescript Generics and Redux

Hello there I am currently working on minimizing repetitive code in my react application by utilizing Redux state. After choosing the Redux structure to use (refer to Context), I now aim to make it more concise. To achieve this, I have developed a generic ...

Complete and automatically submit a form in a view using AngularJS

I have developed a basic AngularJS application that is functioning smoothly. Currently, I am looking to populate certain fields and submit the form directly from the view without requiring any user input. Below, you'll find some simplified JavaScrip ...

Leveraging random attributes in Next.js without encountering the "server/client mismatch" issue

Is there a way to generate unique IDs for form inputs dynamically, in order to avoid conflicts with other elements that share the same ID? If multiple login forms are present on a single page, each with an 'email' field, setting the id property b ...

Specify the width of one element to always be the same as the width of another

One common challenge is maintaining the width of one element equal to another when the page loads (refer to link description here). However, it becomes tricky when the side width changes, such as resizing the browser window. Is there a way to dynamically ...

There seems to be an issue with the CSS file linking properly within an Express application

Every time I run my app.js file, the index.html file is displayed. However, when I inspect the page, I notice that the CSS changes are not taking effect. Strangely, if I open the HTML file using a live server, the CSS changes are visible. Can someone exp ...

Tips for automatically closing a Bootstrap 3 modal when AJAX request succeeds?

I'm trying to close a modal upon ajax success, but I'm encountering an issue. Here is my code snippet: Javascript success: function() { console.log("delete success"); $('#deleteContactModal').modal('hide'); $( "# ...