@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.11

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 (279) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -21
  3. package/dist/api/args.d.ts +1 -1
  4. package/dist/api/args.js +2 -5
  5. package/dist/api/color.d.ts +4 -4
  6. package/dist/api/color.js +11 -17
  7. package/dist/api/content.d.ts +20 -13
  8. package/dist/api/content.js +44 -29
  9. package/dist/api/descriptor.d.ts +2 -2
  10. package/dist/api/descriptor.js +121 -31
  11. package/dist/api/dimension.d.ts +76 -0
  12. package/dist/api/dimension.js +43 -0
  13. package/dist/api/font-url.d.ts +28 -0
  14. package/dist/api/font-url.js +134 -0
  15. package/dist/api/forms.d.ts +347 -0
  16. package/dist/api/forms.js +189 -0
  17. package/dist/api/gradient.d.ts +53 -0
  18. package/dist/api/gradient.js +97 -0
  19. package/dist/api/index.d.ts +13 -8
  20. package/dist/api/index.js +13 -24
  21. package/dist/api/insets.d.ts +19 -12
  22. package/dist/api/insets.js +19 -12
  23. package/dist/api/layout.d.ts +149 -25
  24. package/dist/api/layout.js +182 -51
  25. package/dist/api/page-builder.d.ts +40 -0
  26. package/dist/api/page-builder.js +44 -0
  27. package/dist/api/structure.d.ts +107 -13
  28. package/dist/api/structure.js +187 -88
  29. package/dist/api/table.d.ts +5 -5
  30. package/dist/api/table.js +38 -26
  31. package/dist/api/text.d.ts +90 -4
  32. package/dist/api/text.js +116 -31
  33. package/dist/assets/font-data.d.ts +2 -0
  34. package/dist/assets/font-data.js +21 -0
  35. package/dist/assets/font-data.ts +37 -0
  36. package/dist/common/color.js +1 -5
  37. package/dist/constants/page-sizes.js +3 -6
  38. package/dist/constants/pdf-parts.js +1 -4
  39. package/dist/crypto/legacy-handler.d.ts +43 -0
  40. package/dist/crypto/legacy-handler.js +145 -0
  41. package/dist/crypto/security-handler.d.ts +62 -0
  42. package/dist/crypto/security-handler.js +155 -0
  43. package/dist/crypto/webcrypto.d.ts +11 -0
  44. package/dist/crypto/webcrypto.js +62 -0
  45. package/dist/edit/acroform-reader.d.ts +70 -0
  46. package/dist/edit/acroform-reader.js +126 -0
  47. package/dist/edit/appearance.d.ts +47 -0
  48. package/dist/edit/appearance.js +171 -0
  49. package/dist/edit/document.d.ts +145 -0
  50. package/dist/edit/document.js +642 -0
  51. package/dist/edit/fill.d.ts +65 -0
  52. package/dist/edit/fill.js +424 -0
  53. package/dist/edit/flatten.d.ts +54 -0
  54. package/dist/edit/flatten.js +462 -0
  55. package/dist/edit/index.d.ts +20 -0
  56. package/dist/edit/index.js +18 -0
  57. package/dist/edit/lexer.d.ts +30 -0
  58. package/dist/edit/lexer.js +285 -0
  59. package/dist/edit/objects.d.ts +68 -0
  60. package/dist/edit/objects.js +83 -0
  61. package/dist/edit/writer.d.ts +60 -0
  62. package/dist/edit/writer.js +283 -0
  63. package/dist/elements/container-element.d.ts +50 -5
  64. package/dist/elements/container-element.js +100 -39
  65. package/dist/elements/forms/checkbox-element.d.ts +51 -0
  66. package/dist/elements/forms/checkbox-element.js +48 -0
  67. package/dist/elements/forms/choice-element.d.ts +53 -0
  68. package/dist/elements/forms/choice-element.js +59 -0
  69. package/dist/elements/forms/push-button-element.d.ts +56 -0
  70. package/dist/elements/forms/push-button-element.js +49 -0
  71. package/dist/elements/forms/radio-element.d.ts +52 -0
  72. package/dist/elements/forms/radio-element.js +49 -0
  73. package/dist/elements/forms/signature-element.d.ts +54 -0
  74. package/dist/elements/forms/signature-element.js +47 -0
  75. package/dist/elements/forms/text-field-element.d.ts +61 -0
  76. package/dist/elements/forms/text-field-element.js +54 -0
  77. package/dist/elements/image-element.d.ts +58 -6
  78. package/dist/elements/image-element.js +145 -111
  79. package/dist/elements/index.d.ts +26 -10
  80. package/dist/elements/index.js +26 -28
  81. package/dist/elements/layout/anchor-element.d.ts +23 -0
  82. package/dist/elements/layout/anchor-element.js +27 -0
  83. package/dist/elements/layout/bookmark-element.d.ts +27 -0
  84. package/dist/elements/layout/bookmark-element.js +30 -0
  85. package/dist/elements/layout/default-text-style-element.d.ts +30 -0
  86. package/dist/elements/layout/default-text-style-element.js +47 -0
  87. package/dist/elements/layout/deferred-element.d.ts +3 -3
  88. package/dist/elements/layout/deferred-element.js +4 -8
  89. package/dist/elements/layout/expanded-element.d.ts +6 -4
  90. package/dist/elements/layout/expanded-element.js +22 -17
  91. package/dist/elements/layout/keep-together-element.d.ts +42 -0
  92. package/dist/elements/layout/keep-together-element.js +85 -0
  93. package/dist/elements/layout/link-element.d.ts +33 -0
  94. package/dist/elements/layout/link-element.js +39 -0
  95. package/dist/elements/layout/padding-element.d.ts +10 -5
  96. package/dist/elements/layout/padding-element.js +25 -16
  97. package/dist/elements/layout/page-break-element.d.ts +17 -0
  98. package/dist/elements/layout/page-break-element.js +24 -0
  99. package/dist/elements/layout/page-builder-element.d.ts +32 -0
  100. package/dist/elements/layout/page-builder-element.js +45 -0
  101. package/dist/elements/layout/positioned-element.d.ts +47 -0
  102. package/dist/elements/layout/positioned-element.js +73 -0
  103. package/dist/elements/layout/repeating-header-element.d.ts +3 -3
  104. package/dist/elements/layout/repeating-header-element.js +8 -12
  105. package/dist/elements/layout/rotated-box-element.d.ts +35 -0
  106. package/dist/elements/layout/rotated-box-element.js +54 -0
  107. package/dist/elements/layout/rotated-element.d.ts +32 -0
  108. package/dist/elements/layout/rotated-element.js +41 -0
  109. package/dist/elements/layout/sized-container-element.d.ts +2 -2
  110. package/dist/elements/layout/sized-container-element.js +6 -11
  111. package/dist/elements/layout/struct-group.d.ts +28 -0
  112. package/dist/elements/layout/struct-group.js +42 -0
  113. package/dist/elements/line-element.d.ts +3 -3
  114. package/dist/elements/line-element.js +5 -10
  115. package/dist/elements/page-element.d.ts +26 -6
  116. package/dist/elements/page-element.js +72 -27
  117. package/dist/elements/pdf-document-element.d.ts +10 -4
  118. package/dist/elements/pdf-document-element.js +11 -10
  119. package/dist/elements/pdf-element.d.ts +150 -4
  120. package/dist/elements/pdf-element.js +138 -20
  121. package/dist/elements/rectangle-element.d.ts +52 -11
  122. package/dist/elements/rectangle-element.js +129 -36
  123. package/dist/elements/row-element.d.ts +45 -4
  124. package/dist/elements/row-element.js +66 -18
  125. package/dist/elements/text-element.d.ts +139 -11
  126. package/dist/elements/text-element.js +234 -57
  127. package/dist/forms/acroform.d.ts +63 -0
  128. package/dist/forms/acroform.js +427 -0
  129. package/dist/forms/appearance.d.ts +58 -0
  130. package/dist/forms/appearance.js +204 -0
  131. package/dist/forms/field.d.ts +164 -0
  132. package/dist/forms/field.js +1 -0
  133. package/dist/forms/pdf.d.ts +19 -0
  134. package/dist/forms/pdf.js +28 -0
  135. package/dist/index.d.ts +3 -3
  136. package/dist/index.js +3 -19
  137. package/dist/ir/display-list.d.ts +197 -8
  138. package/dist/ir/display-list.js +8 -2
  139. package/dist/layout/alignment.d.ts +10 -0
  140. package/dist/layout/alignment.js +6 -0
  141. package/dist/layout/box-constraints.d.ts +61 -0
  142. package/dist/layout/box-constraints.js +88 -6
  143. package/dist/layout/collect-images.d.ts +9 -0
  144. package/dist/layout/collect-images.js +34 -0
  145. package/dist/layout/fragmentation.d.ts +20 -1
  146. package/dist/layout/fragmentation.js +74 -18
  147. package/dist/layout/insets.d.ts +28 -0
  148. package/dist/layout/insets.js +20 -0
  149. package/dist/platform/browser-fs.d.ts +2 -0
  150. package/dist/platform/browser-fs.js +9 -0
  151. package/dist/platform/browser-image.d.ts +5 -0
  152. package/dist/platform/browser-image.js +13 -0
  153. package/dist/platform/node-fs.d.ts +2 -0
  154. package/dist/platform/node-fs.js +10 -0
  155. package/dist/platform/node-image.d.ts +5 -0
  156. package/dist/platform/node-image.js +9 -0
  157. package/dist/renderer/anchor-renderer.d.ts +6 -0
  158. package/dist/renderer/anchor-renderer.js +11 -0
  159. package/dist/renderer/bookmark-renderer.d.ts +6 -0
  160. package/dist/renderer/bookmark-renderer.js +11 -0
  161. package/dist/renderer/container-renderer.d.ts +3 -3
  162. package/dist/renderer/container-renderer.js +11 -28
  163. package/dist/renderer/default-text-style-renderer.d.ts +6 -0
  164. package/dist/renderer/default-text-style-renderer.js +10 -0
  165. package/dist/renderer/deferred-renderer.d.ts +3 -3
  166. package/dist/renderer/deferred-renderer.js +8 -23
  167. package/dist/renderer/emoji-image.d.ts +9 -0
  168. package/dist/renderer/emoji-image.js +68 -0
  169. package/dist/renderer/expanded-renderer.d.ts +3 -3
  170. package/dist/renderer/expanded-renderer.js +6 -21
  171. package/dist/renderer/form-field-renderer.d.ts +20 -0
  172. package/dist/renderer/form-field-renderer.js +12 -0
  173. package/dist/renderer/image-renderer.d.ts +4 -4
  174. package/dist/renderer/image-renderer.js +86 -75
  175. package/dist/renderer/index.d.ts +10 -10
  176. package/dist/renderer/index.js +10 -26
  177. package/dist/renderer/keep-together-renderer.d.ts +10 -0
  178. package/dist/renderer/keep-together-renderer.js +12 -0
  179. package/dist/renderer/line-renderer.d.ts +3 -3
  180. package/dist/renderer/line-renderer.js +13 -28
  181. package/dist/renderer/link-renderer.d.ts +6 -0
  182. package/dist/renderer/link-renderer.js +11 -0
  183. package/dist/renderer/padding-renderer.d.ts +3 -3
  184. package/dist/renderer/padding-renderer.js +6 -21
  185. package/dist/renderer/page-break-renderer.d.ts +14 -0
  186. package/dist/renderer/page-break-renderer.js +15 -0
  187. package/dist/renderer/page-builder-renderer.d.ts +6 -0
  188. package/dist/renderer/page-builder-renderer.js +11 -0
  189. package/dist/renderer/page-renderer.d.ts +2 -2
  190. package/dist/renderer/page-renderer.js +115 -77
  191. package/dist/renderer/pdf-backend.d.ts +31 -4
  192. package/dist/renderer/pdf-backend.js +287 -49
  193. package/dist/renderer/pdf-config.js +4 -7
  194. package/dist/renderer/pdf-document-class.d.ts +5 -5
  195. package/dist/renderer/pdf-document-class.js +27 -41
  196. package/dist/renderer/pdf-document-renderer.d.ts +19 -10
  197. package/dist/renderer/pdf-document-renderer.js +135 -91
  198. package/dist/renderer/pdf-renderer.d.ts +2 -2
  199. package/dist/renderer/pdf-renderer.js +165 -90
  200. package/dist/renderer/positioned-renderer.d.ts +6 -0
  201. package/dist/renderer/positioned-renderer.js +10 -0
  202. package/dist/renderer/rectangle-renderer.d.ts +3 -3
  203. package/dist/renderer/rectangle-renderer.js +49 -44
  204. package/dist/renderer/repeating-header-renderer.d.ts +3 -3
  205. package/dist/renderer/repeating-header-renderer.js +10 -26
  206. package/dist/renderer/rotated-renderer.d.ts +7 -0
  207. package/dist/renderer/rotated-renderer.js +29 -0
  208. package/dist/renderer/row-renderer.d.ts +3 -3
  209. package/dist/renderer/row-renderer.js +11 -28
  210. package/dist/renderer/struct-group-renderer.d.ts +10 -0
  211. package/dist/renderer/struct-group-renderer.js +24 -0
  212. package/dist/renderer/text-renderer.d.ts +9 -5
  213. package/dist/renderer/text-renderer.js +437 -89
  214. package/dist/text/advance.d.ts +34 -0
  215. package/dist/text/advance.js +45 -0
  216. package/dist/text/arabic.d.ts +20 -0
  217. package/dist/text/arabic.js +101 -0
  218. package/dist/text/bidi.d.ts +33 -0
  219. package/dist/text/bidi.js +94 -0
  220. package/dist/text/emoji-codepoints.d.ts +1 -0
  221. package/dist/text/emoji-codepoints.js +29 -0
  222. package/dist/text/font-fallback.d.ts +25 -0
  223. package/dist/text/font-fallback.js +38 -0
  224. package/dist/text/glyph-coverage.d.ts +15 -0
  225. package/dist/text/glyph-coverage.js +77 -0
  226. package/dist/text/line-breaker.d.ts +50 -12
  227. package/dist/text/line-breaker.js +200 -54
  228. package/dist/text/line-metrics.d.ts +49 -0
  229. package/dist/text/line-metrics.js +57 -0
  230. package/dist/text/orphans-widows.d.ts +27 -0
  231. package/dist/text/orphans-widows.js +53 -0
  232. package/dist/text/shape.d.ts +29 -0
  233. package/dist/text/shape.js +67 -0
  234. package/dist/text/text-decoration.d.ts +50 -0
  235. package/dist/text/text-decoration.js +62 -0
  236. package/dist/text/text-style.d.ts +60 -0
  237. package/dist/text/text-style.js +79 -0
  238. package/dist/text/whitespace.d.ts +4 -0
  239. package/dist/text/whitespace.js +43 -0
  240. package/dist/utils/afm-parser.d.ts +34 -0
  241. package/dist/utils/afm-parser.js +76 -17
  242. package/dist/utils/bytes.d.ts +24 -0
  243. package/dist/utils/bytes.js +76 -0
  244. package/dist/utils/dest-registry.d.ts +22 -0
  245. package/dist/utils/dest-registry.js +32 -0
  246. package/dist/utils/flex-layout.d.ts +36 -9
  247. package/dist/utils/flex-layout.js +207 -30
  248. package/dist/utils/font-metrics.d.ts +26 -1
  249. package/dist/utils/font-metrics.js +1 -2
  250. package/dist/utils/font-path.js +3 -6
  251. package/dist/utils/gsub.d.ts +36 -0
  252. package/dist/utils/gsub.js +178 -0
  253. package/dist/utils/image-helper.d.ts +6 -5
  254. package/dist/utils/image-helper.js +101 -111
  255. package/dist/utils/inflate.d.ts +16 -0
  256. package/dist/utils/inflate.js +52 -0
  257. package/dist/utils/md5.d.ts +4 -0
  258. package/dist/utils/md5.js +79 -0
  259. package/dist/utils/outline.d.ts +21 -0
  260. package/dist/utils/outline.js +79 -0
  261. package/dist/utils/pdf-object-manager.d.ts +127 -8
  262. package/dist/utils/pdf-object-manager.js +615 -141
  263. package/dist/utils/renderer-registry.d.ts +13 -1
  264. package/dist/utils/renderer-registry.js +23 -6
  265. package/dist/utils/struct-tree.d.ts +44 -0
  266. package/dist/utils/struct-tree.js +129 -0
  267. package/dist/utils/ttf-parser.d.ts +134 -2
  268. package/dist/utils/ttf-parser.js +873 -50
  269. package/dist/utils/ttf-subsetter.d.ts +1 -1
  270. package/dist/utils/ttf-subsetter.js +40 -42
  271. package/dist/utils/ua-xmp.d.ts +9 -0
  272. package/dist/utils/ua-xmp.js +32 -0
  273. package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
  274. package/dist/utils/utf8-to-windows1252-encoder.js +11 -4
  275. package/dist/utils/woff.d.ts +10 -0
  276. package/dist/utils/woff.js +116 -0
  277. package/dist/validators/element-validator.d.ts +2 -2
  278. package/dist/validators/element-validator.js +31 -26
  279. package/package.json +22 -2
