Hopding / pdf-lib

Create and modify PDF documents in any JavaScript environment
https://pdf-lib.js.org
MIT License
6.89k stars 656 forks source link

Resaving document with fontkit after adding text results in error #1492

Open vanaigr opened 1 year ago

vanaigr commented 1 year ago

What were you trying to do?

I was trying to reuse already created PDFDocument by removing all pages and working with it as if it is new (the speedup, when it works, is around 30% in my case). The example would not contain removing and readding pages, because it has no effect on the error showing up as far as I tested.

How did you attempt to do it?

What actually happened?

Saving the document the second time resulted in exception, the exact place and type of the error depended on the font being embedded in the document

The exceptions:

Cannot read properties of undefined (reading 'advanceWidth') ``` Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'advanceWidth') at CustomFontEmbedder.computeWidths (CustomFontEmbedder.js:230:40) at CustomFontSubsetEmbedder. (CustomFontEmbedder.js:141:37) at step (tslib.es6.js:100:23) at Object.next (tslib.es6.js:81:53) at fulfilled (tslib.es6.js:71:58) CustomFontEmbedder.computeWidths @ CustomFontEmbedder.js:230 (anonymous) @ CustomFontEmbedder.js:141 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 fulfilled @ tslib.es6.js:71 await in fulfilled (async) (anonymous) @ test3.html:22 ```
Uncaught RangeError: Index out of range ``` Uncaught RangeError: Index out of range at checkInt (fontkit.umd.js:1531:43) at Uint8Array.writeUInt16BE (fontkit.umd.js:1596:21) at TTFSubset._addGlyph (fontkit.umd.js:40914:17) at TTFSubset.encode (fontkit.umd.js:40952:13) at fontkit.umd.js:40692:14 at Item.run (fontkit.umd.js:2632:13) at drainQueue (fontkit.umd.js:2597:35) checkInt @ fontkit.umd.js:1531 writeUInt16BE @ fontkit.umd.js:1596 _addGlyph @ fontkit.umd.js:40914 encode @ fontkit.umd.js:40952 (anonymous) @ fontkit.umd.js:40692 Item.run @ fontkit.umd.js:2632 drainQueue @ fontkit.umd.js:2597 setTimeout (async) runTimeout @ fontkit.umd.js:2509 nextTick @ fontkit.umd.js:2622 encodeStream @ fontkit.umd.js:40691 (anonymous) @ CustomFontSubsetEmbedder.js:57 CustomFontSubsetEmbedder.serializeFont @ CustomFontSubsetEmbedder.js:54 (anonymous) @ CustomFontEmbedder.js:195 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 CustomFontEmbedder.embedFontStream @ CustomFontEmbedder.js:189 (anonymous) @ CustomFontEmbedder.js:154 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 CustomFontEmbedder.embedFontDescriptor @ CustomFontEmbedder.js:149 (anonymous) @ CustomFontEmbedder.js:127 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 CustomFontEmbedder.embedCIDFontDict @ CustomFontEmbedder.js:123 (anonymous) @ CustomFontEmbedder.js:95 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 CustomFontEmbedder.embedFontDict @ CustomFontEmbedder.js:91 CustomFontEmbedder.embedIntoContext @ CustomFontEmbedder.js:88 (anonymous) @ PDFFont.js:113 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 PDFFont.embed @ PDFFont.js:108 (anonymous) @ PDFDocument.js:1321 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 PDFDocument.embedAll @ PDFDocument.js:1312 (anonymous) @ PDFDocument.js:1203 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 PDFDocument.flush @ PDFDocument.js:1200 (anonymous) @ PDFDocument.js:1258 step @ tslib.es6.js:100 (anonymous) @ tslib.es6.js:81 (anonymous) @ tslib.es6.js:74 __awaiter @ tslib.es6.js:70 PDFDocument.save @ PDFDocument.js:1241 (anonymous) @ test3.html:18 await in (anonymous) (async) (anonymous) @ test3.html:22 ```

(filename is test3.html, previous two are also bugs)

What did you expect to happen?

Save the document without receiving exceptions, since I haven't found any signs that I cannot save the document multiple times.

How can we reproduce the issue?

<html><head>
<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.js"></script>
<script src="https://unpkg.com/@pdf-lib/fontkit@1.1.1/dist/fontkit.umd.js"></script>
<script>
(async() => {
    const link = 'https://fonts.cdnfonts.com/s/29105/ARIAL.woff' //Cannot read properties of undefined (reading 'advanceWidth')
    //const link = 'https://fonts.gstatic.com/s/roboto/v30/KFOkCnqEu92Fr1MmgVxMIzIFKw.woff2' //index out of range
    const fontBytes = await fetch(link).then(res => res.arrayBuffer()); 
    const pdfDoc = await PDFLib.PDFDocument.create();
    pdfDoc.registerFontkit(window.fontkit);

    const font = await pdfDoc.embedFont(fontBytes, { subset: true });

    const c1 = String.fromCodePoint(1072) //cyrrilic 'а'
    const c2 = String.fromCodePoint(1073) //cyrrilic 'б'
    const page = pdfDoc.addPage([1,1]);
    page.drawText(c1, { font });
    await pdfDoc.save();
    page.drawText(c2, { font });
    await pdfDoc.save();
    console.log('done')
})()
</script>
</head></html>

Version

pdf-lib: 1.17.1, fontkit: 1.1.1

What environment are you running pdf-lib in?

Browser

Checklist

Additional Notes

Browser: Chrome 114 OS: ChromeOS 114

mixth commented 10 months ago

Hi. I've got the similar error but in different scenario.

How did you attempt to do it?

  1. Load PDF document from a file
  2. Register new font
  3. Embed the font
  4. Copy the first page
  5. Fill in with text and image
  6. Add the page to the same document
  7. Save the document

What actually happened?

Same error reported ``` /node_modules/pdf-lib/src/core/embedders/CustomFontEmbedder.ts:231 currSection.push(currGlyph.advanceWidth * this.scale); ^ TypeError: Cannot read properties of undefined (reading 'advanceWidth') at CustomFontSubsetEmbedder.CustomFontEmbedder.computeWidths (/node_modules/pdf-lib/src/core/embedders/CustomFontEmbedder.ts:231:34) at CustomFontSubsetEmbedder. (/node_modules/pdf-lib/src/core/embedders/CustomFontEmbedder.ts:155:15) at step (/node_modules/pdf-lib/node_modules/tslib/tslib.js:141:27) at Object.next (/node_modules/pdf-lib/node_modules/tslib/tslib.js:122:57) at fulfilled (/node_modules/pdf-lib/node_modules/tslib/tslib.js:112:62) ```

Version

pdf-lib: 1.17.1 fontkit: 1.1.1

What environment are you running pdf-lib in?

node 18.13.0


What I found

My error was caused by this line:

const [page] = await doc.copyPages(doc, [0]);
// Fill in texts and images
doc.addPage(page);

Which I attempted to duplicate the first page to the new page, fill text and image, then add the page back to the same doc.

I resolved this by

Instead of copying to the same doc, I create a new doc and copy the page to the new doc instead. Something similar to this:

const targetedDoc = await PDFDocument.create();
const [page] = await targetedDoc.copyPages(doc, [0]);
// Fill in texts and images
targetedDoc.addPage(page);

Additional info

I'll try to explain a bit into the behavior of the error.

Hope this info might be useful for this bug.