├── test ├── .gitignore ├── pdfs │ ├── cell │ │ ├── xy.pdf │ │ ├── border.pdf │ │ ├── nested.pdf │ │ ├── simple.pdf │ │ ├── border-left.pdf │ │ ├── border-top.pdf │ │ ├── min-height.pdf │ │ ├── border-bottom.pdf │ │ ├── border-mixed.pdf │ │ ├── border-right.pdf │ │ ├── break-multiple.pdf │ │ ├── break-nested.pdf │ │ ├── break-single.pdf │ │ ├── retrospective.pdf │ │ ├── border-break-long.pdf │ │ ├── border-break-single.pdf │ │ ├── retrospective-long.pdf │ │ ├── border-retrospective.pdf │ │ ├── simple.js │ │ ├── break-single.js │ │ ├── border-break-single.js │ │ ├── break-multiple.js │ │ ├── xy.js │ │ ├── border-break-long.js │ │ ├── retrospective.js │ │ ├── border.js │ │ ├── nested.js │ │ ├── retrospective-long.js │ │ ├── border-top.js │ │ ├── break-nested.js │ │ ├── border-left.js │ │ ├── border-right.js │ │ ├── border-bottom.js │ │ ├── border-mixed.js │ │ ├── border-retrospective.js │ │ └── min-height.js │ ├── image │ │ ├── pdf.pdf │ │ ├── jpeg.pdf │ │ ├── nowrap.pdf │ │ ├── pagebreak.pdf │ │ ├── pdf-complex.pdf │ │ ├── pdf-complex.js │ │ ├── pagebreak.js │ │ ├── nowrap.js │ │ ├── pdf.js │ │ └── jpeg.js │ ├── parser │ │ ├── word.pdf │ │ ├── google.pdf │ │ ├── itext2.pdf │ │ ├── itext5.pdf │ │ ├── itext7.pdf │ │ ├── objstream.pdf │ │ ├── photoshop.pdf │ │ ├── illustrator.pdf │ │ ├── pixelmator.pdf │ │ ├── fixtures │ │ │ ├── word.pdf │ │ │ ├── google.pdf │ │ │ ├── itext2.pdf │ │ │ ├── itext5.pdf │ │ │ ├── itext7.pdf │ │ │ ├── objstream.pdf │ │ │ ├── photoshop.pdf │ │ │ ├── pixelmator.pdf │ │ │ ├── illustrator.pdf │ │ │ ├── indesign-print.pdf │ │ │ ├── ms-print-to-pdf.pdf │ │ │ └── indesign-interactive.pdf │ │ ├── indesign-print.pdf │ │ ├── ms-print-to-pdf.pdf │ │ ├── indesign-interactive.pdf │ │ ├── objstream.js │ │ ├── google.js │ │ ├── illustrator.js │ │ ├── ms-print-to-pdf.js │ │ ├── itext2.js │ │ ├── itext5.js │ │ ├── itext7.js │ │ ├── word.js │ │ ├── photoshop.js │ │ ├── pixelmator.js │ │ ├── indesign-print.js │ │ └── indesign-interactive.js │ ├── text │ │ ├── append.pdf │ │ ├── simple.pdf │ │ ├── kerning.pdf │ │ ├── pagebreak.pdf │ │ ├── styling.pdf │ │ ├── underline.pdf │ │ ├── winansi.pdf │ │ ├── wordbreak.pdf │ │ ├── alignment-afm.pdf │ │ ├── alignment-otf.pdf │ │ ├── append-style.pdf │ │ ├── simple.js │ │ ├── strikethrough.pdf │ │ ├── winansi.js │ │ ├── pagebreak.js │ │ ├── wordbreak.js │ │ ├── append.js │ │ ├── alignment-afm.js │ │ ├── append-style.js │ │ ├── styling.js │ │ ├── alignment-otf.js │ │ ├── kerning.js │ │ ├── underline.js │ │ └── strikethrough.js │ ├── external │ │ ├── self.pdf │ │ ├── firstpage.pdf │ │ ├── otherpage.pdf │ │ ├── pagesize.pdf │ │ ├── template.pdf │ │ ├── addallpages.pdf │ │ ├── addinbetween.pdf │ │ ├── fixtures │ │ │ └── 200x200.pdf │ │ ├── collision-prevention.pdf │ │ ├── templatefirstpageonly.pdf │ │ ├── addallpages.js │ │ ├── firstpage.js │ │ ├── otherpage.js │ │ ├── template.js │ │ ├── addinbetween.js │ │ ├── collision-prevention.js │ │ ├── self.js │ │ ├── pagesize.js │ │ └── templatefirstpageonly.js │ ├── issue │ │ ├── issue-41.pdf │ │ ├── issue-6.pdf │ │ ├── issue-68.pdf │ │ ├── issue-81.pdf │ │ ├── issue-112.pdf │ │ ├── issue-133.pdf │ │ ├── issue-159-br.pdf │ │ ├── fixtures │ │ │ ├── issue-117.pdf │ │ │ ├── issue-260.pdf │ │ │ └── msoffice-toc.pdf │ │ ├── issue-113-even-pages.pdf │ │ ├── issue-191-row-breaks.pdf │ │ ├── issue-260-empty-name.pdf │ │ ├── issue-117-nested-pages.pdf │ │ ├── issue-202-page-vs-table-header.pdf │ │ ├── issue-41.js │ │ ├── issue-150-nested-pages-in-templates.pdf │ │ ├── issue-175-cell-text-opts-inheritance.pdf │ │ ├── issue-81.js │ │ ├── issue-202-page-vs-table-header.js │ │ ├── issue-133.js │ │ ├── issue-68.js │ │ ├── issue-260-empty-name.js │ │ ├── issue-6.js │ │ ├── issue-112.js │ │ ├── issue-150-nested-pages-in-templates.js │ │ ├── issue-117-nested-pages.js │ │ ├── issue-175-cell-text-opts-inheritance.js │ │ ├── issue-113-even-pages.js │ │ ├── issue-191-row-breaks.js │ │ └── issue-159-br.js │ ├── ops │ │ ├── absolute.pdf │ │ ├── relative.pdf │ │ ├── absolute.js │ │ └── relative.js │ ├── table │ │ ├── border.pdf │ │ ├── colspan.pdf │ │ ├── header.pdf │ │ ├── header2.pdf │ │ ├── header3.pdf │ │ ├── header4.pdf │ │ ├── header5.pdf │ │ ├── simple.pdf │ │ ├── min-height.pdf │ │ ├── pagebreak1.pdf │ │ ├── pagebreak2.pdf │ │ ├── pagebreak3.pdf │ │ ├── pagebreak4.pdf │ │ ├── pagebreak5.pdf │ │ ├── pagebreak6.pdf │ │ ├── pagebreak7.pdf │ │ ├── pagebreak8.pdf │ │ ├── border-color.pdf │ │ ├── break-nested.pdf │ │ ├── header-border.pdf │ │ ├── header-only.pdf │ │ ├── missing-cells.pdf │ │ ├── retrospective.pdf │ │ ├── border-colorful.pdf │ │ ├── border-vertical.pdf │ │ ├── border-break-long.pdf │ │ ├── border-horizontal.pdf │ │ ├── border-horizontal2.pdf │ │ ├── break-empty-cells.pdf │ │ ├── break-pagefooter.pdf │ │ ├── break-pageheader.pdf │ │ ├── header-pagebreak.pdf │ │ ├── border-break-single.pdf │ │ ├── border-retrospective.pdf │ │ ├── retrospective-uneven.pdf │ │ ├── large-rows-with-headers.pdf │ │ ├── multiple-headers-only.pdf │ │ ├── simple-multiple-headers.pdf │ │ ├── retrospective-pagefooter.pdf │ │ ├── header-only.js │ │ ├── border.js │ │ ├── border-horizontal2.js │ │ ├── pagebreak5.js │ │ ├── border-color.js │ │ ├── missing-cells.js │ │ ├── pagebreak6.js │ │ ├── pagebreak7.js │ │ ├── pagebreak4.js │ │ ├── pagebreak3.js │ │ ├── pagebreak8.js │ │ ├── border-break-long.js │ │ ├── break-empty-cells.js │ │ ├── pagebreak2.js │ │ ├── border-break-single.js │ │ ├── simple.js │ │ ├── header2.js │ │ ├── retrospective-uneven.js │ │ ├── break-nested.js │ │ ├── pagebreak1.js │ │ ├── header-pagebreak.js │ │ ├── retrospective.js │ │ ├── border-retrospective.js │ │ ├── border-horizontal.js │ │ ├── header3.js │ │ ├── header4.js │ │ ├── header5.js │ │ ├── break-pageheader.js │ │ ├── simple-multiple-headers.js │ │ ├── border-vertical.js │ │ ├── break-pagefooter.js │ │ ├── retrospective-pagefooter.js │ │ ├── min-height.js │ │ ├── header.js │ │ ├── border-colorful.js │ │ ├── header-border.js │ │ ├── multiple-headers-only.js │ │ ├── large-rows-with-headers.js │ │ └── colspan.js │ ├── doc │ │ ├── padding-zero.pdf │ │ └── padding-zero.js │ ├── headerfooter │ │ ├── both.pdf │ │ ├── footer.pdf │ │ ├── header.pdf │ │ ├── pagecount.pdf │ │ ├── pagenumber.pdf │ │ ├── footer-change.pdf │ │ ├── header-change.pdf │ │ ├── header-change.js │ │ ├── footer-change.js │ │ ├── footer.js │ │ ├── header.js │ │ ├── pagenumber.js │ │ ├── pagecount.js │ │ └── both.js │ ├── outlines │ │ ├── outlines.pdf │ │ └── outlines.js │ └── annotations │ │ ├── img-goto.pdf │ │ ├── img-link.pdf │ │ ├── text-link.pdf │ │ ├── doc-destination.pdf │ │ ├── img-destination.pdf │ │ ├── text-destination.pdf │ │ ├── img-link.js │ │ ├── text-link.js │ │ ├── doc-destination.js │ │ ├── img-goto.js │ │ ├── img-destination.js │ │ └── text-destination.js ├── others │ ├── asBuffer.pdf │ ├── multiple-table-headers.js │ └── asBuffer.js ├── fixtures │ ├── image │ │ ├── pdfjs.jpg │ │ ├── pdfjs.pdf │ │ └── complex.pdf │ ├── document │ │ ├── test.pdf │ │ └── pdfjs-created.pdf │ ├── font │ │ └── opensans │ │ │ ├── bold.ttf │ │ │ └── regular.ttf │ └── index.js ├── README.md └── index.js ├── .gitattributes ├── lib ├── errors │ └── already-ended.js ├── object │ ├── number.js │ ├── null.js │ ├── boolean.js │ ├── index.js │ ├── value.js │ ├── array.js │ ├── trailer.js │ ├── nametree.js │ ├── stream.js │ ├── dictionary.js │ ├── name.js │ ├── object.js │ └── reference.js ├── index.js ├── content.js ├── image │ ├── image.js │ ├── pdf.js │ ├── jpeg.js │ └── render.js ├── font │ ├── base.js │ ├── subset.js │ └── afm.js ├── footer.js ├── cursor.js ├── tableheader.js ├── util.js ├── ops.js └── parser │ └── parser.js ├── .npmignore ├── font ├── Courier.js ├── Symbol.js ├── Helvetica.js ├── Times-Bold.js ├── Courier-Bold.js ├── Times-Italic.js ├── Times-Roman.js ├── ZapfDingbats.js ├── Courier-Oblique.js ├── Helvetica-Bold.js ├── Helvetica-Oblique.js ├── Times-BoldItalic.js ├── Courier-BoldOblique.js ├── Helvetica-BoldOblique.js ├── afm │ ├── MustRead.html │ ├── winansi_characters.txt │ └── convert.js ├── Courier.json ├── Courier-Bold.json ├── Courier-Oblique.json ├── Courier-BoldOblique.json ├── Symbol.json └── ZapfDingbats.json ├── .gitignore ├── .github └── workflows │ └── ci.yml ├── docs ├── text.md ├── table.md ├── row.md └── header.md ├── types └── tsconfig.json ├── README.md ├── LICENSE ├── package.json └── logo.svg /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.result.pdf 2 | preflight* -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pdf binary 2 | *.pdf diff 3 | -------------------------------------------------------------------------------- /lib/errors/already-ended.js: -------------------------------------------------------------------------------- 1 | module.exports = class AlreadyEndedError extends Error {}; 2 | -------------------------------------------------------------------------------- /test/pdfs/cell/xy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/xy.pdf -------------------------------------------------------------------------------- /test/pdfs/image/pdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/image/pdf.pdf -------------------------------------------------------------------------------- /test/others/asBuffer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/others/asBuffer.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/nested.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/nested.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/simple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/simple.pdf -------------------------------------------------------------------------------- /test/pdfs/image/jpeg.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/image/jpeg.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/word.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/word.pdf -------------------------------------------------------------------------------- /test/pdfs/text/append.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/append.pdf -------------------------------------------------------------------------------- /test/pdfs/text/simple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/simple.pdf -------------------------------------------------------------------------------- /test/pdfs/external/self.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/self.pdf -------------------------------------------------------------------------------- /test/pdfs/image/nowrap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/image/nowrap.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-41.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-41.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-6.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-68.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-68.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-81.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-81.pdf -------------------------------------------------------------------------------- /test/pdfs/ops/absolute.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/ops/absolute.pdf -------------------------------------------------------------------------------- /test/pdfs/ops/relative.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/ops/relative.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/google.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/google.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/itext2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/itext2.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/itext5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/itext5.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/itext7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/itext7.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border.pdf -------------------------------------------------------------------------------- /test/pdfs/table/colspan.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/colspan.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header2.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header3.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header4.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header5.pdf -------------------------------------------------------------------------------- /test/pdfs/table/simple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/simple.pdf -------------------------------------------------------------------------------- /test/pdfs/text/kerning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/kerning.pdf -------------------------------------------------------------------------------- /test/pdfs/text/pagebreak.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/pagebreak.pdf -------------------------------------------------------------------------------- /test/pdfs/text/styling.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/styling.pdf -------------------------------------------------------------------------------- /test/pdfs/text/underline.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/underline.pdf -------------------------------------------------------------------------------- /test/pdfs/text/winansi.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/winansi.pdf -------------------------------------------------------------------------------- /test/pdfs/text/wordbreak.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/wordbreak.pdf -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs 2 | font/afm 3 | test 4 | logo.svg 5 | .* 6 | yarn.lock 7 | test.* 8 | /*.pdf 9 | playground -------------------------------------------------------------------------------- /test/fixtures/image/pdfjs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/image/pdfjs.jpg -------------------------------------------------------------------------------- /test/fixtures/image/pdfjs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/image/pdfjs.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border-left.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-left.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border-top.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-top.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/min-height.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/min-height.pdf -------------------------------------------------------------------------------- /test/pdfs/doc/padding-zero.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/doc/padding-zero.pdf -------------------------------------------------------------------------------- /test/pdfs/image/pagebreak.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/image/pagebreak.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-112.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-112.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-133.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-133.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/objstream.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/objstream.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/photoshop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/photoshop.pdf -------------------------------------------------------------------------------- /test/pdfs/table/min-height.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/min-height.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak1.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak2.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak3.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak4.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak5.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak6.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak7.pdf -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/pagebreak8.pdf -------------------------------------------------------------------------------- /test/fixtures/document/test.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/document/test.pdf -------------------------------------------------------------------------------- /test/fixtures/image/complex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/image/complex.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border-bottom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-bottom.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border-mixed.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-mixed.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border-right.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-right.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/break-multiple.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/break-multiple.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/break-nested.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/break-nested.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/break-single.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/break-single.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/retrospective.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/retrospective.pdf -------------------------------------------------------------------------------- /test/pdfs/external/firstpage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/firstpage.pdf -------------------------------------------------------------------------------- /test/pdfs/external/otherpage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/otherpage.pdf -------------------------------------------------------------------------------- /test/pdfs/external/pagesize.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/pagesize.pdf -------------------------------------------------------------------------------- /test/pdfs/external/template.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/template.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/both.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/both.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/footer.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/footer.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/header.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/header.pdf -------------------------------------------------------------------------------- /test/pdfs/image/pdf-complex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/image/pdf-complex.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-159-br.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-159-br.pdf -------------------------------------------------------------------------------- /test/pdfs/outlines/outlines.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/outlines/outlines.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/illustrator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/illustrator.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/pixelmator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/pixelmator.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-color.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-color.pdf -------------------------------------------------------------------------------- /test/pdfs/table/break-nested.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/break-nested.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header-border.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header-border.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header-only.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header-only.pdf -------------------------------------------------------------------------------- /test/pdfs/table/missing-cells.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/missing-cells.pdf -------------------------------------------------------------------------------- /test/pdfs/table/retrospective.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/retrospective.pdf -------------------------------------------------------------------------------- /test/pdfs/text/alignment-afm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/alignment-afm.pdf -------------------------------------------------------------------------------- /test/pdfs/text/alignment-otf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/alignment-otf.pdf -------------------------------------------------------------------------------- /test/pdfs/text/append-style.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/append-style.pdf -------------------------------------------------------------------------------- /test/pdfs/text/simple.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text("Hello World"); 3 | }; 4 | -------------------------------------------------------------------------------- /test/pdfs/text/strikethrough.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/text/strikethrough.pdf -------------------------------------------------------------------------------- /test/pdfs/text/winansi.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text("Hællo, 1000€"); 3 | }; 4 | -------------------------------------------------------------------------------- /font/Courier.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Courier.json')) -------------------------------------------------------------------------------- /font/Symbol.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Symbol.json')) -------------------------------------------------------------------------------- /test/pdfs/annotations/img-goto.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/annotations/img-goto.pdf -------------------------------------------------------------------------------- /test/pdfs/annotations/img-link.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/annotations/img-link.pdf -------------------------------------------------------------------------------- /test/pdfs/annotations/text-link.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/annotations/text-link.pdf -------------------------------------------------------------------------------- /test/pdfs/external/addallpages.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/addallpages.pdf -------------------------------------------------------------------------------- /test/pdfs/external/addinbetween.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/addinbetween.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/word.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/word.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/indesign-print.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/indesign-print.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-colorful.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-colorful.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-vertical.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-vertical.pdf -------------------------------------------------------------------------------- /font/Helvetica.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Helvetica.json')) -------------------------------------------------------------------------------- /font/Times-Bold.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Times-Bold.json')) -------------------------------------------------------------------------------- /test/fixtures/font/opensans/bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/font/opensans/bold.ttf -------------------------------------------------------------------------------- /test/pdfs/cell/border-break-long.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-break-long.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/border-break-single.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-break-single.pdf -------------------------------------------------------------------------------- /test/pdfs/cell/retrospective-long.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/retrospective-long.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/pagecount.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/pagecount.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/pagenumber.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/pagenumber.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/fixtures/issue-117.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/fixtures/issue-117.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/fixtures/issue-260.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/fixtures/issue-260.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/google.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/google.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/itext2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/itext2.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/itext5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/itext5.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/itext7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/itext7.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/ms-print-to-pdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/ms-print-to-pdf.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-break-long.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-break-long.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-horizontal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-horizontal.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-horizontal2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-horizontal2.pdf -------------------------------------------------------------------------------- /test/pdfs/table/break-empty-cells.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/break-empty-cells.pdf -------------------------------------------------------------------------------- /test/pdfs/table/break-pagefooter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/break-pagefooter.pdf -------------------------------------------------------------------------------- /test/pdfs/table/break-pageheader.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/break-pageheader.pdf -------------------------------------------------------------------------------- /test/pdfs/table/header-pagebreak.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/header-pagebreak.pdf -------------------------------------------------------------------------------- /font/Courier-Bold.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Courier-Bold.json')) -------------------------------------------------------------------------------- /font/Times-Italic.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Times-Italic.json')) -------------------------------------------------------------------------------- /font/Times-Roman.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Times-Roman.json')) -------------------------------------------------------------------------------- /font/ZapfDingbats.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./ZapfDingbats.json')) -------------------------------------------------------------------------------- /test/fixtures/document/pdfjs-created.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/document/pdfjs-created.pdf -------------------------------------------------------------------------------- /test/fixtures/font/opensans/regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/fixtures/font/opensans/regular.ttf -------------------------------------------------------------------------------- /test/pdfs/cell/border-retrospective.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/cell/border-retrospective.pdf -------------------------------------------------------------------------------- /test/pdfs/external/fixtures/200x200.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/fixtures/200x200.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/footer-change.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/footer-change.pdf -------------------------------------------------------------------------------- /test/pdfs/headerfooter/header-change.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/headerfooter/header-change.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-113-even-pages.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-113-even-pages.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-191-row-breaks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-191-row-breaks.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-260-empty-name.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-260-empty-name.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/objstream.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/objstream.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/photoshop.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/photoshop.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/pixelmator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/pixelmator.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-break-single.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-break-single.pdf -------------------------------------------------------------------------------- /test/pdfs/table/border-retrospective.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/border-retrospective.pdf -------------------------------------------------------------------------------- /test/pdfs/table/retrospective-uneven.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/retrospective-uneven.pdf -------------------------------------------------------------------------------- /font/Courier-Oblique.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Courier-Oblique.json')) -------------------------------------------------------------------------------- /font/Helvetica-Bold.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Helvetica-Bold.json')) -------------------------------------------------------------------------------- /test/pdfs/annotations/doc-destination.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/annotations/doc-destination.pdf -------------------------------------------------------------------------------- /test/pdfs/annotations/img-destination.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/annotations/img-destination.pdf -------------------------------------------------------------------------------- /test/pdfs/annotations/text-destination.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/annotations/text-destination.pdf -------------------------------------------------------------------------------- /test/pdfs/external/collision-prevention.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/collision-prevention.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/fixtures/msoffice-toc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/fixtures/msoffice-toc.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-117-nested-pages.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-117-nested-pages.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/illustrator.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/illustrator.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/indesign-interactive.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/indesign-interactive.pdf -------------------------------------------------------------------------------- /test/pdfs/table/large-rows-with-headers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/large-rows-with-headers.pdf -------------------------------------------------------------------------------- /test/pdfs/table/multiple-headers-only.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/multiple-headers-only.pdf -------------------------------------------------------------------------------- /test/pdfs/table/simple-multiple-headers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/simple-multiple-headers.pdf -------------------------------------------------------------------------------- /font/Helvetica-Oblique.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Helvetica-Oblique.json')) -------------------------------------------------------------------------------- /font/Times-BoldItalic.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Times-BoldItalic.json')) -------------------------------------------------------------------------------- /test/pdfs/external/templatefirstpageonly.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/external/templatefirstpageonly.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/indesign-print.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/indesign-print.pdf -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/ms-print-to-pdf.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/ms-print-to-pdf.pdf -------------------------------------------------------------------------------- /test/pdfs/table/retrospective-pagefooter.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/table/retrospective-pagefooter.pdf -------------------------------------------------------------------------------- /font/Courier-BoldOblique.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Courier-BoldOblique.json')) -------------------------------------------------------------------------------- /font/Helvetica-BoldOblique.js: -------------------------------------------------------------------------------- 1 | const AFMFont = require('../lib/font/afm') 2 | module.exports = new AFMFont(require('./Helvetica-BoldOblique.json')) -------------------------------------------------------------------------------- /test/pdfs/issue/issue-202-page-vs-table-header.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-202-page-vs-table-header.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-41.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text("https://github.com/rkusa/pdfjs << click me"); 3 | }; 4 | -------------------------------------------------------------------------------- /test/pdfs/parser/fixtures/indesign-interactive.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/parser/fixtures/indesign-interactive.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dotfiles 2 | .* 3 | !.git* 4 | !.travis.yml 5 | 6 | # dependencies 7 | node_modules 8 | 9 | # local files 10 | test.* 11 | /*.pdf 12 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-150-nested-pages-in-templates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-150-nested-pages-in-templates.pdf -------------------------------------------------------------------------------- /test/pdfs/issue/issue-175-cell-text-opts-inheritance.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rkusa/pdfjs/HEAD/test/pdfs/issue/issue-175-cell-text-opts-inheritance.pdf -------------------------------------------------------------------------------- /test/pdfs/text/pagebreak.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.long, { fontSize: 20 }); 3 | doc.text(lorem.long, { fontSize: 20 }); 4 | }; 5 | -------------------------------------------------------------------------------- /test/pdfs/annotations/img-link.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.image(image.jpeg, { 3 | link: "https://github.com/rkusa/pdfjs", 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /test/pdfs/image/pdf-complex.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.text(lorem.shorter); 3 | 4 | doc.image(image.complexPdf); 5 | 6 | doc.text(lorem.shorter); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/cell/simple.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.cell(fixtures.lorem.short, { 3 | width: 200, 4 | padding: 20, 5 | backgroundColor: 0xeeeeee, 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/text/wordbreak.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | const cell = doc.cell({ padding: 10, width: 50, backgroundColor: 0xeeeeee }); 3 | cell.text("abcdefghijklmnopqrstuvw abcd"); 4 | }; 5 | -------------------------------------------------------------------------------- /test/pdfs/external/addallpages.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document }) { 2 | const external = document.test; 3 | 4 | doc.addPagesOf(external); 5 | 6 | doc.text("Should be on third page ..."); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/external/firstpage.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document }) { 2 | const external = document.test; 3 | 4 | doc.addPageOf(1, external); 5 | 6 | doc.text("Should be on second page ..."); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/external/otherpage.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document }) { 2 | const external = document.test; 3 | 4 | doc.addPageOf(2, external); 5 | 6 | doc.text("Should be on second page ..."); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/image/pagebreak.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.cell("a cell to move the image down", { paddingTop: 770 }); 3 | doc.image(image.jpeg); 4 | doc.text("after image"); 5 | }; 6 | -------------------------------------------------------------------------------- /test/pdfs/ops/absolute.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | // absolute 3 | doc.op(0, 0, 1, "sc"); 4 | doc.op(0, 830, 297.6648, 11.896, "re"); 5 | doc.op("f"); 6 | 7 | doc.text(lorem.short); 8 | }; 9 | -------------------------------------------------------------------------------- /test/pdfs/image/nowrap.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, image }) { 2 | doc.image(image.pdf, { 3 | wrap: false, 4 | y: 831.896, 5 | x: 10, 6 | }); 7 | 8 | doc.text(lorem.shorter); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/annotations/text-link.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc 3 | .text("pdfjs", { link: "https://github.com/rkusa/pdfjs" }) 4 | .add("issues", { link: "https://github.com/rkusa/pdfjs/issues" }); 5 | }; 6 | -------------------------------------------------------------------------------- /test/pdfs/annotations/doc-destination.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../lib"); 2 | 3 | module.exports = function (doc, { lorem, font }) { 4 | doc.text("goto", { goTo: "here" }); 5 | 6 | doc.pageBreak(); 7 | doc.destination("here"); 8 | }; 9 | -------------------------------------------------------------------------------- /test/pdfs/annotations/img-goto.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.image(image.jpeg, { 3 | goTo: "here", 4 | }); 5 | 6 | doc.pageBreak(); 7 | 8 | doc.text("here", { destination: "here" }); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/annotations/img-destination.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.text("goto", { goTo: "here" }); 3 | 4 | doc.pageBreak(); 5 | 6 | doc.image(image.jpeg, { 7 | destination: "here", 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-81.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font }) { 2 | doc 3 | .text() 4 | .add("Manage your calories by", { font: font.opensans.regular }) 5 | .add("eating a quarter pack per day.", { font: font.opensans.bold }); 6 | }; 7 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-202-page-vs-table-header.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.header().text("Page Header"); 3 | 4 | const table = doc.table({ widths: [null] }); 5 | table.header().cell("Table header"); 6 | table.row().cell("Content"); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/external/template.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document, lorem }) { 2 | doc.setTemplate(document.test); 3 | 4 | doc.text("TEST", { fontSize: 40 }); 5 | 6 | doc.pageBreak(); 7 | 8 | doc.text(lorem.short, { fontSize: 20 }); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/external/addinbetween.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document }) { 2 | const external = document.test; 3 | 4 | doc.text("Should be on first page ..."); 5 | 6 | doc.addPagesOf(external); 7 | 8 | doc.text("Should be on fourth page ..."); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/cell/break-single.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const cell = doc.cell({ 3 | backgroundColor: 0xeeeeee, 4 | width: 200, 5 | padding: 10, 6 | }); 7 | 8 | cell.text(lorem.long, { textAlign: "justify", fontSize: 15 }); 9 | }; 10 | -------------------------------------------------------------------------------- /lib/object/number.js: -------------------------------------------------------------------------------- 1 | exports.parse = function (xref, lexer, trial) { 2 | const n = lexer.readNumber(true); 3 | 4 | if (n === undefined) { 5 | if (trial) { 6 | return undefined; 7 | } 8 | 9 | throw new Error("Invalid number"); 10 | } 11 | 12 | return n; 13 | }; 14 | -------------------------------------------------------------------------------- /test/pdfs/text/append.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc 3 | .text("Lorem ipsum dolor sit amet, consetetur sadipscing elitr") 4 | .append(",") 5 | .add( 6 | "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.", 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /test/pdfs/ops/relative.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.short); 3 | 4 | // relative 5 | doc.op(1, 0, 0, "sc"); 6 | doc.op((x, y) => { 7 | const height = 40; 8 | return [x, y - height, x + 60, height, "re"]; 9 | }); 10 | doc.op("f"); 11 | }; 12 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-break-single.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const cell = doc.cell({ 3 | backgroundColor: 0xeeeeee, 4 | width: 200, 5 | padding: 0, 6 | borderWidth: 10, 7 | }); 8 | 9 | cell.text(lorem.long, { textAlign: "justify", fontSize: 15 }); 10 | }; 11 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/header-change.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, image }) { 2 | let header = doc.header(); 3 | header.text("First"); 4 | 5 | doc.text("Hello World 1"); 6 | 7 | header = doc.header(); 8 | header.text("Second"); 9 | 10 | doc.text("Hello World 2"); 11 | }; 12 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.Document = require("./document"); 4 | exports.Font = require("./font/otf"); 5 | 6 | exports.Image = require("./image/image"); 7 | 8 | exports.ExternalDocument = require("./external"); 9 | 10 | exports.mm = 0.0393700787 * 72; 11 | exports.cm = exports.mm * 10; 12 | -------------------------------------------------------------------------------- /test/pdfs/doc/padding-zero.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../lib"); 2 | 3 | module.exports = function (_, { lorem, font }) { 4 | const doc = new pdf.Document({ 5 | font: font.afm.regular, 6 | paddingLeft: 0, 7 | paddingRight: 0, 8 | }); 9 | doc.text(lorem.short); 10 | return doc; 11 | }; 12 | -------------------------------------------------------------------------------- /test/pdfs/external/collision-prevention.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font, document, lorem }) { 2 | doc.text("TEST", { fontSize: 200, color: 0xdddddd, textAlign: "center" }); 3 | 4 | doc.setTemplate(document.pdfjsCreated); 5 | 6 | doc.text("TEST", { fontSize: 200, color: 0xdddddd, textAlign: "center" }); 7 | }; 8 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-133.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | for (var i = 0; i < 10; i++) { 3 | const table = doc 4 | .table({ widths: [null, null], borderWidth: 1, padding: 100 }) 5 | .row(); 6 | table.cell().text(`L ${i}`); 7 | table.cell().text(`R ${i}`); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /lib/object/null.js: -------------------------------------------------------------------------------- 1 | exports.parse = function (xref, lexer, trial) { 2 | const isNull = lexer.getString(4) === "null"; 3 | 4 | if (!isNull) { 5 | if (trial) { 6 | return undefined; 7 | } 8 | 9 | throw new Error("Invalid null"); 10 | } 11 | 12 | lexer.shift(4); 13 | 14 | return null; 15 | }; 16 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/footer-change.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | new Array(3).fill(lorem.short).forEach((t, i) => { 3 | const h = doc.header(); 4 | h.text("Header " + i); 5 | const f = doc.footer(); 6 | f.text("Footer \n\n\n\n Part #" + i); 7 | doc.text(t); 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-68.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font }) { 2 | doc.header().text("Test", { font: font.afm.bold }); 3 | 4 | doc.footer().text("Footer", { font: font.afm.mono }); 5 | 6 | doc.text("TEST:", { fontSize: 16, font: font.afm.bold }); 7 | doc.text().add("TE").append("ST", { font: font.afm.monoRegular }); 8 | }; 9 | -------------------------------------------------------------------------------- /test/pdfs/cell/break-multiple.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const cell = doc.cell({ 3 | backgroundColor: 0xeeeeee, 4 | width: 250, 5 | padding: 10, 6 | }); 7 | 8 | cell.text(lorem.long, { textAlign: "justify", fontSize: 20 }); 9 | cell.text(lorem.long, { textAlign: "justify", fontSize: 20 }); 10 | }; 11 | -------------------------------------------------------------------------------- /test/pdfs/parser/objstream.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const external = new pdf.ExternalDocument( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/objstream.pdf")), 8 | ); 9 | doc.addPagesOf(external); 10 | }; 11 | -------------------------------------------------------------------------------- /test/pdfs/cell/xy.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.short); 3 | 4 | const cell = doc.cell({ 5 | backgroundColor: 0xeeeeee, 6 | padding: 10, 7 | width: 256, 8 | borderWidth: 1, 9 | x: 256, 10 | y: 256, 11 | }); 12 | cell.text(lorem.short); 13 | 14 | doc.text(lorem.short); 15 | }; 16 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-260-empty-name.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | const pdf = require("../../../"); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | 6 | const external = new pdf.ExternalDocument( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/issue-260.pdf")), 8 | ); 9 | doc.addPagesOf(external); 10 | }; 11 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-6.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text( 3 | "абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ", 4 | { font: fixtures.font.opensans.regular }, 5 | ); 6 | doc.text("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", { 7 | font: fixtures.font.opensans.regular, 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /test/pdfs/text/alignment-afm.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text(fixtures.lorem.short + "\n\n", { textAlign: "left" }); 3 | doc.text(fixtures.lorem.short + "\n\n", { textAlign: "center" }); 4 | doc.text(fixtures.lorem.short + "\n\n", { textAlign: "right" }); 5 | doc.text(fixtures.lorem.short + "\n\n", { textAlign: "justify" }); 6 | }; 7 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-112.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font }) { 2 | const pdf = require("../../../"); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | 6 | const external = new pdf.ExternalDocument( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/msoffice-toc.pdf")), 8 | ); 9 | doc.addPagesOf(external); 10 | }; 11 | -------------------------------------------------------------------------------- /test/pdfs/table/header-only.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [null, null], 4 | borderWidth: 1, 5 | }); 6 | 7 | const header = table.header(); 8 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 9 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 10 | }; 11 | -------------------------------------------------------------------------------- /test/pdfs/annotations/text-destination.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../lib"); 2 | 3 | module.exports = function (doc, { lorem, font }) { 4 | doc.text("goto B", { goTo: "B" }); 5 | doc.text("goto A", { goTo: "A" }); 6 | 7 | doc.pageBreak(); 8 | doc.text("A", { destination: "A" }); 9 | 10 | doc.pageBreak(); 11 | doc.text("B", { destination: "B" }); 12 | }; 13 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-break-long.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const cell = doc.cell({ 3 | backgroundColor: 0xeeeeee, 4 | width: 270, 5 | padding: 0, 6 | borderWidth: 10, 7 | }); 8 | 9 | cell.text(lorem.long, { textAlign: "justify", fontSize: 20 }); 10 | cell.text(lorem.long, { textAlign: "justify", fontSize: 20 }); 11 | }; 12 | -------------------------------------------------------------------------------- /test/pdfs/external/self.js: -------------------------------------------------------------------------------- 1 | // Add PDF generated by pdfjs as external document. 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const pdf = require("../../../lib"); 5 | 6 | module.exports = function (doc) { 7 | doc.addPagesOf( 8 | new pdf.ExternalDocument( 9 | fs.readFileSync(path.join(__dirname, "addallpages.pdf")), 10 | ), 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /test/pdfs/table/border.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [256, 256], 4 | borderWidth: 10, 5 | }); 6 | 7 | const row = table.row(); 8 | 9 | row.cell(lorem.shorter, { textAlign: "justify", fontSize: 20, padding: 10 }); 10 | row.cell(lorem.shorter, { textAlign: "justify", fontSize: 20, padding: 10 }); 11 | }; 12 | -------------------------------------------------------------------------------- /test/pdfs/table/border-horizontal2.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.cell({ y: 60 }); 3 | 4 | const table = doc.table({ 5 | widths: [null, null], 6 | borderWidth: 1, 7 | padding: 10, 8 | }); 9 | 10 | for (let i = 0; i < 3; ++i) { 11 | const row = table.row(); 12 | row.cell("Cell " + i); 13 | row.cell("Cell " + i); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/footer.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, image }) { 2 | const footer = doc.footer(); 3 | footer.text("text"); 4 | 5 | const cell = footer.cell({ padding: 20, backgroundColor: 0xdddddd }); 6 | cell.text("TESTING"); 7 | cell.image(image.pdf); 8 | 9 | doc.text("Hello"); 10 | 11 | doc.pageBreak(); 12 | 13 | doc.text(lorem.long, { fontSize: 20 }); 14 | }; 15 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/header.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, image }) { 2 | const header = doc.header(); 3 | header.text("text"); 4 | 5 | const cell = header.cell({ padding: 20, backgroundColor: 0xdddddd }); 6 | cell.text("TESTING"); 7 | cell.image(image.pdf); 8 | 9 | doc.text("Hello"); 10 | 11 | doc.pageBreak(); 12 | 13 | doc.text(lorem.long, { fontSize: 20 }); 14 | }; 15 | -------------------------------------------------------------------------------- /test/pdfs/parser/google.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/google.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak5.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ widths: [200, 200] }); 3 | const row = table.row(); 4 | row.cell(lorem.short, { 5 | backgroundColor: 0xeeeeee, 6 | padding: 10, 7 | fontSize: 20, 8 | }); 9 | row.cell("Uneven ...", { 10 | backgroundColor: 0xbbbbbb, 11 | padding: 10, 12 | fontSize: 20, 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /test/pdfs/cell/retrospective.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | for (let i = 0; i < 3; ++i) { 3 | doc.text(lorem.short, { fontSize: 20 }); 4 | } 5 | 6 | // should be moved to the next page retrospectively 7 | doc.cell(lorem.short, { 8 | backgroundColor: 0xeeeeee, 9 | padding: 30, 10 | fontSize: 20, 11 | }); 12 | 13 | doc.text(lorem.short, { fontSize: 20 }); 14 | }; 15 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-150-nested-pages-in-templates.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font }) { 2 | const pdf = require("../../../"); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | 6 | const external = new pdf.ExternalDocument( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/issue-117.pdf")), 8 | ); 9 | doc.setTemplate(external); 10 | doc.text("Works"); 11 | }; 12 | -------------------------------------------------------------------------------- /test/pdfs/parser/illustrator.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/illustrator.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /test/pdfs/table/border-color.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [256, 256], 4 | borderWidth: 10, 5 | borderColor: 0xe74c3c, 6 | }); 7 | 8 | const row = table.row(); 9 | 10 | row.cell(lorem.shorter, { textAlign: "justify", fontSize: 20, padding: 10 }); 11 | row.cell(lorem.shorter, { textAlign: "justify", fontSize: 20, padding: 10 }); 12 | }; 13 | -------------------------------------------------------------------------------- /test/pdfs/external/pagesize.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document }) { 2 | doc.text("First"); 3 | 4 | const pdf = require("../../../"); 5 | const path = require("path"); 6 | const fs = require("fs"); 7 | 8 | const external = new pdf.ExternalDocument( 9 | fs.readFileSync(path.join(__dirname, "/fixtures/200x200.pdf")), 10 | ); 11 | doc.addPagesOf(external); 12 | 13 | doc.text("Second"); 14 | }; 15 | -------------------------------------------------------------------------------- /test/pdfs/parser/ms-print-to-pdf.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/ms-print-to-pdf.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /test/pdfs/parser/itext2.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/itext2.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/parser/itext5.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/itext5.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/parser/itext7.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/itext7.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/parser/word.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/word.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: push 2 | name: Continuous Integration 3 | jobs: 4 | ci: 5 | name: Continuous Integration 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - name: Use Node.js 10.x 10 | uses: actions/setup-node@v1 11 | with: 12 | version: 10.x 13 | - name: Install Dependencies and Run Tests 14 | run: | 15 | npm install 16 | npm test 17 | -------------------------------------------------------------------------------- /test/pdfs/parser/photoshop.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/photoshop.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/parser/pixelmator.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/pixelmator.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-117-nested-pages.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font }) { 2 | const pdf = require("../../../"); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | 6 | const external = new pdf.ExternalDocument( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/issue-117.pdf")), 8 | ); 9 | doc.addPageOf(43, external); // should add "Hello 41" as the first page 10 | doc.addPagesOf(external); 11 | }; 12 | -------------------------------------------------------------------------------- /test/pdfs/parser/indesign-print.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/indesign-print.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/table/missing-cells.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ widths: [null, null, null], borderWidth: 1 }); 3 | { 4 | const row = table.row(); 5 | row.cell("A"); 6 | row.cell("B"); 7 | row.cell("C"); 8 | } 9 | { 10 | const row = table.row(); 11 | row.cell("A"); 12 | // missing cells should be added automatically 13 | } 14 | 15 | doc.text(lorem.shorter); 16 | }; 17 | -------------------------------------------------------------------------------- /test/pdfs/cell/border.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.text("before"); 3 | 4 | doc.cell("Cell 1", { 5 | fontSize: 15, 6 | width: 256, 7 | padding: 10, 8 | borderWidth: 5, 9 | }); 10 | 11 | doc.text("in between"); 12 | 13 | doc.cell("Cell 2", { 14 | fontSize: 15, 15 | width: 256, 16 | padding: 10, 17 | borderWidth: 1, 18 | borderColor: 0x2980b9, 19 | }); 20 | 21 | doc.text("after"); 22 | }; 23 | -------------------------------------------------------------------------------- /test/pdfs/cell/nested.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text(fixtures.lorem.shorter); 3 | const outer = doc.cell({ 4 | width: 400, 5 | padding: 20, 6 | backgroundColor: 0xeeeeee, 7 | }); 8 | const inner = outer.cell({ padding: 20, backgroundColor: 0xdddddd }); 9 | inner.text(fixtures.lorem.short); 10 | inner.text(fixtures.lorem.short); 11 | outer.text("Hello World"); 12 | doc.text("Hello World"); 13 | }; 14 | -------------------------------------------------------------------------------- /test/pdfs/parser/indesign-interactive.js: -------------------------------------------------------------------------------- 1 | const pdf = require("../../../"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | 5 | module.exports = function (doc) { 6 | const img = new pdf.Image( 7 | fs.readFileSync(path.join(__dirname, "/fixtures/indesign-interactive.pdf")), 8 | ); 9 | 10 | doc.image(img, { 11 | wrap: false, 12 | x: 0, 13 | y: 841.89, 14 | width: 595.28, 15 | height: 841.89, 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/pdfs/cell/retrospective-long.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | for (let i = 0; i < 3; ++i) { 3 | doc.text(lorem.short, { fontSize: 20 }); 4 | } 5 | 6 | // should be moved to the next page retrospectively 7 | const cell = doc.cell({ backgroundColor: 0xeeeeee, padding: 20 }); 8 | for (let i = 0; i < 4; ++i) { 9 | cell.text(lorem.short, { fontSize: 20 }); 10 | } 11 | 12 | doc.text(lorem.short, { fontSize: 20 }); 13 | }; 14 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak6.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.cell({ y: 70 }); 3 | 4 | const table = doc.table({ 5 | widths: [null, null], 6 | borderWidth: 1, 7 | padding: 10, 8 | }); 9 | 10 | for (let i = 0; i < 2; ++i) { 11 | const row = table.row(); 12 | row.cell("Cell " + i); 13 | const cell = row.cell(); 14 | for (let i = 0; i < 3; ++i) { 15 | cell.text("text " + i); 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak7.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.cell({ y: 120 }); 3 | 4 | const table = doc.table({ 5 | widths: [null, null], 6 | borderWidth: 1, 7 | padding: 10, 8 | }); 9 | 10 | for (let i = 0; i < 2; ++i) { 11 | const row = table.row(); 12 | row.cell("Cell " + i); 13 | const cell = row.cell(); 14 | for (let i = 0; i < 3; ++i) { 15 | cell.text("text " + i); 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-top.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.text("before"); 3 | 4 | doc.cell("Cell 1", { 5 | fontSize: 15, 6 | width: 256, 7 | padding: 10, 8 | borderTopWidth: 5, 9 | }); 10 | 11 | doc.text("in between"); 12 | 13 | doc.cell("Cell 2", { 14 | fontSize: 15, 15 | width: 256, 16 | padding: 10, 17 | borderTopWidth: 1, 18 | borderTopColor: 0x2980b9, 19 | }); 20 | 21 | doc.text("after"); 22 | }; 23 | -------------------------------------------------------------------------------- /test/pdfs/cell/break-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const cell = doc.cell({ 3 | backgroundColor: 0xeeeeee, 4 | width: 250, 5 | padding: 10, 6 | }); 7 | 8 | cell.text(lorem.shorter, { fontSize: 20 }); 9 | 10 | const inner = cell.cell({ 11 | backgroundColor: 0xdddddd, 12 | padding: 10, 13 | }); 14 | 15 | inner.text(lorem.long, { fontSize: 20 }); 16 | 17 | cell.text(lorem.shorter, { fontSize: 20 }); 18 | }; 19 | -------------------------------------------------------------------------------- /test/others/multiple-table-headers.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | const pdf = require("../../lib"); 3 | 4 | test("table headers created in-between rows", function (t) { 5 | const doc = new pdf.Document(); 6 | const table = doc.table({ widths: [] }); 7 | table.header(); 8 | table.header(); 9 | table.row(); 10 | t.throws( 11 | () => table.header(), 12 | /The table already has rows, cannot add additional headers/, 13 | ); 14 | t.end(); 15 | }); 16 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-left.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.text("before"); 3 | 4 | doc.cell("Cell 1", { 5 | fontSize: 15, 6 | width: 256, 7 | padding: 10, 8 | borderLeftWidth: 5, 9 | }); 10 | 11 | doc.text("in between"); 12 | 13 | doc.cell("Cell 2", { 14 | fontSize: 15, 15 | width: 256, 16 | padding: 10, 17 | borderLeftWidth: 1, 18 | borderLeftColor: 0x2980b9, 19 | }); 20 | 21 | doc.text("after"); 22 | }; 23 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-right.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.text("before"); 3 | 4 | doc.cell("Cell 1", { 5 | fontSize: 15, 6 | width: 256, 7 | padding: 10, 8 | borderRightWidth: 5, 9 | }); 10 | 11 | doc.text("in between"); 12 | 13 | doc.cell("Cell 2", { 14 | fontSize: 15, 15 | width: 256, 16 | padding: 10, 17 | borderRightWidth: 1, 18 | borderRightColor: 0x2980b9, 19 | }); 20 | 21 | doc.text("after"); 22 | }; 23 | -------------------------------------------------------------------------------- /lib/object/boolean.js: -------------------------------------------------------------------------------- 1 | exports.parse = function (xref, lexer, trial) { 2 | const isTrue = lexer.getString(4) === "true"; 3 | const isFalse = !isTrue && lexer.getString(5) === "false"; 4 | 5 | if (!isTrue && !isFalse) { 6 | if (trial) { 7 | return undefined; 8 | } 9 | 10 | throw new Error("Invalid boolean"); 11 | } 12 | 13 | if (isTrue) { 14 | lexer.shift(4); 15 | } else { 16 | lexer.shift(5); 17 | } 18 | 19 | return isTrue; 20 | }; 21 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-bottom.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.text("before"); 3 | 4 | doc.cell("Cell 1", { 5 | fontSize: 15, 6 | width: 256, 7 | padding: 10, 8 | borderBottomWidth: 5, 9 | }); 10 | 11 | doc.text("in between"); 12 | 13 | doc.cell("Cell 2", { 14 | fontSize: 15, 15 | width: 256, 16 | padding: 10, 17 | borderBottomWidth: 1, 18 | borderBottomColor: 0x2980b9, 19 | }); 20 | 21 | doc.text("after"); 22 | }; 23 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-mixed.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.text("before"); 3 | 4 | doc.cell("Cell 1", { 5 | fontSize: 15, 6 | width: 256, 7 | padding: 10, 8 | borderTopWidth: 2, 9 | borderTopColor: 0xe74c3c, 10 | borderRightWidth: 4, 11 | borderRightColor: 0x2980b9, 12 | borderBottomWidth: 6, 13 | borderBottomColor: 0x27ae60, 14 | borderLeftWidth: 8, 15 | borderLeftColor: 0xf1c40f, 16 | }); 17 | 18 | doc.text("after"); 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/pagenumber.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | const footer = doc.footer(); 3 | footer.text("before", { textAlign: "center" }); 4 | footer.pageNumber({ textAlign: "center", fontSize: 16 }); 5 | 6 | const header = doc.header(); 7 | header.pageNumber({ textAlign: "center", fontSize: 16 }); 8 | header.text("after", { textAlign: "center" }); 9 | 10 | doc.text("Hello World 1"); 11 | 12 | doc.pageBreak(); 13 | 14 | doc.text("Hello World 2"); 15 | }; 16 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-175-cell-text-opts-inheritance.js: -------------------------------------------------------------------------------- 1 | const HelveticaBold = require("../../../font/Helvetica-Bold.js"); 2 | 3 | const cm = 0.0393700787 * 72 * 10; 4 | 5 | module.exports = function (doc, { image }) { 6 | const table = doc.table({ 7 | widths: [null], 8 | borderWidth: 1, 9 | padding: 4, 10 | }); 11 | 12 | table.row({ font: HelveticaBold }).cell().text("Bold").add("Bold"); 13 | table.row({ font: HelveticaBold }).cell().text("Bold").text("Bold"); 14 | }; 15 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak4.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ widths: [205, 205] }); 3 | 4 | for (let r = 0; r < 3; ++r) { 5 | const row = table.row(); 6 | 7 | for (let c = 0; c < 2; ++c) { 8 | const cell = row.cell({ padding: 10, backgroundColor: 0xbbbbbb }); 9 | for (let i = 0; i < 2; ++i) { 10 | cell.text(lorem.short, { fontSize: 20 }); 11 | } 12 | } 13 | } 14 | 15 | doc.text(lorem.shorter); 16 | }; 17 | -------------------------------------------------------------------------------- /docs/text.md: -------------------------------------------------------------------------------- 1 | ## Text 2 | 3 | ### .add(text, [opts]) 4 | 5 | Adds text to the text fragment. 6 | 7 | **Arguments:** 8 | 9 | - **str** - the string 10 | - **opts** - styling options 11 | 12 | **Options:** 13 | 14 | Same as [doc.text([text], [opts])](fragment.md#texttext-opts) 15 | 16 | ### .append(text, [opts]) 17 | 18 | Same as [text.add(text, [opts])](fragment.md#texttext-opts), but directly appends the text, i.e., adds the text without a space. 19 | 20 | ### .br() 21 | 22 | Adds a line break. 23 | -------------------------------------------------------------------------------- /lib/content.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDF = require("./object"); 4 | 5 | module.exports = class ContentChunk { 6 | constructor(doc, obj) { 7 | this._doc = doc; 8 | this._fonts = {}; 9 | this._xobjects = {}; 10 | 11 | this._object = obj || new PDF.Object(); 12 | this._length = new PDF.Object(); 13 | 14 | doc._registerObject(this._object); 15 | doc._registerObject(this._length); 16 | 17 | this._object.prop("Length", this._length.toReference()); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak3.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.shorter); 3 | 4 | const table = doc.table({ widths: [200, 200] }); 5 | 6 | for (let r = 0; r < 4; ++r) { 7 | const row = table.row(); 8 | 9 | for (let c = 0; c < 2; ++c) { 10 | const cell = row.cell({ padding: 10, backgroundColor: 0xbbbbbb }); 11 | for (let i = 0; i < 2; ++i) { 12 | cell.text(lorem.shorter, { fontSize: 20 }); 13 | } 14 | } 15 | } 16 | 17 | doc.text(lorem.shorter); 18 | }; 19 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak8.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.header().text("Header"); 3 | doc.footer().text("Footer"); 4 | 5 | doc.cell({ y: 120 }); 6 | 7 | const table = doc.table({ 8 | widths: [null, null], 9 | borderWidth: 1, 10 | padding: 10, 11 | }); 12 | 13 | for (let i = 0; i < 2; ++i) { 14 | const row = table.row(); 15 | row.cell("Cell " + i); 16 | const cell = row.cell(); 17 | for (let i = 0; i < 3; ++i) { 18 | cell.text("text " + i); 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/pdfs/text/append-style.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { font }) { 2 | const txt1 = doc 3 | .text("foo", { font: font.afm.monoRegular }) 4 | .append("bar", { font: font.afm.monoBold }) 5 | .append("_"); 6 | 7 | for (let i = 0; i < 100; ++i) { 8 | txt1.add("."); 9 | } 10 | 11 | const txt2 = doc 12 | .text("foo", { font: font.afm.monoRegular, textAlign: "center" }) 13 | .append("bar", { font: font.afm.monoBold }) 14 | .append("_"); 15 | 16 | for (let i = 0; i < 30; ++i) { 17 | txt2.add("."); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/table/border-break-long.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [256, 256], 4 | padding: 0, 5 | borderWidth: 10, 6 | }); 7 | 8 | const row = table.row(); 9 | 10 | row.cell(lorem.short, { 11 | textAlign: "justify", 12 | fontSize: 20, 13 | padding: 10, 14 | backgroundColor: 0xdddddd, 15 | }); 16 | row.cell(lorem.long + "\n" + lorem.long, { 17 | textAlign: "justify", 18 | fontSize: 20, 19 | padding: 10, 20 | backgroundColor: 0xeeeeee, 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /test/pdfs/text/styling.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | const text = doc.text(); 3 | text 4 | .add("Regular") 5 | .add("Bold", { font: fixtures.font.afm.bold }) 6 | .add("Regular", { font: fixtures.font.afm.regular }) 7 | .add("Big", { fontSize: 20 }) 8 | .add("BigBold", { fontSize: 20, font: fixtures.font.afm.bold }) 9 | .add("Red", { color: 0xff0000 }) 10 | .add("Regular") 11 | // test changing line heights 12 | .add("\nRegular") 13 | .add("\nBigger", { fontSize: 40 }) 14 | .add("\nRegular"); 15 | }; 16 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-113-even-pages.js: -------------------------------------------------------------------------------- 1 | module.exports = async function (doc, fixtures, t) { 2 | await doc._endPage(); 3 | t.equal(doc._pages.length, 0); 4 | 5 | doc.text("Foobar"); 6 | 7 | // necessary to render the last started content 8 | if (doc._current) { 9 | doc._current.end(); 10 | doc._current = null; 11 | } 12 | 13 | // render all queued content 14 | await doc._next(); 15 | await doc._endPage(); 16 | 17 | t.equal(doc._pages.length, 1); 18 | 19 | if (doc._pages.length % 2 !== 0) { 20 | await doc._startPage(); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /test/pdfs/table/break-empty-cells.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.cell({ y: 29 }); 3 | 4 | const table = doc.table({ 5 | widths: [null, null, null, null, null, null, null], 6 | borderWidth: 1, 7 | padding: 0, 8 | }); 9 | 10 | for (let i = 0; i < 3; ++i) { 11 | const row = table.row(); 12 | row.cell("Cell " + i); 13 | row.cell(); 14 | row.cell(); 15 | row.cell("Cell " + i); 16 | row.cell(); 17 | row.cell("Cell " + i); 18 | row.cell("Cell " + i); 19 | } 20 | }; 21 | 22 | module.exports.padding = 0; 23 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak2.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.shorter, { fontSize: 20 }); 3 | 4 | const table = doc.table({ widths: [200, 200] }); 5 | const row = table.row(); 6 | 7 | const cell1 = row.cell({ padding: 20, backgroundColor: 0xbbbbbb }); 8 | for (let i = 0; i < 2; ++i) { 9 | cell1.text(lorem.short, { fontSize: 20 }); 10 | } 11 | 12 | const cell2 = row.cell({ padding: 10, backgroundColor: 0xdddddd }); 13 | cell2.text(lorem.short, { fontSize: 20 }); 14 | 15 | doc.text(lorem.shorter, { fontSize: 20 }); 16 | }; 17 | -------------------------------------------------------------------------------- /test/pdfs/cell/border-retrospective.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | for (let i = 0; i < 3; ++i) { 3 | doc.text(lorem.short, { fontSize: 20 }); 4 | } 5 | 6 | doc.text("--------------------------", { fontSize: 20 }); 7 | 8 | // should be moved to the next page retrospectively 9 | const cell = doc.cell({ 10 | backgroundColor: 0xeeeeee, 11 | padding: 0, 12 | borderWidth: 1, 13 | }); 14 | for (let i = 0; i < 4; ++i) { 15 | cell.text(lorem.short, { fontSize: 20 }); 16 | } 17 | 18 | doc.text(lorem.short, { fontSize: 20 }); 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/image/pdf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.image(image.pdf, { 3 | width: 64, 4 | align: "center", 5 | wrap: false, 6 | x: 10, 7 | y: 30, 8 | }); 9 | 10 | doc.text(lorem.shorter); 11 | 12 | doc.image(image.pdf); 13 | 14 | doc.image(image.pdf, { 15 | width: 128, 16 | align: "left", 17 | }); 18 | 19 | doc.image(image.pdf, { 20 | height: 55, 21 | align: "center", 22 | }); 23 | 24 | doc.image(image.pdf, { 25 | width: 128, 26 | align: "right", 27 | }); 28 | 29 | doc.text(lorem.shorter); 30 | }; 31 | -------------------------------------------------------------------------------- /test/pdfs/image/jpeg.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | doc.image(image.jpeg, { 3 | width: 64, 4 | align: "center", 5 | wrap: false, 6 | x: 10, 7 | y: 30, 8 | }); 9 | 10 | doc.text(lorem.shorter); 11 | 12 | doc.image(image.jpeg); 13 | 14 | doc.image(image.jpeg, { 15 | width: 128, 16 | align: "left", 17 | }); 18 | 19 | doc.image(image.jpeg, { 20 | height: 55, 21 | align: "center", 22 | }); 23 | 24 | doc.image(image.jpeg, { 25 | width: 128, 26 | align: "right", 27 | }); 28 | 29 | doc.text(lorem.shorter); 30 | }; 31 | -------------------------------------------------------------------------------- /test/pdfs/table/border-break-single.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.short, { fontSize: 20 }); 3 | 4 | const table = doc.table({ 5 | widths: [256, 256], 6 | padding: 0, 7 | borderWidth: 10, 8 | }); 9 | 10 | const row = table.row(); 11 | 12 | row.cell(lorem.short, { 13 | textAlign: "justify", 14 | fontSize: 20, 15 | padding: 10, 16 | backgroundColor: 0xdddddd, 17 | }); 18 | row.cell(lorem.shorter, { 19 | textAlign: "justify", 20 | fontSize: 20, 21 | padding: 10, 22 | backgroundColor: 0xeeeeee, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /test/pdfs/text/alignment-otf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, fixtures) { 2 | doc.text(fixtures.lorem.short + "\n\n", { 3 | font: fixtures.font.opensans.regular, 4 | textAlign: "left", 5 | }); 6 | doc.text(fixtures.lorem.short + "\n\n", { 7 | font: fixtures.font.opensans.regular, 8 | textAlign: "center", 9 | }); 10 | doc.text(fixtures.lorem.short + "\n\n", { 11 | font: fixtures.font.opensans.regular, 12 | textAlign: "right", 13 | }); 14 | doc.text(fixtures.lorem.short + "\n\n", { 15 | font: fixtures.font.opensans.regular, 16 | textAlign: "justify", 17 | }); 18 | }; 19 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/pagecount.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | const footer = doc.footer(); 3 | footer.pageNumber((curr, total) => `${curr} / ${total}`, { 4 | textAlign: "center", 5 | fontSize: 16, 6 | }); 7 | footer.text("after", { textAlign: "center" }); 8 | 9 | const header = doc.header(); 10 | header.text("before", { textAlign: "center" }); 11 | header.pageNumber((curr, total) => `${curr} / ${total}`, { 12 | textAlign: "center", 13 | fontSize: 16, 14 | }); 15 | 16 | doc.text("Hello World 1"); 17 | 18 | doc.pageBreak(); 19 | 20 | doc.text("Hello World 2"); 21 | }; 22 | -------------------------------------------------------------------------------- /test/pdfs/table/simple.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.shorter); 3 | 4 | const table = doc.table({ widths: [null, null, null] }); 5 | const row = table.row(); 6 | 7 | const cell1 = row.cell({ padding: 0, backgroundColor: 0xeeeeee }); 8 | cell1.text(lorem.short); 9 | 10 | const cell2 = row.cell({ padding: 0, backgroundColor: 0xbbbbbb }); 11 | for (let i = 0; i < 3; ++i) { 12 | cell2.text(lorem.short); 13 | } 14 | 15 | const cell3 = row.cell({ padding: 20, backgroundColor: 0xdddddd }); 16 | cell3.text(lorem.shorter); 17 | 18 | doc.text(lorem.shorter); 19 | }; 20 | -------------------------------------------------------------------------------- /types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "ES2017", 5 | "noEmit": true, 6 | "skipLibCheck": true, 7 | "strict": true /* Enable all strict type-checking options. */, 8 | "noUnusedLocals": true /* Report errors on unused locals. */, 9 | "noUnusedParameters": true /* Report errors on unused parameters. */, 10 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, 11 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ 12 | }, 13 | "files": ["pdfjs-tests.ts", "main.d.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-191-row-breaks.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.cell({ y: 140 }); 3 | 4 | const table = doc.table({ 5 | widths: [100, null], 6 | borderWidth: 1, 7 | borderColor: 0xf00f00, 8 | }); 9 | const row = table.row(); 10 | 11 | row.cell(`heading`, { 12 | padding: 12, 13 | }); 14 | 15 | const cell = row.cell(); 16 | 17 | cell.cell(`value - 1`, { 18 | padding: 12, 19 | }); 20 | 21 | cell.cell(`value - 2`, { 22 | padding: 10, 23 | }); 24 | 25 | cell.cell(`value - 3`, { 26 | padding: 10, 27 | }); 28 | 29 | cell.cell(`value - 4`, { 30 | padding: 10, 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /test/pdfs/headerfooter/both.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, image }) { 2 | // header 3 | const header = doc.header(); 4 | header.text("text"); 5 | 6 | let cell = header.cell({ padding: 20, backgroundColor: 0xdddddd }); 7 | cell.text("TESTING"); 8 | cell.image(image.pdf); 9 | 10 | // footer 11 | const footer = doc.footer(); 12 | footer.text("text"); 13 | 14 | cell = footer.cell({ padding: 20, backgroundColor: 0xdddddd }); 15 | cell.image(image.complexPdf); 16 | cell.text("TESTING"); 17 | 18 | // body 19 | 20 | doc.text("Hello"); 21 | 22 | doc.pageBreak(); 23 | 24 | doc.text(lorem.long, { fontSize: 20 }); 25 | }; 26 | -------------------------------------------------------------------------------- /lib/object/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.PDFArray = exports.Array = require("./array"); 4 | exports.PDFDictionary = exports.Dictionary = require("./dictionary"); 5 | exports.PDFName = exports.Name = require("./name"); 6 | exports.PDFNameTree = exports.NameTree = require("./nametree"); 7 | exports.PDFObject = exports.Object = require("./object"); 8 | exports.PDFReference = exports.Reference = require("./reference"); 9 | exports.PDFStream = exports.Stream = require("./stream"); 10 | exports.PDFString = exports.String = require("./string"); 11 | exports.PDFTrailer = exports.Trailer = require("./trailer"); 12 | exports.PDFXref = exports.Xref = require("./xref"); 13 | -------------------------------------------------------------------------------- /test/pdfs/table/header2.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text("Foo").br().br().br().br().br().br().br().br().br(); 3 | 4 | const table = doc.table({ 5 | widths: [null, null], 6 | borderWidth: 1, 7 | }); 8 | 9 | const header = table.header(); 10 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 11 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 12 | 13 | const row1 = table.row(); 14 | 15 | row1.cell("Cell 1", { fontSize: 11, padding: 10, backgroundColor: 0xdddddd }); 16 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 17 | 18 | doc.text("Bar"); 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/table/retrospective-uneven.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | for (let i = 0; i < 3; ++i) { 3 | doc.text(lorem.short, { fontSize: 20 }); 4 | } 5 | 6 | doc.text("---------------------"); 7 | 8 | const table = doc.table({ widths: [200, 200] }); 9 | 10 | // should be moved to the next page retrospectively 11 | const row = table.row(); 12 | row.cell(lorem.short, { 13 | backgroundColor: 0xeeeeee, 14 | padding: 10, 15 | fontSize: 20, 16 | }); 17 | row.cell("Uneven ...", { 18 | backgroundColor: 0xbbbbbb, 19 | padding: 10, 20 | fontSize: 20, 21 | }); 22 | 23 | doc.text(lorem.short, { fontSize: 20 }); 24 | }; 25 | -------------------------------------------------------------------------------- /test/pdfs/table/break-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ widths: [null, null] }); 3 | const row = table.row(); 4 | 5 | const cell1 = row.cell({ padding: 0, backgroundColor: 0xeeeeee }); 6 | cell1.text(lorem.short, { fontSize: 20 }); 7 | 8 | const cell2 = row.cell({ padding: 20, backgroundColor: 0xbbbbbb }); 9 | 10 | const inner = cell2.table({ widths: [null] }); 11 | const innerRow = inner.row(); 12 | const innerCell = innerRow.cell({ padding: 10, backgroundColor: 0xdddddd }); 13 | 14 | for (let i = 0; i < 2; ++i) { 15 | innerCell.text(lorem.short, { fontSize: 20 }); 16 | } 17 | 18 | doc.text(lorem.shorter, { fontSize: 20 }); 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/table/pagebreak1.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.shorter, { fontSize: 20 }); 3 | 4 | const table = doc.table({ widths: [null, null, null] }); 5 | const row = table.row(); 6 | 7 | const cell1 = row.cell({ padding: 0, backgroundColor: 0xeeeeee }); 8 | cell1.text(lorem.short, { fontSize: 20 }); 9 | 10 | const cell2 = row.cell({ padding: 20, backgroundColor: 0xbbbbbb }); 11 | for (let i = 0; i < 2; ++i) { 12 | cell2.text(lorem.short, { fontSize: 20 }); 13 | } 14 | 15 | const cell3 = row.cell({ padding: 10, backgroundColor: 0xdddddd }); 16 | cell3.text(lorem.shorter, { fontSize: 20 }); 17 | 18 | doc.text(lorem.shorter, { fontSize: 20 }); 19 | }; 20 | -------------------------------------------------------------------------------- /test/pdfs/table/header-pagebreak.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.header().text("PAGE HEADER"); 3 | 4 | doc.cell({ paddingTop: 750 }); 5 | 6 | const table = doc.table({ 7 | widths: [200, 200], 8 | borderWidth: 1, 9 | fontSize: 11, 10 | }); 11 | 12 | const header = table.header(); 13 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 14 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 15 | 16 | for (let i = 0; i < 1; ++i) { 17 | const r = table.row(); 18 | r.cell("Cell 1", { padding: 10, paddingBottom: 100 }); 19 | r.cell("Cell 2", { padding: 10, paddingBottom: 100 }); 20 | } 21 | 22 | doc.text("Foo"); 23 | }; 24 | -------------------------------------------------------------------------------- /test/pdfs/table/retrospective.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | for (let i = 0; i < 3; ++i) { 3 | doc.text(lorem.short, { fontSize: 20 }); 4 | } 5 | 6 | doc.text("---------------------"); 7 | 8 | const table = doc.table({ widths: [200, 200] }); 9 | 10 | // should be moved to the next page retrospectively 11 | const row = table.row(); 12 | row.cell(lorem.short, { 13 | backgroundColor: 0xeeeeee, 14 | padding: 10, 15 | fontSize: 20, 16 | }); 17 | row.cell(lorem.short, { 18 | backgroundColor: 0xbbbbbb, 19 | padding: 10, 20 | fontSize: 20, 21 | }); 22 | 23 | doc.text(lorem.short, { fontSize: 20 }); 24 | doc.text(lorem.short, { fontSize: 20 }); 25 | }; 26 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | JavaScript files inside the [`./pdfs`](pdfs) directory are picked up automatically and their result is compared to their *.pdf* counterparts. 2 | 3 | There will be false negatives. 4 | 5 | Ideas for a better PDF testing setup are welcome. 6 | 7 | Run specific tests: 8 | 9 | ```bash 10 | node --harmony-async-await test/index.js test/pdfs/text/*.js 11 | ``` 12 | 13 | Additional PDF validation could be done using [preflight](https://pdfbox.apache.org/download.cgi): 14 | 15 | ```bash 16 | java -classpath ./preflight-app-2.0.4.jar org.apache.pdfbox.preflight.Validator_A1b ./test.pdf 17 | ``` 18 | 19 | Use all results as future expectations: 20 | 21 | ```bash 22 | rename -f 's/\.result\.pdf$/\.pdf/' test/pdfs/**/*.pdf 23 | ``` -------------------------------------------------------------------------------- /test/pdfs/table/border-retrospective.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | for (let i = 0; i < 3; ++i) { 3 | doc.text(lorem.short, { fontSize: 20 }); 4 | } 5 | 6 | doc.text("---------------------"); 7 | 8 | const table = doc.table({ widths: [200, 200], borderWidth: 10 }); 9 | 10 | // should be moved to the next page retrospectively 11 | const row = table.row(); 12 | row.cell(lorem.short, { 13 | backgroundColor: 0xeeeeee, 14 | padding: 10, 15 | fontSize: 20, 16 | }); 17 | row.cell(lorem.short, { 18 | backgroundColor: 0xbbbbbb, 19 | padding: 10, 20 | fontSize: 20, 21 | }); 22 | 23 | doc.text(lorem.short, { fontSize: 20 }); 24 | doc.text(lorem.short, { fontSize: 20 }); 25 | }; 26 | -------------------------------------------------------------------------------- /docs/table.md: -------------------------------------------------------------------------------- 1 | ## Table 2 | 3 | ### .header([opts]) 4 | 5 | Add a table header. Returns a [Row Object](row.md). 6 | 7 | **Example:** 8 | 9 | ```js 10 | const table = doc.table({ 11 | widths: [200, 200], 12 | borderWidth: 1, 13 | }) 14 | 15 | const header = table.header() 16 | header.cell('Header Left') 17 | header.cell('Header Right') 18 | ``` 19 | 20 | ### .row([opts]) 21 | 22 | Starts a table row. Returns a [Row Object](row.md). 23 | 24 | **Options:** 25 | 26 | - **minHeight** (default: 0) - the minimum height of the row 27 | 28 | **Example:** 29 | 30 | ```js 31 | const table = doc.table({ 32 | widths: [200, 200], 33 | borderWidth: 1, 34 | }) 35 | 36 | const row = table.row() 37 | row.cell('Cell Left') 38 | row.cell('Cell Right') 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/row.md: -------------------------------------------------------------------------------- 1 | ## Row 2 | 3 | ### .cell([text], [opts]) 4 | 5 | Add a cell to the row. Returns a [Fragment object](fragment.md). 6 | 7 | **Arguments:** 8 | 9 | - **text** - a string that should be rendered into the cell 10 | - **opts** - styling options 11 | 12 | **Options:** 13 | 14 | - **colspan** (default: 1) - how many columns the cell should span 15 | - **padding**, **paddingTop**, **paddingRight**, **paddingBottom**, **paddingLeft** (default: 0) - the cell padding 16 | - **backgroundColor** (default: none) - the background color the cell 17 | - **minHeight** (default: 0) - the minimum height of the cell 18 | 19 | **Example:** 20 | 21 | ```js 22 | const table = doc.table({ 23 | widths: [200, 200] 24 | }) 25 | 26 | const row = table.row() 27 | row.cell('Cell Left') 28 | row.cell('Cell Right') 29 | ``` 30 | -------------------------------------------------------------------------------- /lib/object/value.js: -------------------------------------------------------------------------------- 1 | const Objects = []; 2 | 3 | exports.parse = function (xref, lexer) { 4 | // lazy load, cause circular referecnes 5 | if (!Objects.length) { 6 | Objects.push.apply(Objects, [ 7 | require("./boolean"), 8 | require("./null"), 9 | require("./name"), 10 | require("./dictionary"), // must be tried before string! 11 | require("./string"), 12 | require("./array"), 13 | require("./reference"), // must be tried before number! 14 | require("./number"), 15 | ]); 16 | } 17 | 18 | // try 19 | for (let i = 0; i < Objects.length; ++i) { 20 | const value = Objects[i].parse(xref, lexer, true); 21 | if (value !== undefined) { 22 | return value; 23 | } 24 | } 25 | 26 | lexer._error("Invalid value"); 27 | return undefined; 28 | }; 29 | -------------------------------------------------------------------------------- /test/pdfs/table/border-horizontal.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | { 3 | const table = doc.table({ 4 | widths: [256, 256], 5 | borderHorizontalWidth: 10, 6 | }); 7 | 8 | for (let i = 0; i < 3; ++i) { 9 | const row = table.row(); 10 | 11 | row.cell("Left " + i, { fontSize: 20, padding: 10 }); 12 | row.cell("Right " + i, { fontSize: 20, padding: 10 }); 13 | } 14 | } 15 | 16 | doc.text("–––––"); 17 | 18 | { 19 | const table = doc.table({ 20 | widths: [256, 256], 21 | borderHorizontalWidths: (i) => (i + 1) * 5, 22 | }); 23 | 24 | for (let i = 0; i < 3; ++i) { 25 | const row = table.row(); 26 | 27 | row.cell("Left " + i, { fontSize: 20, padding: 10 }); 28 | row.cell("Right " + i, { fontSize: 20, padding: 10 }); 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lib/image/image.js: -------------------------------------------------------------------------------- 1 | const util = require("../util"); 2 | const PDFImage = require("./pdf"); 3 | const JPEGImage = require("./jpeg"); 4 | 5 | module.exports = class Image { 6 | constructor(b) { 7 | const src = util.toArrayBuffer(b); 8 | 9 | switch (determineType(src)) { 10 | case "pdf": 11 | return new PDFImage(src); 12 | case "jpeg": 13 | return new JPEGImage(src); 14 | default: 15 | throw new TypeError("Unsupported image type"); 16 | } 17 | } 18 | }; 19 | 20 | function determineType(buffer) { 21 | const pdf = String.fromCharCode.apply(null, new Uint8Array(buffer, 0, 5)); 22 | if (pdf === "%PDF-") { 23 | return "pdf"; 24 | } 25 | 26 | const view = new DataView(buffer); 27 | if (view.getUint8(0) === 0xff || view.getUint8(1) === 0xd8) { 28 | return "jpeg"; 29 | } 30 | 31 | return null; 32 | } 33 | -------------------------------------------------------------------------------- /test/pdfs/issue/issue-159-br.js: -------------------------------------------------------------------------------- 1 | const Helvetica = require("../../../font/Helvetica.js"); 2 | const HelveticaBold = require("../../../font/Helvetica-Bold.js"); 3 | 4 | const cm = 0.0393700787 * 72 * 10; 5 | 6 | module.exports = function (doc, { image }) { 7 | const table = doc.table({ 8 | widths: [null, null], 9 | borderHorizontalWidths: function (i) { 10 | return i < 2 ? 1 : 0.1; 11 | }, 12 | padding: 5, 13 | }); 14 | 15 | function addRow(index) { 16 | const tr = table.row(); 17 | tr.cell(index.toString()); 18 | 19 | tr.cell() 20 | .text() 21 | .add("A:") 22 | .br() 23 | .add("B:") 24 | .br() 25 | .add("C:") 26 | .br() 27 | .add("D:") 28 | .br() 29 | .add("E:") 30 | .br() 31 | .add("F:"); 32 | } 33 | 34 | for (let i = 1; i < 11; ++i) { 35 | addRow(i); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /test/pdfs/table/header3.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.long); 3 | 4 | const table = doc.table({ 5 | widths: [200, 200], 6 | borderWidth: 1, 7 | }); 8 | 9 | const header = table.header(); 10 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 11 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 12 | 13 | const row1 = table.row(); 14 | 15 | row1.cell(lorem.short, { 16 | fontSize: 11, 17 | padding: 10, 18 | backgroundColor: 0xdddddd, 19 | }); 20 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 21 | 22 | const row2 = table.row(); 23 | 24 | row2.cell(lorem.short, { 25 | fontSize: 16, 26 | padding: 10, 27 | backgroundColor: 0xdddddd, 28 | }); 29 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 30 | 31 | doc.text("Foo"); 32 | }; 33 | -------------------------------------------------------------------------------- /test/pdfs/table/header4.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.text(lorem.long); 3 | 4 | const table = doc.table({ 5 | widths: [200, 200], 6 | borderWidth: 1, 7 | }); 8 | 9 | const header = table.header(); 10 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 11 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 12 | 13 | const row1 = table.row(); 14 | 15 | row1.cell(lorem.short, { 16 | fontSize: 15, 17 | padding: 10, 18 | backgroundColor: 0xdddddd, 19 | }); 20 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 21 | 22 | const row2 = table.row(); 23 | 24 | row2.cell(lorem.short, { 25 | fontSize: 15, 26 | padding: 10, 27 | backgroundColor: 0xdddddd, 28 | }); 29 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 30 | 31 | doc.text("Foo"); 32 | }; 33 | -------------------------------------------------------------------------------- /lib/font/base.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Font { 4 | static isFont(font) { 5 | return ( 6 | font && 7 | (font instanceof Font || 8 | (typeof font === "object" && 9 | typeof font.encode === "function" && 10 | typeof font.stringWidth === "function" && 11 | typeof font.lineHeight === "function" && 12 | typeof font.ascent === "function" && 13 | typeof font.descent === "function" && 14 | typeof font.underlinePosition === "function" && 15 | typeof font.underlineThickness === "function" && 16 | typeof font.write === "function")) 17 | ); 18 | } 19 | } 20 | 21 | class StringWidth { 22 | constructor(width, kerning) { 23 | this.width = width; 24 | this.kerning = kerning; 25 | } 26 | 27 | valueOf() { 28 | return this.width; 29 | } 30 | } 31 | 32 | Font.StringWidth = StringWidth; 33 | module.exports = Font; 34 | -------------------------------------------------------------------------------- /font/afm/MustRead.html: -------------------------------------------------------------------------------- 1 | Core 14 AFM Files - ReadMe or
This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files. Col
-------------------------------------------------------------------------------- /docs/header.md: -------------------------------------------------------------------------------- 1 | ## Header / Footer 2 | 3 | Includes all methods of a [Fragment](fragment.md) 4 | 5 | ### .pageNumber([fn], [opts]) 6 | 7 | Add page numbers. 8 | 9 | **Arguments:** 10 | 11 | - **fn** - a function that is used to format the page numbers 12 | - **opts** - options 13 | 14 | **Options:** 15 | 16 | - **font** - expects an `pdf.Font` object being either a AFM font or a OTF font 17 | - **fontSize** (default: 11) - the font size 18 | - **color** (default: black) - the font color as hex number (e.g. 0x000000) 19 | - **lineHeight** (default: 1.15) - the line height 20 | - **textAlign** / **alignment** (default: 'left') - the text alignment (possible options: left, right, center, justify) 21 | 22 | **Examples:** 23 | 24 | ```js 25 | const footer = doc.footer() 26 | footer.pageNumber({ textAlign: 'center' }) 27 | ``` 28 | 29 | ```js 30 | const header = doc.header() 31 | header.pageNumber((curr, total) => `${curr} / ${total}`) 32 | ``` 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![pdfjs](https://cdn.rawgit.com/rkusa/pdfjs/2.x/logo.svg) 2 | 3 | A Portable Document Format (PDF) generation library targeting both the server- and client-side. 4 | 5 | [![NPM][npm]](https://npmjs.org/package/pdfjs) 6 | 7 | [Documentation](docs) | [Changelog](https://github.com/rkusa/pdfjs/blob/master/CHANGELOG.md) | [Playground](https://stackblitz.com/edit/js-hkxfhq?file=index.js) | [Examples](https://github.com/rkusa/pdfjs/tree/master/test/pdfs) 8 | 9 | ```bash 10 | npm install pdfjs 11 | ``` 12 | 13 | ## Maintenance Status 14 | 15 | On life-support. No new features planned. Only gets fixes if time allows. 16 | I recommend that you use something else. 17 | 18 | ## MIT License 19 | 20 | [MIT](LICENSE) 21 | 22 | [npm]: https://img.shields.io/npm/v/pdfjs.svg?style=flat-square 23 | [deps]: https://img.shields.io/david/rkusa/pdfjs.svg?style=flat-square 24 | [travis]: https://img.shields.io/travis/rkusa/pdfjs/master.svg?style=flat-square 25 | -------------------------------------------------------------------------------- /lib/object/array.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFValue = require("./value"); 4 | 5 | class PDFArray { 6 | constructor(array) { 7 | if (!array) { 8 | array = []; 9 | } 10 | 11 | array.toString = function () { 12 | return "[" + this.map((item) => String(item)).join(" ") + "]"; 13 | }; 14 | 15 | return array; 16 | } 17 | 18 | static parse(xref, lexer, trial) { 19 | if (lexer.getString(1) !== "[") { 20 | if (trial) { 21 | return undefined; 22 | } 23 | 24 | throw new Error("Invalid array"); 25 | } 26 | 27 | lexer.shift(1); 28 | lexer.skipWhitespace(null, true); 29 | 30 | const values = []; 31 | 32 | while (lexer.getString(1) !== "]") { 33 | values.push(PDFValue.parse(xref, lexer)); 34 | lexer.skipWhitespace(null, true); 35 | } 36 | 37 | lexer.shift(1); 38 | 39 | return new PDFArray(values); 40 | } 41 | } 42 | 43 | module.exports = PDFArray; 44 | -------------------------------------------------------------------------------- /test/pdfs/table/header5.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.header().text("HAS HEADER"); 3 | 4 | doc.text(lorem.long); 5 | 6 | const table = doc.table({ 7 | widths: [200, 200], 8 | borderWidth: 1, 9 | }); 10 | 11 | const header = table.header(); 12 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 13 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 14 | 15 | const row1 = table.row(); 16 | 17 | row1.cell(lorem.short, { 18 | fontSize: 15, 19 | padding: 10, 20 | backgroundColor: 0xdddddd, 21 | }); 22 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 23 | 24 | const row2 = table.row(); 25 | 26 | row2.cell(lorem.short, { 27 | fontSize: 15, 28 | padding: 10, 29 | backgroundColor: 0xdddddd, 30 | }); 31 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 32 | 33 | doc.text("Foo"); 34 | }; 35 | -------------------------------------------------------------------------------- /test/pdfs/table/break-pageheader.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.header().text("HAS HEADER"); 3 | 4 | doc.text(lorem.long); 5 | 6 | const table = doc.table({ 7 | widths: [200, 200], 8 | borderWidth: 1, 9 | }); 10 | 11 | const header = table.header(); 12 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 13 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 14 | 15 | const row1 = table.row(); 16 | 17 | row1.cell(lorem.short, { 18 | fontSize: 14, 19 | padding: 10, 20 | backgroundColor: 0xdddddd, 21 | }); 22 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 23 | 24 | const row2 = table.row(); 25 | 26 | row2.cell(lorem.short, { 27 | fontSize: 14, 28 | padding: 10, 29 | backgroundColor: 0xdddddd, 30 | }); 31 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 32 | 33 | doc.text("Foo"); 34 | }; 35 | -------------------------------------------------------------------------------- /test/pdfs/table/simple-multiple-headers.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [null, null, null], 4 | borderWidth: 1, 5 | }); 6 | const header_props = { 7 | textAlign: "center", 8 | paddingTop: 20, 9 | paddingBottom: 20, 10 | paddingLeft: 5, 11 | paddingRight: 5, 12 | }; 13 | for (let i = 0; i < 3; ++i) { 14 | const header = table.header(); 15 | header.cell("Header Left " + i, header_props); 16 | header.cell("Header Center " + i, header_props); 17 | header.cell("Header Right " + i, header_props); 18 | } 19 | 20 | const row_props = { fontSize: 11, padding: 30 }; 21 | for (let i = 0; i < 25; ++i) { 22 | const row = table.row(); 23 | row.cell("Cell Left " + i, { ...row_props, backgroundColor: 0xcccccc }); 24 | row.cell("Cell Center " + i, { ...row_props, backgroundColor: 0xdddddd }); 25 | row.cell("Cell Right " + i, { ...row_props, backgroundColor: 0xeeeeee }); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /test/pdfs/table/border-vertical.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | { 3 | const table = doc.table({ 4 | widths: [256, 256], 5 | borderWidth: 1, 6 | borderVerticalWidth: 10, 7 | }); 8 | 9 | const row = table.row(); 10 | 11 | row.cell(lorem.shorter, { 12 | textAlign: "justify", 13 | fontSize: 20, 14 | padding: 10, 15 | }); 16 | row.cell(lorem.shorter, { 17 | textAlign: "justify", 18 | fontSize: 20, 19 | padding: 10, 20 | }); 21 | } 22 | 23 | doc.text("----"); 24 | 25 | { 26 | const table = doc.table({ 27 | widths: [256, 256], 28 | borderVerticalWidths: [5, 15, 20], 29 | }); 30 | 31 | const row = table.row(); 32 | 33 | row.cell(lorem.shorter, { 34 | textAlign: "justify", 35 | fontSize: 20, 36 | padding: 10, 37 | }); 38 | row.cell(lorem.shorter, { 39 | textAlign: "justify", 40 | fontSize: 20, 41 | padding: 10, 42 | }); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /test/pdfs/table/break-pagefooter.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.header().text("HAS HEADER"); 3 | doc.footer().text("HAS FOOTER"); 4 | 5 | doc.text(lorem.long); 6 | 7 | const table = doc.table({ 8 | widths: [200, 200], 9 | borderWidth: 1, 10 | }); 11 | 12 | const header = table.header(); 13 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 14 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 15 | 16 | const row1 = table.row(); 17 | 18 | row1.cell(lorem.short, { 19 | fontSize: 13, 20 | padding: 10, 21 | backgroundColor: 0xdddddd, 22 | }); 23 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 24 | 25 | const row2 = table.row(); 26 | 27 | row2.cell(lorem.short, { 28 | fontSize: 13, 29 | padding: 10, 30 | backgroundColor: 0xdddddd, 31 | }); 32 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 33 | 34 | doc.text("Foo"); 35 | }; 36 | -------------------------------------------------------------------------------- /test/pdfs/table/retrospective-pagefooter.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | doc.header().text("HAS HEADER"); 3 | doc.footer().text("HAS FOOTER"); 4 | 5 | doc.text(lorem.long); 6 | 7 | const table = doc.table({ 8 | widths: [200, 200], 9 | borderWidth: 1, 10 | }); 11 | 12 | const header = table.header(); 13 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 14 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 15 | 16 | const row1 = table.row(); 17 | 18 | row1.cell(lorem.short, { 19 | fontSize: 14, 20 | padding: 10, 21 | backgroundColor: 0xdddddd, 22 | }); 23 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 24 | 25 | const row2 = table.row(); 26 | 27 | row2.cell(lorem.short, { 28 | fontSize: 14, 29 | padding: 10, 30 | backgroundColor: 0xdddddd, 31 | }); 32 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 33 | 34 | doc.text("Foo"); 35 | }; 36 | -------------------------------------------------------------------------------- /lib/object/trailer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFDictionary = require("./dictionary"); 4 | const PDFArray = require("./array"); 5 | const PDFString = require("./string"); 6 | 7 | class PDFTrailer extends PDFDictionary { 8 | constructor(size, root, id, infoObj) { 9 | super(); 10 | 11 | this.set("Size", size); 12 | this.set("Root", root && root.toReference()); 13 | 14 | const encodedId = new PDFString(id).toHexString(); 15 | this.set("ID", new PDFArray([encodedId, encodedId])); 16 | this.set("Info", infoObj.toReference()); 17 | } 18 | 19 | toString() { 20 | return "trailer\n" + PDFDictionary.prototype.toString.call(this); 21 | } 22 | 23 | static parse(xref, lexer) { 24 | lexer.skipWhitespace(null, true); 25 | 26 | if (lexer.readString(7) !== "trailer") { 27 | throw new Error("Invalid trailer: trailer expected but not found"); 28 | } 29 | 30 | lexer.skipWhitespace(null, true); 31 | 32 | const dict = PDFDictionary.parse(xref, lexer); 33 | return dict; 34 | } 35 | } 36 | 37 | module.exports = PDFTrailer; 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2013-2017 Markus Ast 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /test/pdfs/table/min-height.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | { 3 | const table = doc.table({ widths: [null, null, null], borderWidth: 1 }); 4 | const row = table.row(); 5 | 6 | row.cell("First", { backgroundColor: 0xeeeeee }); 7 | row.cell("Second", { backgroundColor: 0xdddddd, minHeight: 60 }); 8 | row.cell("Third", { backgroundColor: 0xcccccc, minHeight: 100 }); 9 | } 10 | 11 | // row minHeight 12 | { 13 | const table = doc.table({ widths: [null, null], borderWidth: 1 }); 14 | const row = table.row({ minHeight: 60 }); 15 | 16 | row.cell("First", { backgroundColor: 0xeeeeee }); 17 | row.cell("Second", { backgroundColor: 0xdddddd }); 18 | } 19 | 20 | doc.text("Done"); 21 | 22 | // page break 23 | { 24 | const table = doc.table({ widths: [null, null], borderWidth: 1 }); 25 | const row = table.row(); 26 | 27 | row.cell("First", { backgroundColor: 0xeeeeee }); 28 | row.cell("Second", { 29 | backgroundColor: 0xdddddd, 30 | minHeight: doc._cursor.startY - doc._cursor.bottom, 31 | }); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /test/pdfs/external/templatefirstpageonly.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { document, lorem }) { 2 | doc.setTemplate(document.test, true); 3 | 4 | doc.text( 5 | "1st page after setTemplate(document.test, true): TEMPLATE VISIBLE", 6 | { fontSize: 18 }, 7 | ); 8 | 9 | doc.pageBreak(); 10 | 11 | doc.text("2nd page after setTemplate(document.test, true): NO TEMPLATE", { 12 | fontSize: 18, 13 | }); 14 | 15 | doc.setTemplate(document.test, true); 16 | 17 | doc.text( 18 | "1st page after setTemplate(document.test, true): TEMPLATE VISIBLE AGAIN", 19 | { fontSize: 18 }, 20 | ); 21 | 22 | doc.pageBreak(); 23 | 24 | doc.text("2nd page after setTemplate(document.test, true): NO TEMPLATE", { 25 | fontSize: 18, 26 | }); 27 | 28 | doc.setTemplate(document.test, false); 29 | 30 | doc.text( 31 | "1st page after setTemplate(document.test, false): TEMPLATE VISIBLE", 32 | { fontSize: 18 }, 33 | ); 34 | 35 | doc.pageBreak(); 36 | 37 | doc.text( 38 | "2nd page after setTemplate(document.test, false): TEMPLATE VISIBLE", 39 | { fontSize: 18 }, 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /test/pdfs/table/header.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [200, 200], 4 | borderWidth: 1, 5 | }); 6 | 7 | const header = table.header(); 8 | header.cell("Header Left", { textAlign: "center", padding: 30 }); 9 | header.cell("Header Right", { textAlign: "center", padding: 30 }); 10 | 11 | const row1 = table.row(); 12 | 13 | row1.cell(lorem.long, { 14 | fontSize: 11, 15 | padding: 10, 16 | backgroundColor: 0xdddddd, 17 | }); 18 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 19 | 20 | const row2 = table.row(); 21 | 22 | row2.cell(lorem.long, { 23 | fontSize: 16, 24 | padding: 10, 25 | backgroundColor: 0xdddddd, 26 | }); 27 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 28 | 29 | const row3 = table.row(); 30 | 31 | row3.cell("Cell 1", { fontSize: 16, padding: 10, backgroundColor: 0xdddddd }); 32 | row3.cell(lorem.short, { 33 | fontSize: 11, 34 | padding: 10, 35 | backgroundColor: 0xeeeeee, 36 | }); 37 | 38 | doc.text("Foo"); 39 | }; 40 | -------------------------------------------------------------------------------- /test/pdfs/table/border-colorful.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | { 3 | const table = doc.table({ 4 | widths: [256, 256], 5 | borderHorizontalWidth: 10, 6 | borderHorizontalColor: 0xe74c3c, 7 | borderVerticalWidth: 10, 8 | borderVerticalColor: 0x2980b9, 9 | }); 10 | 11 | for (let i = 0; i < 3; ++i) { 12 | const row = table.row(); 13 | 14 | row.cell("Left " + i, { fontSize: 20, padding: 10 }); 15 | row.cell("Right " + i, { fontSize: 20, padding: 10 }); 16 | } 17 | } 18 | 19 | doc.text("–––––"); 20 | 21 | { 22 | const colors = [0xe74c3c, 0x2980b9, 0x27ae60, 0xf1c40f]; 23 | const table = doc.table({ 24 | widths: [256, 256], 25 | borderHorizontalWidth: 10, 26 | borderHorizontalColors: (i) => colors[i], 27 | borderVerticalWidth: 10, 28 | borderVerticalColors: [0xe74c3c, 0x2980b9, 0x27ae60], 29 | }); 30 | 31 | for (let i = 0; i < 3; ++i) { 32 | const row = table.row(); 33 | 34 | row.cell("Left " + i, { fontSize: 20, padding: 10 }); 35 | row.cell("Right " + i, { fontSize: 20, padding: 10 }); 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /test/pdfs/cell/min-height.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc) { 2 | doc.cell("First", { 3 | fontSize: 15, 4 | width: 100, 5 | padding: 10, 6 | borderWidth: 1, 7 | backgroundColor: 0xeeeeee, 8 | y: doc._cursor.startY, 9 | minHeight: 60, 10 | }); 11 | 12 | doc.cell("Second", { 13 | fontSize: 15, 14 | width: 100, 15 | padding: 0, 16 | borderWidth: 1, 17 | backgroundColor: 0xbbbbbb, 18 | x: 120, 19 | y: doc._cursor.startY, 20 | minHeight: 60, 21 | }); 22 | 23 | doc.cell("Toooooooooooo loooooong for minHeight", { 24 | fontSize: 15, 25 | width: 100, 26 | padding: 10, 27 | borderWidth: 1, 28 | backgroundColor: 0xdddddd, 29 | x: 230, 30 | y: doc._cursor.startY, 31 | minHeight: 60, 32 | }); 33 | 34 | doc.text("after"); 35 | 36 | // minHeight pageBreak 37 | doc.cell("Next page", { 38 | fontSize: 15, 39 | padding: 10, 40 | borderWidth: 1, 41 | minHeight: doc._cursor.startY - doc._cursor.bottom, 42 | }); 43 | 44 | // minHeight > document height 45 | doc.cell("Next page", { 46 | fontSize: 15, 47 | padding: 10, 48 | borderWidth: 1, 49 | minHeight: 900, 50 | }); 51 | }; 52 | -------------------------------------------------------------------------------- /test/pdfs/text/kerning.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, font }) { 2 | doc.text("AVA"); 3 | doc.text("A").append("VA"); 4 | 5 | // alignment 6 | doc.text("AVA\n_ V _", { textAlign: "center" }); 7 | doc.text("AVA\n_ A", { textAlign: "right" }); 8 | doc.text("AVA " + lorem.short, { textAlign: "justify" }); 9 | 10 | // break long 11 | doc.text( 12 | "AVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV", 13 | ); 14 | 15 | // OTF font 16 | 17 | doc.text("AVA", { font: font.opensans.regular }); 18 | doc 19 | .text("A", { font: font.opensans.regular }) 20 | .append("VA", { font: font.opensans.regular }); 21 | 22 | // alignment 23 | doc.text("AVA\n_ V _", { textAlign: "center", font: font.opensans.regular }); 24 | doc.text("AVA\n_ A", { textAlign: "right", font: font.opensans.regular }); 25 | doc.text("AVA " + lorem.short, { 26 | textAlign: "justify", 27 | font: font.opensans.regular, 28 | }); 29 | 30 | // break long 31 | doc.text( 32 | "AVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV", 33 | { font: font.opensans.regular }, 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /test/pdfs/table/header-border.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | // borders should be on the odd horizontal lines 3 | const odd_borders = doc.table({ 4 | widths: [null, null], 5 | borderHorizontalWidths: (i) => (i % 2) * 5, 6 | borderColor: 0x00ff00, 7 | }); 8 | for (let i = 0; i < 3; ++i) { 9 | const header = odd_borders.header(); 10 | header.cell("Odd Left " + i, { 11 | textAlign: "center", 12 | padding: 30, 13 | backgroundColor: 0xff0000, 14 | }); 15 | header.cell("Odd Right " + i, { 16 | textAlign: "center", 17 | padding: 30, 18 | backgroundColor: 0xff0000, 19 | }); 20 | } 21 | 22 | // borders should be on the odd horizontal lines 23 | const even_borders = doc.table({ 24 | widths: [null, null], 25 | borderHorizontalWidths: (i) => ((i + 1) % 2) * 5, 26 | borderColor: 0x0000ff, 27 | }); 28 | for (let i = 0; i < 3; ++i) { 29 | const header = even_borders.header(); 30 | header.cell("Even Left " + i, { 31 | textAlign: "center", 32 | padding: 30, 33 | backgroundColor: 0xff0000, 34 | }); 35 | header.cell("Even Right " + i, { 36 | textAlign: "center", 37 | padding: 30, 38 | backgroundColor: 0xff0000, 39 | }); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /font/Courier.json: -------------------------------------------------------------------------------- 1 | {"fontName":"Courier","fullName":"Courier","familyName":"Courier","italicAngle":0,"characterSet":"ExtendedRoman","fontBBox":[-23,-250,715,805],"underlinePosition":-100,"underlineThickness":50,"capHeight":562,"xHeight":426,"ascender":629,"descender":-157,"kerning":{},"widths":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,null,600,null,600,600,600,600,600,600,600,600,600,600,600,null,600,null,null,600,600,600,600,600,600,600,600,600,600,600,600,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pdfjs", 3 | "author": "Markus Ast ", 4 | "version": "2.5.4", 5 | "description": "A Portable Document Format (PDF) generation library targeting both the server- and client-side.", 6 | "keywords": [ 7 | "pdf", 8 | "generator" 9 | ], 10 | "license": "MIT", 11 | "homepage": "https://github.com/rkusa/pdfjs", 12 | "bugs": "https://github.com/rkusa/pdfjs/issues", 13 | "main": "lib/index.js", 14 | "scripts": { 15 | "fmt": "prettier --write './{lib,test}/**/*.js'", 16 | "test": "npm run test:pdfs && npm run test:types", 17 | "test:pdfs": "node test/index.js", 18 | "test:types": "tsc --project ./types" 19 | }, 20 | "dependencies": { 21 | "@rkusa/linebreak": "^1.0.0", 22 | "opentype.js": "^1.3.3", 23 | "pako": "^2.0.3", 24 | "readable-stream": "^3.6.0", 25 | "unorm": "^1.6.0", 26 | "uuid": "^8.3.1" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "^15.12.4", 30 | "buffer": "^6.0.3", 31 | "events": "^3.3.0", 32 | "prettier": "^3.0.0", 33 | "tape": "^5.0.1", 34 | "typescript": "^4.0.3" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "git://github.com/rkusa/pdfjs.git" 39 | }, 40 | "engines": { 41 | "node": ">=7" 42 | }, 43 | "types": "./types/main.d.ts" 44 | } 45 | -------------------------------------------------------------------------------- /font/Courier-Bold.json: -------------------------------------------------------------------------------- 1 | {"fontName":"Courier-Bold","fullName":"Courier Bold","familyName":"Courier","italicAngle":0,"characterSet":"ExtendedRoman","fontBBox":[-113,-250,749,801],"underlinePosition":-100,"underlineThickness":50,"capHeight":562,"xHeight":439,"ascender":629,"descender":-157,"kerning":{},"widths":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,null,600,null,600,600,600,600,600,600,600,600,600,600,600,null,600,null,null,600,600,600,600,600,600,600,600,600,600,600,600,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} -------------------------------------------------------------------------------- /font/Courier-Oblique.json: -------------------------------------------------------------------------------- 1 | {"fontName":"Courier-Oblique","fullName":"Courier Oblique","familyName":"Courier","italicAngle":-12,"characterSet":"ExtendedRoman","fontBBox":[-27,-250,849,805],"underlinePosition":-100,"underlineThickness":50,"capHeight":562,"xHeight":426,"ascender":629,"descender":-157,"kerning":{},"widths":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,null,600,null,600,600,600,600,600,600,600,600,600,600,600,null,600,null,null,600,600,600,600,600,600,600,600,600,600,600,600,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} -------------------------------------------------------------------------------- /font/Courier-BoldOblique.json: -------------------------------------------------------------------------------- 1 | {"fontName":"Courier-BoldOblique","fullName":"Courier Bold Oblique","familyName":"Courier","italicAngle":-12,"characterSet":"ExtendedRoman","fontBBox":[-57,-250,869,801],"underlinePosition":-100,"underlineThickness":50,"capHeight":562,"xHeight":439,"ascender":629,"descender":-157,"kerning":{},"widths":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,null,600,null,600,600,600,600,600,600,600,600,600,600,600,null,600,null,null,600,600,600,600,600,600,600,600,600,600,600,600,null,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} -------------------------------------------------------------------------------- /lib/object/nametree.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFName = require("./name"); 4 | const PDFString = require("./string"); 5 | const PDFDictionary = require("./dictionary"); 6 | const PDFArray = require("./array"); 7 | 8 | class PDFNameTree extends PDFDictionary { 9 | constructor(dictionary) { 10 | super(dictionary); 11 | } 12 | 13 | add(key, val) { 14 | if (typeof val === "string") { 15 | val = new PDFName(val); 16 | } 17 | this.dictionary[key] = val; 18 | } 19 | 20 | has(key) { 21 | return String(key) in this.dictionary; 22 | } 23 | 24 | get(key) { 25 | return this.dictionary[key]; 26 | } 27 | 28 | del(key) { 29 | delete this.dictionary[key]; 30 | } 31 | 32 | toString() { 33 | const sortedKeys = Object.keys(this.dictionary); 34 | sortedKeys.sort((lhs, rhs) => lhs.localeCompare(rhs)); 35 | 36 | const names = []; 37 | for (const key of sortedKeys) { 38 | names.push(new PDFString(key), this.dictionary[key]); 39 | } 40 | 41 | const dict = new PDFDictionary(); 42 | dict.set("Names", new PDFArray(names)); 43 | dict.set( 44 | "Limits", 45 | new PDFArray([ 46 | new PDFString(sortedKeys[0]), 47 | new PDFString(sortedKeys[sortedKeys.length - 1]), 48 | ]), 49 | ); 50 | return dict.toString(); 51 | } 52 | } 53 | 54 | module.exports = PDFNameTree; 55 | -------------------------------------------------------------------------------- /lib/object/stream.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // page 60 4 | // Filters: page 65 5 | 6 | const PDFObject = require("./object"); 7 | 8 | module.exports = class PDFStream { 9 | constructor(object) { 10 | if (!object) { 11 | object = new PDFObject(); 12 | } 13 | 14 | object.content = this; 15 | this.object = object; 16 | this.content = ""; 17 | } 18 | 19 | // slice(begin, end) { 20 | // this.content = this.content.slice(begin, end) 21 | // this.object.prop('Length', this.content.length - 1) 22 | // } 23 | 24 | writeLine(str) { 25 | this.content += str + "\n"; 26 | this.object.prop("Length", this.content.length - 1); 27 | } 28 | 29 | toReference() { 30 | return this.object.toReference(); 31 | } 32 | 33 | toString() { 34 | let content = this.content; 35 | if (content instanceof Uint8Array) { 36 | content = uint8ToString(content) + "\n"; 37 | } 38 | 39 | return "stream\n" + content + "endstream"; 40 | } 41 | }; 42 | 43 | // source: http://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326 44 | function uint8ToString(u8a) { 45 | const CHUNK_SZ = 0x8000; 46 | const c = []; 47 | for (let i = 0; i < u8a.length; i += CHUNK_SZ) { 48 | c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ))); 49 | } 50 | return c.join(""); 51 | } 52 | -------------------------------------------------------------------------------- /font/Symbol.json: -------------------------------------------------------------------------------- 1 | {"fontName":"Symbol","fullName":"Symbol","familyName":"Symbol","italicAngle":0,"characterSet":"Special","fontBBox":[-180,-293,1090,1010],"underlinePosition":-100,"underlineThickness":50,"kerning":{},"widths":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,250,333,null,500,null,833,778,null,333,333,null,549,250,null,250,278,500,500,500,500,500,500,500,500,500,500,278,278,549,549,549,444,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,333,null,333,null,500,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,480,200,480,null,null,750,null,null,500,null,1000,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,460,null,null,null,null,null,null,null,null,null,null,250,null,null,null,null,null,null,null,null,null,null,null,713,null,null,null,400,549,null,null,null,576,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,549,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,549,null,null,null,null,null,null,null,null]} -------------------------------------------------------------------------------- /test/pdfs/table/multiple-headers-only.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [null, null], 4 | borderWidth: 1, 5 | }); 6 | 7 | const header1 = table.header(); 8 | header1.cell("Header Left1", { textAlign: "center", padding: 30 }); 9 | header1.cell("Header Right1", { textAlign: "center", padding: 30 }); 10 | 11 | const header2 = table.header(); 12 | header2.cell("Header Left2", { textAlign: "center", padding: 30 }); 13 | header2.cell("Header Right2", { textAlign: "center", padding: 30 }); 14 | 15 | const header3 = table.header(); 16 | header3.cell("Header Left3", { textAlign: "center", padding: 30 }); 17 | header3.cell("Header Right3", { textAlign: "center", padding: 30 }); 18 | 19 | const borderless_table = doc.table({ 20 | widths: [null, null], 21 | borderWidth: 0, 22 | }); 23 | 24 | const header4 = borderless_table.header(); 25 | header4.cell("Header Left1", { textAlign: "center", padding: 30 }); 26 | header4.cell("Header Right1", { textAlign: "center", padding: 30 }); 27 | 28 | const header5 = borderless_table.header(); 29 | header5.cell("Header Left2", { textAlign: "center", padding: 30 }); 30 | header5.cell("Header Right2", { textAlign: "center", padding: 30 }); 31 | 32 | const header6 = borderless_table.header(); 33 | header6.cell("Header Left3", { textAlign: "center", padding: 30 }); 34 | header6.cell("Header Right3", { textAlign: "center", padding: 30 }); 35 | }; 36 | -------------------------------------------------------------------------------- /font/ZapfDingbats.json: -------------------------------------------------------------------------------- 1 | {"fontName":"ZapfDingbats","fullName":"ITC Zapf Dingbats","familyName":"ZapfDingbats","italicAngle":0,"characterSet":"Special","fontBBox":[-1,-143,981,820],"underlinePosition":-100,"underlineThickness":50,"kerning":{},"widths":[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,278,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,278,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]} -------------------------------------------------------------------------------- /lib/footer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDF = require("./object"); 4 | const Header = require("./header"); 5 | 6 | module.exports = class Footer extends Header { 7 | constructor(doc, parent) { 8 | super(doc, parent); 9 | } 10 | 11 | /// private API 12 | 13 | _createObject() { 14 | const xobj = Header.prototype._createObject.call(this); 15 | // since the footer has to be moved to the bottom of the page a Matrix property is added here 16 | xobj.prop("Matrix", this._matrix.toReference()); 17 | return xobj; 18 | } 19 | 20 | async _pageBreak(level) { 21 | throw new Error( 22 | "Footer is to long (tried to execute a page break inside the footer)", 23 | ); 24 | } 25 | 26 | async _start() { 27 | this._matrix = new PDF.Object(); 28 | this._doc._registerObject(this._matrix); 29 | 30 | await Header.prototype._start.call(this); 31 | } 32 | 33 | async _end() { 34 | // keep track of the innerHeight to calculate the offset (to move the footer to the bottom 35 | // of the page) below 36 | const innerHeight = this._doc._cursor.startY - this._doc.paddingBottom; 37 | 38 | await Header.prototype._end.call(this); 39 | 40 | // calculate the offset and set the Matrix accordingly 41 | const offset = innerHeight - this.height; 42 | this._matrix.content = new PDF.Array([1, 0, 0, 1, 0, -offset]); 43 | await this._doc._writeObject(this._matrix); 44 | 45 | // also move the page numbers by the offset (otherwise they would be rendered on top of the 46 | // page) 47 | for (const instance of this._pageNumbers) { 48 | instance.y -= offset; 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /test/pdfs/table/large-rows-with-headers.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | const table = doc.table({ 3 | widths: [200, 200], 4 | borderWidth: 1, 5 | }); 6 | 7 | // The behavior with the headers not appearing on the remaining pages is intentional 8 | // See issue #292 https://github.com/rkusa/pdfjs/issues/292 9 | const header1 = table.header(); 10 | header1.cell("Header Left1", { textAlign: "center", padding: 30 }); 11 | header1.cell("Header Right1", { textAlign: "center", padding: 30 }); 12 | 13 | const header2 = table.header(); 14 | header2.cell("Header Left2", { textAlign: "center", padding: 30 }); 15 | header2.cell("Header Right2", { textAlign: "center", padding: 30 }); 16 | 17 | const header3 = table.header(); 18 | header3.cell("Header Left3", { textAlign: "center", padding: 30 }); 19 | header3.cell("Header Right3", { textAlign: "center", padding: 30 }); 20 | 21 | const row1 = table.row(); 22 | 23 | row1.cell(lorem.long, { 24 | fontSize: 11, 25 | padding: 10, 26 | backgroundColor: 0xdddddd, 27 | }); 28 | row1.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 29 | 30 | const row2 = table.row(); 31 | 32 | row2.cell(lorem.long, { 33 | fontSize: 16, 34 | padding: 10, 35 | backgroundColor: 0xdddddd, 36 | }); 37 | row2.cell("Cell 2", { fontSize: 11, padding: 10, backgroundColor: 0xeeeeee }); 38 | 39 | const row3 = table.row(); 40 | 41 | row3.cell("Cell 1", { fontSize: 16, padding: 10, backgroundColor: 0xdddddd }); 42 | row3.cell(lorem.short, { 43 | fontSize: 11, 44 | padding: 10, 45 | backgroundColor: 0xeeeeee, 46 | }); 47 | 48 | doc.text("Foo"); 49 | }; 50 | -------------------------------------------------------------------------------- /lib/font/subset.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const opentype = require("opentype.js"); 4 | 5 | module.exports = class FontSubset { 6 | constructor(font) { 7 | this.font = font; 8 | this.name = "PDFJS+" + this.font.names.fontFamily.en; 9 | 10 | this.glyphs = { 11 | 0: this.font.charToGlyph(String.fromCharCode(0)), // notDef glyph 12 | 32: this.font.charToGlyph(String.fromCharCode(32)), // space 13 | }; 14 | this.subset = { 0: 0, 32: 32 }; 15 | this.mapping = { 0: 0, 32: 32 }; 16 | this.pos = 33; 17 | } 18 | 19 | use(chars) { 20 | for (let i = 0, len = chars.length; i < len; ++i) { 21 | const code = chars.charCodeAt(i); 22 | if (code in this.mapping || code < 33) { 23 | continue; 24 | } 25 | 26 | const glyph = this.font.charToGlyph(chars[i]); 27 | 28 | this.subset[this.pos] = code; 29 | this.mapping[code] = this.pos; 30 | this.glyphs[this.pos] = glyph; 31 | 32 | this.pos++; 33 | } 34 | } 35 | 36 | encode(str) { 37 | const codes = []; 38 | for (let i = 0, len = str.length; i < len; ++i) { 39 | codes.push(this.mapping[str.charCodeAt(i)]); 40 | } 41 | return String.fromCharCode.apply(String, codes); 42 | } 43 | 44 | cmap() { 45 | return this.subset; 46 | } 47 | 48 | save() { 49 | const glyphs = []; 50 | for (const pos in this.glyphs) { 51 | glyphs.push(this.glyphs[pos]); 52 | } 53 | const font = new opentype.Font({ 54 | familyName: this.name, 55 | styleName: this.font.names.fontSubfamily.en, 56 | unitsPerEm: this.font.unitsPerEm, 57 | ascender: this.font.ascender, 58 | descender: this.font.descender, 59 | glyphs: glyphs, 60 | }); 61 | return font.toArrayBuffer(); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /lib/cursor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class Cursor { 4 | constructor(width, height, x, y) { 5 | this.width = width; 6 | this.height = height; 7 | if (x !== undefined) { 8 | this.startX = this.x = x; 9 | } 10 | if (y !== undefined) { 11 | this.startY = this.y = y; 12 | this._bottom = this.y - this.height; 13 | } 14 | this.bottomOffset = 0; 15 | } 16 | 17 | /// public API 18 | 19 | get bottom() { 20 | return this._bottom + this.bottomOffset; 21 | } 22 | 23 | reset() { 24 | this.x = this.startX; 25 | this.y = this.startY; 26 | } 27 | 28 | doesFit(height) { 29 | return this.y - height >= this.bottom; 30 | } 31 | 32 | clone() { 33 | return new ClonedCursor(this); 34 | } 35 | } 36 | 37 | // A ClonedCursor has its own `width`, `height`, `bottom`, `startX` and `startY`, but shares 38 | // `x` and `y` with all other Cursors and ClonedCursors. 39 | class ClonedCursor extends Cursor { 40 | constructor(cursor) { 41 | super(cursor.width, cursor.height); 42 | this.startX = cursor.startX; 43 | this.startY = cursor.startY; 44 | this.bottomOffset = 0; 45 | this._root = cursor._root || cursor; 46 | } 47 | 48 | /// public API 49 | 50 | get bottom() { 51 | return this._root.bottom + this.bottomOffset; 52 | } 53 | 54 | get x() { 55 | return this._root.x; 56 | } 57 | 58 | set x(val) { 59 | if (val < 0) { 60 | console.warn("set cursor.x to negative value"); 61 | } 62 | this._root.x = val; 63 | } 64 | 65 | get y() { 66 | return this._root.y; 67 | } 68 | 69 | set y(val) { 70 | // No warning about negative y value as this fine for elements in content objects that are 71 | // later placed with an offset anyway. 72 | this._root.y = val; 73 | } 74 | } 75 | 76 | module.exports = Cursor; 77 | -------------------------------------------------------------------------------- /test/others/asBuffer.js: -------------------------------------------------------------------------------- 1 | const test = require("tape"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const fixtures = require("../fixtures"); 5 | const pdf = require("../../lib"); 6 | 7 | const f = fixtures.create(); 8 | 9 | test("asBuffer", function (t) { 10 | let doc = new pdf.Document({ 11 | font: f.font.afm.regular, 12 | padding: 10, 13 | lineHeight: 1, 14 | properties: { 15 | creationDate: new Date(2015, 1, 19, 22, 33, 26), 16 | producer: "pdfjs tests (github.com/rkusa/pdfjs)", 17 | }, 18 | }); 19 | doc.info.id = "42"; 20 | doc.text(f.lorem.shorter); 21 | 22 | const expectationPath = path.join(__dirname, "asBuffer.pdf"); 23 | const resultPath = path.join(__dirname, "asBuffer.result.pdf"); 24 | const w = fs.createWriteStream(resultPath); 25 | doc 26 | .asBuffer() 27 | .then((data) => { 28 | w.write(data); 29 | w.close((err) => { 30 | if (err) { 31 | t.error(err); 32 | return; 33 | } 34 | 35 | try { 36 | var result = fs.readFileSync(resultPath, "binary"); 37 | var expectation = fs.readFileSync(expectationPath, "binary"); 38 | } catch (err) { 39 | t.error(err); 40 | } 41 | 42 | t.ok(result === expectation, "asBuffer"); 43 | t.end(); 44 | }); 45 | }) 46 | .catch(t.error); 47 | }); 48 | 49 | test("error in pending queue", async function (t) { 50 | t.plan(1); 51 | 52 | let doc = new pdf.Document({ 53 | font: f.font.afm.regular, 54 | }); 55 | 56 | doc._pending.push(() => { 57 | return new Promise(() => { 58 | throw new Error("Test Error"); 59 | }); 60 | }); 61 | 62 | await doc 63 | .asBuffer() 64 | .then(() => t.fail("asBuffer expected to reject")) 65 | .catch(() => t.pass("as Buffer rejected")); 66 | }); 67 | -------------------------------------------------------------------------------- /test/pdfs/table/colspan.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem }) { 2 | let table = doc.table({ 3 | widths: [null, null, null], 4 | borderWidth: 1, 5 | borderVerticalWidths: [2, 4, 6, 8], 6 | }); 7 | 8 | { 9 | const row = table.row(); 10 | row.cell("First", { fontSize: 20, padding: 10 }); 11 | row.cell("Second", { fontSize: 20, padding: 10 }); 12 | row.cell("Third", { fontSize: 20, padding: 10 }); 13 | } 14 | 15 | { 16 | const row = table.row(); 17 | row.cell("First", { fontSize: 20, padding: 10, colspan: 2 }); 18 | row.cell("Second", { fontSize: 20, padding: 10 }); 19 | } 20 | 21 | { 22 | const row = table.row(); 23 | row.cell("First", { fontSize: 20, padding: 10 }); 24 | row.cell("Second", { fontSize: 20, padding: 10 }); 25 | row.cell("Third", { fontSize: 20, padding: 10 }); 26 | } 27 | 28 | { 29 | const row = table.row(); 30 | row.cell("First", { fontSize: 20, padding: 10 }); 31 | row.cell("Second", { fontSize: 20, padding: 10, colspan: 2 }); 32 | } 33 | 34 | { 35 | const row = table.row(); 36 | row.cell("First", { fontSize: 20, padding: 10, colspan: 3 }); 37 | } 38 | 39 | { 40 | const row = table.row(); 41 | row.cell("First", { fontSize: 20, padding: 10 }); 42 | row.cell("Second", { fontSize: 20, padding: 10 }); 43 | row.cell("Third", { fontSize: 20, padding: 10 }); 44 | } 45 | 46 | // test issue #99 (colspan without borderVerticalWidths) 47 | table = doc.table({ 48 | widths: [null, null, null], 49 | borderWidth: 1, 50 | }); 51 | 52 | { 53 | const row = table.row(); 54 | row.cell("First", { fontSize: 20, padding: 10 }); 55 | row.cell("Second", { fontSize: 20, padding: 10 }); 56 | row.cell("Third", { fontSize: 20, padding: 10 }); 57 | } 58 | 59 | { 60 | const row = table.row(); 61 | row.cell("First", { fontSize: 20, padding: 10, colspan: 2 }); 62 | row.cell("Second", { fontSize: 20, padding: 10 }); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /test/pdfs/text/underline.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, font }) { 2 | { 3 | // afm 4 | const text = doc.text(); 5 | text.add(lorem.shorter, { underline: true }); 6 | text.add("foobar"); 7 | text.add("foobar", { underline: true, fontSize: 18 }); 8 | text.add("foobar"); 9 | text.add("foobar", { fontSize: 18 }); 10 | text.add("foobar", { underline: true }); 11 | text.add("foobar"); 12 | text.add("foobar", { underline: true }); 13 | text.add("foobar", { underline: true, fontSize: 18 }); 14 | text.add("foobar", { underline: true }); 15 | } 16 | 17 | { 18 | // otf 19 | const text = doc.text({ font: font.opensans.regular, fontSize: 9.5 }); 20 | text.add(lorem.shorter, { underline: true }); 21 | text.add("foobar"); 22 | text.add("foobar", { underline: true, fontSize: 18 }); 23 | text.add("foobar"); 24 | text.add("foobar", { fontSize: 18 }); 25 | text.add("foobar", { underline: true }); 26 | text.add("foobar"); 27 | text.add("foobar", { underline: true }); 28 | text.add("foobar", { underline: true, fontSize: 18 }); 29 | text.add("foobar", { underline: true }); 30 | } 31 | 32 | { 33 | // single word line break 34 | doc.text("foobar", { underline: true, fontSize: 8.5 }); 35 | doc.text(lorem.shorter, { underline: true, fontSize: 8.5 }); 36 | } 37 | 38 | { 39 | // long word 40 | const text = doc.text(); 41 | text.add( 42 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 43 | { underline: true }, 44 | ); 45 | text.add("foobar"); 46 | } 47 | 48 | { 49 | // color change 50 | const text = doc.text(); 51 | text.add("foo", { underline: true }); 52 | text.add("bar", { underline: true, color: 0xff0000 }); 53 | } 54 | 55 | { 56 | // append 57 | doc 58 | .text() 59 | .add("fo", { underline: true }) 60 | .append("ob") 61 | .append("ar", { underline: true }); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /lib/object/dictionary.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFName = require("./name"); 4 | const PDFValue = require("./value"); 5 | 6 | class PDFDictionary { 7 | constructor(dictionary) { 8 | this.dictionary = {}; 9 | if (dictionary) { 10 | for (const key in dictionary) { 11 | this.add(key, dictionary[key]); 12 | } 13 | } 14 | } 15 | 16 | add(key, val) { 17 | if (typeof val === "string") { 18 | val = new PDFName(val); 19 | } 20 | this.dictionary[new PDFName(key)] = val; 21 | } 22 | 23 | set(key, val) { 24 | this.add(key, val); 25 | } 26 | 27 | has(key) { 28 | return String(new PDFName(key)) in this.dictionary; 29 | } 30 | 31 | get(key) { 32 | return this.dictionary[new PDFName(key)]; 33 | } 34 | 35 | del(key) { 36 | delete this.dictionary[new PDFName(key)]; 37 | } 38 | 39 | get length() { 40 | let length = 0; 41 | for (const key in this.dictionary) { 42 | length++; 43 | } 44 | return length; 45 | } 46 | 47 | toString() { 48 | let str = ""; 49 | for (const key in this.dictionary) { 50 | const val = this.dictionary[key]; 51 | str += 52 | `${key} ${val === null ? "null" : val}`.replace(/^/gm, "\t") + "\n"; 53 | } 54 | return `<<\n${str}>>`; 55 | } 56 | 57 | static parse(xref, lexer, trial) { 58 | if (lexer.getString(2) !== "<<") { 59 | if (trial) { 60 | return undefined; 61 | } 62 | 63 | throw new Error("Invalid dictionary"); 64 | } 65 | 66 | lexer.shift(2); 67 | lexer.skipWhitespace(null, true); 68 | 69 | const dictionary = new PDFDictionary(); 70 | 71 | while (lexer.getString(2) !== ">>") { 72 | const key = PDFName.parse(xref, lexer); 73 | lexer.skipWhitespace(null, true); 74 | 75 | const value = PDFValue.parse(xref, lexer); 76 | dictionary.set(key, value); 77 | 78 | lexer.skipWhitespace(null, true); 79 | } 80 | 81 | lexer.shift(2); 82 | 83 | return dictionary; 84 | } 85 | } 86 | 87 | module.exports = PDFDictionary; 88 | -------------------------------------------------------------------------------- /test/pdfs/text/strikethrough.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { lorem, font }) { 2 | { 3 | // afm 4 | const text = doc.text(); 5 | text.add(lorem.shorter, { strikethrough: true }); 6 | text.add("foobar"); 7 | text.add("foobar", { strikethrough: true, fontSize: 18 }); 8 | text.add("foobar"); 9 | text.add("foobar", { fontSize: 18 }); 10 | text.add("foobar", { strikethrough: true }); 11 | text.add("foobar"); 12 | text.add("foobar", { strikethrough: true }); 13 | text.add("foobar", { strikethrough: true, fontSize: 18 }); 14 | text.add("foobar", { strikethrough: true }); 15 | } 16 | 17 | { 18 | // otf 19 | const text = doc.text({ font: font.opensans.regular, fontSize: 9.5 }); 20 | text.add(lorem.shorter, { strikethrough: true }); 21 | text.add("foobar"); 22 | text.add("foobar", { strikethrough: true, fontSize: 18 }); 23 | text.add("foobar"); 24 | text.add("foobar", { fontSize: 18 }); 25 | text.add("foobar", { strikethrough: true }); 26 | text.add("foobar"); 27 | text.add("foobar", { strikethrough: true }); 28 | text.add("foobar", { strikethrough: true, fontSize: 18 }); 29 | text.add("foobar", { strikethrough: true }); 30 | } 31 | 32 | { 33 | // single word line break 34 | doc.text("foobar", { strikethrough: true, fontSize: 8.5 }); 35 | doc.text(lorem.shorter, { strikethrough: true, fontSize: 8.5 }); 36 | } 37 | 38 | { 39 | // long word 40 | const text = doc.text(); 41 | text.add( 42 | "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 43 | { strikethrough: true }, 44 | ); 45 | text.add("foobar"); 46 | } 47 | 48 | { 49 | // color change 50 | const text = doc.text(); 51 | text.add("foo", { strikethrough: true }); 52 | text.add("bar", { strikethrough: true, color: 0xff0000 }); 53 | } 54 | 55 | { 56 | // append 57 | doc 58 | .text() 59 | .add("fo", { strikethrough: true }) 60 | .append("ob") 61 | .append("ar", { strikethrough: true }); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /lib/image/pdf.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDF = require("../object"); 4 | const Parser = require("../parser/parser"); 5 | 6 | module.exports = class PDFImage { 7 | constructor(src) { 8 | const parser = new Parser(src); 9 | parser.parse(); 10 | 11 | const catalog = parser.trailer.get("Root").object.properties; 12 | const pages = catalog.get("Pages").object.properties; 13 | const first = pages.get("Kids")[0].object.properties; 14 | const mediaBox = first.get("MediaBox") || pages.get("MediaBox"); 15 | 16 | this.page = first; 17 | this.width = mediaBox[2]; 18 | this.height = mediaBox[3]; 19 | 20 | const contents = this.page.get("Contents"); 21 | this.xobjCount = Array.isArray(contents) ? contents.length : 1; 22 | } 23 | 24 | async write(doc, xobjs) { 25 | const resources = this.page.get("Resources"); 26 | const bbox = new PDF.Array([0, 0, this.width, this.height]); 27 | 28 | for (let i = 0; i < this.xobjCount; ++i) { 29 | const xobj = xobjs[i]; 30 | 31 | xobj.prop("Subtype", "Form"); 32 | xobj.prop("FormType", 1); 33 | xobj.prop("BBox", bbox); 34 | xobj.prop( 35 | "Resources", 36 | resources instanceof PDF.Object ? resources.toReference() : resources, 37 | ); 38 | 39 | let contents = this.page.get("Contents"); 40 | if (Array.isArray(contents)) { 41 | contents = contents[i].object; 42 | } else { 43 | contents = contents.object; 44 | } 45 | 46 | const content = new PDF.Stream(xobj); 47 | content.content = contents.content.content; 48 | 49 | if (contents.properties.has("Filter")) { 50 | xobj.prop("Filter", contents.properties.get("Filter")); 51 | } 52 | xobj.prop("Length", contents.properties.get("Length")); 53 | if (contents.properties.has("Length1")) { 54 | xobj.prop("Length1", contents.properties.get("Length1")); 55 | } 56 | 57 | const objects = []; 58 | Parser.addObjectsRecursive(objects, xobj); 59 | 60 | // first, register objects to assign IDs (for references) 61 | for (const obj of objects) { 62 | doc._registerObject(obj, true); 63 | } 64 | 65 | // write objects 66 | for (const obj of objects) { 67 | await doc._writeObject(obj); 68 | } 69 | 70 | await doc._writeObject(xobj); 71 | } 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /lib/tableheader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // const Fragment = require('./fragment') 4 | // const util = require('./util') 5 | // const ops = require('./ops') 6 | // const Cell = require('./cell') 7 | const PDF = require("./object"); 8 | const Row = require("./row"); 9 | const Header = require("./header"); 10 | 11 | module.exports = class TableHeader extends Row { 12 | constructor(doc, parent, opts) { 13 | super(doc, parent, opts); 14 | 15 | this._previousContents = null; 16 | this._hadPreviousContent = false; 17 | 18 | // a header could consist out of multiple FormXObjects and this property is later used keep 19 | // track of them 20 | this._objects = []; 21 | 22 | // The cursor.y before the header was rendered 23 | this._startY = null; 24 | } 25 | 26 | // The y coordinate the header was rendered for. Can be used to calculate the offset when re- 27 | // using the header. 28 | get startedAtY() { 29 | return this._cursor.startY; 30 | } 31 | 32 | /// private API 33 | 34 | // prevent page breaks inside a header 35 | async _pageBreak() { 36 | throw new Error( 37 | "Table Header is to long (tried to execute a page break inside the header)", 38 | ); 39 | } 40 | 41 | async _start() { 42 | // Render the header as if it would start at the top of the page and move it to its actual 43 | // position later on. This prevents page breaks inside of the header when first rendering it 44 | // close to the bottom of the document. 45 | this._startY = this._cursor.y; 46 | this._cursor.y = this._cursor.startY; 47 | 48 | await super._start(); 49 | 50 | this._hadPreviousContent = !!this._doc._currentContent; 51 | await this._doc._endContentObject(); 52 | 53 | this._previousContents = this._doc._contents; 54 | this._doc._contents = []; 55 | 56 | await Header.prototype._setup.call(this); 57 | 58 | this._cursor.y = this._y; 59 | } 60 | 61 | _createObject() { 62 | return Header.prototype._createObject.call(this); 63 | } 64 | 65 | async _end() { 66 | await super._end(); 67 | const height = this._cursor.startY - this._cursor.y; 68 | await Header.prototype._end.call(this); 69 | this.height = height; 70 | 71 | this._doc._contents = this._previousContents; 72 | this._previousContents = null; 73 | 74 | if (this._hadPreviousContent) { 75 | await this._doc._startContentObject(); 76 | } 77 | 78 | this._cursor.y = this._startY; //- this.height 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /test/pdfs/outlines/outlines.js: -------------------------------------------------------------------------------- 1 | module.exports = function (doc, { image, lorem }) { 2 | // Initialise the document with destination samples 3 | doc.pageBreak(); 4 | doc.text("Text", { destination: "Text" }); 5 | doc.pageBreak(); 6 | doc.destination("Doc"); 7 | doc.pageBreak(); 8 | doc.image(image.jpeg, { 9 | destination: "Image", 10 | }); 11 | 12 | doc.outline("1. An outline", "Text"); 13 | 14 | // Outlines can be set to any kind of destinations 15 | doc.outline("2. Works with any kind of dest", "Doc"); 16 | doc.outline("2.1. Image", "Image", "2. Works with any kind of dest"); 17 | doc.outline("2.2. Text", "Text", "2. Works with any kind of dest"); 18 | doc.outline("2.3. Doc", "Doc", "2. Works with any kind of dest"); 19 | 20 | // Outlines can be deeply nested 21 | doc.outline("3. Can be deeply nested", "Text"); 22 | doc.outline("3.1 Level 1", "Image", "3. Can be deeply nested"); 23 | doc.outline("3.1.1 Level 2", "Text", "3.1 Level 1"); 24 | 25 | // An outline with an unknown or empty destination is added to the outlines but is not reactive 26 | doc.outline("4. Empty/Unknown destinations", ""); 27 | doc.outline( 28 | "4.1. Can have children", 29 | "Unknown", 30 | "4. Empty/Unknown destinations", 31 | ); 32 | doc.outline( 33 | "4.2. But are not reactive", 34 | "Unknown", 35 | "4. Empty/Unknown destinations", 36 | ); 37 | 38 | // If an outline is defined with a parent that has not already been declared, then the parent is added to the root with the same destination before the child outline is added 39 | doc.outline("5.1. Can have children", "Image", "5. Undeclared parents"); 40 | 41 | // An outline with an empty or undefined parent is attached to the root 42 | doc.outline( 43 | "6. An outline with an undefined parent is added to the root", 44 | "Text", 45 | ); 46 | doc.outline("7. So is an outline with an empty parent", "Doc", ""); 47 | 48 | // Outlines can have the same name. 49 | // Provide their respective id instead of their name if you want to add children to them 50 | doc.outline("8. Outlines can have the same name (siblings)", "Image"); 51 | const firstSibling = doc.outline( 52 | "Sibling", 53 | "Text", 54 | "8. Outlines can have the same name (siblings)", 55 | ); 56 | const secondSibling = doc.outline( 57 | "Sibling", 58 | "Doc", 59 | "8. Outlines can have the same name (siblings)", 60 | ); 61 | doc.outline("Has a specific child", "Image", firstSibling); 62 | doc.outline("Has his own child", "Text", secondSibling); 63 | 64 | // An outline with undefined title and/or destination is skipped 65 | doc.outline(); 66 | doc.outline(""); 67 | doc.outline("Is not added to the outlines"); 68 | doc.outline(undefined, "somewhere"); 69 | }; 70 | -------------------------------------------------------------------------------- /lib/image/jpeg.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDF = require("../object"); 4 | 5 | module.exports = class JPEGImage { 6 | constructor(src) { 7 | this.src = src; 8 | this.xobjCount = 1; 9 | 10 | const view = new DataView(src); 11 | if (view.getUint8(0) !== 0xff || view.getUint8(1) !== 0xd8) { 12 | throw new Error("Invalid JPEG image."); 13 | } 14 | 15 | let blockLength = view.getUint8(4) * 256 + view.getUint8(5); 16 | const len = view.byteLength; 17 | let i = 4; 18 | 19 | while (i < len) { 20 | i += blockLength; 21 | 22 | if (view.getUint8(i) !== 0xff) { 23 | throw new Error("Could not read JPEG the image size"); 24 | } 25 | 26 | if ( 27 | view.getUint8(i + 1) === 0xc0 || //(SOF) Huffman - Baseline DCT 28 | view.getUint8(i + 1) === 0xc1 || //(SOF) Huffman - Extended sequential DCT 29 | view.getUint8(i + 1) === 0xc2 || // Progressive DCT (SOF2) 30 | view.getUint8(i + 1) === 0xc3 || // Spatial (sequential) lossless (SOF3) 31 | view.getUint8(i + 1) === 0xc4 || // Differential sequential DCT (SOF5) 32 | view.getUint8(i + 1) === 0xc5 || // Differential progressive DCT (SOF6) 33 | view.getUint8(i + 1) === 0xc6 || // Differential spatial (SOF7) 34 | view.getUint8(i + 1) === 0xc7 35 | ) { 36 | this.height = view.getUint8(i + 5) * 256 + view.getUint8(i + 6); 37 | this.width = view.getUint8(i + 7) * 256 + view.getUint8(i + 8); 38 | 39 | const colorSpace = view.getUint8(i + 9); 40 | switch (colorSpace) { 41 | case 3: 42 | this.colorSpace = "DeviceRGB"; 43 | break; 44 | case 1: 45 | this.colorSpace = "DeviceGray"; 46 | break; 47 | } 48 | 49 | break; 50 | } else { 51 | i += 2; 52 | blockLength = view.getUint8(i) * 256 + view.getUint8(i + 1); 53 | } 54 | } 55 | } 56 | 57 | async write(doc, xobjs) { 58 | const xobj = xobjs[0]; 59 | 60 | xobj.prop("Subtype", "Image"); 61 | xobj.prop("Width", this.width); 62 | xobj.prop("Height", this.height); 63 | xobj.prop("ColorSpace", this.colorSpace); 64 | xobj.prop("BitsPerComponent", 8); 65 | 66 | const hex = asHex(this.src); 67 | xobj.prop("Filter", new PDF.Array(["/ASCIIHexDecode", "/DCTDecode"])); 68 | xobj.prop("Length", hex.length + 1); 69 | xobj.prop("Length1", this.src.byteLength); 70 | 71 | const content = new PDF.Stream(xobj); 72 | content.content = hex + ">\n"; 73 | 74 | await doc._writeObject(xobj); 75 | } 76 | }; 77 | 78 | function asHex(ab) { 79 | const view = new Uint8Array(ab); 80 | let hex = ""; 81 | for (let i = 0, len = ab.byteLength; i < len; ++i) { 82 | hex += toHex(view[i]); 83 | } 84 | return hex; 85 | } 86 | 87 | function toHex(n) { 88 | if (n < 16) return "0" + n.toString(16); 89 | return n.toString(16); 90 | } 91 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Converts a hex color expr. like #123456 into an array [r, g, b], 4 | // where r, g, b are in the range of 0 and 1 5 | exports.colorToRgb = function (hex) { 6 | if (hex === undefined || hex === null) { 7 | return; 8 | } 9 | 10 | if (typeof hex === "string") { 11 | hex = parseInt(hex.replace("#", ""), 16); 12 | } 13 | 14 | const r = (hex >> 16) / 255; 15 | const g = ((hex >> 8) & 255) / 255; 16 | const b = (hex & 255) / 255; 17 | 18 | return [r, g, b]; 19 | }; 20 | 21 | exports.rgbEqual = function (lhs, rhs) { 22 | return ( 23 | lhs && rhs && lhs[0] === rhs[0] && lhs[1] === rhs[1] && lhs[2] === rhs[2] 24 | ); 25 | }; 26 | 27 | exports.toArrayBuffer = function (b) { 28 | if (b instanceof ArrayBuffer) { 29 | return b; 30 | } else { 31 | return b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength); 32 | } 33 | }; 34 | 35 | exports.defaults = function (val, def) { 36 | return val !== undefined ? val : def; 37 | }; 38 | 39 | exports.inflate = function (obj) { 40 | let filters = obj.properties.get("Filter"); 41 | let filter; 42 | if (filters && Array.isArray(filters)) { 43 | filter = filters.shift(); 44 | } else { 45 | filter = filters; 46 | filters = []; 47 | } 48 | 49 | if (!filter || filter.name !== "FlateDecode" || filters.length > 0) { 50 | throw new Error("Only FlateDecode filter are supported"); 51 | } 52 | 53 | let columns = 1; 54 | let predictor = 1; 55 | const params = obj.properties.get("DecodeParms"); 56 | if (params) { 57 | columns = params.get("Columns"); 58 | predictor = params.get("Predictor"); 59 | } 60 | 61 | const inflate = require("pako/lib/inflate.js").inflate; 62 | let res = inflate(obj.content.content); 63 | 64 | if (predictor === 1) { 65 | return res; 66 | } 67 | 68 | if (predictor >= 10 && predictor <= 15) { 69 | // PNG filter 70 | res = pngFilter(res, columns); 71 | } else { 72 | throw new Error("Unsupported predictor " + predictor); 73 | } 74 | 75 | return res; 76 | }; 77 | 78 | function pngFilter(src, columns) { 79 | const columnCount = columns + 1; 80 | const rowCount = src.length / columnCount; 81 | 82 | const res = new Uint8Array(columns * rowCount); 83 | for (let y = 0; y < rowCount; ++y) { 84 | const filter = src[y * columnCount]; 85 | if (filter === 0) { 86 | for (let x = 0; x < columns; ++x) { 87 | res[y * columns + x] = src[y * columnCount + 1 + x]; 88 | } 89 | } else if (filter === 2) { 90 | for (let x = 0; x < columns; x++) { 91 | const prev = y === 0 ? 0 : res[(y - 1) * columns + x]; 92 | res[y * columns + x] = (prev + src[y * columnCount + 1 + x]) & 0xff; 93 | } 94 | } else { 95 | throw new Error("Unsupported PNG filter " + filter); 96 | } 97 | } 98 | return res; 99 | } 100 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const path = require("path"); 3 | const fs = require("fs"); 4 | const test = require("tape"); 5 | const fixtures = require("./fixtures"); 6 | const pdf = require("../lib"); 7 | 8 | process.env.TZ = "Europe/Berlin"; 9 | 10 | const args = process.argv.slice(2); 11 | if (args.length) { 12 | run( 13 | args.map((a) => path.join(__dirname, "../", a)), 14 | true, 15 | ); 16 | } else { 17 | glob(path.join(__dirname, "{pdfs,others}/**/*.js"), function (err, files) { 18 | if (err) throw err; 19 | run(files); 20 | }); 21 | } 22 | 23 | // mock current time 24 | const _Date = Date; 25 | Date = class extends _Date { 26 | constructor(year, month, day, hour, minute, second) { 27 | if (arguments.length === 0) { 28 | return new _Date(2015, 1, 19, 22, 33, 26); 29 | } else { 30 | return new _Date(year, month, day, hour, minute, second); 31 | } 32 | } 33 | }; 34 | 35 | function run(files, force) { 36 | const f = fixtures.create(); 37 | 38 | for (const scriptPath of files) { 39 | const relativePath = path.relative(__dirname, scriptPath); 40 | const dirname = path.dirname(scriptPath); 41 | const basename = path.basename(scriptPath, ".js"); 42 | 43 | // ignore tests starting with _ and named `test` 44 | if (!force && (basename[0] === "_" || basename === "test")) { 45 | continue; 46 | } 47 | 48 | if (relativePath.startsWith("others")) { 49 | require(scriptPath); 50 | continue; 51 | } 52 | 53 | const pdfsPath = path.relative(path.join(__dirname, "pdfs"), dirname); 54 | const expectationPath = path.join(dirname, basename + ".pdf"); 55 | const resultPath = path.join(dirname, basename + ".result.pdf"); 56 | const script = require(scriptPath); 57 | 58 | test(path.join(pdfsPath, basename), function (t) { 59 | let doc = new pdf.Document({ 60 | font: f.font.afm.regular, 61 | padding: script.padding >= 0 ? script.padding : 10, 62 | lineHeight: 1, 63 | }); 64 | 65 | const newDoc = script(doc, f, t); 66 | if (newDoc instanceof pdf.Document) { 67 | doc = newDoc; 68 | } 69 | 70 | doc.info.id = "42"; 71 | doc.info.creationDate = new Date(2015, 1, 19, 22, 33, 26); 72 | doc.info.producer = "pdfjs tests (github.com/rkusa/pdfjs)"; 73 | 74 | const w = fs.createWriteStream(resultPath); 75 | doc.pipe(w); 76 | 77 | w.on("close", () => { 78 | try { 79 | var result = fs.readFileSync(resultPath, "binary"); 80 | var expectation = fs.readFileSync(expectationPath, "binary"); 81 | } catch (err) { 82 | t.error(err); 83 | } 84 | 85 | t.ok(result === expectation, basename); 86 | t.end(); 87 | }); 88 | 89 | const p = newDoc instanceof Promise ? newDoc : Promise.resolve(); 90 | p.then(() => { 91 | doc.end().catch((err) => { 92 | t.error(err); 93 | }); 94 | }).catch((err) => t.error(err)); 95 | }); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pdfjs 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /lib/object/name.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | class PDFName { 4 | constructor(name) { 5 | if (name === undefined || name === null) { 6 | throw new Error("A Name cannot be undefined"); 7 | } 8 | 9 | if (name instanceof PDFName) { 10 | return name; 11 | } 12 | 13 | if (name[0] === "/") { 14 | name = name.substr(1); 15 | } 16 | 17 | // white-space characters are not allowed 18 | if (name.match(/[\x00]/)) { 19 | throw new Error("A Name mustn't contain the null characters"); 20 | } 21 | 22 | name = name.toString(); 23 | 24 | // Beginning with PDF 1.2, any character except null (character code 0) 25 | // may be included in a name by writing its 2-digit hexadecimal code, 26 | // preceded by the number sign character (#) 27 | // ... it is recommended but not required for characters whose codes 28 | // are outside the range 33 (!) to 126 (~) 29 | name = name.replace(/[^\x21-\x7e]/g, function (c) { 30 | let code = c.charCodeAt(0); 31 | // replace unicode characters with `_` 32 | if (code > 0xff) { 33 | code = 0x5f; 34 | } 35 | return "#" + Number(code).toString(16); 36 | }); 37 | 38 | // Add # in front of delimiter characters 39 | // 25 % 40 | // 28 ( 41 | // 29 ) 42 | // 2f / 43 | // 3c < 44 | // 3e > 45 | // 5b [ 46 | // 5d ] 47 | // 7b { 48 | // 7d } 49 | name = name.replace( 50 | /[\x25\x28\x29\x2f\x3c\x3e\x5b\x5d\x7b\x7d]/g, 51 | function (c) { 52 | let code = c.charCodeAt(0); 53 | return "#" + Number(code).toString(16); 54 | }, 55 | ); 56 | 57 | this.name = name; 58 | } 59 | 60 | toString() { 61 | return "/" + this.name; 62 | } 63 | 64 | static parse(xref, lexer, trial) { 65 | if (lexer.getString(1) !== "/") { 66 | if (trial) { 67 | return undefined; 68 | } 69 | 70 | throw new Error( 71 | "Name must start with a leading slash, found: " + lexer.getString(1), 72 | ); 73 | } 74 | 75 | lexer.shift(1); 76 | 77 | let name = ""; 78 | 79 | let done = false; 80 | let c; 81 | while (!done && (c = lexer._nextCharCode()) >= 0) { 82 | switch (true) { 83 | case c === 0x28: // ( 84 | case c === 0x29: // ) 85 | case c === 0x3c: // < 86 | case c === 0x3e: // > 87 | case c === 0x5b: // [ 88 | case c === 0x5d: // ] 89 | case c === 0x7b: // { 90 | case c === 0x7d: // } 91 | case c === 0x2f: // / 92 | case c === 0x25: // % 93 | done = true; 94 | break; 95 | case c === 0x23: // # 96 | const hex = lexer.readString(2); 97 | name += String.fromCharCode(parseInt(hex, 16)); 98 | break; 99 | case c >= 0x22 && c <= 0x7e: // inside range of 33 (!) to 126 (~) 100 | name += String.fromCharCode(c); 101 | break; 102 | default: 103 | done = true; 104 | break; 105 | } 106 | } 107 | 108 | lexer.shift(-1); 109 | 110 | return new PDFName(name); 111 | } 112 | } 113 | 114 | module.exports = PDFName; 115 | -------------------------------------------------------------------------------- /font/afm/winansi_characters.txt: -------------------------------------------------------------------------------- 1 | .notdef .notdef .notdef .notdef 2 | .notdef .notdef .notdef .notdef 3 | .notdef .notdef .notdef .notdef 4 | .notdef .notdef .notdef .notdef 5 | .notdef .notdef .notdef .notdef 6 | .notdef .notdef .notdef .notdef 7 | .notdef .notdef .notdef .notdef 8 | .notdef .notdef .notdef .notdef 9 | 10 | space exclam quotedbl numbersign 11 | dollar percent ampersand quotesingle 12 | parenleft parenright asterisk plus 13 | comma hyphen period slash 14 | zero one two three 15 | four five six seven 16 | eight nine colon semicolon 17 | less equal greater question 18 | 19 | at A B C 20 | D E F G 21 | H I J K 22 | L M N O 23 | P Q R S 24 | T U V W 25 | X Y Z bracketleft 26 | backslash bracketright asciicircum underscore 27 | 28 | grave a b c 29 | d e f g 30 | h i j k 31 | l m n o 32 | p q r s 33 | t u v w 34 | x y z braceleft 35 | bar braceright asciitilde .notdef 36 | 37 | Euro .notdef quotesinglbase florin 38 | quotedblbase ellipsis dagger daggerdbl 39 | circumflex perthousand Scaron guilsinglleft 40 | OE .notdef Zcaron .notdef 41 | .notdef quoteleft quoteright quotedblleft 42 | quotedblright bullet endash emdash 43 | tilde trademark scaron guilsinglright 44 | oe .notdef zcaron ydieresis 45 | 46 | space exclamdown cent sterling 47 | currency yen brokenbar section 48 | dieresis copyright ordfeminine guillemotleft 49 | logicalnot hyphen registered macron 50 | degree plusminus twosuperior threesuperior 51 | acute mu paragraph periodcentered 52 | cedilla onesuperior ordmasculine guillemotright 53 | onequarter onehalf threequarters questiondown 54 | 55 | Agrave Aacute Acircumflex Atilde 56 | Adieresis Aring AE Ccedilla 57 | Egrave Eacute Ecircumflex Edieresis 58 | Igrave Iacute Icircumflex Idieresis 59 | Eth Ntilde Ograve Oacute 60 | Ocircumflex Otilde Odieresis multiply 61 | Oslash Ugrave Uacute Ucircumflex 62 | Udieresis Yacute Thorn germandbls 63 | 64 | agrave aacute acircumflex atilde 65 | adieresis aring ae ccedilla 66 | egrave eacute ecircumflex edieresis 67 | igrave iacute icircumflex idieresis 68 | eth ntilde ograve oacute 69 | ocircumflex otilde odieresis divide 70 | oslash ugrave uacute ucircumflex 71 | udieresis yacute thorn ydieresis -------------------------------------------------------------------------------- /lib/image/render.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ops = require("../ops"); 4 | const PDFImage = require("./pdf"); 5 | const PDF = require("../object"); 6 | 7 | module.exports = async function (img, doc, parent, opts) { 8 | if (!img) { 9 | throw TypeError("No image provided"); 10 | } 11 | 12 | if (!doc._currentContent) { 13 | await doc._startPage(); 14 | } 15 | 16 | const _cursor = parent._cursor; 17 | 18 | let renderWidth, renderHeight; 19 | if (opts.width && opts.height) { 20 | renderWidth = opts.width; 21 | renderHeight = opts.height; 22 | } else if (opts.width) { 23 | renderWidth = opts.width; 24 | renderHeight = img.height * (opts.width / img.width); 25 | } else if (opts.height) { 26 | renderHeight = opts.height; 27 | renderWidth = img.width * (opts.height / img.height); 28 | } else { 29 | renderWidth = Math.min(img.width, _cursor.width); 30 | renderHeight = img.height * (renderWidth / img.width); 31 | 32 | if (renderHeight > _cursor.height) { 33 | renderHeight = _cursor.height; 34 | renderWidth = img.width * (renderHeight / img.height); 35 | } 36 | } 37 | 38 | let x = _cursor.x; 39 | let y = _cursor.y; 40 | 41 | if (opts.wrap === false) { 42 | if (opts.x !== undefined && opts.x !== null) { 43 | x = opts.x; 44 | } 45 | 46 | if (opts.y !== undefined && opts.y !== null) { 47 | y = opts.y; 48 | } 49 | } else { 50 | if (!_cursor.doesFit(renderHeight)) { 51 | await parent._pageBreak(1); 52 | } 53 | y = _cursor.y; 54 | _cursor.y -= renderHeight; 55 | } 56 | 57 | y -= renderHeight; 58 | 59 | switch (opts.align) { 60 | case "right": 61 | x += _cursor.width - renderWidth; 62 | break; 63 | case "center": 64 | x += (_cursor.width - renderWidth) / 2; 65 | break; 66 | case "left": 67 | default: 68 | break; 69 | } 70 | 71 | if (img instanceof PDFImage) { 72 | // in percent 73 | renderWidth /= img.width; 74 | renderHeight /= img.height; 75 | } 76 | 77 | let chunk = ops.q() + ops.cm(renderWidth, 0, 0, renderHeight, x, y); 78 | 79 | const aliases = doc._useXObject(img); 80 | for (const alias of aliases) { 81 | chunk += ops.Do(alias); 82 | } 83 | 84 | chunk += ops.Q(); 85 | 86 | if (opts.link) { 87 | doc._annotations.push( 88 | new PDF.Dictionary({ 89 | Type: "Annot", 90 | Subtype: "Link", 91 | Rect: new PDF.Array([x, y, x + renderWidth, y + renderHeight]), 92 | Border: new PDF.Array([0, 0, 0]), 93 | A: new PDF.Dictionary({ 94 | Type: "Action", 95 | S: "URI", 96 | URI: new PDF.String(opts.link), 97 | }), 98 | }), 99 | ); 100 | } 101 | if (opts.goTo) { 102 | doc._annotations.push( 103 | new PDF.Dictionary({ 104 | Type: "Annot", 105 | Subtype: "Link", 106 | Rect: new PDF.Array([x, y, x + renderWidth, y + renderHeight]), 107 | Border: new PDF.Array([0, 0, 0]), 108 | A: new PDF.Dictionary({ 109 | S: "GoTo", 110 | D: new PDF.String(opts.goTo), 111 | }), 112 | }), 113 | ); 114 | } 115 | if (opts.destination) { 116 | doc._destinations.set( 117 | opts.destination, 118 | new PDF.Array([ 119 | doc._currentPage.toReference(), 120 | new PDF.Name("XYZ"), 121 | _cursor.x, 122 | _cursor.y + renderHeight, 123 | null, 124 | ]), 125 | ); 126 | } 127 | 128 | await doc._write(chunk); 129 | }; 130 | -------------------------------------------------------------------------------- /font/afm/convert.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | // thanks to https://github.com/prawnpdf/prawn 5 | const CODE_TO_NAME = fs.readFileSync( 6 | path.join(__dirname, 'winansi_characters.txt'), 7 | 'utf8' 8 | ).split(/\s+/) 9 | 10 | const NAME_TO_CODE = {} 11 | CODE_TO_NAME.forEach((v, k) => NAME_TO_CODE[v] = k) 12 | 13 | const files = fs.readdirSync(__dirname) 14 | for (const filename of files) { 15 | if (path.extname(filename) !== '.afm') { 16 | continue 17 | } 18 | 19 | const data = fs.readFileSync(path.join(__dirname, filename), 'utf8') 20 | 21 | const properties = {} 22 | const glyphWidths = {} 23 | const kerning = {} 24 | 25 | const lines = data.split('\r\n') 26 | for (let i = 0; i < lines.length; ++i) { 27 | const line = lines[i] 28 | const match = line.match(/^([A-Z]\w+)\s+(.*)/) 29 | if (!match) { 30 | continue 31 | } 32 | 33 | const key = match[1][0].toLowerCase() + match[1].slice(1) 34 | const val = match[2] 35 | 36 | switch (key) { 37 | case 'startCharMetrics': 38 | const metrics = lines.splice(i + 1, parseInt(val)) 39 | 40 | metrics.forEach(function(metric) { 41 | const name = metric.match(/\bN\s+(\.?\w+)\s*;/)[1] 42 | glyphWidths[name] = parseInt(metric.match(/\bWX\s+(\d+)\s*;/)[1], 10) 43 | }) 44 | // C 32 ; WX 278 ; N space ; B 0 0 0 0 ; 45 | 46 | break 47 | 48 | case 'startKernPairs': 49 | const pairs = lines.splice(i + 1, parseInt(val)) 50 | 51 | for (const pair of pairs) { 52 | // KPX o comma -40 53 | const values = pair.split(' ') 54 | const left = NAME_TO_CODE[values[1]] 55 | const right = NAME_TO_CODE[values[2]] 56 | 57 | if (left === undefined || right === undefined) { 58 | continue 59 | } 60 | 61 | if (!kerning[left]) { 62 | kerning[left] = {} 63 | } 64 | 65 | kerning[left][right] = parseFloat(values[3], 10) 66 | } 67 | 68 | break 69 | 70 | // number 71 | case 'capHeight': 72 | case 'xHeight': 73 | case 'ascender': 74 | case 'descender': 75 | case 'italicAngle': 76 | case 'underlinePosition': 77 | case 'underlineThickness': 78 | properties[key] = parseFloat(val, 10) 79 | break 80 | 81 | // number array 82 | case 'fontBBox': 83 | properties[key] = val.split(/\s+/g) 84 | .filter(v => v !== '') 85 | .map(v => parseFloat(v, 10)) 86 | break 87 | 88 | // string 89 | case 'fontName': 90 | case 'fullName': 91 | case 'familyName': 92 | case 'characterSet': 93 | properties[key] = val 94 | break 95 | 96 | // ignore other properties 97 | default: 98 | // console.log('property', key, 'ignored') 99 | break 100 | } 101 | } 102 | 103 | properties.kerning = kerning 104 | 105 | const widths = new Array(256) 106 | for (let i = 0; i < 256; ++i) { 107 | widths[i] = glyphWidths[CODE_TO_NAME[i]] 108 | } 109 | 110 | properties.widths = widths 111 | 112 | const basename = path.basename(filename, '.afm') 113 | 114 | fs.writeFileSync( 115 | path.join(__dirname, '../', basename + '.json'), 116 | JSON.stringify(properties), 117 | { encoding: 'utf8' } 118 | ) 119 | 120 | fs.writeFileSync( 121 | path.join(__dirname, '../', basename + '.js'), 122 | `const AFMFont = require('../lib/font/afm')\n` + 123 | `module.exports = new AFMFont(require('./${basename}.json'))`, 124 | { encoding: 'utf8' } 125 | ) 126 | 127 | // console.log(widths) 128 | // console.log(properties) 129 | // console.log(kerning) 130 | } 131 | -------------------------------------------------------------------------------- /lib/ops.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFString = require("./object/string"); 4 | 5 | const precision = 3; 6 | 7 | // low level PDF operations 8 | module.exports = { 9 | BT() { 10 | return this.write("BT"); 11 | }, 12 | 13 | ET() { 14 | return this.write("ET"); 15 | }, 16 | 17 | Tf(font, size) { 18 | return this.write(font, size, "Tf"); 19 | }, 20 | 21 | // use SC instead 22 | // rg(r, g, b) { 23 | // return this.write(r, g, b, 'rg') 24 | // }, 25 | 26 | Tm(a, b, c, d, e, f) { 27 | return this.write(a, b, c, d, e, f, "Tm"); 28 | }, 29 | 30 | Tj(str, asHex) { 31 | return this.write(str, "Tj"); 32 | }, 33 | 34 | TJ(arr) { 35 | return this.write( 36 | "[" + 37 | arr 38 | .map((v) => { 39 | if (typeof v === "number") { 40 | return this.toFixed(v, precision); 41 | } else { 42 | return v; 43 | } 44 | }) 45 | .join(" ") + 46 | "]", 47 | "TJ", 48 | ); 49 | }, 50 | 51 | Td(x, y) { 52 | return this.write(x, y, "Td"); 53 | }, 54 | 55 | // set the current color space to use for stroking operations 56 | CS(name) { 57 | return this.write(name, "CS"); 58 | }, 59 | 60 | // same as CS but used for nonstroking operations. 61 | cs(name) { 62 | return this.write(name, "cs"); 63 | }, 64 | 65 | // set the color to use for stroking operations 66 | SC(c1, c2, c3) { 67 | return this.write(c1, c2, c3, "SC"); 68 | }, 69 | 70 | // same as SC but used for nonstroking operations. 71 | sc(c1, c2, c3) { 72 | return this.write(c1, c2, c3, "sc"); 73 | }, 74 | 75 | // modify the current transformation matrix 76 | // translate: [ 1 0 0 1 x y ] 77 | // scale: [ x 0 0 y 0 0 ] 78 | // rotate: [ cosθ sinθ −sinθ cosθ 0 0 ] 79 | cm(a, b, c, d, e, f) { 80 | return this.write(a, b, c, d, e, f, "cm"); 81 | }, 82 | 83 | // save the current graphics state on the graphics state stack 84 | q() { 85 | return this.write("q"); 86 | }, 87 | 88 | // restore the graphics state by removing the most recently saved state from the stack 89 | Q() { 90 | return this.write("Q"); 91 | }, 92 | 93 | // append a rectangle to the current path as a complete subpath 94 | re(x, y, width, height) { 95 | return this.write(x, y, width, height, "re"); 96 | }, 97 | 98 | // fill the path 99 | f() { 100 | return this.write("f"); 101 | }, 102 | 103 | // set the text leading (used by T*) 104 | TL(leading) { 105 | return this.write(leading, "TL"); 106 | }, 107 | 108 | // T* move to the start of the next line, same as: 0 leading Td 109 | Tstar() { 110 | return this.write("T*"); 111 | }, 112 | 113 | // paint xobject 114 | Do(alias) { 115 | return this.write(alias, "Do"); 116 | }, 117 | 118 | // line width 119 | w(lineWidth) { 120 | return this.write(lineWidth, "w"); 121 | }, 122 | 123 | // stroke the path 124 | S() { 125 | const args = Array.prototype.slice.call(arguments); 126 | args.push("S"); 127 | return this.write.apply(this, args); 128 | }, 129 | 130 | write() { 131 | const line = Array.prototype.map.call(arguments, (arg) => { 132 | if (arg === undefined || arg === null) { 133 | console.warn("received an undefined/null operation argument"); 134 | } 135 | // TODO: use precision option 136 | return typeof arg === "number" ? this.toFixed(arg, precision) : arg; 137 | }); 138 | return line.join(" ") + "\n"; 139 | }, 140 | 141 | toFixed(num, precision) { 142 | return (+(Math.floor(+(num + "e" + precision)) + "e" + -precision)).toFixed( 143 | precision, 144 | ); 145 | }, 146 | }; 147 | -------------------------------------------------------------------------------- /lib/object/object.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFDictionary = require("./dictionary"); 4 | const PDFReference = require("./reference"); 5 | const PDFValue = require("./value"); 6 | 7 | class PDFObject { 8 | constructor(type) { 9 | this.id = null; 10 | this.rev = 0; 11 | this.properties = new PDFDictionary(); 12 | this.reference = new PDFReference(this); 13 | this.content = null; 14 | 15 | if (type) { 16 | this.prop("Type", type); 17 | } 18 | 19 | // TODO: still necessary? 20 | // used to have obj.object API for both indirect and direct objects 21 | // this.object = this 22 | } 23 | 24 | prop(key, val) { 25 | this.properties.add(key, val); 26 | } 27 | 28 | toReference() { 29 | return this.reference; 30 | } 31 | 32 | toString() { 33 | return ( 34 | this.id.toString() + 35 | " " + 36 | this.rev + 37 | " obj\n" + 38 | (this.properties.length ? this.properties.toString() + "\n" : "") + 39 | (this.content !== null ? this.content.toString() + "\n" : "") + 40 | "endobj" 41 | ); 42 | } 43 | 44 | static parse(xref, lexer, trial) { 45 | const before = lexer.pos; 46 | 47 | lexer.skipWhitespace(null, true); 48 | const id = lexer.readNumber(trial); 49 | if (id === undefined && !trial) { 50 | throw new Error("Invalid object"); 51 | } 52 | lexer.skipWhitespace(1, trial); 53 | const generation = lexer.readNumber(trial); 54 | if (generation === undefined && !trial) { 55 | throw new Error("Invalid object"); 56 | } 57 | 58 | lexer.skipWhitespace(1, trial); 59 | if (lexer.getString(3) !== "obj") { 60 | if (trial) { 61 | lexer.pos = before; 62 | return undefined; 63 | } 64 | 65 | throw new Error("Invalid object"); 66 | } 67 | 68 | lexer.shift(3); 69 | 70 | lexer.skipEOL(1, true); 71 | lexer.skipWhitespace(null, true); 72 | 73 | const obj = PDFObject.parseInner(xref, lexer); 74 | 75 | lexer.skipWhitespace(null, true); 76 | 77 | if (lexer.readString(3) !== "end") { 78 | throw new Error("Invalid object: `end` not found"); 79 | } 80 | 81 | return obj; 82 | } 83 | 84 | static parseInner(xref, lexer) { 85 | const value = PDFValue.parse(xref, lexer, true); 86 | if (value === undefined) { 87 | throw new Error("Empty object"); 88 | } 89 | 90 | lexer.skipWhitespace(null, true); 91 | 92 | const obj = new PDFObject(); 93 | if (value instanceof PDFDictionary) { 94 | obj.properties = value; 95 | 96 | if (!lexer.isEOF() && lexer.getString(6) === "stream") { 97 | lexer.shift(6); 98 | lexer.skipEOL(1); 99 | 100 | let length = obj.properties.get("Length"); 101 | if (length === undefined) { 102 | throw new Error("Invalid Stream: no length specified"); 103 | } 104 | 105 | if (typeof length === "object") { 106 | const pos = lexer.pos; 107 | length = length.object.content; 108 | lexer.pos = pos; 109 | } 110 | 111 | const PDFStream = require("./stream"); // lazy load, cause circular referecnes 112 | const stream = new PDFStream(obj); 113 | stream.content = lexer.read(length); 114 | lexer.skipEOL(1, true); 115 | 116 | // not to be expected according to the PDF spec, but there are some PDF files that indent 117 | // the stream 118 | lexer.skipWhitespace(null, true); 119 | 120 | if (lexer.readString(9) !== "endstream") { 121 | throw new Error("Invalid stream: `endstream` not found"); 122 | } 123 | 124 | lexer.skipEOL(1, true); 125 | } 126 | } else { 127 | obj.content = value; 128 | } 129 | 130 | return obj; 131 | } 132 | } 133 | 134 | module.exports = PDFObject; 135 | -------------------------------------------------------------------------------- /lib/font/afm.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PDFName = require("../object/name"); 4 | const PDFObject = require("../object/object"); 5 | const PDFString = require("../object/string"); 6 | const PDFArray = require("../object/array"); 7 | const Base = require("./base"); 8 | const StringWidth = Base.StringWidth; 9 | 10 | module.exports = class AFMFont extends Base { 11 | constructor(data) { 12 | super(); 13 | 14 | this._data = data; 15 | this.lineGap = 16 | this._data.fontBBox[3] - 17 | this._data.fontBBox[1] - 18 | (this._data.ascender - this._data.descender); 19 | this.parent = this; 20 | } 21 | 22 | instance() { 23 | return this; 24 | } 25 | 26 | encode(str) { 27 | let encoded = ""; 28 | for (let i = 0, len = str.length; i < len; ++i) { 29 | switch (str[i]) { 30 | case "\\": 31 | encoded += "\\\\"; 32 | break; 33 | case "(": 34 | encoded += "\\("; 35 | break; 36 | case ")": 37 | encoded += "\\)"; 38 | break; 39 | default: 40 | encoded += String.fromCharCode(this._charCodeFor(str[i])); 41 | } 42 | } 43 | 44 | return "(" + encoded + ")"; 45 | } 46 | 47 | _charCodeFor(c) { 48 | return c in UNICODE_TO_WIN1252 ? UNICODE_TO_WIN1252[c] : c.charCodeAt(0); 49 | } 50 | 51 | stringWidth(str, size) { 52 | const scale = size / 1000; 53 | let width = 0; 54 | const kerning = []; 55 | for (let i = 0, len = str.length; i < len; ++i) { 56 | const left = this._charCodeFor(str[i]); 57 | 58 | const advanceWidth = this._data.widths[left]; 59 | if (advanceWidth) { 60 | width += advanceWidth; 61 | } 62 | 63 | if (str[i + 1] !== undefined && left in this._data.kerning) { 64 | const right = this._charCodeFor(str[i + 1]); 65 | const offset = this._data.kerning[left][right]; 66 | if (offset !== undefined) { 67 | width += offset; 68 | kerning.push({ pos: i + 1, offset: -offset }); 69 | } 70 | } 71 | } 72 | 73 | return new StringWidth(width * scale, kerning); 74 | } 75 | 76 | lineHeight(size, includeGap) { 77 | if (includeGap == null) { 78 | includeGap = false; 79 | } 80 | 81 | const gap = includeGap ? this.lineGap : 0; 82 | 83 | return ((this._data.ascender - this._data.descender) * size) / 1000; 84 | } 85 | 86 | ascent(size) { 87 | return (this._data.ascender * size) / 1000; 88 | } 89 | 90 | descent(size) { 91 | return (this._data.descender * size) / 1000; 92 | } 93 | 94 | underlinePosition(size) { 95 | return (this._data.underlinePosition * size) / 1000; 96 | } 97 | 98 | underlineThickness(size) { 99 | return (this._data.underlineThickness * size) / 1000; 100 | } 101 | 102 | async write(doc, fontObj) { 103 | fontObj.prop("Subtype", "Type1"); 104 | fontObj.prop("BaseFont", this._data.fontName); 105 | fontObj.prop("Encoding", "WinAnsiEncoding"); 106 | 107 | await doc._writeObject(fontObj); 108 | } 109 | }; 110 | 111 | // only the once different from ISO-8859-1 are relevant, see 112 | // https://en.wikipedia.org/wiki/Windows-1252 113 | const UNICODE_TO_WIN1252 = { 114 | "\u20ac": 128, 115 | "\u201a": 130, 116 | "\u0192": 131, 117 | "\u201e": 132, 118 | "\u2026": 133, 119 | "\u2020": 134, 120 | "\u2021": 135, 121 | "\u02c6": 136, 122 | "\u2030": 137, 123 | "\u0160": 138, 124 | "\u2039": 139, 125 | "\u0152": 140, 126 | "\u017d": 142, 127 | "\u2018": 145, 128 | "\u2019": 146, 129 | "\u201c": 147, 130 | "\u201d": 148, 131 | "\u2022": 149, 132 | "\u2013": 150, 133 | "\u2014": 151, 134 | "\u02dc": 152, 135 | "\u2122": 153, 136 | "\u0161": 154, 137 | "\u203a": 155, 138 | "\u0153": 156, 139 | "\u017e": 158, 140 | "\u0178": 159, 141 | }; 142 | -------------------------------------------------------------------------------- /lib/object/reference.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const util = require("../util"); 4 | 5 | class PDFReference { 6 | constructor(obj) { 7 | Object.defineProperty(this, "object", { 8 | enumerable: true, 9 | get: () => { 10 | if (!obj) { 11 | return undefined; 12 | } 13 | 14 | if (typeof obj === "function") { 15 | obj = obj(); 16 | } 17 | 18 | return obj; 19 | }, 20 | }); 21 | } 22 | 23 | toString() { 24 | if (this.object.id === null) { 25 | throw new TypeError("Tried to write reference with `null` object id"); 26 | } 27 | return this.object.id + " " + this.object.rev + " R"; 28 | } 29 | 30 | static parse(xref, lexer, trial) { 31 | const before = lexer.pos; 32 | 33 | const id = lexer.readNumber(trial); 34 | if (id === undefined && !trial) { 35 | throw new Error("Invalid indirect"); 36 | } 37 | 38 | lexer.skipWhitespace(1, trial); 39 | const generation = lexer.readNumber(trial); 40 | if (generation === undefined && !trial) { 41 | throw new Error("Invalid indirect"); 42 | } 43 | 44 | lexer.skipWhitespace(1, trial); 45 | if (lexer.getString(1) !== "R") { 46 | if (trial) { 47 | lexer.pos = before; 48 | return undefined; 49 | } 50 | 51 | throw new Error("Invalid indirect"); 52 | } 53 | 54 | lexer.shift(1); 55 | 56 | if (!lexer.state.references) { 57 | lexer.state.references = new Map(); 58 | } 59 | const key = `${id} ${generation}`; 60 | if (lexer.state.references.has(key)) { 61 | return lexer.state.references.get(key); 62 | } 63 | 64 | const ref = new PDFReference(parseObject.bind(null, xref, lexer.outer, id)); 65 | lexer.state.references.set(key, ref); 66 | return ref; 67 | } 68 | } 69 | 70 | module.exports = PDFReference; 71 | 72 | function parseObject(xref, lexer, id) { 73 | const PDFObject = require("./object"); 74 | const Lexer = require("../parser/lexer"); 75 | 76 | const obj = xref.get(id); 77 | if (obj) { 78 | return obj; 79 | } 80 | 81 | const offset = xref.getOffset(id); 82 | if (offset === null) { 83 | const entry = xref.objects[id]; 84 | if (entry.compressed) { 85 | if (!entry.obj) { 86 | lexer.pos = xref.getOffset(entry.id); 87 | const obj = PDFObject.parse(xref, lexer); 88 | 89 | const type = obj.properties.get("Type"); 90 | if (type && type.name !== "ObjStm") { 91 | throw new Error("Expected compressed object stream"); 92 | } 93 | 94 | const src = util.inflate(obj); 95 | // console.log("STRING: ", String.fromCharCode.apply(null, src)) 96 | const innerLexer = new Lexer(src, lexer); 97 | 98 | obj.lexer = innerLexer; 99 | obj.innerObjects = []; 100 | const n = obj.properties.get("N"); 101 | for (let i = 0; i < n; ++i) { 102 | const id = innerLexer.readNumber(false); 103 | innerLexer.skipWhitespace(null, false); 104 | const offset = innerLexer.readNumber(false); 105 | innerLexer.skipWhitespace(null, true); 106 | 107 | obj.innerObjects.push({ 108 | id: id, 109 | offset: offset, 110 | obj: null, 111 | }); 112 | } 113 | 114 | entry.obj = obj; 115 | } 116 | 117 | const inner = entry.obj.innerObjects[entry.ix]; 118 | if (!inner.obj) { 119 | const innerLexer = entry.obj.lexer; 120 | innerLexer.pos = entry.obj.properties.get("First") + inner.offset; 121 | 122 | inner.obj = PDFObject.parseInner(xref, innerLexer); 123 | } 124 | 125 | return inner.obj; 126 | } else { 127 | throw new Error("Expected compressed object stream"); 128 | } 129 | } else { 130 | lexer.pos = offset; 131 | return PDFObject.parse(xref, lexer); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /test/fixtures/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const pdf = require("../../lib"); 4 | 5 | const openSansRegular = fs.readFileSync( 6 | path.join(__dirname, "font/opensans/regular.ttf"), 7 | ); 8 | const openSansBold = fs.readFileSync( 9 | path.join(__dirname, "font/opensans/bold.ttf"), 10 | ); 11 | const jpegImage = fs.readFileSync(path.join(__dirname, "image/pdfjs.jpg")); 12 | const pdfImage = fs.readFileSync(path.join(__dirname, "image/pdfjs.pdf")); 13 | const complexPdfImage = fs.readFileSync( 14 | path.join(__dirname, "image/complex.pdf"), 15 | ); 16 | 17 | exports.create = function () { 18 | return { 19 | font: { 20 | opensans: { 21 | regular: new pdf.Font(openSansRegular), 22 | bold: new pdf.Font(openSansBold), 23 | }, 24 | afm: { 25 | regular: require("../../font/Helvetica"), 26 | bold: require("../../font/Helvetica-Bold"), 27 | monoRegular: require("../../font/Courier"), 28 | monoBold: require("../../font/Courier-Bold"), 29 | }, 30 | }, 31 | image: { 32 | jpeg: new pdf.Image(jpegImage), 33 | pdf: new pdf.Image(pdfImage), 34 | complexPdf: new pdf.Image(complexPdfImage), 35 | }, 36 | lorem: { 37 | long: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\nDuis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.\n\nUt wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.", 38 | short: 39 | "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", 40 | shorter: 41 | "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.", 42 | }, 43 | document: { 44 | test: new pdf.ExternalDocument( 45 | fs.readFileSync(path.join(__dirname, "document/test.pdf")), 46 | ), 47 | pdfjsCreated: new pdf.ExternalDocument( 48 | fs.readFileSync(path.join(__dirname, "document/pdfjs-created.pdf")), 49 | ), 50 | }, 51 | }; 52 | }; 53 | -------------------------------------------------------------------------------- /lib/parser/parser.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const Lexer = require("./lexer"); 4 | const PDF = require("../object"); 5 | const util = require("../util"); 6 | 7 | class Parser { 8 | // ab ... ArrayBuffer 9 | constructor(ab) { 10 | this.src = new Uint8Array(util.toArrayBuffer(ab)); 11 | } 12 | 13 | parse() { 14 | let index = lastIndexOf(this.src, "startxref", 128); 15 | if (index === -1) { 16 | throw new Error("Invalid PDF: startxref not found"); 17 | } 18 | 19 | index += "startxref".length; 20 | 21 | // skip whitespaces 22 | while (Lexer.isWhiteSpace(this.src[++index])) {} 23 | 24 | let str = ""; 25 | while (this.src[index] >= 0x30 && this.src[index] <= 0x39) { 26 | // between 0 and 9 27 | str += String.fromCharCode(this.src[index++]); 28 | } 29 | 30 | const startXRef = parseInt(str, 10); 31 | 32 | if (isNaN(startXRef)) { 33 | throw new Error("Invalid PDF: startxref is not a number"); 34 | } 35 | 36 | const lexer = new Lexer(this.src); 37 | lexer.shift(startXRef); 38 | 39 | this.xref = PDF.Xref.parse(null, lexer); 40 | this.trailer = this.xref.trailer || PDF.Trailer.parse(this.xref, lexer); 41 | 42 | let trailer = this.trailer; 43 | while (trailer.has("Prev")) { 44 | const prevTrailerPos = trailer.get("Prev"); 45 | // Ignore prev pointers to the document beginning. This is not according to the PDF spec, but 46 | // there are PDFs out there doing it anyway. 47 | if (prevTrailerPos === 0) { 48 | break; 49 | } 50 | 51 | lexer.pos = trailer.get("Prev"); 52 | const xref = PDF.Xref.parse(null, lexer); 53 | 54 | for (let i = 0; i < xref.objects.length; ++i) { 55 | const obj = xref.objects[i]; 56 | if (obj && !this.xref.objects[i]) { 57 | this.xref.objects[i] = obj; 58 | } 59 | } 60 | 61 | trailer = xref.trailer || PDF.Trailer.parse(xref, lexer); 62 | } 63 | } 64 | 65 | static addObjectsRecursive(objects, value) { 66 | switch (true) { 67 | case value instanceof PDF.Reference: 68 | if (objects.indexOf(value.object) > -1) { 69 | break; 70 | } 71 | 72 | // skip references to other pages 73 | const type = value.object.properties.get("Type"); 74 | if (type && type.toString() === "/Page") { 75 | break; 76 | } 77 | 78 | objects.push(value.object); 79 | Parser.addObjectsRecursive(objects, value.object); 80 | break; 81 | case value instanceof PDF.Object: 82 | Parser.addObjectsRecursive(objects, value.properties); 83 | Parser.addObjectsRecursive(objects, value.content); 84 | break; 85 | case value instanceof PDF.Dictionary: 86 | for (const key in value.dictionary) { 87 | if (key === "/Parent") { 88 | // ignore parent property to prevent moving above Page objects 89 | continue; 90 | } 91 | Parser.addObjectsRecursive(objects, value.dictionary[key]); 92 | } 93 | break; 94 | case Array.isArray(value): 95 | value.forEach(function (item) { 96 | Parser.addObjectsRecursive(objects, item); 97 | }); 98 | break; 99 | } 100 | } 101 | } 102 | 103 | module.exports = Parser; 104 | 105 | function lastIndexOf(src, key, step) { 106 | if (!step) step = 1024; 107 | let pos = src.length, 108 | index = -1; 109 | 110 | while (index === -1 && pos > 0) { 111 | pos -= step - key.length; 112 | index = find(src, key, Math.max(pos, 0), step, true); 113 | } 114 | 115 | return index; 116 | } 117 | 118 | function find(src, key, pos, limit, backwards) { 119 | if (pos + limit > src.length) { 120 | limit = src.length - pos; 121 | } 122 | 123 | const str = String.fromCharCode.apply(null, src.subarray(pos, pos + limit)); 124 | let index = backwards ? str.lastIndexOf(key) : str.indexOf(key); 125 | if (index > -1) { 126 | index += pos; 127 | } 128 | return index; 129 | } 130 | --------------------------------------------------------------------------------