@laminapdf/sdk 0.2.0-alpha.2 → 0.2.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -8
- package/dist/api.d.ts +8 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +4 -2
- package/dist/api.js.map +1 -1
- package/dist/builder.d.ts +43 -22
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +122 -83
- package/dist/builder.js.map +1 -1
- package/dist/document.d.ts +89 -22
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +177 -67
- package/dist/document.js.map +1 -1
- package/dist/errors.d.ts +5 -5
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +10 -16
- package/dist/errors.js.map +1 -1
- package/dist/extract-job.d.ts +100 -0
- package/dist/extract-job.d.ts.map +1 -0
- package/dist/extract-job.js +177 -0
- package/dist/extract-job.js.map +1 -0
- package/dist/extract.d.ts +57 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +5 -0
- package/dist/extract.js.map +1 -0
- package/dist/font.d.ts +13 -6
- package/dist/font.d.ts.map +1 -1
- package/dist/font.js +50 -20
- package/dist/font.js.map +1 -1
- package/dist/generated/builder-enums.d.ts +3 -3
- package/dist/generated/builder-enums.d.ts.map +1 -1
- package/dist/generated/builder-enums.js +10 -0
- package/dist/generated/builder-enums.js.map +1 -1
- package/dist/generated/codes.d.ts +18 -6
- package/dist/generated/codes.d.ts.map +1 -1
- package/dist/generated/codes.js +114 -5
- package/dist/generated/codes.js.map +1 -1
- package/dist/generated/explain.d.ts.map +1 -1
- package/dist/generated/explain.js +45 -11
- package/dist/generated/explain.js.map +1 -1
- package/dist/generated/extract.d.ts +282 -0
- package/dist/generated/extract.d.ts.map +1 -0
- package/dist/generated/extract.js +4 -0
- package/dist/generated/extract.js.map +1 -0
- package/dist/init.d.ts +13 -5
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +23 -11
- package/dist/init.js.map +1 -1
- package/dist/internal/handle.d.ts +58 -0
- package/dist/internal/handle.d.ts.map +1 -0
- package/dist/internal/handle.js +113 -0
- package/dist/internal/handle.js.map +1 -0
- package/dist/internal/instance.d.ts +56 -0
- package/dist/internal/instance.d.ts.map +1 -0
- package/dist/internal/instance.js +125 -0
- package/dist/internal/instance.js.map +1 -0
- package/dist/source/http.d.ts +1 -1
- package/dist/source/http.js +2 -2
- package/dist/source/http.js.map +1 -1
- package/dist/source/range.d.ts +6 -1
- package/dist/source/range.d.ts.map +1 -1
- package/dist/source/range.js +10 -6
- package/dist/source/range.js.map +1 -1
- package/dist/text.d.ts +3 -3
- package/dist/text.d.ts.map +1 -1
- package/dist/text.js +3 -3
- package/dist/text.js.map +1 -1
- package/dist/types.d.ts +19 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/dist/worker/client.d.ts +3 -2
- package/dist/worker/client.d.ts.map +1 -1
- package/dist/worker/client.js +6 -4
- package/dist/worker/client.js.map +1 -1
- package/dist/worker/entry.js +1 -1
- package/dist/worker/entry.js.map +1 -1
- package/dist/worker/server.d.ts.map +1 -1
- package/dist/worker/server.js +42 -9
- package/dist/worker/server.js.map +1 -1
- package/dist/worker/support.d.ts +13 -3
- package/dist/worker/support.d.ts.map +1 -1
- package/dist/worker/support.js +5 -0
- package/dist/worker/support.js.map +1 -1
- package/dist/worker/types.d.ts +8 -1
- package/dist/worker/types.d.ts.map +1 -1
- package/docs/AGENTS.md +14 -0
- package/docs/api/README.md +166 -0
- package/docs/api/classes/Builder.md +467 -0
- package/docs/api/classes/Document.md +854 -0
- package/docs/api/classes/ExtractJob.md +236 -0
- package/docs/api/classes/Font.md +74 -0
- package/docs/api/classes/Image.md +140 -0
- package/docs/api/classes/LaminaPdfError.md +189 -0
- package/docs/api/classes/Page.md +178 -0
- package/docs/api/classes/PageRef.md +253 -0
- package/docs/api/classes/PageText.md +129 -0
- package/docs/api/classes/PageTextRun.md +167 -0
- package/docs/api/classes/PendingPath.md +97 -0
- package/docs/api/classes/SaveJob.md +205 -0
- package/docs/api/classes/Surface.md +331 -0
- package/docs/api/classes/Transaction.md +111 -0
- package/docs/api/functions/callbackSource.md +33 -0
- package/docs/api/functions/extractWithModels.md +70 -0
- package/docs/api/functions/httpRangeSource.md +48 -0
- package/docs/api/functions/init.md +36 -0
- package/docs/api/functions/isExtractLoaded.md +21 -0
- package/docs/api/functions/isInitialized.md +19 -0
- package/docs/api/functions/isLaminaPdfError.md +41 -0
- package/docs/api/functions/isTextLoaded.md +19 -0
- package/docs/api/functions/loadExtract.md +28 -0
- package/docs/api/functions/loadText.md +28 -0
- package/docs/api/functions/onLeak.md +29 -0
- package/docs/api/functions/reset.md +22 -0
- package/docs/api/functions/wasmMemory.md +22 -0
- package/docs/api/interfaces/BuilderOptions.md +108 -0
- package/docs/api/interfaces/ByteRange.md +29 -0
- package/docs/api/interfaces/ChunkOptions.md +37 -0
- package/docs/api/interfaces/Diagnostic.md +67 -0
- package/docs/api/interfaces/ExtractOptions.md +92 -0
- package/docs/api/interfaces/ExtractResult.md +37 -0
- package/docs/api/interfaces/FontInfo.md +37 -0
- package/docs/api/interfaces/HostModels.md +57 -0
- package/docs/api/interfaces/HttpRangeOptions.md +78 -0
- package/docs/api/interfaces/InitOptions.md +40 -0
- package/docs/api/interfaces/LayoutRegion.md +38 -0
- package/docs/api/interfaces/Leak.md +21 -0
- package/docs/api/interfaces/Location.md +53 -0
- package/docs/api/interfaces/ModelInfo.md +45 -0
- package/docs/api/interfaces/ModelRequest.md +45 -0
- package/docs/api/interfaces/Models.md +45 -0
- package/docs/api/interfaces/OcrWord.md +37 -0
- package/docs/api/interfaces/OpLog.md +25 -0
- package/docs/api/interfaces/OpenOptions.md +37 -0
- package/docs/api/interfaces/PageTextOptions.md +21 -0
- package/docs/api/interfaces/PathBuilder.md +195 -0
- package/docs/api/interfaces/QuadPoint.md +25 -0
- package/docs/api/interfaces/RangeOptions.md +54 -0
- package/docs/api/interfaces/RangeSource.md +59 -0
- package/docs/api/interfaces/StrokeStyle.md +61 -0
- package/docs/api/interfaces/TagSpec.md +69 -0
- package/docs/api/interfaces/TextBoxOptions.md +93 -0
- package/docs/api/interfaces/TextGlyph.md +29 -0
- package/docs/api/interfaces/TextOptions.md +101 -0
- package/docs/api/interfaces/WasmMemory.md +37 -0
- package/docs/api/type-aliases/AfRelationship.md +15 -0
- package/docs/api/type-aliases/Align.md +15 -0
- package/docs/api/type-aliases/ArtifactKind.md +15 -0
- package/docs/api/type-aliases/AttachmentOptions.md +15 -0
- package/docs/api/type-aliases/BlendMode.md +15 -0
- package/docs/api/type-aliases/BoxRect.md +15 -0
- package/docs/api/type-aliases/BuildOptions.md +15 -0
- package/docs/api/type-aliases/Cell.md +71 -0
- package/docs/api/type-aliases/Chunk.md +79 -0
- package/docs/api/type-aliases/Color.md +16 -0
- package/docs/api/type-aliases/Coordinates.md +55 -0
- package/docs/api/type-aliases/Direction.md +15 -0
- package/docs/api/type-aliases/DocumentInfo.md +15 -0
- package/docs/api/type-aliases/DropReason.md +15 -0
- package/docs/api/type-aliases/Dropped.md +59 -0
- package/docs/api/type-aliases/Edited.md +15 -0
- package/docs/api/type-aliases/ErrorCode.md +15 -0
- package/docs/api/type-aliases/ErrorKind.md +15 -0
- package/docs/api/type-aliases/ExtractFormat.md +15 -0
- package/docs/api/type-aliases/ExtractJobOptions.md +31 -0
- package/docs/api/type-aliases/ExtractStep.md +61 -0
- package/docs/api/type-aliases/ExtractedBlock.md +94 -0
- package/docs/api/type-aliases/ExtractedPage.md +73 -0
- package/docs/api/type-aliases/ExtractedSource.md +48 -0
- package/docs/api/type-aliases/Extraction.md +22 -0
- package/docs/api/type-aliases/FillRule.md +15 -0
- package/docs/api/type-aliases/Fit.md +15 -0
- package/docs/api/type-aliases/Flags.md +15 -0
- package/docs/api/type-aliases/Generator.md +43 -0
- package/docs/api/type-aliases/GlyphQuad.md +15 -0
- package/docs/api/type-aliases/InfoPatch.md +15 -0
- package/docs/api/type-aliases/LaminaPdfWarning.md +16 -0
- package/docs/api/type-aliases/LimitName.md +15 -0
- package/docs/api/type-aliases/Limits.md +15 -0
- package/docs/api/type-aliases/LineBox.md +15 -0
- package/docs/api/type-aliases/LineCap.md +15 -0
- package/docs/api/type-aliases/LineJoin.md +15 -0
- package/docs/api/type-aliases/LinkTarget.md +15 -0
- package/docs/api/type-aliases/Metadata.md +15 -0
- package/docs/api/type-aliases/ModelResult.md +15 -0
- package/docs/api/type-aliases/Op.md +15 -0
- package/docs/api/type-aliases/OutlineEntry.md +15 -0
- package/docs/api/type-aliases/OutlineNode.md +15 -0
- package/docs/api/type-aliases/Overflow.md +15 -0
- package/docs/api/type-aliases/PageInfo.md +15 -0
- package/docs/api/type-aliases/PageLabel.md +15 -0
- package/docs/api/type-aliases/PageSizeName.md +15 -0
- package/docs/api/type-aliases/Point.md +15 -0
- package/docs/api/type-aliases/Profile.md +15 -0
- package/docs/api/type-aliases/Progress.md +15 -0
- package/docs/api/type-aliases/ReadingOrder.md +15 -0
- package/docs/api/type-aliases/Rect.md +15 -0
- package/docs/api/type-aliases/Region.md +31 -0
- package/docs/api/type-aliases/RegionLabel.md +15 -0
- package/docs/api/type-aliases/RenderMode.md +15 -0
- package/docs/api/type-aliases/Rotation.md +15 -0
- package/docs/api/type-aliases/SaveOptions.md +15 -0
- package/docs/api/type-aliases/SaveReport.md +15 -0
- package/docs/api/type-aliases/Span.md +74 -0
- package/docs/api/type-aliases/SplitOn.md +15 -0
- package/docs/api/type-aliases/Standard14.md +15 -0
- package/docs/api/type-aliases/StructRef.md +41 -0
- package/docs/api/type-aliases/StructSource.md +17 -0
- package/docs/api/type-aliases/StructureKid.md +15 -0
- package/docs/api/type-aliases/StructureNode.md +16 -0
- package/docs/api/type-aliases/TableDetector.md +15 -0
- package/docs/api/type-aliases/TableMode.md +15 -0
- package/docs/api/type-aliases/TableScope.md +15 -0
- package/docs/api/type-aliases/Tag.md +15 -0
- package/docs/api/type-aliases/TagName.md +15 -0
- package/docs/api/type-aliases/Target.md +15 -0
- package/docs/api/type-aliases/TargetView.md +18 -0
- package/docs/api/type-aliases/TextLayer.md +72 -0
- package/docs/api/type-aliases/TextLayerStatus.md +15 -0
- package/docs/api/type-aliases/TextLayout.md +15 -0
- package/docs/api/type-aliases/TextRange.md +33 -0
- package/docs/api/type-aliases/TextRunInfo.md +15 -0
- package/docs/api/type-aliases/Transform.md +15 -0
- package/docs/api/type-aliases/UndoOutcome.md +15 -0
- package/docs/api/type-aliases/ViewerPreferences.md +15 -0
- package/docs/api/type-aliases/WarningCode.md +15 -0
- package/docs/api/type-aliases/WasmSource.md +16 -0
- package/docs/api/variables/Matrix.md +99 -0
- package/docs/api/variables/PAGE_SIZES.md +15 -0
- package/docs/api/variables/TAG_NAMES.md +15 -0
- package/docs/errors/README.md +173 -0
- package/docs/errors/aes-padding-invalid.md +34 -0
- package/docs/errors/already-initialized.md +34 -0
- package/docs/errors/binary-output-to-terminal.md +35 -0
- package/docs/errors/builder-finished.md +35 -0
- package/docs/errors/cancelled.md +35 -0
- package/docs/errors/cmap-invalid.md +42 -0
- package/docs/errors/collection-index.md +33 -0
- package/docs/errors/color-components.md +33 -0
- package/docs/errors/content-bad-operands.md +36 -0
- package/docs/errors/content-path-unpainted.md +36 -0
- package/docs/errors/content-resource-missing.md +37 -0
- package/docs/errors/content-syntax.md +36 -0
- package/docs/errors/content-unbalanced-bt.md +36 -0
- package/docs/errors/content-unbalanced-marked.md +36 -0
- package/docs/errors/content-unbalanced-q.md +36 -0
- package/docs/errors/content-unknown-operator.md +37 -0
- package/docs/errors/covered-text.md +33 -0
- package/docs/errors/data-pending.md +34 -0
- package/docs/errors/decompress.md +34 -0
- package/docs/errors/device-color-without-output-intent.md +33 -0
- package/docs/errors/direct-page.md +34 -0
- package/docs/errors/disposed.md +34 -0
- package/docs/errors/document-changed.md +34 -0
- package/docs/errors/embedded-file-forbidden.md +35 -0
- package/docs/errors/embedded-file-without-mime-type.md +36 -0
- package/docs/errors/encoding-full.md +34 -0
- package/docs/errors/encrypted-base.md +33 -0
- package/docs/errors/extract-column-order-uncertain.md +38 -0
- package/docs/errors/extract-limit-blocks.md +35 -0
- package/docs/errors/extract-module-not-loaded.md +37 -0
- package/docs/errors/extract-needs-ocr.md +37 -0
- package/docs/errors/extract-reading-order-ambiguous.md +32 -0
- package/docs/errors/extract-struct-tree-ignored.md +32 -0
- package/docs/errors/extract-table-ambiguous.md +32 -0
- package/docs/errors/extract-table-limit.md +35 -0
- package/docs/errors/extract-untagged-content.md +32 -0
- package/docs/errors/fetch-failed.md +34 -0
- package/docs/errors/flate-truncated.md +34 -0
- package/docs/errors/flow-content-too-large.md +34 -0
- package/docs/errors/flow-keep-broken.md +34 -0
- package/docs/errors/flow-layout-unstable.md +38 -0
- package/docs/errors/font-invalid.md +36 -0
- package/docs/errors/font-mismatch.md +34 -0
- package/docs/errors/font-not-embeddable.md +34 -0
- package/docs/errors/font-not-embedded.md +35 -0
- package/docs/errors/font-not-shapable.md +33 -0
- package/docs/errors/glyph-out-of-range.md +34 -0
- package/docs/errors/glyph-without-unicode.md +37 -0
- package/docs/errors/heading-level-skipped.md +36 -0
- package/docs/errors/image-data-length.md +33 -0
- package/docs/errors/image-too-large.md +32 -0
- package/docs/errors/inline-image-unterminated.md +36 -0
- package/docs/errors/input-unreadable.md +37 -0
- package/docs/errors/instance-reset.md +34 -0
- package/docs/errors/invalid-argument.md +33 -0
- package/docs/errors/invalid-axis-value.md +32 -0
- package/docs/errors/invalid-box.md +34 -0
- package/docs/errors/invalid-color.md +32 -0
- package/docs/errors/invalid-command-line.md +44 -0
- package/docs/errors/invalid-date.md +34 -0
- package/docs/errors/invalid-entry.md +34 -0
- package/docs/errors/invalid-feature.md +32 -0
- package/docs/errors/invalid-font.md +32 -0
- package/docs/errors/invalid-geometry.md +32 -0
- package/docs/errors/invalid-icc-profile.md +32 -0
- package/docs/errors/invalid-image.md +33 -0
- package/docs/errors/invalid-op-log.md +33 -0
- package/docs/errors/invalid-page-label.md +34 -0
- package/docs/errors/invalid-page-labels.md +32 -0
- package/docs/errors/invalid-page-range.md +37 -0
- package/docs/errors/invalid-rotate.md +34 -0
- package/docs/errors/invalid-spans.md +39 -0
- package/docs/errors/invalid-structure-nesting.md +38 -0
- package/docs/errors/invalid-structure.md +34 -0
- package/docs/errors/invalid-text-range.md +33 -0
- package/docs/errors/job-finished.md +34 -0
- package/docs/errors/limit-exceeded.md +38 -0
- package/docs/errors/link-without-contents.md +33 -0
- package/docs/errors/link-without-structure.md +37 -0
- package/docs/errors/markdown-html-ignored.md +33 -0
- package/docs/errors/markdown-image-missing.md +34 -0
- package/docs/errors/missing-alt-text.md +33 -0
- package/docs/errors/missing-glyph.md +33 -0
- package/docs/errors/missing-header.md +37 -0
- package/docs/errors/missing-lang.md +32 -0
- package/docs/errors/missing-root.md +34 -0
- package/docs/errors/missing-title.md +32 -0
- package/docs/errors/model-failed.md +33 -0
- package/docs/errors/model-needs-host.md +40 -0
- package/docs/errors/model-result-unexpected.md +36 -0
- package/docs/errors/name-tree-node-skipped.md +34 -0
- package/docs/errors/nested-transaction.md +34 -0
- package/docs/errors/no-pending-save.md +32 -0
- package/docs/errors/no-transaction.md +33 -0
- package/docs/errors/not-initialized.md +35 -0
- package/docs/errors/not-pdf.md +33 -0
- package/docs/errors/not-rebased.md +35 -0
- package/docs/errors/object-number-out-of-range.md +38 -0
- package/docs/errors/objstm-index-mismatch.md +36 -0
- package/docs/errors/out-of-memory.md +38 -0
- package/docs/errors/outline-cycle.md +34 -0
- package/docs/errors/outline-item-skipped.md +34 -0
- package/docs/errors/output-unwritable.md +37 -0
- package/docs/errors/overwrite-refused.md +36 -0
- package/docs/errors/page-count-mismatch.md +34 -0
- package/docs/errors/page-index-out-of-range.md +34 -0
- package/docs/errors/page-tree-node-skipped.md +35 -0
- package/docs/errors/password-required.md +36 -0
- package/docs/errors/path-outside-roots.md +32 -0
- package/docs/errors/perms-mismatch.md +34 -0
- package/docs/errors/profile-version-conflict.md +35 -0
- package/docs/errors/redaction-not-applied.md +32 -0
- package/docs/errors/reference-cycle.md +35 -0
- package/docs/errors/render-backend-unavailable.md +32 -0
- package/docs/errors/render-failed.md +33 -0
- package/docs/errors/render-too-large.md +36 -0
- package/docs/errors/repaired-base.md +34 -0
- package/docs/errors/requires-rewrite.md +38 -0
- package/docs/errors/revision-index-out-of-range.md +34 -0
- package/docs/errors/saved-not-rebased.md +34 -0
- package/docs/errors/sink-not-empty.md +33 -0
- package/docs/errors/sink.md +31 -0
- package/docs/errors/source-budget-exceeded.md +34 -0
- package/docs/errors/source-changed.md +33 -0
- package/docs/errors/source.md +30 -0
- package/docs/errors/stale-generation.md +34 -0
- package/docs/errors/stream-length-recovered.md +37 -0
- package/docs/errors/strict.md +34 -0
- package/docs/errors/subset-failed.md +31 -0
- package/docs/errors/syntax.md +34 -0
- package/docs/errors/tag-element-not-open.md +41 -0
- package/docs/errors/tag-element-unclosed.md +34 -0
- package/docs/errors/text-in-earlier-revision.md +36 -0
- package/docs/errors/text-module-not-loaded.md +36 -0
- package/docs/errors/text-overflow.md +34 -0
- package/docs/errors/tounicode-conflict.md +32 -0
- package/docs/errors/type.md +34 -0
- package/docs/errors/unexpected-eof.md +34 -0
- package/docs/errors/unknown-axis.md +33 -0
- package/docs/errors/unknown-code.md +36 -0
- package/docs/errors/unknown-destination.md +33 -0
- package/docs/errors/unknown-font.md +33 -0
- package/docs/errors/unknown-option.md +33 -0
- package/docs/errors/unknown-page.md +34 -0
- package/docs/errors/unmapped-glyph.md +36 -0
- package/docs/errors/unnumbered-heading-misused.md +34 -0
- package/docs/errors/unset-object.md +30 -0
- package/docs/errors/unsupported-filter.md +34 -0
- package/docs/errors/unsupported-image.md +33 -0
- package/docs/errors/unsupported-layout-option.md +33 -0
- package/docs/errors/unsupported-security-handler.md +34 -0
- package/docs/errors/unsupported-version.md +32 -0
- package/docs/errors/untagged-content.md +35 -0
- package/docs/errors/warnings-truncated.md +35 -0
- package/docs/errors/wasm-trap.md +37 -0
- package/docs/errors/whole-font-rebuilt.md +30 -0
- package/docs/errors/worker-crashed.md +33 -0
- package/docs/errors/worker-failed.md +32 -0
- package/docs/errors/worker-version-mismatch.md +34 -0
- package/docs/errors/would-invalidate-signatures.md +38 -0
- package/docs/errors/wrong-document.md +33 -0
- package/docs/errors/wrong-password.md +34 -0
- package/docs/errors/xobject-cycle.md +36 -0
- package/docs/errors/xref-repaired.md +36 -0
- package/docs/errors/xrefstm-ignored.md +34 -0
- package/docs/llms.txt +94 -0
- package/docs/pages/concepts/dependencies.md +44 -0
- package/docs/pages/concepts/drawing-pages.md +76 -0
- package/docs/pages/concepts/errors-and-warnings.md +68 -0
- package/docs/pages/concepts/how-lamina-thinks.md +98 -0
- package/docs/pages/concepts/pages-and-coordinates.md +75 -0
- package/docs/pages/guides/accessible-pdfs.md +262 -0
- package/docs/pages/guides/archival-pdfs.md +433 -0
- package/docs/pages/guides/edit-a-pdf.md +143 -0
- package/docs/pages/guides/flowing-pages.md +379 -0
- package/docs/pages/guides/fonts-and-text.md +204 -0
- package/docs/pages/guides/images-and-vectors.md +221 -0
- package/docs/pages/guides/keep-the-page-responsive.md +85 -0
- package/docs/pages/guides/links-outlines-and-metadata.md +224 -0
- package/docs/pages/guides/open-large-files.md +69 -0
- package/docs/pages/guides/password-protected-files.md +70 -0
- package/docs/pages/guides/save-a-pdf.md +154 -0
- package/docs/pages/guides/test-generated-pdfs.md +324 -0
- package/docs/pages/guides/untrusted-files.md +82 -0
- package/docs/pages/index.md +33 -0
- package/docs/pages/install.md +79 -0
- package/docs/pages/quick-start/browser.md +71 -0
- package/docs/pages/quick-start/bun.md +64 -0
- package/docs/pages/quick-start/deno.md +62 -0
- package/docs/pages/quick-start/node.md +65 -0
- package/docs/pages/quick-start/rust.md +62 -0
- package/docs/pages/quick-start/workers.md +139 -0
- package/docs/pages/recipes/cloudflare-worker.md +115 -0
- package/docs/pages/recipes/element-flow.md +248 -0
- package/docs/pages/recipes/extract-markdown.md +152 -0
- package/docs/pages/recipes/generate-then-edit.md +142 -0
- package/docs/pages/recipes/hello.md +101 -0
- package/docs/pages/recipes/images-and-vectors.md +216 -0
- package/docs/pages/recipes/invoice.md +233 -0
- package/docs/pages/recipes/links-and-outlines.md +222 -0
- package/docs/pages/recipes/multilingual.md +196 -0
- package/docs/pages/recipes/open-untrusted.md +65 -0
- package/docs/pages/recipes/pdf-a-archive.md +212 -0
- package/docs/pages/recipes/pdf-a3-invoice-with-xml.md +229 -0
- package/docs/pages/recipes/pdf-ua-report.md +244 -0
- package/docs/pages/recipes/report.md +125 -0
- package/docs/pages/recipes/web-worker-client.md +64 -0
- package/docs/pages/recipes.md +48 -0
- package/docs/pages/reference/benchmarks.md +93 -0
- package/docs/pages/reference/changelog.md +134 -0
- package/docs/pages/reference/codes.md +14 -0
- package/docs/pages/reference/glossary.md +108 -0
- package/docs/pages/reference/rust.md +36 -0
- package/docs/pages/reference/stability.md +59 -0
- package/docs/pages/start/coding-agents.md +45 -0
- package/docs/pages/start/first-pdf.md +81 -0
- package/docs/pages/troubleshooting.md +74 -0
- package/docs/renames.json +86 -0
- package/docs/schemas/error.schema.json +322 -0
- package/docs/schemas/renames.schema.json +29 -0
- package/package.json +7 -3
- package/schemas/error.schema.json +37 -3
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Install"
|
|
3
|
+
description: "Install Lamina PDF from npm or crates.io, and find every published package."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/install/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Install
|
|
9
|
+
|
|
10
|
+
Install Lamina PDF from npm or crates.io, and find every published package.
|
|
11
|
+
|
|
12
|
+
Lamina PDF is published to npm as [`@laminapdf/sdk`](https://www.npmjs.com/package/@laminapdf/sdk) and to crates.io as [`laminapdf`](https://crates.io/crates/laminapdf). Both are alpha releases, so name the version when you install them. A plain `npm install` or `cargo add` picks a `0.0.0` placeholder that only reserves the name.
|
|
13
|
+
|
|
14
|
+
## TypeScript: `@laminapdf/sdk`
|
|
15
|
+
|
|
16
|
+
Install the version these docs describe:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @laminapdf/sdk@0.2.0-alpha.4
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`npm install @laminapdf/sdk@alpha` installs the newest alpha, whichever version that is. pnpm, Yarn and Bun take the same name (`pnpm add @laminapdf/sdk@0.2.0-alpha.4`), and Deno takes `npm:@laminapdf/sdk@0.2.0-alpha.4`. The package brings [`@laminapdf/wasm`](https://www.npmjs.com/package/@laminapdf/wasm), its WebAssembly modules, as a dependency.
|
|
23
|
+
|
|
24
|
+
The package is ESM only. It has one entry per runtime (the `workerd`, `deno`, `browser`, `node` and `default` export conditions), and each entry loads the `.wasm` its own way. The [quick starts](../pages/quick-start/browser.md) show each one.
|
|
25
|
+
|
|
26
|
+
## Rust: `laminapdf`
|
|
27
|
+
|
|
28
|
+
Add the crate at the version these docs describe:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
cargo add laminapdf@=0.2.0-alpha.4
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The command writes `laminapdf = "=0.2.0-alpha.4"` to `Cargo.toml`, which keeps your project on this release until you change it: alphas promise nothing to each other (see the [stability policy](../pages/reference/stability.md)). The default features are `text` (fonts and text, which includes the builder and text extraction) and `saslprep` (passwords for AES-256 files). For reading and editing only, add `--no-default-features`.
|
|
35
|
+
|
|
36
|
+
## Every published package
|
|
37
|
+
|
|
38
|
+
Most projects need only the two above. The table links every published package to its registry page:
|
|
39
|
+
|
|
40
|
+
| Package | Registry | What it holds |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `@laminapdf/sdk` | [npm](https://www.npmjs.com/package/@laminapdf/sdk) | The TypeScript API, for every JavaScript runtime |
|
|
43
|
+
| `@laminapdf/wasm` | [npm](https://www.npmjs.com/package/@laminapdf/wasm) | The WebAssembly modules under `@laminapdf/sdk` |
|
|
44
|
+
| `@laminapdf/render` | [npm](https://www.npmjs.com/package/@laminapdf/render) | The interface that page renderers implement |
|
|
45
|
+
| `@laminapdf/render-pdfium` | [npm](https://www.npmjs.com/package/@laminapdf/render-pdfium) | The default web renderer, built on PDFium |
|
|
46
|
+
| `@laminapdf/render-hayro` | [npm](https://www.npmjs.com/package/@laminapdf/render-hayro) | A second web renderer, built on hayro |
|
|
47
|
+
| `@laminapdf/viewer` | [npm](https://www.npmjs.com/package/@laminapdf/viewer) | The core of a web PDF viewer, one worker per document |
|
|
48
|
+
| `laminapdf` | [crates.io](https://crates.io/crates/laminapdf) | The Rust API: open, read, edit, save and build PDFs |
|
|
49
|
+
| `laminapdf-syntax` | [crates.io](https://crates.io/crates/laminapdf-syntax) | Objects, the lexer, the parser and stream filters |
|
|
50
|
+
| `laminapdf-cos` | [crates.io](https://crates.io/crates/laminapdf-cos) | File structure: the tolerant reader and the writer |
|
|
51
|
+
| `laminapdf-model` | [crates.io](https://crates.io/crates/laminapdf-model) | Typed views of pages, outlines, metadata and labels |
|
|
52
|
+
| `laminapdf-ops` | [crates.io](https://crates.io/crates/laminapdf-ops) | Editing ops, transactions, undo and redo |
|
|
53
|
+
| `laminapdf-save` | [crates.io](https://crates.io/crates/laminapdf-save) | Incremental and full saves |
|
|
54
|
+
| `laminapdf-content` | [crates.io](https://crates.io/crates/laminapdf-content) | The content-stream interpreter |
|
|
55
|
+
| `laminapdf-text` | [crates.io](https://crates.io/crates/laminapdf-text) | Fonts, shaping and line breaking |
|
|
56
|
+
| `laminapdf-build` | [crates.io](https://crates.io/crates/laminapdf-build) | The builder that generates PDFs |
|
|
57
|
+
| `laminapdf-source` | [crates.io](https://crates.io/crates/laminapdf-source) | Byte sources filled range by range, for remote files |
|
|
58
|
+
| `laminapdf-render` | [crates.io](https://crates.io/crates/laminapdf-render) | The Rust interface that page renderers implement |
|
|
59
|
+
| `laminapdf-render-pdfium` | [crates.io](https://crates.io/crates/laminapdf-render-pdfium) | The native PDFium renderer |
|
|
60
|
+
|
|
61
|
+
*Each JavaScript package and what it depends on, beside the Rust API.*
|
|
62
|
+
|
|
63
|
+
In JavaScript, `@laminapdf/sdk` is the TypeScript API, and it depends on `@laminapdf/wasm`, whose WebAssembly the SDK loads as three modules: base, extract and text. `@laminapdf/viewer`, a web PDF viewer, depends on `@laminapdf/sdk`, on `@laminapdf/render`, the interface that renderers implement, and on both renderers. Those renderers are `@laminapdf/render-pdfium`, the default, and `@laminapdf/render-hayro`, which also uses `@laminapdf/wasm`. In Rust, the API is one crate, `laminapdf`, with features instead of modules.
|
|
64
|
+
|
|
65
|
+
## Try a change before it's released
|
|
66
|
+
|
|
67
|
+
An unreleased change is only in the repository, so build the packages from a checkout. You need Rust (the toolchain in `rust-toolchain.toml` installs itself), Node 22 or newer and pnpm:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
pnpm install
|
|
71
|
+
cargo xtask wasm # builds packages/wasm/dist/ and packages/sdk/dist/, then tests them
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then run `pnpm pack` in `packages/wasm` and in `packages/sdk`, and install both `.tgz` files in one command: `@laminapdf/sdk` depends on `@laminapdf/wasm` at the same version, which the registry may not have yet. In Rust, use a path dependency: `laminapdf = { path = "../laminapdf/crates/laminapdf" }`.
|
|
75
|
+
|
|
76
|
+
## API docs
|
|
77
|
+
|
|
78
|
+
- TypeScript: the [`@laminapdf/sdk` reference](../api/README.md), generated from the package's TSDoc comments.
|
|
79
|
+
- Rust: the [Rust API](../pages/reference/rust.md) page, which links each crate's docs on docs.rs.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Browser and bundlers"
|
|
3
|
+
description: "Load @laminapdf/sdk in a web page through Vite or another bundler."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/quick-start/browser/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Browser and bundlers
|
|
9
|
+
|
|
10
|
+
Load @laminapdf/sdk in a web page through Vite or another bundler.
|
|
11
|
+
|
|
12
|
+
In a browser, `@laminapdf/sdk` finds its `.wasm` files with `new URL("…", import.meta.url)`. Your bundler emits them as assets next to your code, so they load from your own origin.
|
|
13
|
+
|
|
14
|
+
This app builds one A4 page that says "Hello, world!" and offers it to your user as `hello.pdf`:
|
|
15
|
+
|
|
16
|
+
**TypeScript**
|
|
17
|
+
|
|
18
|
+
`packages/sdk/examples/quick-start/browser.ts`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Builds a one-page PDF that says "Hello, world!" in the browser and offers it as a download.
|
|
22
|
+
import { Builder, type BuilderOptions, Font, init } from "@laminapdf/sdk";
|
|
23
|
+
|
|
24
|
+
/** The space between the page's edge and the text, in points (72 points = 1 inch). */
|
|
25
|
+
const MARGIN = 72;
|
|
26
|
+
/** The text's size in points; its baseline sits this far below the margin. */
|
|
27
|
+
const FONT_SIZE = 24;
|
|
28
|
+
|
|
29
|
+
/** Offers the PDF to your user as hello.pdf. */
|
|
30
|
+
function download(pdf: Uint8Array): void {
|
|
31
|
+
// `slice()` copies the bytes into a plain ArrayBuffer, which is what a Blob accepts.
|
|
32
|
+
const url = URL.createObjectURL(new Blob([pdf.slice()], { type: "application/pdf" }));
|
|
33
|
+
const link = document.createElement("a");
|
|
34
|
+
link.href = url;
|
|
35
|
+
link.download = "hello.pdf";
|
|
36
|
+
link.click();
|
|
37
|
+
// Revoke the URL after the click has been handled; revoking it at once can cancel the download.
|
|
38
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Builds the page and hands its bytes to `save`, which offers a download unless you pass another `save` function. */
|
|
42
|
+
export async function hello(
|
|
43
|
+
save: (pdf: Uint8Array) => void | Promise<void> = download,
|
|
44
|
+
options: BuilderOptions = {},
|
|
45
|
+
): Promise<void> {
|
|
46
|
+
await init({ text: true });
|
|
47
|
+
using builder = new Builder(options);
|
|
48
|
+
const page = builder.page("A4");
|
|
49
|
+
// Points from the page's top-left corner, y down; the text's baseline sits FONT_SIZE points below the margin.
|
|
50
|
+
page.surface().text("Hello, world!", {
|
|
51
|
+
font: Font.standard("Helvetica"),
|
|
52
|
+
size: FONT_SIZE,
|
|
53
|
+
at: [MARGIN, MARGIN + FONT_SIZE],
|
|
54
|
+
});
|
|
55
|
+
page.finish();
|
|
56
|
+
using doc = builder.finish();
|
|
57
|
+
await save(doc.save());
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Result:** [hello.pdf](https://docs.laminapdf.com/examples/pdf/hello.pdf)
|
|
62
|
+
|
|
63
|
+
Import `hello` from your page's script and call it when your user asks for the file, for example from a button: `button.addEventListener("click", () => hello())`. Then start the page with your bundler, such as `npx vite`.
|
|
64
|
+
|
|
65
|
+
Lamina PDF's tests bundle this app with Vite, run it in Chromium, Firefox and WebKit, and check that it builds the reference PDF byte for byte.
|
|
66
|
+
|
|
67
|
+
- **Modules on demand:** a page that never draws text never downloads the text module; see [three modules in JavaScript](../../pages/concepts/how-lamina-thinks.md#three-modules-in-javascript).
|
|
68
|
+
- **Vite:** exclude the package from dependency pre-bundling (`optimizeDeps.exclude: ["@laminapdf/sdk"]` in `vite.config.ts`), so the `.wasm` files stay next to the JavaScript that loads them.
|
|
69
|
+
- **Content Security Policy:** compiling WebAssembly needs `script-src 'wasm-unsafe-eval'`. Nothing else is needed.
|
|
70
|
+
|
|
71
|
+
Next: draw text with [fonts](../../pages/guides/fonts-and-text.md), or [open and edit](../../pages/guides/edit-a-pdf.md) a file.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Bun"
|
|
3
|
+
description: "Use @laminapdf/sdk in Bun."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/quick-start/bun/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Bun
|
|
9
|
+
|
|
10
|
+
Use @laminapdf/sdk in Bun.
|
|
11
|
+
|
|
12
|
+
Bun loads `@laminapdf/sdk` the way Node does: it reads the `.wasm` files from the `@laminapdf/wasm` package. Call `await init({ text: true })` once; after that, every call is synchronous.
|
|
13
|
+
|
|
14
|
+
This app builds one A4 page that says "Hello, world!" and writes it to `hello.pdf` with `Bun.write`:
|
|
15
|
+
|
|
16
|
+
**TypeScript**
|
|
17
|
+
|
|
18
|
+
`packages/sdk/examples/quick-start/bun.ts`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Builds a one-page PDF that says "Hello, world!" and writes it to hello.pdf, in Bun.
|
|
22
|
+
import { Builder, type BuilderOptions, Font, init } from "@laminapdf/sdk";
|
|
23
|
+
|
|
24
|
+
/** The space between the page's edge and the text, in points (72 points = 1 inch). */
|
|
25
|
+
const MARGIN = 72;
|
|
26
|
+
/** The text's size in points; its baseline sits this far below the margin. */
|
|
27
|
+
const FONT_SIZE = 24;
|
|
28
|
+
|
|
29
|
+
/** Builds the page and hands its bytes to `save`, which writes hello.pdf unless you pass another `save` function. */
|
|
30
|
+
export async function hello(
|
|
31
|
+
save: (pdf: Uint8Array) => void | Promise<void> = async (pdf) => {
|
|
32
|
+
await Bun.write("hello.pdf", pdf);
|
|
33
|
+
},
|
|
34
|
+
options: BuilderOptions = {},
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
await init({ text: true });
|
|
37
|
+
using builder = new Builder(options);
|
|
38
|
+
const page = builder.page("A4");
|
|
39
|
+
// Points from the page's top-left corner, y down; the text's baseline sits FONT_SIZE points below the margin.
|
|
40
|
+
page.surface().text("Hello, world!", {
|
|
41
|
+
font: Font.standard("Helvetica"),
|
|
42
|
+
size: FONT_SIZE,
|
|
43
|
+
at: [MARGIN, MARGIN + FONT_SIZE],
|
|
44
|
+
});
|
|
45
|
+
page.finish();
|
|
46
|
+
using doc = builder.finish();
|
|
47
|
+
await save(doc.save());
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// `bun hello.ts` runs it; imported, it only exports `hello`.
|
|
51
|
+
if (import.meta.main) await hello();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Result:** [hello.pdf](https://docs.laminapdf.com/examples/pdf/hello.pdf)
|
|
55
|
+
|
|
56
|
+
Save it as `hello.ts` in a project with `@laminapdf/sdk` [installed](../../pages/install.md), and run it:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
bun hello.ts
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Lamina PDF's tests run this app in Bun and check that it builds the reference PDF byte for byte.
|
|
63
|
+
|
|
64
|
+
Next: draw text with [fonts](../../pages/guides/fonts-and-text.md), or [open and edit](../../pages/guides/edit-a-pdf.md) a file.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Deno"
|
|
3
|
+
description: "Use @laminapdf/sdk in Deno."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/quick-start/deno/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Deno
|
|
9
|
+
|
|
10
|
+
Use @laminapdf/sdk in Deno.
|
|
11
|
+
|
|
12
|
+
In Deno, `@laminapdf/sdk` fetches its `.wasm` files from next to its own code, through `file:` URLs. Reading them needs `--allow-read`, and writing `hello.pdf` needs `--allow-write`. Call `await init({ text: true })` once; after that, every call is synchronous.
|
|
13
|
+
|
|
14
|
+
This app builds one A4 page that says "Hello, world!" and writes it to `hello.pdf`:
|
|
15
|
+
|
|
16
|
+
**TypeScript**
|
|
17
|
+
|
|
18
|
+
`packages/sdk/examples/quick-start/deno.ts`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Builds a one-page PDF that says "Hello, world!" and writes it to hello.pdf, in Deno.
|
|
22
|
+
import { Builder, type BuilderOptions, Font, init } from "@laminapdf/sdk";
|
|
23
|
+
|
|
24
|
+
/** The space between the page's edge and the text, in points (72 points = 1 inch). */
|
|
25
|
+
const MARGIN = 72;
|
|
26
|
+
/** The text's size in points; its baseline sits this far below the margin. */
|
|
27
|
+
const FONT_SIZE = 24;
|
|
28
|
+
|
|
29
|
+
/** Builds the page and hands its bytes to `save`, which writes hello.pdf unless you pass another `save` function. */
|
|
30
|
+
export async function hello(
|
|
31
|
+
save: (pdf: Uint8Array) => void | Promise<void> = (pdf) => Deno.writeFile("hello.pdf", pdf),
|
|
32
|
+
options: BuilderOptions = {},
|
|
33
|
+
): Promise<void> {
|
|
34
|
+
await init({ text: true });
|
|
35
|
+
using builder = new Builder(options);
|
|
36
|
+
const page = builder.page("A4");
|
|
37
|
+
// Points from the page's top-left corner, y down; the text's baseline sits FONT_SIZE points below the margin.
|
|
38
|
+
page.surface().text("Hello, world!", {
|
|
39
|
+
font: Font.standard("Helvetica"),
|
|
40
|
+
size: FONT_SIZE,
|
|
41
|
+
at: [MARGIN, MARGIN + FONT_SIZE],
|
|
42
|
+
});
|
|
43
|
+
page.finish();
|
|
44
|
+
using doc = builder.finish();
|
|
45
|
+
await save(doc.save());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// `deno run --allow-read --allow-write hello.ts` runs it; imported, it only exports `hello`.
|
|
49
|
+
if (import.meta.main) await hello();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Result:** [hello.pdf](https://docs.laminapdf.com/examples/pdf/hello.pdf)
|
|
53
|
+
|
|
54
|
+
Save it as `hello.ts` in a project with `@laminapdf/sdk` [installed](../../pages/install.md), and run it:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
deno run --allow-read --allow-write hello.ts
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Lamina PDF's tests run this app in Deno and check that it builds the reference PDF byte for byte.
|
|
61
|
+
|
|
62
|
+
Next: draw text with [fonts](../../pages/guides/fonts-and-text.md), or [open and edit](../../pages/guides/edit-a-pdf.md) a file.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Node"
|
|
3
|
+
description: "Generate a PDF from Node with @laminapdf/sdk."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/quick-start/node/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Node
|
|
9
|
+
|
|
10
|
+
Generate a PDF from Node with @laminapdf/sdk.
|
|
11
|
+
|
|
12
|
+
In Node, `@laminapdf/sdk` reads its `.wasm` files from the `@laminapdf/wasm` package as bytes. Call `await init({ text: true })` once; after that, every call is synchronous.
|
|
13
|
+
|
|
14
|
+
This app builds one A4 page that says "Hello, world!" and writes it to `hello.pdf`:
|
|
15
|
+
|
|
16
|
+
**TypeScript**
|
|
17
|
+
|
|
18
|
+
`packages/sdk/examples/quick-start/node.ts`:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Builds a one-page PDF that says "Hello, world!" and writes it to hello.pdf, in Node.
|
|
22
|
+
import { writeFile } from "node:fs/promises";
|
|
23
|
+
import { Builder, type BuilderOptions, Font, init } from "@laminapdf/sdk";
|
|
24
|
+
|
|
25
|
+
/** The space between the page's edge and the text, in points (72 points = 1 inch). */
|
|
26
|
+
const MARGIN = 72;
|
|
27
|
+
/** The text's size in points; its baseline sits this far below the margin. */
|
|
28
|
+
const FONT_SIZE = 24;
|
|
29
|
+
|
|
30
|
+
/** Builds the page and hands its bytes to `save`, which writes hello.pdf unless you pass another `save` function. */
|
|
31
|
+
export async function hello(
|
|
32
|
+
save: (pdf: Uint8Array) => void | Promise<void> = (pdf) => writeFile("hello.pdf", pdf),
|
|
33
|
+
options: BuilderOptions = {},
|
|
34
|
+
): Promise<void> {
|
|
35
|
+
await init({ text: true });
|
|
36
|
+
using builder = new Builder(options);
|
|
37
|
+
const page = builder.page("A4");
|
|
38
|
+
// Points from the page's top-left corner, y down; the text's baseline sits FONT_SIZE points below the margin.
|
|
39
|
+
page.surface().text("Hello, world!", {
|
|
40
|
+
font: Font.standard("Helvetica"),
|
|
41
|
+
size: FONT_SIZE,
|
|
42
|
+
at: [MARGIN, MARGIN + FONT_SIZE],
|
|
43
|
+
});
|
|
44
|
+
page.finish();
|
|
45
|
+
using doc = builder.finish();
|
|
46
|
+
await save(doc.save());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// `node hello.ts` runs it (Node 24.2 or newer); imported, it only exports `hello`.
|
|
50
|
+
if (import.meta.main) await hello();
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Result:** [hello.pdf](https://docs.laminapdf.com/examples/pdf/hello.pdf)
|
|
54
|
+
|
|
55
|
+
Save it as `hello.ts` in a project with `@laminapdf/sdk` [installed](../../pages/install.md), and run it with Node 24.2 or newer, which runs TypeScript files as they are:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
node hello.ts
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Node 22 can't parse the `using` that frees the builder and the document, so compile the app with `tsc` first there. Lamina PDF's tests run this app in Node 22 (compiled), 24 and 26 and check that it builds the reference PDF byte for byte.
|
|
62
|
+
|
|
63
|
+
A long-running server should free documents the same way; [how Lamina PDF thinks about a PDF](../../pages/concepts/how-lamina-thinks.md#you-free-what-you-create) explains why.
|
|
64
|
+
|
|
65
|
+
Next: draw text with [fonts](../../pages/guides/fonts-and-text.md), or [open and edit](../../pages/guides/edit-a-pdf.md) a file.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Rust"
|
|
3
|
+
description: "Generate a PDF with the Lamina PDF crate."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/quick-start/rust/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Rust
|
|
9
|
+
|
|
10
|
+
Generate a PDF with the Lamina PDF crate.
|
|
11
|
+
|
|
12
|
+
The `laminapdf` crate is the one to depend on: it wraps Lamina PDF's core crates in one API. Add it with `cargo add laminapdf@=0.2.0-alpha.4`, then build a document. [Install](../../pages/install.md#rust-laminapdf) explains the version. This is the `hello` example, which is tested on every change:
|
|
13
|
+
|
|
14
|
+
**Rust**
|
|
15
|
+
|
|
16
|
+
`crates/laminapdf/examples/hello.rs`:
|
|
17
|
+
|
|
18
|
+
````rust
|
|
19
|
+
//! The smallest document: one A4 page with a line of text in a standard 14 font.
|
|
20
|
+
//!
|
|
21
|
+
//! ```sh
|
|
22
|
+
//! cargo run -p laminapdf --example hello -- [out-dir] # writes <out-dir>/hello.pdf (default target/examples-out)
|
|
23
|
+
//! ```
|
|
24
|
+
|
|
25
|
+
use std::error::Error;
|
|
26
|
+
use std::path::PathBuf;
|
|
27
|
+
|
|
28
|
+
use laminapdf::{BuildOptions, Builder, PageSize, PointPt, Pt, SaveOptions, Standard14};
|
|
29
|
+
|
|
30
|
+
fn main() -> Result<(), Box<dyn Error>> {
|
|
31
|
+
let out = PathBuf::from(std::env::args().nth(1).unwrap_or_else(|| "target/examples-out".into()));
|
|
32
|
+
std::fs::create_dir_all(&out)?;
|
|
33
|
+
std::fs::write(out.join("hello.pdf"), build(BuildOptions::new())?)?;
|
|
34
|
+
Ok(())
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// The document's bytes. `options` can fix the file ID, the producer and the creation date, so
|
|
38
|
+
/// that the same input gives the same bytes on every run.
|
|
39
|
+
pub(crate) fn build(options: BuildOptions) -> Result<Vec<u8>, Box<dyn Error>> {
|
|
40
|
+
let mut b = Builder::new(options);
|
|
41
|
+
let helvetica = b.standard_font(Standard14::Helvetica)?;
|
|
42
|
+
let mut page = b.page(PageSize::A4);
|
|
43
|
+
// Page space: points from the top-left corner, y down; `y` is the text's baseline.
|
|
44
|
+
page.surface().text(
|
|
45
|
+
"Hello, world!",
|
|
46
|
+
&helvetica,
|
|
47
|
+
Pt::new(24.0),
|
|
48
|
+
PointPt::from_points(72.0, 96.0),
|
|
49
|
+
)?;
|
|
50
|
+
page.finish()?;
|
|
51
|
+
let mut doc = b.finish()?;
|
|
52
|
+
Ok(doc.save(&SaveOptions::new())?.bytes)
|
|
53
|
+
}
|
|
54
|
+
````
|
|
55
|
+
|
|
56
|
+
**Result:** [hello.pdf](https://docs.laminapdf.com/examples/pdf/hello.pdf)
|
|
57
|
+
|
|
58
|
+
To run it, save it as `src/main.rs` in a project that depends on `laminapdf`, then run `cargo run`. It writes `target/examples-out/hello.pdf`, or `hello.pdf` in the folder you pass: `cargo run -- out`.
|
|
59
|
+
|
|
60
|
+
- **Same input, same bytes:** `build` takes `BuildOptions`: fix the file ID, the producer and the creation date in them, and the output never changes; see [how Lamina PDF thinks about a PDF](../../pages/concepts/how-lamina-thinks.md#same-input-same-bytes).
|
|
61
|
+
- **Stopping a save:** `save` runs to the end. To let another thread stop it, pass a `CancelToken` to `save_with`, or call `save_job` for a job that you step through with a work budget.
|
|
62
|
+
- The [Rust API reference](../../pages/reference/rust.md) documents every public item.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Cloudflare Workers"
|
|
3
|
+
description: "Generate PDFs per request in a Cloudflare Worker."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/quick-start/workers/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Cloudflare Workers
|
|
9
|
+
|
|
10
|
+
Generate PDFs per request in a Cloudflare Worker.
|
|
11
|
+
|
|
12
|
+
Cloudflare Workers can't compile WebAssembly at run time. So Wrangler bundles the package's entry for Cloudflare Workers, which imports both `.wasm` files as `WebAssembly.Module`s. `init` instantiates them once per isolate.
|
|
13
|
+
|
|
14
|
+
This Cloudflare Worker returns a one-page letter at `/?name=…` in a standard font, and the same letter at `/embedded?name=…` in a font bundled with the Cloudflare Worker. Lamina PDF's tests run it in workerd, Cloudflare's runtime:
|
|
15
|
+
|
|
16
|
+
`packages/sdk/examples/worker/src/index.ts`:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// A Worker that generates a one-page letter per request: at `/?name=` in a standard font, which
|
|
20
|
+
// needs no font file, and at `/embedded?name=` in a font bundled with the Worker, which the PDF
|
|
21
|
+
// embeds. A name the font can't draw gets a 400 with the error as JSON. The bundled font is a file
|
|
22
|
+
// in `src/fonts/` that Wrangler bundles as bytes (wrangler.toml's `rules`); put the fonts your
|
|
23
|
+
// documents need there.
|
|
24
|
+
import { Builder, Font, init, isLaminaPdfError, type LaminaPdfError } from "@laminapdf/sdk";
|
|
25
|
+
import notoSans from "./fonts/NotoSans-Regular.ttf";
|
|
26
|
+
|
|
27
|
+
/** The longest `?name=` the letter takes. */
|
|
28
|
+
const MAX_NAME = 100;
|
|
29
|
+
|
|
30
|
+
/** The letter's font for each path: Helvetica, a standard 14 font, or the bundled Noto Sans. */
|
|
31
|
+
const FONTS = new Map([
|
|
32
|
+
["/", Font.standard("Helvetica")],
|
|
33
|
+
["/embedded", Font.fromBytes(notoSans)],
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
/** A one-page letter to `name` in `font`. */
|
|
37
|
+
function letter(name: string, font: Font): Uint8Array<ArrayBuffer> {
|
|
38
|
+
const b = new Builder({ producer: "lamina Worker example" });
|
|
39
|
+
try {
|
|
40
|
+
const page = b.page("A4");
|
|
41
|
+
page.surface().text(`Hello, ${name}!`, { font, size: 24, at: [72, 96] });
|
|
42
|
+
page.finish();
|
|
43
|
+
return saveAndFree(b);
|
|
44
|
+
} finally {
|
|
45
|
+
// A no-op after finish(); when drawing threw, it frees the builder's WebAssembly side now
|
|
46
|
+
// rather than whenever the garbage collector gets to it (an isolate serves many requests).
|
|
47
|
+
b.free();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Finishes `b`, saves the document and frees it (the bytes are a copy in JS memory). */
|
|
52
|
+
function saveAndFree(b: Builder): Uint8Array<ArrayBuffer> {
|
|
53
|
+
const doc = b.finish();
|
|
54
|
+
try {
|
|
55
|
+
return doc.save();
|
|
56
|
+
} finally {
|
|
57
|
+
doc.free();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pdf = (bytes: Uint8Array<ArrayBuffer>): Response =>
|
|
62
|
+
new Response(bytes, { headers: { "content-type": "application/pdf" } });
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether `e` says the font can't draw the name: the caller's input, not a server fault. Other
|
|
66
|
+
* errors, such as `invalid-font` for a broken font file, stay a 500.
|
|
67
|
+
*/
|
|
68
|
+
function isNameError(e: unknown): e is LaminaPdfError {
|
|
69
|
+
return isLaminaPdfError(e, "missing-glyph") || isLaminaPdfError(e, "encoding-full");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A 400 with the error's ADR 0026 object as JSON. */
|
|
73
|
+
const badRequest = (body: unknown): Response => Response.json(body, { status: 400 });
|
|
74
|
+
|
|
75
|
+
// A default export because that is how a Worker module exposes its handlers (the named-exports
|
|
76
|
+
// rule is for package code).
|
|
77
|
+
export default {
|
|
78
|
+
async fetch(request: Request): Promise<Response> {
|
|
79
|
+
// Loads both modules once per isolate; later requests return at once.
|
|
80
|
+
await init({ text: true });
|
|
81
|
+
const url = new URL(request.url);
|
|
82
|
+
const font = FONTS.get(url.pathname);
|
|
83
|
+
if (font === undefined) return new Response("not found", { status: 404 });
|
|
84
|
+
const name = url.searchParams.get("name") ?? "world";
|
|
85
|
+
if (name.length > MAX_NAME) return badRequest({ message: `name is longer than ${MAX_NAME} characters` });
|
|
86
|
+
try {
|
|
87
|
+
return pdf(letter(name, font));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// A script the font lacks (`missing-glyph`: Chinese in either font) or, in Helvetica, more
|
|
90
|
+
// distinct non-WinAnsi Latin letters than its 37 spare codes (`encoding-full`).
|
|
91
|
+
if (isNameError(e)) return badRequest(e.toJSON());
|
|
92
|
+
throw e;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Its Wrangler configuration bundles font files as `Data` modules, so importing a font gives its bytes:
|
|
99
|
+
|
|
100
|
+
`packages/sdk/examples/worker/wrangler.toml`:
|
|
101
|
+
|
|
102
|
+
```toml
|
|
103
|
+
# A Cloudflare Worker that generates a PDF per request with @laminapdf/sdk. Run `npx wrangler dev`
|
|
104
|
+
# in this folder. Wrangler bundles the package's `workerd` export condition, which imports both
|
|
105
|
+
# `.wasm` files as `WebAssembly.Module`s (Workers can't compile WebAssembly at run time).
|
|
106
|
+
name = "lamina-pdf-example"
|
|
107
|
+
main = "src/index.ts"
|
|
108
|
+
compatibility_date = "2026-10-01"
|
|
109
|
+
|
|
110
|
+
# Font files are bundled with the Worker as ArrayBuffers.
|
|
111
|
+
[[rules]]
|
|
112
|
+
type = "Data"
|
|
113
|
+
globs = ["**/*.ttf", "**/*.otf"]
|
|
114
|
+
fallthrough = true
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
TypeScript learns what a font import is from a declaration file next to the code:
|
|
118
|
+
|
|
119
|
+
`packages/sdk/examples/worker/src/data.d.ts`:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// Font files import as their bytes: Wrangler bundles them as `Data` modules (wrangler.toml's
|
|
123
|
+
// `rules`), and this file tells TypeScript what such an import is.
|
|
124
|
+
declare module "*.ttf" {
|
|
125
|
+
const data: ArrayBuffer;
|
|
126
|
+
export default data;
|
|
127
|
+
}
|
|
128
|
+
declare module "*.otf" {
|
|
129
|
+
const data: ArrayBuffer;
|
|
130
|
+
export default data;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
To run it, install `@laminapdf/sdk` and `wrangler` in the Cloudflare Worker's folder (`npm install @laminapdf/sdk@0.2.0-alpha.4 wrangler`), put a copy of [Noto Sans](https://fonts.google.com/noto/specimen/Noto+Sans) at `src/fonts/NotoSans-Regular.ttf`, and run `npx wrangler dev`.
|
|
135
|
+
|
|
136
|
+
- **Errors the caller caused** become a `400` with the error's JSON: a name the font can't draw (`missing-glyph`, `encoding-full`). Anything else, such as a broken font file, stays a `500`. See [Errors and warnings](../../pages/concepts/errors-and-warnings.md).
|
|
137
|
+
- **Memory:** the letter frees its builder and document as soon as the bytes are saved, because one isolate serves many requests.
|
|
138
|
+
|
|
139
|
+
Next: the [Cloudflare Worker recipe](../../pages/recipes/cloudflare-worker.md) says what to change in it, and [Troubleshooting](../../pages/troubleshooting.md) covers a Cloudflare Worker that can't load the WebAssembly.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "A Cloudflare Worker"
|
|
3
|
+
description: "A Cloudflare Worker that returns a one-page letter per request, in a standard 14 font or a bundled one, and a 400 for a name the font can't draw."
|
|
4
|
+
version: "0.2.0-alpha.4"
|
|
5
|
+
url: "https://docs.laminapdf.com/recipes/cloudflare-worker/"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# A Cloudflare Worker
|
|
9
|
+
|
|
10
|
+
A Cloudflare Worker that returns a one-page letter per request, in a standard 14 font or a bundled one, and a 400 for a name the font can't draw.
|
|
11
|
+
|
|
12
|
+
You'll make a Cloudflare Worker that returns a one-page letter to the name in its URL. You need this to make PDFs on request, such as tickets or receipts, without running a server of your own.
|
|
13
|
+
|
|
14
|
+
The Cloudflare Worker draws the letter in Helvetica at `/?name=` and in a bundled Noto Sans at `/embedded?name=`, then returns the PDF:
|
|
15
|
+
|
|
16
|
+
`packages/sdk/examples/worker/src/index.ts`:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// A Worker that generates a one-page letter per request: at `/?name=` in a standard font, which
|
|
20
|
+
// needs no font file, and at `/embedded?name=` in a font bundled with the Worker, which the PDF
|
|
21
|
+
// embeds. A name the font can't draw gets a 400 with the error as JSON. The bundled font is a file
|
|
22
|
+
// in `src/fonts/` that Wrangler bundles as bytes (wrangler.toml's `rules`); put the fonts your
|
|
23
|
+
// documents need there.
|
|
24
|
+
import { Builder, Font, init, isLaminaPdfError, type LaminaPdfError } from "@laminapdf/sdk";
|
|
25
|
+
import notoSans from "./fonts/NotoSans-Regular.ttf";
|
|
26
|
+
|
|
27
|
+
/** The longest `?name=` the letter takes. */
|
|
28
|
+
const MAX_NAME = 100;
|
|
29
|
+
|
|
30
|
+
/** The letter's font for each path: Helvetica, a standard 14 font, or the bundled Noto Sans. */
|
|
31
|
+
const FONTS = new Map([
|
|
32
|
+
["/", Font.standard("Helvetica")],
|
|
33
|
+
["/embedded", Font.fromBytes(notoSans)],
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
/** A one-page letter to `name` in `font`. */
|
|
37
|
+
function letter(name: string, font: Font): Uint8Array<ArrayBuffer> {
|
|
38
|
+
const b = new Builder({ producer: "lamina Worker example" });
|
|
39
|
+
try {
|
|
40
|
+
const page = b.page("A4");
|
|
41
|
+
page.surface().text(`Hello, ${name}!`, { font, size: 24, at: [72, 96] });
|
|
42
|
+
page.finish();
|
|
43
|
+
return saveAndFree(b);
|
|
44
|
+
} finally {
|
|
45
|
+
// A no-op after finish(); when drawing threw, it frees the builder's WebAssembly side now
|
|
46
|
+
// rather than whenever the garbage collector gets to it (an isolate serves many requests).
|
|
47
|
+
b.free();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Finishes `b`, saves the document and frees it (the bytes are a copy in JS memory). */
|
|
52
|
+
function saveAndFree(b: Builder): Uint8Array<ArrayBuffer> {
|
|
53
|
+
const doc = b.finish();
|
|
54
|
+
try {
|
|
55
|
+
return doc.save();
|
|
56
|
+
} finally {
|
|
57
|
+
doc.free();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const pdf = (bytes: Uint8Array<ArrayBuffer>): Response =>
|
|
62
|
+
new Response(bytes, { headers: { "content-type": "application/pdf" } });
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether `e` says the font can't draw the name: the caller's input, not a server fault. Other
|
|
66
|
+
* errors, such as `invalid-font` for a broken font file, stay a 500.
|
|
67
|
+
*/
|
|
68
|
+
function isNameError(e: unknown): e is LaminaPdfError {
|
|
69
|
+
return isLaminaPdfError(e, "missing-glyph") || isLaminaPdfError(e, "encoding-full");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A 400 with the error's ADR 0026 object as JSON. */
|
|
73
|
+
const badRequest = (body: unknown): Response => Response.json(body, { status: 400 });
|
|
74
|
+
|
|
75
|
+
// A default export because that is how a Worker module exposes its handlers (the named-exports
|
|
76
|
+
// rule is for package code).
|
|
77
|
+
export default {
|
|
78
|
+
async fetch(request: Request): Promise<Response> {
|
|
79
|
+
// Loads both modules once per isolate; later requests return at once.
|
|
80
|
+
await init({ text: true });
|
|
81
|
+
const url = new URL(request.url);
|
|
82
|
+
const font = FONTS.get(url.pathname);
|
|
83
|
+
if (font === undefined) return new Response("not found", { status: 404 });
|
|
84
|
+
const name = url.searchParams.get("name") ?? "world";
|
|
85
|
+
if (name.length > MAX_NAME) return badRequest({ message: `name is longer than ${MAX_NAME} characters` });
|
|
86
|
+
try {
|
|
87
|
+
return pdf(letter(name, font));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// A script the font lacks (`missing-glyph`: Chinese in either font) or, in Helvetica, more
|
|
90
|
+
// distinct non-WinAnsi Latin letters than its 37 spare codes (`encoding-full`).
|
|
91
|
+
if (isNameError(e)) return badRequest(e.toJSON());
|
|
92
|
+
throw e;
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Open `/?name=Ada` and your browser shows one A4 page that says "Hello, Ada!" in 24-point type. To configure Wrangler and run it, follow the [Cloudflare Workers quick start](../../pages/quick-start/workers.md).
|
|
99
|
+
|
|
100
|
+
## How it works
|
|
101
|
+
|
|
102
|
+
- `init({ text: true })` loads the WebAssembly modules the first time an isolate runs `fetch`, and returns at once after that. An isolate is one running copy of the Cloudflare Worker, which serves many requests.
|
|
103
|
+
- `FONTS` maps each path to its font, made once per isolate. Helvetica is one of the [standard 14 fonts](../../pages/reference/glossary.md#standard-14-fonts), so that PDF embeds no font. Noto Sans arrives as bytes from `src/fonts/`, and that PDF embeds only the glyphs the letter uses.
|
|
104
|
+
- An unknown path gets a 404, and a name longer than `MAX_NAME` characters gets a 400 before anything is drawn.
|
|
105
|
+
- `isNameError` turns two codes into a 400 with the error's JSON. `missing-glyph` means a character the font lacks, and `encoding-full` means more than 37 different letters outside Helvetica's Western European set. Any other error, such as a broken font file, becomes a 500.
|
|
106
|
+
- `letter` frees its builder in `finally`, and `saveAndFree` frees the document once its bytes are copied out, because the isolate goes on to serve other requests.
|
|
107
|
+
|
|
108
|
+
## What to change
|
|
109
|
+
|
|
110
|
+
- **The document:** draw your own pages in `letter`, and keep both `finally` blocks, so a request that fails still frees what it made.
|
|
111
|
+
- **Your fonts:** put the font files your documents need in `src/fonts/`, import each one and add it to `FONTS`. The quick start's Wrangler configuration bundles every `.ttf` and `.otf` file as bytes.
|
|
112
|
+
- **Errors the caller caused:** add a code to `isNameError` when the request can cause it, such as `text-overflow` for a text box drawn with `overflow: "error"`. See [Errors and warnings](../../pages/concepts/errors-and-warnings.md).
|
|
113
|
+
- **A download name:** add `content-disposition: attachment; filename="letter.pdf"` to the headers in `pdf`, so browsers save the file instead of showing it.
|
|
114
|
+
|
|
115
|
+
Next: [Troubleshooting](../../pages/troubleshooting.md), for the problems people hit most and how to fix them.
|