@laminapdf/sdk 0.2.0-alpha.2 → 0.2.0-alpha.3

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.
Files changed (401) hide show
  1. package/README.md +42 -7
  2. package/dist/api.d.ts +5 -2
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js +3 -2
  5. package/dist/api.js.map +1 -1
  6. package/dist/builder.d.ts +35 -19
  7. package/dist/builder.d.ts.map +1 -1
  8. package/dist/builder.js +114 -80
  9. package/dist/builder.js.map +1 -1
  10. package/dist/document.d.ts +47 -21
  11. package/dist/document.d.ts.map +1 -1
  12. package/dist/document.js +112 -67
  13. package/dist/document.js.map +1 -1
  14. package/dist/errors.d.ts +5 -5
  15. package/dist/errors.d.ts.map +1 -1
  16. package/dist/errors.js +10 -16
  17. package/dist/errors.js.map +1 -1
  18. package/dist/extract.d.ts +57 -0
  19. package/dist/extract.d.ts.map +1 -0
  20. package/dist/extract.js +5 -0
  21. package/dist/extract.js.map +1 -0
  22. package/dist/font.d.ts +13 -6
  23. package/dist/font.d.ts.map +1 -1
  24. package/dist/font.js +50 -20
  25. package/dist/font.js.map +1 -1
  26. package/dist/generated/codes.d.ts +18 -6
  27. package/dist/generated/codes.d.ts.map +1 -1
  28. package/dist/generated/codes.js +59 -4
  29. package/dist/generated/codes.js.map +1 -1
  30. package/dist/generated/explain.d.ts.map +1 -1
  31. package/dist/generated/explain.js +27 -10
  32. package/dist/generated/explain.js.map +1 -1
  33. package/dist/generated/extract.d.ts +235 -0
  34. package/dist/generated/extract.d.ts.map +1 -0
  35. package/dist/generated/extract.js +4 -0
  36. package/dist/generated/extract.js.map +1 -0
  37. package/dist/init.d.ts +13 -5
  38. package/dist/init.d.ts.map +1 -1
  39. package/dist/init.js +23 -11
  40. package/dist/init.js.map +1 -1
  41. package/dist/internal/handle.d.ts +58 -0
  42. package/dist/internal/handle.d.ts.map +1 -0
  43. package/dist/internal/handle.js +113 -0
  44. package/dist/internal/handle.js.map +1 -0
  45. package/dist/internal/instance.d.ts +56 -0
  46. package/dist/internal/instance.d.ts.map +1 -0
  47. package/dist/internal/instance.js +125 -0
  48. package/dist/internal/instance.js.map +1 -0
  49. package/dist/source/http.js +1 -1
  50. package/dist/source/http.js.map +1 -1
  51. package/dist/source/range.d.ts +6 -1
  52. package/dist/source/range.d.ts.map +1 -1
  53. package/dist/source/range.js +10 -6
  54. package/dist/source/range.js.map +1 -1
  55. package/dist/text.d.ts +3 -3
  56. package/dist/text.d.ts.map +1 -1
  57. package/dist/text.js +3 -3
  58. package/dist/text.js.map +1 -1
  59. package/dist/version.d.ts +1 -1
  60. package/dist/version.js +2 -2
  61. package/dist/version.js.map +1 -1
  62. package/dist/worker/client.d.ts +3 -2
  63. package/dist/worker/client.d.ts.map +1 -1
  64. package/dist/worker/client.js +6 -4
  65. package/dist/worker/client.js.map +1 -1
  66. package/dist/worker/entry.js +1 -1
  67. package/dist/worker/entry.js.map +1 -1
  68. package/dist/worker/server.d.ts.map +1 -1
  69. package/dist/worker/server.js +42 -9
  70. package/dist/worker/server.js.map +1 -1
  71. package/dist/worker/support.d.ts +6 -2
  72. package/dist/worker/support.d.ts.map +1 -1
  73. package/dist/worker/support.js +1 -0
  74. package/dist/worker/support.js.map +1 -1
  75. package/dist/worker/types.d.ts +8 -1
  76. package/dist/worker/types.d.ts.map +1 -1
  77. package/docs/AGENTS.md +14 -0
  78. package/docs/api/README.md +147 -0
  79. package/docs/api/classes/Builder.md +467 -0
  80. package/docs/api/classes/Document.md +742 -0
  81. package/docs/api/classes/Font.md +74 -0
  82. package/docs/api/classes/Image.md +140 -0
  83. package/docs/api/classes/LaminaPdfError.md +189 -0
  84. package/docs/api/classes/Page.md +178 -0
  85. package/docs/api/classes/PageRef.md +253 -0
  86. package/docs/api/classes/PageText.md +129 -0
  87. package/docs/api/classes/PageTextRun.md +167 -0
  88. package/docs/api/classes/PendingPath.md +97 -0
  89. package/docs/api/classes/SaveJob.md +205 -0
  90. package/docs/api/classes/Surface.md +326 -0
  91. package/docs/api/classes/Transaction.md +111 -0
  92. package/docs/api/functions/callbackSource.md +33 -0
  93. package/docs/api/functions/httpRangeSource.md +48 -0
  94. package/docs/api/functions/init.md +36 -0
  95. package/docs/api/functions/isExtractLoaded.md +21 -0
  96. package/docs/api/functions/isInitialized.md +19 -0
  97. package/docs/api/functions/isLaminaPdfError.md +41 -0
  98. package/docs/api/functions/isTextLoaded.md +19 -0
  99. package/docs/api/functions/loadExtract.md +28 -0
  100. package/docs/api/functions/loadText.md +28 -0
  101. package/docs/api/functions/onLeak.md +29 -0
  102. package/docs/api/functions/reset.md +22 -0
  103. package/docs/api/functions/wasmMemory.md +22 -0
  104. package/docs/api/interfaces/BuilderOptions.md +108 -0
  105. package/docs/api/interfaces/ByteRange.md +29 -0
  106. package/docs/api/interfaces/ChunkOptions.md +37 -0
  107. package/docs/api/interfaces/Diagnostic.md +67 -0
  108. package/docs/api/interfaces/ExtractOptions.md +92 -0
  109. package/docs/api/interfaces/ExtractResult.md +37 -0
  110. package/docs/api/interfaces/FontInfo.md +37 -0
  111. package/docs/api/interfaces/HttpRangeOptions.md +78 -0
  112. package/docs/api/interfaces/InitOptions.md +40 -0
  113. package/docs/api/interfaces/Leak.md +21 -0
  114. package/docs/api/interfaces/Location.md +53 -0
  115. package/docs/api/interfaces/OpLog.md +25 -0
  116. package/docs/api/interfaces/OpenOptions.md +37 -0
  117. package/docs/api/interfaces/PageTextOptions.md +21 -0
  118. package/docs/api/interfaces/PathBuilder.md +195 -0
  119. package/docs/api/interfaces/QuadPoint.md +25 -0
  120. package/docs/api/interfaces/RangeOptions.md +54 -0
  121. package/docs/api/interfaces/RangeSource.md +59 -0
  122. package/docs/api/interfaces/StrokeStyle.md +61 -0
  123. package/docs/api/interfaces/TagSpec.md +69 -0
  124. package/docs/api/interfaces/TextBoxOptions.md +93 -0
  125. package/docs/api/interfaces/TextGlyph.md +29 -0
  126. package/docs/api/interfaces/TextOptions.md +101 -0
  127. package/docs/api/interfaces/WasmMemory.md +37 -0
  128. package/docs/api/type-aliases/AfRelationship.md +15 -0
  129. package/docs/api/type-aliases/Align.md +15 -0
  130. package/docs/api/type-aliases/ArtifactKind.md +15 -0
  131. package/docs/api/type-aliases/AttachmentOptions.md +15 -0
  132. package/docs/api/type-aliases/BlendMode.md +15 -0
  133. package/docs/api/type-aliases/BoxRect.md +15 -0
  134. package/docs/api/type-aliases/BuildOptions.md +15 -0
  135. package/docs/api/type-aliases/Cell.md +71 -0
  136. package/docs/api/type-aliases/Chunk.md +79 -0
  137. package/docs/api/type-aliases/Color.md +16 -0
  138. package/docs/api/type-aliases/Coordinates.md +55 -0
  139. package/docs/api/type-aliases/Direction.md +15 -0
  140. package/docs/api/type-aliases/DocumentInfo.md +15 -0
  141. package/docs/api/type-aliases/DropReason.md +15 -0
  142. package/docs/api/type-aliases/Dropped.md +59 -0
  143. package/docs/api/type-aliases/Edited.md +15 -0
  144. package/docs/api/type-aliases/ErrorCode.md +15 -0
  145. package/docs/api/type-aliases/ErrorKind.md +15 -0
  146. package/docs/api/type-aliases/ExtractFormat.md +15 -0
  147. package/docs/api/type-aliases/ExtractedBlock.md +82 -0
  148. package/docs/api/type-aliases/ExtractedPage.md +65 -0
  149. package/docs/api/type-aliases/ExtractedSource.md +48 -0
  150. package/docs/api/type-aliases/Extraction.md +22 -0
  151. package/docs/api/type-aliases/FillRule.md +15 -0
  152. package/docs/api/type-aliases/Fit.md +15 -0
  153. package/docs/api/type-aliases/Flags.md +15 -0
  154. package/docs/api/type-aliases/Generator.md +41 -0
  155. package/docs/api/type-aliases/GlyphQuad.md +15 -0
  156. package/docs/api/type-aliases/InfoPatch.md +15 -0
  157. package/docs/api/type-aliases/LaminaPdfWarning.md +16 -0
  158. package/docs/api/type-aliases/LimitName.md +15 -0
  159. package/docs/api/type-aliases/Limits.md +15 -0
  160. package/docs/api/type-aliases/LineBox.md +15 -0
  161. package/docs/api/type-aliases/LineCap.md +15 -0
  162. package/docs/api/type-aliases/LineJoin.md +15 -0
  163. package/docs/api/type-aliases/LinkTarget.md +15 -0
  164. package/docs/api/type-aliases/Metadata.md +15 -0
  165. package/docs/api/type-aliases/Op.md +15 -0
  166. package/docs/api/type-aliases/OutlineEntry.md +15 -0
  167. package/docs/api/type-aliases/Overflow.md +15 -0
  168. package/docs/api/type-aliases/PageInfo.md +15 -0
  169. package/docs/api/type-aliases/PageLabel.md +15 -0
  170. package/docs/api/type-aliases/PageSizeName.md +15 -0
  171. package/docs/api/type-aliases/Point.md +15 -0
  172. package/docs/api/type-aliases/Profile.md +15 -0
  173. package/docs/api/type-aliases/Progress.md +15 -0
  174. package/docs/api/type-aliases/ReadingOrder.md +15 -0
  175. package/docs/api/type-aliases/Rect.md +15 -0
  176. package/docs/api/type-aliases/Region.md +31 -0
  177. package/docs/api/type-aliases/RenderMode.md +15 -0
  178. package/docs/api/type-aliases/Rotation.md +15 -0
  179. package/docs/api/type-aliases/SaveOptions.md +15 -0
  180. package/docs/api/type-aliases/SaveReport.md +15 -0
  181. package/docs/api/type-aliases/Span.md +63 -0
  182. package/docs/api/type-aliases/SplitOn.md +15 -0
  183. package/docs/api/type-aliases/Standard14.md +15 -0
  184. package/docs/api/type-aliases/StructRef.md +41 -0
  185. package/docs/api/type-aliases/StructSource.md +17 -0
  186. package/docs/api/type-aliases/TableDetector.md +15 -0
  187. package/docs/api/type-aliases/TableMode.md +15 -0
  188. package/docs/api/type-aliases/TableScope.md +15 -0
  189. package/docs/api/type-aliases/Tag.md +15 -0
  190. package/docs/api/type-aliases/TagName.md +15 -0
  191. package/docs/api/type-aliases/TextLayout.md +15 -0
  192. package/docs/api/type-aliases/TextRange.md +33 -0
  193. package/docs/api/type-aliases/TextRunInfo.md +15 -0
  194. package/docs/api/type-aliases/Transform.md +15 -0
  195. package/docs/api/type-aliases/UndoOutcome.md +15 -0
  196. package/docs/api/type-aliases/ViewerPreferences.md +15 -0
  197. package/docs/api/type-aliases/WarningCode.md +15 -0
  198. package/docs/api/type-aliases/WasmSource.md +16 -0
  199. package/docs/api/variables/Matrix.md +99 -0
  200. package/docs/api/variables/PAGE_SIZES.md +15 -0
  201. package/docs/api/variables/TAG_NAMES.md +15 -0
  202. package/docs/errors/README.md +156 -0
  203. package/docs/errors/aes-padding-invalid.md +34 -0
  204. package/docs/errors/already-initialized.md +34 -0
  205. package/docs/errors/binary-output-to-terminal.md +35 -0
  206. package/docs/errors/builder-finished.md +35 -0
  207. package/docs/errors/cancelled.md +35 -0
  208. package/docs/errors/cmap-invalid.md +42 -0
  209. package/docs/errors/collection-index.md +33 -0
  210. package/docs/errors/color-components.md +33 -0
  211. package/docs/errors/content-bad-operands.md +36 -0
  212. package/docs/errors/content-path-unpainted.md +36 -0
  213. package/docs/errors/content-resource-missing.md +37 -0
  214. package/docs/errors/content-syntax.md +36 -0
  215. package/docs/errors/content-unbalanced-bt.md +36 -0
  216. package/docs/errors/content-unbalanced-marked.md +36 -0
  217. package/docs/errors/content-unbalanced-q.md +36 -0
  218. package/docs/errors/content-unknown-operator.md +37 -0
  219. package/docs/errors/data-pending.md +34 -0
  220. package/docs/errors/decompress.md +34 -0
  221. package/docs/errors/device-color-without-output-intent.md +33 -0
  222. package/docs/errors/direct-page.md +34 -0
  223. package/docs/errors/disposed.md +34 -0
  224. package/docs/errors/document-changed.md +34 -0
  225. package/docs/errors/embedded-file-forbidden.md +35 -0
  226. package/docs/errors/embedded-file-without-mime-type.md +36 -0
  227. package/docs/errors/encoding-full.md +34 -0
  228. package/docs/errors/encrypted-base.md +33 -0
  229. package/docs/errors/extract-limit-blocks.md +35 -0
  230. package/docs/errors/extract-module-not-loaded.md +37 -0
  231. package/docs/errors/extract-reading-order-ambiguous.md +32 -0
  232. package/docs/errors/extract-struct-tree-ignored.md +32 -0
  233. package/docs/errors/extract-table-ambiguous.md +32 -0
  234. package/docs/errors/extract-table-limit.md +35 -0
  235. package/docs/errors/extract-untagged-content.md +32 -0
  236. package/docs/errors/fetch-failed.md +34 -0
  237. package/docs/errors/flate-truncated.md +34 -0
  238. package/docs/errors/font-invalid.md +36 -0
  239. package/docs/errors/font-mismatch.md +34 -0
  240. package/docs/errors/font-not-embeddable.md +34 -0
  241. package/docs/errors/font-not-embedded.md +35 -0
  242. package/docs/errors/font-not-shapable.md +33 -0
  243. package/docs/errors/glyph-out-of-range.md +34 -0
  244. package/docs/errors/glyph-without-unicode.md +35 -0
  245. package/docs/errors/heading-level-skipped.md +36 -0
  246. package/docs/errors/image-data-length.md +33 -0
  247. package/docs/errors/image-too-large.md +32 -0
  248. package/docs/errors/inline-image-unterminated.md +36 -0
  249. package/docs/errors/input-unreadable.md +37 -0
  250. package/docs/errors/instance-reset.md +34 -0
  251. package/docs/errors/invalid-argument.md +33 -0
  252. package/docs/errors/invalid-axis-value.md +32 -0
  253. package/docs/errors/invalid-box.md +34 -0
  254. package/docs/errors/invalid-color.md +32 -0
  255. package/docs/errors/invalid-command-line.md +44 -0
  256. package/docs/errors/invalid-date.md +34 -0
  257. package/docs/errors/invalid-entry.md +34 -0
  258. package/docs/errors/invalid-feature.md +32 -0
  259. package/docs/errors/invalid-font.md +32 -0
  260. package/docs/errors/invalid-geometry.md +32 -0
  261. package/docs/errors/invalid-icc-profile.md +32 -0
  262. package/docs/errors/invalid-image.md +33 -0
  263. package/docs/errors/invalid-op-log.md +33 -0
  264. package/docs/errors/invalid-page-label.md +34 -0
  265. package/docs/errors/invalid-page-labels.md +32 -0
  266. package/docs/errors/invalid-page-range.md +37 -0
  267. package/docs/errors/invalid-rotate.md +34 -0
  268. package/docs/errors/invalid-structure-nesting.md +38 -0
  269. package/docs/errors/invalid-structure.md +34 -0
  270. package/docs/errors/invalid-text-range.md +33 -0
  271. package/docs/errors/job-finished.md +34 -0
  272. package/docs/errors/limit-exceeded.md +38 -0
  273. package/docs/errors/link-without-contents.md +33 -0
  274. package/docs/errors/link-without-structure.md +37 -0
  275. package/docs/errors/missing-alt-text.md +33 -0
  276. package/docs/errors/missing-glyph.md +33 -0
  277. package/docs/errors/missing-header.md +37 -0
  278. package/docs/errors/missing-lang.md +32 -0
  279. package/docs/errors/missing-root.md +34 -0
  280. package/docs/errors/missing-title.md +32 -0
  281. package/docs/errors/name-tree-node-skipped.md +34 -0
  282. package/docs/errors/nested-transaction.md +34 -0
  283. package/docs/errors/no-pending-save.md +32 -0
  284. package/docs/errors/no-transaction.md +33 -0
  285. package/docs/errors/not-initialized.md +35 -0
  286. package/docs/errors/not-pdf.md +33 -0
  287. package/docs/errors/not-rebased.md +35 -0
  288. package/docs/errors/object-number-out-of-range.md +38 -0
  289. package/docs/errors/objstm-index-mismatch.md +36 -0
  290. package/docs/errors/out-of-memory.md +38 -0
  291. package/docs/errors/outline-cycle.md +34 -0
  292. package/docs/errors/outline-item-skipped.md +34 -0
  293. package/docs/errors/output-unwritable.md +37 -0
  294. package/docs/errors/overwrite-refused.md +36 -0
  295. package/docs/errors/page-count-mismatch.md +34 -0
  296. package/docs/errors/page-index-out-of-range.md +34 -0
  297. package/docs/errors/page-tree-node-skipped.md +35 -0
  298. package/docs/errors/password-required.md +36 -0
  299. package/docs/errors/path-outside-roots.md +32 -0
  300. package/docs/errors/perms-mismatch.md +34 -0
  301. package/docs/errors/profile-version-conflict.md +35 -0
  302. package/docs/errors/reference-cycle.md +35 -0
  303. package/docs/errors/render-backend-unavailable.md +32 -0
  304. package/docs/errors/render-failed.md +33 -0
  305. package/docs/errors/render-too-large.md +36 -0
  306. package/docs/errors/repaired-base.md +34 -0
  307. package/docs/errors/requires-rewrite.md +38 -0
  308. package/docs/errors/saved-not-rebased.md +34 -0
  309. package/docs/errors/sink-not-empty.md +33 -0
  310. package/docs/errors/sink.md +31 -0
  311. package/docs/errors/source-budget-exceeded.md +34 -0
  312. package/docs/errors/source-changed.md +33 -0
  313. package/docs/errors/source.md +30 -0
  314. package/docs/errors/stale-generation.md +34 -0
  315. package/docs/errors/stream-length-recovered.md +37 -0
  316. package/docs/errors/strict.md +34 -0
  317. package/docs/errors/subset-failed.md +31 -0
  318. package/docs/errors/syntax.md +34 -0
  319. package/docs/errors/text-module-not-loaded.md +36 -0
  320. package/docs/errors/text-overflow.md +34 -0
  321. package/docs/errors/tounicode-conflict.md +32 -0
  322. package/docs/errors/type.md +34 -0
  323. package/docs/errors/unexpected-eof.md +34 -0
  324. package/docs/errors/unknown-axis.md +33 -0
  325. package/docs/errors/unknown-code.md +36 -0
  326. package/docs/errors/unknown-destination.md +33 -0
  327. package/docs/errors/unknown-font.md +33 -0
  328. package/docs/errors/unknown-option.md +33 -0
  329. package/docs/errors/unknown-page.md +34 -0
  330. package/docs/errors/unmapped-glyph.md +36 -0
  331. package/docs/errors/unnumbered-heading-misused.md +34 -0
  332. package/docs/errors/unset-object.md +30 -0
  333. package/docs/errors/unsupported-filter.md +34 -0
  334. package/docs/errors/unsupported-image.md +33 -0
  335. package/docs/errors/unsupported-layout-option.md +33 -0
  336. package/docs/errors/unsupported-security-handler.md +34 -0
  337. package/docs/errors/unsupported-version.md +32 -0
  338. package/docs/errors/untagged-content.md +35 -0
  339. package/docs/errors/warnings-truncated.md +35 -0
  340. package/docs/errors/wasm-trap.md +37 -0
  341. package/docs/errors/whole-font-rebuilt.md +30 -0
  342. package/docs/errors/worker-crashed.md +33 -0
  343. package/docs/errors/worker-failed.md +32 -0
  344. package/docs/errors/worker-version-mismatch.md +34 -0
  345. package/docs/errors/would-invalidate-signatures.md +38 -0
  346. package/docs/errors/wrong-document.md +33 -0
  347. package/docs/errors/wrong-password.md +34 -0
  348. package/docs/errors/xobject-cycle.md +36 -0
  349. package/docs/errors/xref-repaired.md +36 -0
  350. package/docs/errors/xrefstm-ignored.md +34 -0
  351. package/docs/llms.txt +88 -0
  352. package/docs/pages/concepts/dependencies.md +44 -0
  353. package/docs/pages/concepts/errors-and-warnings.md +64 -0
  354. package/docs/pages/concepts/how-lamina-thinks.md +86 -0
  355. package/docs/pages/concepts/pages-and-coordinates.md +67 -0
  356. package/docs/pages/guides/accessible-pdfs.md +260 -0
  357. package/docs/pages/guides/archival-pdfs.md +429 -0
  358. package/docs/pages/guides/edit-a-pdf.md +137 -0
  359. package/docs/pages/guides/fonts-and-text.md +202 -0
  360. package/docs/pages/guides/images-and-vectors.md +219 -0
  361. package/docs/pages/guides/keep-the-page-responsive.md +81 -0
  362. package/docs/pages/guides/links-outlines-and-metadata.md +222 -0
  363. package/docs/pages/guides/password-protected-files.md +70 -0
  364. package/docs/pages/guides/save-a-pdf.md +144 -0
  365. package/docs/pages/guides/untrusted-files.md +80 -0
  366. package/docs/pages/index.md +33 -0
  367. package/docs/pages/install.md +75 -0
  368. package/docs/pages/quick-start/browser.md +67 -0
  369. package/docs/pages/quick-start/bun.md +60 -0
  370. package/docs/pages/quick-start/deno.md +58 -0
  371. package/docs/pages/quick-start/node.md +61 -0
  372. package/docs/pages/quick-start/rust.md +58 -0
  373. package/docs/pages/quick-start/workers.md +139 -0
  374. package/docs/pages/recipes/cloudflare-worker.md +115 -0
  375. package/docs/pages/recipes/extract-markdown.md +114 -0
  376. package/docs/pages/recipes/generate-then-edit.md +135 -0
  377. package/docs/pages/recipes/hello.md +94 -0
  378. package/docs/pages/recipes/images-and-vectors.md +209 -0
  379. package/docs/pages/recipes/invoice.md +226 -0
  380. package/docs/pages/recipes/links-and-outlines.md +215 -0
  381. package/docs/pages/recipes/multilingual.md +189 -0
  382. package/docs/pages/recipes/open-untrusted.md +65 -0
  383. package/docs/pages/recipes/pdf-a-archive.md +205 -0
  384. package/docs/pages/recipes/pdf-a3-invoice-with-xml.md +222 -0
  385. package/docs/pages/recipes/pdf-ua-report.md +237 -0
  386. package/docs/pages/recipes/report.md +125 -0
  387. package/docs/pages/recipes/web-worker-client.md +64 -0
  388. package/docs/pages/recipes.md +47 -0
  389. package/docs/pages/reference/changelog.md +86 -0
  390. package/docs/pages/reference/codes.md +14 -0
  391. package/docs/pages/reference/glossary.md +108 -0
  392. package/docs/pages/reference/rust.md +36 -0
  393. package/docs/pages/reference/stability.md +59 -0
  394. package/docs/pages/start/coding-agents.md +45 -0
  395. package/docs/pages/start/first-pdf.md +77 -0
  396. package/docs/pages/troubleshooting.md +74 -0
  397. package/docs/renames.json +86 -0
  398. package/docs/schemas/error.schema.json +305 -0
  399. package/docs/schemas/renames.schema.json +29 -0
  400. package/package.json +5 -3
  401. package/schemas/error.schema.json +19 -2