@@ -1 +1 @@
1
- export declare function subsetTTF(data: Buffer, used: Set<number>): Buffer;
1
+ export declare function subsetTTF(data: Uint8Array, used: Set<number>): Uint8Array;
@@ -1,32 +1,30 @@
1
- "use strict";
2
1
  // Builds a subset TrueType font that KEEPS the original glyph ids: the PDF content stream emits
3
2
  // original gids and /W + CIDToGIDMap=Identity stay valid, so only the font program shrinks. We drop
4
3
  // the `glyf` outlines of unused glyphs (the bulk of the file) and rebuild `loca`; every other table
5
4
  // is copied verbatim. The used-glyph set is closed over composite-glyph components first, so a glyph
6
5
  // built from others (e.g. "ä") keeps its parts. Fonts without `glyf`/`loca` (CFF/OTF) pass through.
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.subsetTTF = subsetTTF;
9
- const u32 = (n) => n >>> 0;
6
+ import { concatBytes, i16, latin1FromBytes, u16, u32, wi16, wu16, wu32, writeLatin1, } from "./bytes.js";
7
+ const wrap32 = (n) => n >>> 0;
10
8
  /** TrueType table checksum: sum of big-endian uint32 over the 4-byte-padded table data. */
11
9
  function checksum(buf) {
12
10
  let sum = 0;
13
11
  for (let i = 0; i < buf.length; i += 4)
14
- sum = u32(sum + buf.readUInt32BE(i));
12
+ sum = wrap32(sum + u32(buf, i));
15
13
  return sum;
16
14
  }
