How to Accurately Calculate Text Height for Slide Pagination in PptxGenJs?

57 Views Asked by At

I have a problem generating my pptx documents with the PptxGenJs library. The problem does not seem to be with the library, but I can't find a solution. I need a method to determine how much height a text will occupy on a slide, in order to cut the text and move the excess to the next slide. It seems very simple, but I haven't been able to achieve it. I have tried counting characters, taking into account the fontSize, fontFace, and line spacing, but the results are never satisfactory. Has anyone managed to do something like this before, or is there a solution within the library that I have overlooked? I would greatly appreciate the help.

import { Cursor } from './cursor';
import PptxGenJS from 'pptxgenjs';
import TextPropsOptions = PptxGenJS.TextPropsOptions;
import TextProps = PptxGenJS.TextProps;

class Report {
    // Cursor: Maintains the position at (x,y) on the slide.
    constructor(private cursor: Cursor) {}

    private textLargeArray(texts: TextProps[], options: TextLargeOptions, optionsText: TextPropsOptions) {
        const pages: [TextProps[], TextPropsOptions][] = [];

        const defaultFontSize = 12;
        const defaultLineSpacing = (optionsText?.fontSize ?? defaultFontSize) + 4;

        let h = this.cursor.maxY - options.y;
        let totalHeight = h;

        let page: TextProps[] = [];
        let currentParagraph = '';
        let currentPage = '';

        let yS = 0;

        let y = options.y;

        texts.forEach((text: TextProps) => {
            const words = text.text?.split(/(\s|\t)/) ?? [];
            let hasNewPage = false;
            let hasAddWord = false;

            let fontSize = (text?.options?.fontSize ?? optionsText.fontSize ?? defaultFontSize) / 72;
            let interLineSpace = (text?.options?.lineSpacing ?? optionsText.lineSpacing ?? defaultLineSpacing) / 60;
            let numCharactersPerLine = options.numCharactersPerLine ?? Math.floor(options.w / (fontSize / 4.35));

            const ySCurrent = interLineSpace > fontSize ? interLineSpace : fontSize + 4 / 72;
            if (!yS) {
                yS = ySCurrent;
            } else if (ySCurrent > yS) {
                yS = ySCurrent;
            }

            words.forEach((word: string, index: number) => {
                totalHeight = this.getNumLines(currentPage, numCharactersPerLine) * yS;

                const newPage = () => {
                    hasNewPage = true;
                    page.push({ text: currentParagraph, options: text.options });
                    pages.push([page, { y, h: totalHeight, w: options.w }]);
                    page = [];
                    h = 4;
                    y = 0.5;
                    currentParagraph = word;
                    currentPage = word;
                };

                if (totalHeight < h) {
                    if (this.getNumLines(currentPage ? ` ${word}` : word, numCharactersPerLine) * yS >= h) {
                        newPage();
                    }
                    hasAddWord = true;
                    currentParagraph += currentParagraph ? ` ${word}` : word;
                    currentPage += currentPage ? ` ${word}` : word;
                    if (text.options?.breakLine) {
                        currentPage += '\n';
                    }
                } else {
                    newPage();
                }
            });

            if (!hasNewPage) {
                page.push(text);
            } else if (!hasAddWord) {
                page.push({ text: currentParagraph, options: text.options });
            }

            yS = 0;
        });

        pages.push([page, { y, h: totalHeight, w: options.w }]);

        return pages;
    }

    private getNumLines(words: string, numCharactersPerLine: number) {
        let lines = 1;
        let cLine = 0;
        words.split('').map((c) => {
            if (c === '\t') {
                cLine += 24;
            } else if (c === '\n') {
                lines += 1;
                cLine = 0;
            } else {
                cLine++;
            }
            if (cLine >= numCharactersPerLine) {
                lines += 1;
                cLine = 0;
            }
        });
        return lines;
    }
}
0

There are 0 best solutions below