@@ -0,0 +1,222 @@
1
+ ---
2
+ title: "Links, outlines and metadata"
3
+ description: "Add links, named destinations, an outline, page labels, viewer preferences and metadata to a document."
4
+ version: "0.2.0-alpha.3"
5
+ url: "https://docs.laminapdf.com/guides/links-outlines-and-metadata/"
6
+ ---
7
+
8
+ # Links, outlines and metadata
9
+
10
+ Add links, named destinations, an outline, page labels, viewer preferences and metadata to a document.
11
+
12
+ You'll make a document readers can find their way around: links between pages and to the web, an outline (the bookmarks panel), page numbers that read i, ii, 1, 2, and a title. You need this for any document longer than a page or two.
13
+
14
+ ## Link to a page or the web
15
+
16
+ `surface.link(rect, target, contents)` makes a clickable area. The target is a web address (`{ type: "uri" }`), a page (`{ type: "page", index }`) or a named destination (`{ type: "named", name }`). A text run takes a `link` option too. The builder checks every target when it finishes: a missing page fails with `page-index-out-of-range`, and a missing name with `unknown-destination`.
17
+
18
+ `builder.destination(name, index)` names a page, so links can point to it by name.
19
+
20
+ ## Add an outline and page labels
21
+
22
+ - **Outline:** `builder.outline([...])` builds the bookmarks a viewer shows beside the pages, with nested entries, colour, bold, italic and `open` (an entry shown expanded).
23
+ - **Page labels:** `builder.pageLabels([{ start, style }])` sets the page numbers a viewer shows. The example below uses roman numerals for the front matter, then decimal from page 3.
24
+ - **Viewer preferences:** for example, `pageMode: "outlines"` opens the file with the outline showing.
25
+
26
+ ## Set the metadata
27
+
28
+ `builder.metadata({ title, author, … })` sets the document's title, author and the rest (see Details).
29
+
30
+ This example builds four pages that link to each other and to the web, with an outline, page labels and the outline shown on open. It draws its labels as text, so it needs the text module. It sets no metadata ([Edit a PDF](../../pages/guides/edit-a-pdf.md) does). The example:
31
+
32
+ **TypeScript**
33
+
34
+ `packages/sdk/examples/links-and-outlines.ts`:
35
+
36
+ ```ts
37
+ // Links and outlines: four A5 pages joined by links, bookmarks and page labels. The title page
38
+ // links to a website, to a page and through a named destination; every other page has a linked
39
+ // text run back to it. The outline is nested and shown when the file opens.
40
+ import { Builder, type BuilderOptions, Font, type LinkTarget, type Surface } from "@laminapdf/sdk";
41
+
42
+ const TITLES = ["Title page", "Contents", "Chapter 1", "Chapter 2"] as const;
43
+ const MARGIN = 36;
44
+ const TITLE_BASELINE = 60;
45
+ const TITLE_SIZE = 18;
46
+ const LINK_SIZE = 12;
47
+ /** The baselines of the title page's three links. */
48
+ const LINK_BASELINES = [100, 125, 150] as const;
49
+ /** A link's clickable box: this wide, this tall, starting this far above the baseline. */
50
+ const LINK_WIDTH = 220;
51
+ const LINK_HEIGHT = 20;
52
+ const LINK_RISE = 15;
53
+
54
+ /**
55
+ * The document's bytes. Needs `init({ text: true })`.
56
+ *
57
+ * @param options - can fix the file ID, the producer and the creation date.
58
+ */
59
+ export function linksAndOutlines(options: BuilderOptions = {}): Uint8Array<ArrayBuffer> {
60
+ using b = new Builder(options);
61
+ const font = Font.standard("Helvetica");
62
+ b.font(font);
63
+ b.destination("chapter-2", 3);
64
+ TITLES.forEach((title, index) => {
65
+ const page = b.page("A5");
66
+ const s = page.surface();
67
+ s.text(title, { font, size: TITLE_SIZE, at: [MARGIN, TITLE_BASELINE] });
68
+ if (index === 0) titlePageLinks(s, font);
69
+ else {
70
+ const link = { target: { type: "page", index: 0 }, contents: "Title page" } as const;
71
+ s.text("Back to the title page", { font, size: LINK_SIZE, at: [MARGIN, 100], fill: [0, 0.2, 0.8], link });
72
+ }
73
+ page.finish();
74
+ });
75
+ navigation(b);
76
+ using doc = b.finish();
77
+ return doc.save();
78
+ }
79
+
80
+ /** The title page's links: to a website, to a page, and to a named destination. */
81
+ function titlePageLinks(s: Surface, font: Font): void {
82
+ const links: readonly (readonly [label: string, target: LinkTarget, contents?: string])[] = [
83
+ ["Visit example.com", { type: "uri", uri: "https://example.com/" }, "Example"],
84
+ ["Go to chapter 1", { type: "page", index: 2 }],
85
+ ["Go to chapter 2 (named)", { type: "named", name: "chapter-2" }],
86
+ ];
87
+ links.forEach(([label, target, contents], i) => {
88
+ const baseline = LINK_BASELINES[i] ?? 0;
89
+ s.text(label, { font, size: LINK_SIZE, at: [MARGIN, baseline] });
90
+ s.link([MARGIN, baseline - LINK_RISE, LINK_WIDTH, LINK_HEIGHT], target, contents);
91
+ });
92
+ }
93
+
94
+ /** The outline (bookmarks), the page labels, and the viewer opening with the outline shown. */
95
+ function navigation(b: Builder): void {
96
+ b.outline([
97
+ { title: "Title page", target: { type: "page", index: 0 } },
98
+ { title: "Contents", target: { type: "page", index: 1 }, color: [0, 0, 0.6] },
99
+ {
100
+ title: "Chapters",
101
+ open: true,
102
+ bold: true,
103
+ children: [
104
+ { title: "Chapter 1", target: { type: "page", index: 2 } },
105
+ { title: "Chapter 2", target: { type: "named", name: "chapter-2" }, italic: true },
106
+ ],
107
+ },
108
+ ]);
109
+ b.pageLabels([
110
+ { start: 0, style: "lowerRoman" },
111
+ { start: 2, style: "decimal" },
112
+ ]).viewerPreferences({ pageMode: "outlines" });
113
+ }
114
+ ```
115
+
116
+ **Rust**
117
+
118
+ `crates/laminapdf/examples/links_and_outlines.rs`:
119
+
120
+ ````rust
121
+ //! Links and outlines: four A5 pages joined by links, bookmarks and page labels.
122
+ //!
123
+ //! The title page has a URI link, a link to a page and one through a named destination; every
124
+ //! other page has a linked text run back to it. The outline (bookmarks) is nested and open in
125
+ //! the viewer, and the pages are labelled i, ii, 1, 2.
126
+ //!
127
+ //! ```sh
128
+ //! cargo run -p laminapdf --example links_and_outlines -- [out-dir] # writes <out-dir>/links_and_outlines.pdf
129
+ //! ```
130
+
131
+ use std::error::Error;
132
+ use std::path::PathBuf;
133
+
134
+ use laminapdf::{
135
+ BuildOptions, Builder, Color, Fit, FontHandle, LabelStyle, LinkTarget, OutlineEntry, PageLabel, PageMode, PageSize,
136
+ PointPt, Pt, RectPt, SaveOptions, Standard14, Surface, ViewerPreferences,
137
+ };
138
+
139
+ const TITLES: [&str; 4] = ["Title page", "Contents", "Chapter 1", "Chapter 2"];
140
+ const MARGIN: f64 = 36.0;
141
+ const TITLE_AT: PointPt = PointPt::from_points(MARGIN, 60.0);
142
+ const BACK_AT: PointPt = PointPt::from_points(MARGIN, 100.0);
143
+ const TITLE_SIZE: Pt = Pt::new(18.0);
144
+ const LINK_SIZE: Pt = Pt::new(12.0);
145
+ /// The baselines of the title page's three links.
146
+ const LINK_BASELINES: [f64; 3] = [100.0, 125.0, 150.0];
147
+ /// A link's clickable box: this wide, this tall, starting this far above the baseline.
148
+ const LINK_WIDTH: f64 = 220.0;
149
+ const LINK_HEIGHT: f64 = 20.0;
150
+ const LINK_RISE: f64 = 15.0;
151
+
152
+ fn main() -> Result<(), Box<dyn Error>> {
153
+ let out = PathBuf::from(std::env::args().nth(1).unwrap_or_else(|| "target/examples-out".into()));
154
+ std::fs::create_dir_all(&out)?;
155
+ std::fs::write(out.join("links_and_outlines.pdf"), build(BuildOptions::new())?)?;
156
+ Ok(())
157
+ }
158
+
159
+ /// The document's bytes. `options` can fix the file ID, the producer and the creation date.
160
+ pub(crate) fn build(options: BuildOptions) -> Result<Vec<u8>, Box<dyn Error>> {
161
+ let mut b = Builder::new(options);
162
+ let helvetica = b.standard_font(Standard14::Helvetica)?;
163
+ b.destination("chapter-2", 3, Fit::Page);
164
+ for (index, title) in (0_u32..).zip(TITLES) {
165
+ let mut page = b.page(PageSize::A5);
166
+ let s = page.surface();
167
+ s.text(title, &helvetica, TITLE_SIZE, TITLE_AT)?;
168
+ if index == 0 {
169
+ title_page_links(s, &helvetica)?;
170
+ } else {
171
+ s.text("Back to the title page", &helvetica, LINK_SIZE, BACK_AT)?
172
+ .fill(Color::rgb(0.0, 0.2, 0.8))
173
+ .link(LinkTarget::page(0), Some("Title page".into()));
174
+ }
175
+ page.finish()?;
176
+ }
177
+ navigation(&mut b)?;
178
+ let mut doc = b.finish()?;
179
+ Ok(doc.save(&SaveOptions::new())?.bytes)
180
+ }
181
+
182
+ /// The title page's links: to a website, to a page, and to a named destination.
183
+ fn title_page_links(s: &mut Surface, font: &FontHandle) -> Result<(), Box<dyn Error>> {
184
+ let site = LinkTarget::Uri("https://example.com/".into());
185
+ let links = [
186
+ ("Visit example.com", site, Some("Example")),
187
+ ("Go to chapter 1", LinkTarget::page(2), None),
188
+ ("Go to chapter 2 (named)", LinkTarget::Named("chapter-2".into()), None),
189
+ ];
190
+ for ((label, target, contents), baseline) in links.into_iter().zip(LINK_BASELINES) {
191
+ s.text(label, font, LINK_SIZE, PointPt::from_points(MARGIN, baseline))?;
192
+ let area = RectPt::from_points(MARGIN, baseline - LINK_RISE, LINK_WIDTH, LINK_HEIGHT);
193
+ s.link(area, target, contents.map(String::from));
194
+ }
195
+ Ok(())
196
+ }
197
+
198
+ /// The outline (bookmarks), the page labels, and the viewer opening with the outline shown.
199
+ fn navigation(b: &mut Builder) -> Result<(), Box<dyn Error>> {
200
+ b.outline(vec![
201
+ OutlineEntry::new("Title page", LinkTarget::page(0)),
202
+ OutlineEntry::new("Contents", LinkTarget::page(1)).with_color(0.0, 0.0, 0.6),
203
+ OutlineEntry::heading("Chapters")
204
+ .open(true)
205
+ .bold()
206
+ .with_child(OutlineEntry::new("Chapter 1", LinkTarget::page(2)))
207
+ .with_child(OutlineEntry::new("Chapter 2", LinkTarget::Named("chapter-2".into())).italic()),
208
+ ])?;
209
+ b.page_labels(vec![
210
+ PageLabel::new(0, Some(LabelStyle::LowerRoman)),
211
+ PageLabel::new(2, Some(LabelStyle::Decimal)),
212
+ ])
213
+ .viewer_preferences(ViewerPreferences::default().with_page_mode(PageMode::Outlines));
214
+ Ok(())
215
+ }
216
+ ````
217
+
218
+ [See the output](../../pages/recipes/links-and-outlines.md).
219
+
220
+ > **Details:** The metadata goes into two places: an XMP packet, the XML that describes the file, and the Info dictionary, the older list of the same facts. Lamina PDF writes both from the same values, so they always agree.
221
+
222
+ Next: [accessible PDFs](../../pages/guides/accessible-pdfs.md).
@@ -0,0 +1,70 @@
1
+ ---
2
+ title: "Open password-protected files"
3
+ description: "Open PDFs that need a password, ask again when it is missing or wrong, and save them afterwards."
4
+ version: "0.2.0-alpha.3"
5
+ url: "https://docs.laminapdf.com/guides/password-protected-files/"
6
+ ---
7
+
8
+ # Open password-protected files
9
+
10
+ Open PDFs that need a password, ask again when it is missing or wrong, and save them afterwards.
11
+
12
+ You'll open a PDF that needs a password, and ask your user again when the password is missing or wrong. You need this for bank statements, contracts and other files that people protect before they share them.
13
+
14
+ This example opens an upload with an optional password and returns the outcome as a value instead of throwing. Its test opens a file encrypted with AES-256 three times: with the right password, with none and with a wrong one:
15
+
16
+ `packages/sdk/examples/open-untrusted.ts`:
17
+
18
+ ```ts
19
+ // Open bytes from someone else: an optional password, tighter limits than the defaults, and the
20
+ // outcome as a value rather than an exception. The example writes nothing: it returns what it found.
21
+ import { Document, isLaminaPdfError } from "@laminapdf/sdk";
22
+
23
+ const MiB = 1024 * 1024;
24
+ /** Limits for an upload endpoint: each decoded stream, and all of them together. */
25
+ const LIMITS = { maxStreamDecoded: 16 * MiB, maxDecodedTotal: 64 * MiB } as const;
26
+
27
+ /** What opening a file gave: its page count and warning codes, or why it didn't open. */
28
+ export type Opened =
29
+ | { readonly ok: true; readonly pages: number; readonly warnings: readonly string[] }
30
+ | { readonly ok: false; readonly code: string; readonly hint: string | undefined };
31
+
32
+ /** Opens `bytes`, with `password` if the file needs one, under {@link LIMITS}. Needs `init()`. */
33
+ export function openUntrusted(bytes: Uint8Array, password?: string): Opened {
34
+ try {
35
+ using doc = Document.open(bytes, { ...(password === undefined ? {} : { password }), limits: LIMITS });
36
+ // Repairs and dropped values are warnings: the file opened, but not exactly as written.
37
+ return { ok: true, pages: doc.pageCount, warnings: doc.warnings.map((w) => w.code) };
38
+ } catch (e) {
39
+ // `password-required`, `wrong-password`, `not-pdf`, `limit-exceeded`, …: a coded error whose
40
+ // message and hint never contain text from the file. Anything else is a bug: let it throw.
41
+ if (isLaminaPdfError(e)) return { ok: false, code: e.code, hint: e.hint };
42
+ throw e;
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Pass the password
48
+
49
+ Pass the password when you open the file: `Document.open(bytes, { password })`. The password goes only to the WebAssembly module. Lamina PDF never stores or logs it, and never puts it in an error.
50
+
51
+ A PDF can have two passwords. The user password opens the file, and the owner password also lifts its restrictions, such as on printing or copying. Lamina PDF checks your password against both:
52
+
53
+ - **Older files** (RC4 and AES-128) check the user password first.
54
+ - **AES-256 files** check the owner password first, as PDF 2.0 requires. So a password that matches both counts as the owner's.
55
+
56
+ ## When the password is missing or wrong
57
+
58
+ Opening fails with `password-required` when you pass no password, and with `wrong-password` when it matches neither. Nothing else has changed, so ask your user and open the file again.
59
+
60
+ ## Passwords with accents and other symbols
61
+
62
+ A character such as "é" can be written in more than one way in Unicode, so the same password can arrive as different bytes. In TypeScript, Lamina PDF tries the password as you give it and, after `wrong-password`, again in its NFKC form, the Unicode form that makes such spellings match. Older files also accept a password's Latin-1 bytes. AES-256 files accept the password as given or in the normalised form that PDF 2.0 asks for (SASLprep).
63
+
64
+ ## Save an encrypted file
65
+
66
+ Once you've edited an encrypted file, it saves only as an unencrypted [full rewrite](../../pages/reference/glossary.md#full-rewrite), with `removeEncryption: true`. Without that option, saving fails with `encrypted-base` (see [save a PDF](../../pages/guides/save-a-pdf.md#when-a-file-refuses-a-save)). Lamina PDF can't yet write encrypted files.
67
+
68
+ > **Details:** Lamina PDF reads files encrypted with the standard security handler: RC4 (revisions 2–4), AES-128 (revision 4) and AES-256 (revisions 5 and 6), including crypt filters and unencrypted metadata. It decrypts strings and streams as the file loads.
69
+
70
+ Next: [open files you don't trust](../../pages/guides/untrusted-files.md).
@@ -0,0 +1,144 @@
1
+ ---
2
+ title: "Save a PDF"
3
+ description: "Save edits as a revision appended to the original bytes or as a whole new file, and see when a file refuses a save."
4
+ version: "0.2.0-alpha.3"
5
+ url: "https://docs.laminapdf.com/guides/save-a-pdf/"
6
+ ---
7
+
8
+ # Save a PDF
9
+
10
+ Save edits as a revision appended to the original bytes or as a whole new file, and see when a file refuses a save.
11
+
12
+ You'll save an edited PDF, either by appending your changes to the original bytes or by writing the whole file again. The choice matters when a file is signed, was damaged, or is password-protected, because each one limits how it can be saved.
13
+
14
+ This example, from [Edit a PDF](../../pages/guides/edit-a-pdf.md), asks for an [incremental save](../../pages/reference/glossary.md#incremental-save), which keeps the original bytes at the start of the result, unchanged:
15
+
16
+ **TypeScript**
17
+
18
+ `packages/sdk/examples/generate-then-edit.ts`:
19
+
20
+ ```ts
21
+ // Generate a document, then open its bytes, edit them and save incrementally. The edit changes
22
+ // the Info dictionary in one transaction, and the incremental save keeps the generated bytes as
23
+ // they are, with one revision appended.
24
+ import { Builder, type BuilderOptions, Document, Font } from "@laminapdf/sdk";
25
+
26
+ /**
27
+ * The generated file followed by the edit's revision. Needs `init({ text: true })`.
28
+ *
29
+ * @param options - can fix the file ID, the producer and the creation date.
30
+ * @param editedAt - the modification date to record, as a PDF date string.
31
+ */
32
+ export function generateThenEdit(options: BuilderOptions = {}, editedAt?: string): Uint8Array<ArrayBuffer> {
33
+ return edit(generate(options), editedAt);
34
+ }
35
+
36
+ /** A one-page draft, saved (a new document is always saved as a full rewrite). */
37
+ export function generate(options: BuilderOptions = {}): Uint8Array<ArrayBuffer> {
38
+ using b = new Builder(options);
39
+ const page = b.page("A4");
40
+ page.surface().text("Draft report", { font: Font.standard("Helvetica"), size: 20, at: [72, 96] });
41
+ page.finish();
42
+ b.metadata({ title: "Draft report" });
43
+ using doc = b.finish();
44
+ return doc.save();
45
+ }
46
+
47
+ /** Opens `bytes`, edits the Info dictionary in one transaction (which `undo` could revert) and saves incrementally. */
48
+ function edit(bytes: Uint8Array, editedAt?: string): Uint8Array<ArrayBuffer> {
49
+ using doc = Document.open(bytes);
50
+ doc.transact((tx) => tx.setInfo({ title: "Final report", author: "Ada Lovelace" }, editedAt));
51
+ return doc.save({ mode: "incremental" });
52
+ }
53
+ ```
54
+
55
+ **Rust**
56
+
57
+ `crates/laminapdf/examples/generate_then_edit.rs`:
58
+
59
+ ````rust
60
+ //! Generate a document, then open its bytes, edit them and save incrementally.
61
+ //!
62
+ //! The edit changes the Info dictionary with an op (undoable and serialisable), and the
63
+ //! incremental save keeps the generated bytes as they are, with one revision appended.
64
+ //!
65
+ //! ```sh
66
+ //! cargo run -p laminapdf --example generate_then_edit -- [out-dir] # writes <out-dir>/generate_then_edit.pdf
67
+ //! ```
68
+
69
+ use std::error::Error;
70
+ use std::path::PathBuf;
71
+
72
+ use laminapdf::{
73
+ BuildOptions, Builder, Document, InfoPatch, Metadata, Op, OpenOptions, PageSize, PdfDate, PointPt, Pt, SaveMode,
74
+ SaveOptions, SetInfo, Standard14,
75
+ };
76
+
77
+ fn main() -> Result<(), Box<dyn Error>> {
78
+ let out = PathBuf::from(std::env::args().nth(1).unwrap_or_else(|| "target/examples-out".into()));
79
+ std::fs::create_dir_all(&out)?;
80
+ let bytes = build(BuildOptions::new(), None)?;
81
+ std::fs::write(out.join("generate_then_edit.pdf"), bytes)?;
82
+ Ok(())
83
+ }
84
+
85
+ /// The generated file followed by the edit's revision. `edited_at` is recorded as the
86
+ /// modification date; `options` can fix the file ID, the producer and the creation date.
87
+ pub(crate) fn build(options: BuildOptions, edited_at: Option<PdfDate>) -> Result<Vec<u8>, Box<dyn Error>> {
88
+ let generated = generate(options)?;
89
+ edit(generated, edited_at)
90
+ }
91
+
92
+ /// A one-page draft, saved (a new document is always saved as a full rewrite).
93
+ pub(crate) fn generate(options: BuildOptions) -> Result<Vec<u8>, Box<dyn Error>> {
94
+ let mut b = Builder::new(options);
95
+ let helvetica = b.standard_font(Standard14::Helvetica)?;
96
+ let mut page = b.page(PageSize::A4);
97
+ page.surface().text(
98
+ "Draft report",
99
+ &helvetica,
100
+ Pt::new(20.0),
101
+ PointPt::from_points(72.0, 96.0),
102
+ )?;
103
+ page.finish()?;
104
+ b.metadata(Metadata::default().with_title("Draft report"));
105
+ Ok(b.finish()?.save(&SaveOptions::new())?.bytes)
106
+ }
107
+
108
+ /// Opens `bytes`, edits the Info dictionary in one transaction (which `undo` could revert) and
109
+ /// saves incrementally: `bytes` unchanged, then the appended revision.
110
+ fn edit(bytes: Vec<u8>, edited_at: Option<PdfDate>) -> Result<Vec<u8>, Box<dyn Error>> {
111
+ let mut doc = Document::open_bytes(bytes, OpenOptions::new())?;
112
+ let patch = InfoPatch::new()
113
+ .title(Some("Final report"))
114
+ .author(Some("Ada Lovelace"));
115
+ doc.apply(vec![Op::from(SetInfo::new(patch, edited_at))])?;
116
+ let options = SaveOptions::new().with_mode(SaveMode::Incremental);
117
+ Ok(doc.save(&options)?.bytes)
118
+ }
119
+ ````
120
+
121
+ ## Choose a save mode
122
+
123
+ Every save either appends a revision to the bytes you opened (an incremental save) or writes the whole file again (a [full rewrite](../../pages/reference/glossary.md#full-rewrite)). You pick one with the `mode` option:
124
+
125
+ | `mode` | What it writes |
126
+ | --- | --- |
127
+ | `auto` (the default) | Incremental when that's allowed, otherwise a full rewrite |
128
+ | `incremental` | The original bytes unchanged, then one appended revision |
129
+ | `fullRewrite` | The whole file again |
130
+
131
+ `doc.save(options)` returns the bytes, and `doc.saveWithReport(options)` also says what it wrote. `doc.saveJob()` does the same work in steps, so a page can save a large file without freezing. The same file with the same edits always saves to the same bytes.
132
+
133
+ ## When a file refuses a save
134
+
135
+ Every save makes a plan before it writes. The plan reads the file's save constraint: what its signatures, permissions, repairs and encryption allow. Then it picks a mode or refuses:
136
+
137
+ - **A signed file** saves only incrementally, and only with edits its signatures allow. Anything else fails with `would-invalidate-signatures`. Pass `invalidateSignatures: true` to save anyway, which removes the broken signatures from the output.
138
+ - **A repaired file**, one Lamina PDF had to fix as it opened, can't safely take an appended revision. It needs a full rewrite, so asking for `incremental` fails with `requires-rewrite`.
139
+ - **An edited encrypted file** saves only as a full rewrite without its encryption, and only when you pass `removeEncryption: true`. Otherwise every mode fails with `encrypted-base`.
140
+ - **An unchanged document** always saves incrementally, which returns its bytes unchanged.
141
+
142
+ > **Details:** The save constraint covers signature fields, the [DocMDP](../../pages/reference/glossary.md#docmdp) and [UR3](../../pages/reference/glossary.md#ur3) permissions, a signature's `/Lock` entry (the form fields it locks), document timestamps, and whether the file needed repair.
143
+
144
+ Next: [open password-protected files](../../pages/guides/password-protected-files.md).
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: "Open files you don't trust"
3
+ description: "Cap how much work an uploaded PDF can make Lamina PDF do, and refuse damaged files with strict mode."
4
+ version: "0.2.0-alpha.3"
5
+ url: "https://docs.laminapdf.com/guides/untrusted-files/"
6
+ ---
7
+
8
+ # Open files you don't trust
9
+
10
+ Cap how much work an uploaded PDF can make Lamina PDF do, and refuse damaged files with strict mode.
11
+
12
+ You'll open uploads with caps on how much an untrusted file can make Lamina PDF decode, read or draw. You need this on any server or page that opens PDFs from strangers, because a small file can ask for gigabytes of work.
13
+
14
+ This example opens uploads with tighter limits than the defaults: at most 16 MiB decoded from one stream and 64 MiB in all:
15
+
16
+ `packages/sdk/examples/open-untrusted.ts`:
17
+
18
+ ```ts
19
+ // Open bytes from someone else: an optional password, tighter limits than the defaults, and the
20
+ // outcome as a value rather than an exception. The example writes nothing: it returns what it found.
21
+ import { Document, isLaminaPdfError } from "@laminapdf/sdk";
22
+
23
+ const MiB = 1024 * 1024;
24
+ /** Limits for an upload endpoint: each decoded stream, and all of them together. */
25
+ const LIMITS = { maxStreamDecoded: 16 * MiB, maxDecodedTotal: 64 * MiB } as const;
26
+
27
+ /** What opening a file gave: its page count and warning codes, or why it didn't open. */
28
+ export type Opened =
29
+ | { readonly ok: true; readonly pages: number; readonly warnings: readonly string[] }
30
+ | { readonly ok: false; readonly code: string; readonly hint: string | undefined };
31
+
32
+ /** Opens `bytes`, with `password` if the file needs one, under {@link LIMITS}. Needs `init()`. */
33
+ export function openUntrusted(bytes: Uint8Array, password?: string): Opened {
34
+ try {
35
+ using doc = Document.open(bytes, { ...(password === undefined ? {} : { password }), limits: LIMITS });
36
+ // Repairs and dropped values are warnings: the file opened, but not exactly as written.
37
+ return { ok: true, pages: doc.pageCount, warnings: doc.warnings.map((w) => w.code) };
38
+ } catch (e) {
39
+ // `password-required`, `wrong-password`, `not-pdf`, `limit-exceeded`, …: a coded error whose
40
+ // message and hint never contain text from the file. Anything else is a bug: let it throw.
41
+ if (isLaminaPdfError(e)) return { ok: false, code: e.code, hint: e.hint };
42
+ throw e;
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Set only the limits you need
48
+
49
+ That work takes many forms: a stream that inflates to gigabytes, a page tree thousands of levels deep, drawings nested inside drawings. Lamina PDF keeps every file within the `limits` you pass when you open it. Every limit has a safe default, so you set only the ones you want to change.
50
+
51
+ ## Hard and soft limits
52
+
53
+ - **Hard limits** stop the call with `limit-exceeded`. Its `data.limit` names the limit, so you can tell the user which one.
54
+ - **Soft limits** drop the excess with a warning and carry on.
55
+
56
+ | Limit | Kind | Default | What it caps |
57
+ | --- | --- | --- | --- |
58
+ | `maxNestingDepth` | hard | 256 | arrays and dictionaries inside each other; saved graphics states (`q`) |
59
+ | `maxStreamDecoded` | hard | 64 MiB | bytes one stream decodes to, per filter stage |
60
+ | `maxDecodedTotal` | hard | 1 GiB | bytes one call (`open`, one page's content, one read) decodes, across all streams |
61
+ | `maxFilterChain` | hard | 8 | filters on one stream |
62
+ | `maxResolveDepth` | hard | 32 | indirect objects in flight while following a chain of references |
63
+ | `maxPageTreeDepth` | hard | 256 | `/Kids` levels below the page-tree root (a deeper subtree is skipped with a warning) |
64
+ | `maxTreeDepth` | hard | 64 | name and number tree levels, outline depth |
65
+ | `maxStringLength` | hard | 16 MiB | bytes in one string or name |
66
+ | `maxXrefSections` | soft | 4,096 | `/Prev` hops in the [cross-reference table](../../pages/reference/glossary.md#cross-reference-table) chain (past it, Lamina PDF rebuilds the table by scanning) |
67
+ | `maxObjectNumber` | soft | 8,388,607 | the highest object number kept |
68
+ | `maxWarnings` | soft | 1,000 | warnings kept per document (then one `warnings-truncated`) |
69
+ | `maxContentOperators` | hard | 10,000,000 | drawing operators run in one content call, counting a form's each time it is drawn |
70
+ | `maxXobjectDepth` | hard | 32 | forms drawn inside forms |
71
+ | `maxImagePixels` | hard (reserved) | 2²⁸ | pixels of one image |
72
+ | `maxReadWindow` | hard | 256 MiB | one read from a range source (`Document.openRange`) |
73
+
74
+ These are the TypeScript names. In Rust they're builder methods, such as `Limits::new().with_max_stream_decoded(…)`.
75
+
76
+ ## Refuse damaged files
77
+
78
+ Lamina PDF repairs damaged files as it opens them and reports each repair as a warning. Pass `strict: true` to refuse any file that needs repair instead. It then fails with `strict`.
79
+
80
+ Next: [keep the page responsive](../../pages/guides/keep-the-page-responsive.md).
@@ -0,0 +1,33 @@
1
+ ---
2
+ title: "Lamina PDF"
3
+ description: "Create, read, edit and save PDFs from Rust and TypeScript, with one Rust core that runs natively and as WebAssembly."
4
+ version: "0.2.0-alpha.3"
5
+ url: "https://docs.laminapdf.com/"
6
+ ---
7
+
8
+ # Lamina PDF
9
+
10
+ Create, read, edit and save PDFs from Rust and TypeScript, with one Rust core that runs natively and as WebAssembly.
11
+
12
+ Lamina PDF creates, reads, edits and saves PDFs. One Rust core does the work, and it runs natively and as WebAssembly in browsers, Node, Deno, Bun and Cloudflare Workers. It's for apps that make documents, such as invoices, reports and tickets, and for apps that open documents other people send.
13
+
14
+ - **[`laminapdf`](https://crates.io/crates/laminapdf)** on crates.io is the Rust crate. Its [API reference](../pages/reference/rust.md) is on docs.rs.
15
+ - **[`@laminapdf/sdk`](https://www.npmjs.com/package/@laminapdf/sdk)** on npm is the TypeScript API over the WebAssembly build. After one `await init()`, every call is synchronous, and the API is the same in every runtime. Its [API reference](../api/README.md) is on this site.
16
+
17
+ > **Alpha releases:** Both packages are alpha releases, so name the version when you install them: `npm install @laminapdf/sdk@0.2.0-alpha.3` and `cargo add laminapdf@=0.2.0-alpha.3`. [Install](../pages/install.md) lists every published package.
18
+
19
+ ## What works today
20
+
21
+ | Area | What you get | Guide |
22
+ | --- | --- | --- |
23
+ | Generate | Pages, text with shaping, bidi and line breaking, embedded and subset fonts, the standard 14, vectors, colour, images, tagged PDF | [Fonts and text](../pages/guides/fonts-and-text.md), [Images and vectors](../pages/guides/images-and-vectors.md) |
24
+ | Navigate | Links, named destinations, outlines, page labels, Info and XMP metadata | [Links, outlines and metadata](../pages/guides/links-outlines-and-metadata.md) |
25
+ | Edit | Ops in transactions, undo and redo, incremental or full saves that respect signatures | [Edit a PDF](../pages/guides/edit-a-pdf.md), [Save a PDF](../pages/guides/save-a-pdf.md) |
26
+ | Read safely | Repair of damaged files as warnings, resource limits, strict mode, passwords | [Open password-protected files](../pages/guides/password-protected-files.md), [Open files you don't trust](../pages/guides/untrusted-files.md) |
27
+ | Errors | One coded error and warning shape on every surface | [Errors and warnings](../pages/concepts/errors-and-warnings.md) |
28
+
29
+ Every code block on this site is a tested example file, so the code you see is the code that runs. Each example writes the same bytes in Rust and in every JavaScript runtime.
30
+
31
+ ## Where to go next
32
+
33
+ Start with [your first PDF](../pages/start/first-pdf.md), then read [how Lamina PDF thinks about a PDF](../pages/concepts/how-lamina-thinks.md). When you're ready for your own setup, pick [your runtime](../pages/quick-start/browser.md).
@@ -0,0 +1,75 @@
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.3"
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.3
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.3`), and Deno takes `npm:@laminapdf/sdk@0.2.0-alpha.3`. 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.3
32
+ ```
33
+
34
+ The command writes `laminapdf = "=0.2.0-alpha.3"` 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
+ ## Try a change before it's released
62
+
63
+ 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:
64
+
65
+ ```sh
66
+ pnpm install
67
+ cargo xtask wasm # builds packages/wasm/dist/ and packages/sdk/dist/, then tests them
68
+ ```
69
+
70
+ 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" }`.
71
+
72
+ ## API docs
73
+
74
+ - TypeScript: the [`@laminapdf/sdk` reference](../api/README.md), generated from the package's TSDoc comments.
75
+ - Rust: the [Rust API](../pages/reference/rust.md) page, which links each crate's docs on docs.rs.