17
15
  /** Right-pad to a 4-byte boundary (table data is aligned in the sfnt). */
18
16
  function pad4(buf) {
19
17
  const rem = buf.length % 4;
20
- return rem === 0 ? buf : Buffer.concat([buf, Buffer.alloc(4 - rem)]);
18
+ return rem === 0 ? buf : concatBytes([buf, new Uint8Array(4 - rem)]);
21
19
  }
22
- function subsetTTF(data, used) {
23
- const numTables = data.readUInt16BE(4);
20
+ export function subsetTTF(data, used) {
21
+ const numTables = u16(data, 4);
24
22
  const dir = new Map();
25
23
  let p = 12;
26
24
  for (let i = 0; i < numTables; i++) {
27
- dir.set(data.toString("latin1", p, p + 4), {
28
- offset: data.readUInt32BE(p + 8),
29
- length: data.readUInt32BE(p + 12),
25
+ dir.set(latin1FromBytes(data.subarray(p, p + 4)), {
26
+ offset: u32(data, p + 8),
27
+ length: u32(data, p + 12),
30
28
  });
31
29
  p += 16;
32
30
  }
@@ -34,14 +32,14 @@ function subsetTTF(data, used) {
34
32
  if (!dir.has("glyf") || !dir.has("loca") || !dir.has("head") || !dir.has("maxp"))
35
33
  return data;
36
34
  const headOff = dir.get("head").offset;
37
- const longLoca = data.readInt16BE(headOff + 50) === 1;
38
- const numGlyphs = data.readUInt16BE(dir.get("maxp").offset + 4);
35
+ const longLoca = i16(data, headOff + 50) === 1;
36
+ const numGlyphs = u16(data, dir.get("maxp").offset + 4);
39
37
  const locaOff = dir.get("loca").offset;
40
38
  const glyfOff = dir.get("glyf").offset;
41
39
  // Original glyph offsets (numGlyphs + 1 entries; short loca stores half-offsets).
42
40
  const loca = [];
43
41
  for (let g = 0; g <= numGlyphs; g++) {
44
- loca.push(longLoca ? data.readUInt32BE(locaOff + g * 4) : data.readUInt16BE(locaOff + g * 2) * 2);
42
+ loca.push(longLoca ? u32(data, locaOff + g * 4) : u16(data, locaOff + g * 2) * 2);
45
43
  }
46
44
  const glyphBytes = (g) => data.subarray(glyfOff + loca[g], glyfOff + loca[g + 1]);
47
45
  // Close the used set over composite components (and always keep .notdef = gid 0).
@@ -53,12 +51,12 @@ function subsetTTF(data, used) {
53
51
  continue;
54
52
  keep.add(g);
55
53
  const gd = glyphBytes(g);
56
- if (gd.length >= 10 && gd.readInt16BE(0) < 0) {
54
+ if (gd.length >= 10 && i16(gd, 0) < 0) {
57
55
  // Composite glyph: walk the component records to collect the referenced gids.
58
56
  let q = 10; // numberOfContours(2) + xMin/yMin/xMax/yMax(8)
59
57
  for (;;) {
60
- const flags = gd.readUInt16BE(q);
61
- stack.push(gd.readUInt16BE(q + 2));
58
+ const flags = u16(gd, q);
59
+ stack.push(u16(gd, q + 2));
62
60
  q += 4 + (flags & 0x0001 ? 4 : 2); // ARG_1_AND_2_ARE_WORDS
63
61
  if (flags & 0x0008)
64
62
  q += 2; // WE_HAVE_A_SCALE
@@ -73,24 +71,24 @@ function subsetTTF(data, used) {
73
71
  }
74
72
  // Rebuild glyf (kept glyphs only, original numbering) + a long-format loca.
75
73
  const parts = [];
76
- const newLoca = Buffer.alloc((numGlyphs + 1) * 4);
74
+ const newLoca = new Uint8Array((numGlyphs + 1) * 4);
77
75
  let off = 0;
78
76
  for (let g = 0; g < numGlyphs; g++) {
79
- newLoca.writeUInt32BE(off, g * 4);
77
+ wu32(newLoca, off, g * 4);
80
78
  if (keep.has(g)) {
81
- let gd = Buffer.from(glyphBytes(g));
79
+ let gd = glyphBytes(g).slice(); // copy: padded + concatenated, must not view `data`
82
80
  if (gd.length % 2 === 1)
83
- gd = Buffer.concat([gd, Buffer.alloc(1)]); // keep offsets even
81
+ gd = concatBytes([gd, new Uint8Array(1)]); // keep offsets even
84
82
  parts.push(gd);
85
83
  off += gd.length;
86
84
  }
87
85
  }
88
- newLoca.writeUInt32BE(off, numGlyphs * 4);
89
- const newGlyf = Buffer.concat(parts);
86
+ wu32(newLoca, off, numGlyphs * 4);
87
+ const newGlyf = concatBytes(parts);
90
88
  // head copy with indexToLocFormat = 1 (long); checkSumAdjustment zeroed, fixed up at the end.
91
- const newHead = Buffer.from(data.subarray(headOff, headOff + dir.get("head").length));
92
- newHead.writeInt16BE(1, 50);
93
- newHead.writeUInt32BE(0, 8);
89
+ const newHead = data.slice(headOff, headOff + dir.get("head").length);
90
+ wi16(newHead, 1, 50);
91
+ wu32(newHead, 0, 8);
94
92
  // Tables a PDF-embedded CIDFontType2 doesn't need - layout (GSUB/GPOS…), hinting hints and the
95
93
  // signature are dead weight; dropping them is most of the size win. `post` is reduced to format 3
96
94
  // (header only, no glyph names). cmap/name/OS-2 are kept for a still-valid standalone font.
@@ -119,8 +117,8 @@ function subsetTTF(data, used) {
119
117
  body = newHead;
120
118
  else if (tag === "post") {
121
119
  // Keep the 32-byte header, force version 3.0 (drops the per-glyph name list).
122
- body = Buffer.from(data.subarray(dir.get("post").offset, dir.get("post").offset + 32));
123
- body.writeUInt32BE(0x00030000, 0);
120
+ body = data.slice(dir.get("post").offset, dir.get("post").offset + 32);
121
+ wu32(body, 0x00030000, 0);
124
122
  }
125
123
  else {
126
124
  const d = dir.get(tag);
@@ -132,30 +130,30 @@ function subsetTTF(data, used) {
132
130
  const n = tables.length;
133
131
  const maxPow2 = 1 << Math.floor(Math.log2(n));
134
132
  const out = [];
135
- const offsetTable = Buffer.alloc(12);
136
- offsetTable.writeUInt32BE(data.readUInt32BE(0), 0); // sfntVersion
137
- offsetTable.writeUInt16BE(n, 4);
138
- offsetTable.writeUInt16BE(maxPow2 * 16, 6); // searchRange
139
- offsetTable.writeUInt16BE(Math.floor(Math.log2(n)), 8); // entrySelector
140
- offsetTable.writeUInt16BE(n * 16 - maxPow2 * 16, 10); // rangeShift
133
+ const offsetTable = new Uint8Array(12);
134
+ wu32(offsetTable, u32(data, 0), 0); // sfntVersion
135
+ wu16(offsetTable, n, 4);
136
+ wu16(offsetTable, maxPow2 * 16, 6); // searchRange
137
+ wu16(offsetTable, Math.floor(Math.log2(n)), 8); // entrySelector
138
+ wu16(offsetTable, n * 16 - maxPow2 * 16, 10); // rangeShift
141
139
  out.push(offsetTable);
142
- const dirBuf = Buffer.alloc(n * 16);
140
+ const dirBuf = new Uint8Array(n * 16);
143
141
  let tableOffset = 12 + n * 16;
144
142
  const dataBufs = [];
145
143
  tables.forEach((t, i) => {
146
144
  const padded = pad4(t.body);
147
- dirBuf.write(t.tag, i * 16, 4, "latin1");
148
- dirBuf.writeUInt32BE(checksum(padded), i * 16 + 4);
149
- dirBuf.writeUInt32BE(tableOffset, i * 16 + 8);
150
- dirBuf.writeUInt32BE(t.length, i * 16 + 12); // real (unpadded) length
145
+ writeLatin1(dirBuf, t.tag, i * 16);
146
+ wu32(dirBuf, checksum(padded), i * 16 + 4);
147
+ wu32(dirBuf, tableOffset, i * 16 + 8);
148
+ wu32(dirBuf, t.length, i * 16 + 12); // real (unpadded) length
151
149
  tableOffset += padded.length;
152
150
  dataBufs.push(padded);
153
151
  });
154
152
  out.push(dirBuf, ...dataBufs);
155
- const font = Buffer.concat(out);
153
+ const font = concatBytes(out);
156
154
  // head.checkSumAdjustment = 0xB1B0AFBA - checksum(whole font). Locate head in the assembled font.
157
155
  const headIdx = tables.findIndex((t) => t.tag === "head");
158
- const headPos = dirBuf.readUInt32BE(headIdx * 16 + 8);
159
- font.writeUInt32BE(u32(0xb1b0afba - checksum(font)), headPos + 8);
156
+ const headPos = u32(dirBuf, headIdx * 16 + 8);
157
+ wu32(font, wrap32(0xb1b0afba - checksum(font)), headPos + 8);
160
158
  return font;
161
159
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * XMP metadata packet that identifies the document as PDF/UA-1 (accessible). A fully conformant PDF/UA file
3
+ * also needs a displayed title (the /ViewerPreferences /DisplayDocTitle flag + this dc:title) and embedded
4
+ * fonts - the same font rule PDF/A has, so the standard-14 fonts do not qualify. Language is declared once
5
+ * in the catalog `/Lang`, not here (the dc:title uses `x-default`), so this takes only the title.
6
+ */
7
+ export declare function uaXmp(opts?: {
8
+ title?: string;
9
+ }): string;
@@ -0,0 +1,32 @@
1
+ // Escapes text for XML AND keeps the packet ASCII-safe: XML metacharacters become entities, and any
2
+ // non-ASCII / control character becomes a numeric character reference. The PDF writer emits the
3
+ // metadata stream one byte per char (Latin-1), so a raw non-ASCII title like "R\u00E9sum\u00E9" would corrupt
4
+ // the UTF-8 XMP (and misalign /Length); escaping keeps every char <= 0x7e.
5
+ const esc = (s) => s
6
+ .replace(/&/g, "&amp;")
7
+ .replace(/</g, "&lt;")
8
+ .replace(/>/g, "&gt;")
9
+ .replace(/[^ -~]/gu, (c) => `&#${c.codePointAt(0)};`); // any non printable-ASCII char
10
+ /**
11
+ * XMP metadata packet that identifies the document as PDF/UA-1 (accessible). A fully conformant PDF/UA file
12
+ * also needs a displayed title (the /ViewerPreferences /DisplayDocTitle flag + this dc:title) and embedded
13
+ * fonts - the same font rule PDF/A has, so the standard-14 fonts do not qualify. Language is declared once
14
+ * in the catalog `/Lang`, not here (the dc:title uses `x-default`), so this takes only the title.
15
+ */
16
+ export function uaXmp(opts = {}) {
17
+ const title = esc(opts.title ?? "");
18
+ return (
19
+ // The xpacket marker is the 3-byte UTF-8 BOM (EF BB BF) written as Latin-1 chars so the
20
+ // single-byte writer emits it verbatim - a literal U+FEFF would be mangled to "?".
21
+ `<?xpacket begin="\u00EF\u00BB\u00BF" id="W5M0MpCehiHzreSzNTczkc9d"?>` +
22
+ `<x:xmpmeta xmlns:x="adobe:ns:meta/">` +
23
+ `<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">` +
24
+ `<rdf:Description rdf:about="" xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/">` +
25
+ `<pdfuaid:part>1</pdfuaid:part>` +
26
+ `</rdf:Description>` +
27
+ `<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">` +
28
+ `<dc:title><rdf:Alt><rdf:li xml:lang="x-default">${title}</rdf:li></rdf:Alt></dc:title>` +
29
+ `</rdf:Description>` +
30
+ `</rdf:RDF></x:xmpmeta>` +
31
+ `<?xpacket end="w"?>`);
32
+ }
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Whether Windows-1252 can carry this code point - which is exactly what a standard-14 font can draw,
3
+ * since that is the encoding its `/Widths` array is indexed by. Latin-1 passes straight through,
4
+ * except the C1 range, which the table above fills with printable punctuation instead.
5
+ */
6
+ export declare function isWindows1252(codePoint: number): boolean;
1
7
  /** Encodes a JavaScript string to a Windows-1252 byte buffer (the PDF text encoding). */
2
8
  export declare function getArrayBuffer(data: string): ArrayBuffer;
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getArrayBuffer = getArrayBuffer;
4
1
  // Windows-1252 equals Latin-1 (codepoint == byte) EXCEPT in 0x80-0x9F, where it carries
5
2
  // printable punctuation - the Euro sign, smart quotes, dashes, ellipsis, ... - instead of
6
3
  // C1 controls. Those glyphs sit at Unicode codepoints far outside 0x00-0xFF, so a naive
@@ -36,8 +33,18 @@ const CP1252_FROM_UNICODE = {
36
33
  0x017e: 0x9e, // ž z caron
37
34
  0x0178: 0x9f, // Ÿ Y diaeresis
38
35
  };
36
+ /**
37
+ * Whether Windows-1252 can carry this code point - which is exactly what a standard-14 font can draw,
38
+ * since that is the encoding its `/Widths` array is indexed by. Latin-1 passes straight through,
39
+ * except the C1 range, which the table above fills with printable punctuation instead.
40
+ */
41
+ export function isWindows1252(codePoint) {
42
+ if (codePoint in CP1252_FROM_UNICODE)
43
+ return true;
44
+ return codePoint <= 0xff && !(codePoint >= 0x80 && codePoint <= 0x9f);
45
+ }
39
46
  /** Encodes a JavaScript string to a Windows-1252 byte buffer (the PDF text encoding). */
40
- function getArrayBuffer(data) {
47
+ export function getArrayBuffer(data) {
41
48
  const u8 = new Uint8Array(data.length);
42
49
  for (let i = 0; i < data.length; i++) {
43
50
  const code = data.charCodeAt(i);
@@ -0,0 +1,10 @@
1
+ export declare class WoffError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /** Whether these bytes are a WOFF 1.0 container. Cheap enough to call on every font. */
5
+ export declare const isWoff: (bytes: Uint8Array) => boolean;
6
+ /**
7
+ * Unpack a WOFF into the sfnt it wraps. The result is byte-for-byte what a `.ttf` of the same font
8
+ * would be, apart from table padding, so nothing downstream can tell the difference.
9
+ */
10
+ export declare function woffToSfnt(woff: Uint8Array): Uint8Array;
@@ -0,0 +1,116 @@
1
+ import { inflateBounded } from "./inflate.js";
2
+ /**
3
+ * WOFF 1.0 -> plain sfnt, so the existing `TTFParser` never learns a second container format.
4
+ *
5
+ * A WOFF is not a different FONT, it is a different wrapper: the same sfnt tables, each optionally
6
+ * zlib-compressed, behind a small header. Unpacking it back into an sfnt is therefore the whole job -
7
+ * every metric, glyph and cmap path downstream stays exactly as it was.
8
+ *
9
+ * WOFF2 is deliberately NOT here: it compresses with Brotli, which `fflate` does not do, and it also
10
+ * TRANSFORMS the glyf/loca tables rather than merely deflating them. That is a different piece of work,
11
+ * not a bigger version of this one.
12
+ */
13
+ const SIG_WOFF = 0x774f4646; // "wOFF"
14
+ const HEADER = 44;
15
+ const DIR_ENTRY = 20;
16
+ /** One table may not inflate past this. A single sfnt table above 64 MB is not a real font. */
17
+ const MAX_TABLE_BYTES = 64 * 1024 * 1024;
18
+ /**
19
+ * And all of them together may not either. The per-table ceiling alone is not a bound: `numTables` is a
20
+ * uint16, so 65535 directory entries may all point at the SAME small compressed blob, and each would be
21
+ * inflated and kept. Generous enough for a full CJK face, which is the largest real font there is.
22
+ */
23
+ const MAX_TOTAL_BYTES = 64 * 1024 * 1024;
24
+ export class WoffError extends Error {
25
+ constructor(message) {
26
+ super(`@jasy/pdf: ${message}`);
27
+ }
28
+ }
29
+ const be32 = (b, at) => ((b[at] << 24) | (b[at + 1] << 16) | (b[at + 2] << 8) | b[at + 3]) >>> 0;
30
+ const be16 = (b, at) => (b[at] << 8) | b[at + 1];
31
+ /** Whether these bytes are a WOFF 1.0 container. Cheap enough to call on every font. */
32
+ export const isWoff = (bytes) => bytes.length >= HEADER && be32(bytes, 0) === SIG_WOFF;
33
+ /**
34
+ * Unpack a WOFF into the sfnt it wraps. The result is byte-for-byte what a `.ttf` of the same font
35
+ * would be, apart from table padding, so nothing downstream can tell the difference.
36
+ */
37
+ export function woffToSfnt(woff) {
38
+ if (!isWoff(woff))
39
+ throw new WoffError("these bytes are not a WOFF container");
40
+ const flavor = be32(woff, 4);
41
+ const numTables = be16(woff, 12);
42
+ if (numTables === 0)
43
+ throw new WoffError("this WOFF declares no tables");
44
+ if (woff.length < HEADER + numTables * DIR_ENTRY) {
45
+ throw new WoffError("this WOFF is truncated: its table directory does not fit in the file");
46
+ }
47
+ // Read the directory first, then the tables, so a malformed offset is caught before any allocation.
48
+ const entries = Array.from({ length: numTables }, (_, i) => {
49
+ const at = HEADER + i * DIR_ENTRY;
50
+ return {
51
+ tag: be32(woff, at),
52
+ offset: be32(woff, at + 4),
53
+ compLength: be32(woff, at + 8),
54
+ origLength: be32(woff, at + 12),
55
+ checksum: be32(woff, at + 16),
56
+ };
57
+ });
58
+ // Tracked across ALL tables, and checked BEFORE each inflate so a hostile directory costs neither the
59
+ // CPU of unpacking nor the memory of keeping the result.
60
+ let unpacked = 0;
61
+ const claim = (bytes) => {
62
+ unpacked += bytes;
63
+ if (unpacked > MAX_TOTAL_BYTES) {
64
+ throw new WoffError(`this WOFF unpacks to more than ${MAX_TOTAL_BYTES} bytes in total; no real font is that large`);
65
+ }
66
+ };
67
+ const tables = entries.map((e) => {
68
+ if (e.offset + e.compLength > woff.length) {
69
+ throw new WoffError(`this WOFF is truncated: a table runs past the end of the file`);
70
+ }
71
+ claim(e.origLength); // what it DECLARES, before any work is done on it
72
+ const raw = woff.subarray(e.offset, e.offset + e.compLength);
73
+ // Equal lengths mean the table was stored as-is; anything else is zlib, per the spec.
74
+ if (e.compLength === e.origLength)
75
+ return raw;
76
+ let out;
77
+ try {
78
+ out = inflateBounded(raw, MAX_TABLE_BYTES);
79
+ }
80
+ catch (err) {
81
+ throw new WoffError(`a table in this WOFF could not be unpacked: ${String(err?.message ?? err)}`);
82
+ }
83
+ if (out.length !== e.origLength) {
84
+ throw new WoffError(`a table in this WOFF unpacked to ${out.length} bytes where it declared ${e.origLength}`);
85
+ }
86
+ return out;
87
+ });
88
+ // Reassemble: offset table, directory, then the tables each padded to a 4-byte boundary.
89
+ const padded = (n) => (n + 3) & ~3;
90
+ const dirSize = numTables * 16;
91
+ const total = 12 + dirSize + tables.reduce((n, t) => n + padded(t.length), 0);
92
+ const out = new Uint8Array(total);
93
+ const view = new DataView(out.buffer);
94
+ // The binary-search hints an sfnt offset table carries. Readers may ignore them; a correct file has
95
+ // them right, and getting them wrong is the classic way a "valid" font is rejected by a strict one.
96
+ const entrySelector = Math.floor(Math.log2(numTables));
97
+ const searchRange = 2 ** entrySelector * 16;
98
+ view.setUint32(0, flavor);
99
+ view.setUint16(4, numTables);
100
+ view.setUint16(6, searchRange);
101
+ view.setUint16(8, entrySelector);
102
+ view.setUint16(10, numTables * 16 - searchRange);
103
+ let dir = 12;
104
+ let body = 12 + dirSize;
105
+ entries.forEach((e, i) => {
106
+ const data = tables[i];
107
+ view.setUint32(dir, e.tag);
108
+ view.setUint32(dir + 4, e.checksum);
109
+ view.setUint32(dir + 8, body);
110
+ view.setUint32(dir + 12, data.length); // the DECLARED length excludes the padding
111
+ out.set(data, body);
112
+ dir += 16;
113
+ body += padded(data.length);
114
+ });
115
+ return out;
116
+ }
@@ -1,5 +1,5 @@
1
- import { PDFDocumentElement } from "../elements/pdf-document-element";
2
- import { PDFElement, FlexiblePDFElement, SizedPDFElement } from "../elements/pdf-element";
1
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
2
+ import { PDFElement, FlexiblePDFElement, SizedPDFElement } from "../elements/pdf-element.js";
3
3
  export declare class Validator {
4
4
  static validateDocument(document: PDFDocumentElement): void;
5
5
  static validateElement(element: PDFElement): void;
@@ -1,14 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Validator = void 0;
4
- const pdf_document_element_1 = require("../elements/pdf-document-element");
5
- const pdf_element_1 = require("../elements/pdf-element");
6
- class Validator {
1
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
2
+ import { FlexiblePDFElement, hasChildProp, SizedPDFElement, } from "../elements/pdf-element.js";
3
+ export class Validator {
7
4
  static validateDocument(document) {
8
5
  // More validation will be added later...
9
6
  document.getProps().children.forEach((page) => {
10
7
  page.getProps().children.forEach((element) => {
11
- if (element instanceof pdf_document_element_1.PDFDocumentElement) {
8
+ if (element instanceof PDFDocumentElement) {
12
9
  throw new Error("PDFDocument cannot be nested inside another element.");
13
10
  }
14
11
  });
@@ -16,46 +13,54 @@ class Validator {
16
13
  }
17
14
  static validateElement(element) {
18
15
  // Structural validation
19
- if (element instanceof pdf_document_element_1.PDFDocumentElement) {
16
+ if (element instanceof PDFDocumentElement) {
20
17
  throw new Error("PDFDocument cannot be nested inside another element.");
21
18
  }
22
19
  // Layout validation: geometry comes from the typed getSize(), not the props bag.
23
- if (element instanceof pdf_element_1.SizedPDFElement) {
24
- const { x, y, width, height } = element.getSize();
25
- if (x < 0 || y < 0) {
26
- throw new Error(`Element ${element.constructor.name} has invalid coordinates (x: ${x}, y: ${y})`);
27
- }
28
- // 0 is legitimate (a hairline divider, an empty spacer); only a NEGATIVE size is invalid.
20
+ if (element instanceof SizedPDFElement) {
21
+ const { width, height } = element.getSize();
22
+ // Negative coordinates are allowed: a `Positioned` child overflows its frame on purpose,
23
+ // and the page clips anything past its edge. 0 size is legitimate (a hairline divider, an
24
+ // empty spacer); only a NEGATIVE size is invalid.
29
25
  if ((width !== undefined && width < 0) || (height !== undefined && height < 0)) {
30
26
  throw new Error(`Element ${element.constructor.name} has invalid size (width: ${width}, height: ${height})`);
31
27
  }
32
28
  }
33
29
  // Logical validation: Flexible and fixed elements
34
- if (element instanceof pdf_element_1.FlexiblePDFElement) {
30
+ if (element instanceof FlexiblePDFElement) {
35
31
  this.validateFlexElement(element);
36
32
  }
37
33
  }
38
34
  static validateSizedElement(element) {
39
- const { x, y, width, height } = element.getSize();
40
- if (x < 0 || y < 0) {
41
- throw new Error(`Element ${element.constructor.name} has invalid coordinates (x: ${x}, y: ${y})`);
42
- }
43
- // A size must be set, but 0 is legitimate (a hairline divider); only NEGATIVE is invalid.
35
+ const { width, height } = element.getSize();
36
+ // Negative coordinates are legitimate: a `Positioned` child overflows its frame on purpose
37
+ // (a corner badge), and the page clips anything off its edge. A size must be set, but 0 is
38
+ // legitimate (a hairline divider); only a NEGATIVE size is invalid.
44
39
  if (width === undefined || height === undefined || width < 0 || height < 0) {
45
40
  throw new Error(`Element ${element.constructor.name} has invalid size (width: ${width}, height: ${height})`);
46
41
  }
47
42
  }
48
43
  static validateFlexElement(element) {
49
- // Ensure flexible elements have valid flex values
50
- if (element.getFlex() <= 0) {
51
- throw new Error(`Flexible element ${element.constructor.name} has invalid flex value`);
44
+ // Ensure flexible elements have valid flex values. The message names what the CALLER wrote, not the
45
+ // element class: `Spacer` and `Expanded` are what exists in their document, `ExpandedElement` is not.
46
+ const flex = element.getFlex();
47
+ // `flex: 0` used to be meaningless - claiming no share is what leaving the element out does. With a
48
+ // `flexBasis` it is not: the child is then exactly its basis, a fixed slot that still participates
49
+ // in the line. So the rule now reads "claim SOMETHING": a share, a basis, or both.
50
+ // A percentage basis resolves to 0 on an unbounded axis, so it is measured against a finite
51
+ // reference instead - otherwise `flexBasis: "0%"` would claim nothing and still pass.
52
+ const claimsSpace = element.getBasis(Infinity) > 0 || element.getBasis(1) > 0;
53
+ if (flex < 0 || (flex === 0 && !claimsSpace)) {
54
+ throw new Error(`@jasy/pdf: Spacer/Expanded needs a flex above 0, got ${flex}. Flex is a SHARE of the leftover ` +
55
+ "space, so 0 would claim none - which is what leaving the element out does. For a fixed gap " +
56
+ "use the `gap` of the surrounding Column or Row, or a Box with a height. (A `flexBasis` " +
57
+ "makes `flex: 0` meaningful: the child is then exactly its basis.)");
52
58
  }
53
59
  // Ensure a flexible element does not contain another flexible element
54
- if ((0, pdf_element_1.hasChildProp)(element)) {
55
- if (element.child instanceof pdf_element_1.FlexiblePDFElement) {
60
+ if (hasChildProp(element)) {
61
+ if (element.child instanceof FlexiblePDFElement) {
56
62
  throw new Error(`Flexible element ${element.constructor.name} cannot hold another flexible element`);
57
63
  }
58
64
  }
59
65
  }
60
66
  }
61
- exports.Validator = Validator;
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@jasy/pdf",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.11",
4
4
  "description": "Declarative, component-based PDF generation in pure TypeScript - Flutter-style components, real AFM font metrics, a hand-rolled PDF writer. No headless browser, no Java.",
5
5
  "keywords": [
6
6
  "declarative",
7
+ "e-invoice",
7
8
  "factur-x",
8
9
  "invoice",
9
10
  "layout",
@@ -12,6 +13,7 @@
12
13
  "pdf-a",
13
14
  "pdf-generation",
14
15
  "typescript",
16
+ "xrechnung",
15
17
  "zugferd"
16
18
  ],
17
19
  "license": "MIT",
@@ -19,9 +21,27 @@
19
21
  "files": [
20
22
  "dist"
21
23
  ],
24
+ "type": "module",
22
25
  "main": "dist/index.js",
26
+ "browser": {
27
+ "./dist/platform/node-fs.js": "./dist/platform/browser-fs.js",
28
+ "./dist/platform/node-image.js": "./dist/platform/browser-image.js"
29
+ },
23
30
  "types": "dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ },
36
+ "./edit": {
37
+ "types": "./dist/edit/index.d.ts",
38
+ "default": "./dist/edit/index.js"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
24
42
  "dependencies": {
43
+ "bidi-js": "^1.0.3",
44
+ "fflate": "^0.8.3",
25
45
  "jimp": "^1.6.0"
26
46
  },
27
47
  "devDependencies": {
@@ -41,7 +61,7 @@
41
61
  "play": "pnpm --filter @jasy/playground play",
42
62
  "test": "vitest",
43
63
  "test:coverage": "vitest --coverage",
44
- "build": "tsc && pnpm run copy-assets",
64
+ "build": "tsc && node scripts/fix-dts-ext.mjs && pnpm run copy-assets",
45
65
  "lint": "oxlint",
46
66
  "lint:fix": "oxlint --fix",
47
67
  "fmt": "oxfmt",