postext-pdf 0.1.0

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 (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/__bench__/render.bench.test.d.ts +2 -0
  4. package/dist/__bench__/render.bench.test.d.ts.map +1 -0
  5. package/dist/__bench__/render.bench.test.js +86 -0
  6. package/dist/__bench__/render.bench.test.js.map +1 -0
  7. package/dist/__tests__/accessibility.test.d.ts +2 -0
  8. package/dist/__tests__/accessibility.test.d.ts.map +1 -0
  9. package/dist/__tests__/accessibility.test.js +364 -0
  10. package/dist/__tests__/accessibility.test.js.map +1 -0
  11. package/dist/__tests__/contentsLinks.test.d.ts +2 -0
  12. package/dist/__tests__/contentsLinks.test.d.ts.map +1 -0
  13. package/dist/__tests__/contentsLinks.test.js +131 -0
  14. package/dist/__tests__/contentsLinks.test.js.map +1 -0
  15. package/dist/__tests__/fontCache.test.d.ts +2 -0
  16. package/dist/__tests__/fontCache.test.d.ts.map +1 -0
  17. package/dist/__tests__/fontCache.test.js +42 -0
  18. package/dist/__tests__/fontCache.test.js.map +1 -0
  19. package/dist/__tests__/headerFooter.test.d.ts +2 -0
  20. package/dist/__tests__/headerFooter.test.d.ts.map +1 -0
  21. package/dist/__tests__/headerFooter.test.js +68 -0
  22. package/dist/__tests__/headerFooter.test.js.map +1 -0
  23. package/dist/__tests__/rotatedTable.test.d.ts +2 -0
  24. package/dist/__tests__/rotatedTable.test.d.ts.map +1 -0
  25. package/dist/__tests__/rotatedTable.test.js +112 -0
  26. package/dist/__tests__/rotatedTable.test.js.map +1 -0
  27. package/dist/__tests__/svgVector.test.d.ts +2 -0
  28. package/dist/__tests__/svgVector.test.d.ts.map +1 -0
  29. package/dist/__tests__/svgVector.test.js +322 -0
  30. package/dist/__tests__/svgVector.test.js.map +1 -0
  31. package/dist/__tests__/toUnicode.test.d.ts +2 -0
  32. package/dist/__tests__/toUnicode.test.d.ts.map +1 -0
  33. package/dist/__tests__/toUnicode.test.js +57 -0
  34. package/dist/__tests__/toUnicode.test.js.map +1 -0
  35. package/dist/colors.d.ts +23 -0
  36. package/dist/colors.d.ts.map +1 -0
  37. package/dist/colors.js +47 -0
  38. package/dist/colors.js.map +1 -0
  39. package/dist/fontCache.d.ts +41 -0
  40. package/dist/fontCache.d.ts.map +1 -0
  41. package/dist/fontCache.js +154 -0
  42. package/dist/fontCache.js.map +1 -0
  43. package/dist/fontString.d.ts +14 -0
  44. package/dist/fontString.d.ts.map +1 -0
  45. package/dist/fontString.js +43 -0
  46. package/dist/fontString.js.map +1 -0
  47. package/dist/index.d.ts +6 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +5 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/pdf-backend/blockRender.d.ts +15 -0
  52. package/dist/pdf-backend/blockRender.d.ts.map +1 -0
  53. package/dist/pdf-backend/blockRender.js +244 -0
  54. package/dist/pdf-backend/blockRender.js.map +1 -0
  55. package/dist/pdf-backend/fontHelpers.d.ts +6 -0
  56. package/dist/pdf-backend/fontHelpers.d.ts.map +1 -0
  57. package/dist/pdf-backend/fontHelpers.js +81 -0
  58. package/dist/pdf-backend/fontHelpers.js.map +1 -0
  59. package/dist/pdf-backend/headerFooter.d.ts +15 -0
  60. package/dist/pdf-backend/headerFooter.d.ts.map +1 -0
  61. package/dist/pdf-backend/headerFooter.js +145 -0
  62. package/dist/pdf-backend/headerFooter.js.map +1 -0
  63. package/dist/pdf-backend/index.d.ts +47 -0
  64. package/dist/pdf-backend/index.d.ts.map +1 -0
  65. package/dist/pdf-backend/index.js +227 -0
  66. package/dist/pdf-backend/index.js.map +1 -0
  67. package/dist/pdf-backend/links.d.ts +44 -0
  68. package/dist/pdf-backend/links.d.ts.map +1 -0
  69. package/dist/pdf-backend/links.js +110 -0
  70. package/dist/pdf-backend/links.js.map +1 -0
  71. package/dist/pdf-backend/outlines.d.ts +8 -0
  72. package/dist/pdf-backend/outlines.d.ts.map +1 -0
  73. package/dist/pdf-backend/outlines.js +145 -0
  74. package/dist/pdf-backend/outlines.js.map +1 -0
  75. package/dist/pdf-backend/pageDecorations.d.ts +13 -0
  76. package/dist/pdf-backend/pageDecorations.d.ts.map +1 -0
  77. package/dist/pdf-backend/pageDecorations.js +88 -0
  78. package/dist/pdf-backend/pageDecorations.js.map +1 -0
  79. package/dist/pdf-backend/pageLabels.d.ts +8 -0
  80. package/dist/pdf-backend/pageLabels.d.ts.map +1 -0
  81. package/dist/pdf-backend/pageLabels.js +61 -0
  82. package/dist/pdf-backend/pageLabels.js.map +1 -0
  83. package/dist/pdf-backend/primitives.d.ts +46 -0
  84. package/dist/pdf-backend/primitives.d.ts.map +1 -0
  85. package/dist/pdf-backend/primitives.js +162 -0
  86. package/dist/pdf-backend/primitives.js.map +1 -0
  87. package/dist/pdf-backend/renderResourceBlock.d.ts +87 -0
  88. package/dist/pdf-backend/renderResourceBlock.d.ts.map +1 -0
  89. package/dist/pdf-backend/renderResourceBlock.js +753 -0
  90. package/dist/pdf-backend/renderResourceBlock.js.map +1 -0
  91. package/dist/pdf-backend/structureFlow.d.ts +55 -0
  92. package/dist/pdf-backend/structureFlow.d.ts.map +1 -0
  93. package/dist/pdf-backend/structureFlow.js +180 -0
  94. package/dist/pdf-backend/structureFlow.js.map +1 -0
  95. package/dist/pdf-backend/svgVector.d.ts +144 -0
  96. package/dist/pdf-backend/svgVector.d.ts.map +1 -0
  97. package/dist/pdf-backend/svgVector.js +1455 -0
  98. package/dist/pdf-backend/svgVector.js.map +1 -0
  99. package/dist/pdf-backend/tagging.d.ts +136 -0
  100. package/dist/pdf-backend/tagging.d.ts.map +1 -0
  101. package/dist/pdf-backend/tagging.js +296 -0
  102. package/dist/pdf-backend/tagging.js.map +1 -0
  103. package/dist/woff2.d.ts +11 -0
  104. package/dist/woff2.d.ts.map +1 -0
  105. package/dist/woff2.js +47 -0
  106. package/dist/woff2.js.map +1 -0
  107. package/dist/worker/client.d.ts +30 -0
  108. package/dist/worker/client.d.ts.map +1 -0
  109. package/dist/worker/client.js +112 -0
  110. package/dist/worker/client.js.map +1 -0
  111. package/dist/worker/pdf.worker.d.ts +2 -0
  112. package/dist/worker/pdf.worker.d.ts.map +1 -0
  113. package/dist/worker/pdf.worker.js +67 -0
  114. package/dist/worker/pdf.worker.js.map +1 -0
  115. package/dist/worker/protocol.d.ts +49 -0
  116. package/dist/worker/protocol.d.ts.map +1 -0
  117. package/dist/worker/protocol.js +2 -0
  118. package/dist/worker/protocol.js.map +1 -0
  119. package/package.json +67 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fontHelpers.js","sourceRoot":"","sources":["../../src/pdf-backend/fontHelpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { VDTDesignSlot } from 'postext';
2
+ import { type ResourceImageMap } from './renderResourceBlock';
3
+ import { type ArtifactSpec, type StructElem } from './tagging';
4
+ /** How an accessible render tags a design slot: its text goes to the
5
+ * element `text()` returns (created on first use), or counts as an
6
+ * artifact when `text` is absent (running headers / footers); rules,
7
+ * boxes and images are always artifacts of class `artifact`. */
8
+ export interface SlotMark {
9
+ text?: () => StructElem;
10
+ artifact: ArtifactSpec;
11
+ }
12
+ import { FontCache } from '../fontCache';
13
+ import { type PageCtx } from './primitives';
14
+ export declare function renderHeaderFooterSlot(ctx: PageCtx, slot: VDTDesignSlot, fontCache: FontCache, images?: ResourceImageMap, mark?: SlotMark): void;
15
+ //# sourceMappingURL=headerFooter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headerFooter.d.ts","sourceRoot":"","sources":["../../src/pdf-backend/headerFooter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAMd,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAwB,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAA2B,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAExF;;;iEAGiE;AACjE,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,EAAE,MAAM,UAAU,CAAC;IACxB,QAAQ,EAAE,YAAY,CAAC;CACxB;AAQD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,cAAc,CAAC;AAuItB,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,SAAS,EACpB,MAAM,CAAC,EAAE,gBAAgB,EACzB,IAAI,CAAC,EAAE,QAAQ,GACd,IAAI,CAWN"}
@@ -0,0 +1,145 @@
1
+ import { setCharacterSpacing } from 'pdf-lib';
2
+ import { drawEmbeddedResource } from './renderResourceBlock';
3
+ import { tagArtifact, tagContent } from './tagging';
4
+ function tagSlotText(ctx, mark) {
5
+ if (!mark)
6
+ return;
7
+ if (mark.text)
8
+ tagContent(ctx, mark.text());
9
+ else
10
+ tagArtifact(ctx, mark.artifact);
11
+ }
12
+ import { parseFontString } from '../fontString';
13
+ import { colorFromHex, drawTextPx, fillRectPx, pushClipRect, popClip, } from './primitives';
14
+ function drawRoundedBox(ctx, xPx, yPx, wPx, hPx, style) {
15
+ if (wPx <= 0 || hPx <= 0)
16
+ return;
17
+ const { scale, pageHeightPt } = ctx;
18
+ const radius = Math.max(0, Math.min(style.borderRadiusPx, wPx / 2, hPx / 2));
19
+ const width = wPx * scale;
20
+ const height = hPx * scale;
21
+ if (radius <= 0) {
22
+ const x = xPx * scale;
23
+ const y = pageHeightPt - (yPx + hPx) * scale;
24
+ if (style.backgroundColor) {
25
+ ctx.page.drawRectangle({
26
+ x,
27
+ y,
28
+ width,
29
+ height,
30
+ color: colorFromHex(style.backgroundColor, ctx.colorSpace),
31
+ });
32
+ }
33
+ if (style.borderColor && style.borderWidthPx > 0) {
34
+ ctx.page.drawRectangle({
35
+ x,
36
+ y,
37
+ width,
38
+ height,
39
+ borderColor: colorFromHex(style.borderColor, ctx.colorSpace),
40
+ borderWidth: style.borderWidthPx * scale,
41
+ });
42
+ }
43
+ return;
44
+ }
45
+ // pdf-lib 1.x has no borderRadius on drawRectangle, so a rounded box is an
46
+ // SVG path. `drawSvgPath` interprets the path in SVG space (y grows
47
+ // downwards) from the origin passed as `x`/`y`, so anchor it at the top-left
48
+ // corner of the page and express the corners in top-down points.
49
+ const r = radius * scale;
50
+ const sx = xPx * scale;
51
+ const sy = yPx * scale;
52
+ const path = `M ${sx + r} ${sy}` +
53
+ ` L ${sx + width - r} ${sy}` +
54
+ ` A ${r} ${r} 0 0 1 ${sx + width} ${sy + r}` +
55
+ ` L ${sx + width} ${sy + height - r}` +
56
+ ` A ${r} ${r} 0 0 1 ${sx + width - r} ${sy + height}` +
57
+ ` L ${sx + r} ${sy + height}` +
58
+ ` A ${r} ${r} 0 0 1 ${sx} ${sy + height - r}` +
59
+ ` L ${sx} ${sy + r}` +
60
+ ` A ${r} ${r} 0 0 1 ${sx + r} ${sy} Z`;
61
+ const origin = { x: 0, y: pageHeightPt };
62
+ if (style.backgroundColor) {
63
+ ctx.page.drawSvgPath(path, {
64
+ ...origin,
65
+ color: colorFromHex(style.backgroundColor, ctx.colorSpace),
66
+ });
67
+ }
68
+ if (style.borderColor && style.borderWidthPx > 0) {
69
+ ctx.page.drawSvgPath(path, {
70
+ ...origin,
71
+ borderColor: colorFromHex(style.borderColor, ctx.colorSpace),
72
+ borderWidth: style.borderWidthPx * scale,
73
+ });
74
+ }
75
+ }
76
+ function renderTextBlock(ctx, block, fontCache, mark) {
77
+ if (block.box) {
78
+ if (mark)
79
+ tagArtifact(ctx, mark.artifact);
80
+ drawRoundedBox(ctx, block.bbox.x, block.bbox.y, block.bbox.width, block.bbox.height, block.box);
81
+ }
82
+ const font = fontCache.get(block.fontString);
83
+ if (!font)
84
+ return;
85
+ const size = parseFontString(block.fontString)?.sizePx ?? 0;
86
+ const color = colorFromHex(block.color, ctx.colorSpace);
87
+ const clip = block.clip;
88
+ if (clip) {
89
+ pushClipRect(ctx, block.bbox.x, block.bbox.y, block.bbox.width, block.bbox.height);
90
+ }
91
+ const tracked = block.letterSpacingPx !== undefined && block.letterSpacingPx > 0;
92
+ if (tracked)
93
+ ctx.page.pushOperators(setCharacterSpacing(block.letterSpacingPx * ctx.scale));
94
+ tagSlotText(ctx, mark);
95
+ for (const line of block.lines) {
96
+ drawTextPx(ctx, line.text, block.bbox.x + line.xOffset, line.baselineY, font, size, color);
97
+ }
98
+ if (tracked)
99
+ ctx.page.pushOperators(setCharacterSpacing(0));
100
+ if (clip)
101
+ popClip(ctx);
102
+ }
103
+ function renderRuleBlock(ctx, block) {
104
+ const color = colorFromHex(block.color, ctx.colorSpace);
105
+ if (block.direction === 'vertical') {
106
+ fillRectPx(ctx, block.bbox.x, block.bbox.y, block.thicknessPx, block.bbox.height, color);
107
+ }
108
+ else {
109
+ fillRectPx(ctx, block.bbox.x, block.bbox.y, block.bbox.width, block.thicknessPx, color);
110
+ }
111
+ }
112
+ function renderBoxBlock(ctx, block) {
113
+ drawRoundedBox(ctx, block.bbox.x, block.bbox.y, block.bbox.width, block.bbox.height, block.box);
114
+ }
115
+ /** Image block (e.g. a callout icon): drawn from the preloaded resource
116
+ * image map, with a neutral placeholder when the image is absent. */
117
+ function renderImageBlock(ctx, block, images) {
118
+ const { x, y, width, height } = block.bbox;
119
+ if (width <= 0 || height <= 0)
120
+ return;
121
+ const embedded = images?.get(block.fileId);
122
+ if (embedded) {
123
+ drawEmbeddedResource(ctx, embedded, x, y, width, height);
124
+ }
125
+ else {
126
+ fillRectPx(ctx, x, y, width, height, colorFromHex('#e8e8e8', ctx.colorSpace));
127
+ }
128
+ }
129
+ export function renderHeaderFooterSlot(ctx, slot, fontCache, images, mark) {
130
+ for (const block of slot.blocks) {
131
+ if (block.kind === 'text') {
132
+ renderTextBlock(ctx, block, fontCache, mark);
133
+ continue;
134
+ }
135
+ if (mark)
136
+ tagArtifact(ctx, mark.artifact);
137
+ if (block.kind === 'rule')
138
+ renderRuleBlock(ctx, block);
139
+ else if (block.kind === 'image')
140
+ renderImageBlock(ctx, block, images);
141
+ else
142
+ renderBoxBlock(ctx, block);
143
+ }
144
+ }
145
+ //# sourceMappingURL=headerFooter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headerFooter.js","sourceRoot":"","sources":["../../src/pdf-backend/headerFooter.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAyB,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAsC,MAAM,WAAW,CAAC;AAWxF,SAAS,WAAW,CAAC,GAAY,EAAE,IAA0B;IAC3D,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,IAAI,IAAI,CAAC,IAAI;QAAE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;;QACvC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;AACvC,CAAC;AACD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAEL,YAAY,EACZ,UAAU,EACV,UAAU,EACV,YAAY,EACZ,OAAO,GACR,MAAM,cAAc,CAAC;AAEtB,SAAS,cAAc,CACrB,GAAY,EACZ,GAAW,EACX,GAAW,EACX,GAAW,EACX,GAAW,EACX,KAAwB;IAExB,IAAI,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO;IACjC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,GAAG,GAAG,KAAK,CAAC;IAC1B,MAAM,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC;IAC3B,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAChB,MAAM,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC;QACtB,MAAM,CAAC,GAAG,YAAY,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC;QAC7C,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;YAC1B,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC;gBACrB,CAAC;gBACD,CAAC;gBACD,KAAK;gBACL,MAAM;gBACN,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC,UAAU,CAAC;aAC3D,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;YACjD,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC;gBACrB,CAAC;gBACD,CAAC;gBACD,KAAK;gBACL,MAAM;gBACN,WAAW,EAAE,YAAY,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,UAAU,CAAC;gBAC5D,WAAW,EAAE,KAAK,CAAC,aAAa,GAAG,KAAK;aACzC,CAAC,CAAC;QACL,CAAC;QACD,OAAO;IACT,CAAC;IACD,2EAA2E;IAC3E,oEAAoE;IACpE,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,CAAC,GAAG,MAAM,GAAG,KAAK,CAAC;IACzB,MAAM,EAAE,GAAG,GAAG,GAAG,KAAK,CAAC;IACvB,MAAM,EAAE,GAAG,GAAG,GAAG,KAAK,CAAC;IACvB,MAAM,IAAI,GACR,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE;QACnB,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE;QAC5B,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE;QAC5C,MAAM,EAAE,GAAG,KAAK,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,EAAE;QACrC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,GAAG,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE;QACrD,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,MAAM,EAAE;QAC7B,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,MAAM,GAAG,CAAC,EAAE;QAC7C,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE;QACpB,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IAEzC,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YACzB,GAAG,MAAM;YACT,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC,UAAU,CAAC;SAC3D,CAAC,CAAC;IACL,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE;YACzB,GAAG,MAAM;YACT,WAAW,EAAE,YAAY,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,UAAU,CAAC;YAC5D,WAAW,EAAE,KAAK,CAAC,aAAa,GAAG,KAAK;SACzC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CACtB,GAAY,EACZ,KAAyB,EACzB,SAAoB,EACpB,IAA0B;IAE1B,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,IAAI,IAAI;YAAE,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClG,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,IAAI,IAAI,EAAE,CAAC;QACT,YAAY,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrF,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,eAAe,KAAK,SAAS,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC;IACjF,IAAI,OAAO;QAAE,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,eAAgB,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7F,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,UAAU,CACR,GAAG,EACH,IAAI,CAAC,IAAI,EACT,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAC3B,IAAI,CAAC,SAAS,EACd,IAAI,EACJ,IAAI,EACJ,KAAK,CACN,CAAC;IACJ,CAAC;IACD,IAAI,OAAO;QAAE,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,IAAI;QAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,eAAe,CAAC,GAAY,EAAE,KAAyB;IAC9D,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QACnC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3F,CAAC;SAAM,CAAC;QACN,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC1F,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,GAAY,EAAE,KAAwB;IAC5D,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;AAClG,CAAC;AAED;sEACsE;AACtE,SAAS,gBAAgB,CAAC,GAAY,EAAE,KAA0B,EAAE,MAAoC;IACtG,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC;IAC3C,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO;IACtC,MAAM,QAAQ,GAAG,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,QAAQ,EAAE,CAAC;QACb,oBAAoB,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,GAAY,EACZ,IAAmB,EACnB,SAAoB,EACpB,MAAyB,EACzB,IAAe;IAEf,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,eAAe,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAC7C,SAAS;QACX,CAAC;QACD,IAAI,IAAI;YAAE,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;YAAE,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;aAClD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;;YACjE,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { SvgRasterizer } from './renderResourceBlock';
2
+ import type { PdfColorSpace, VDTDocument } from 'postext';
3
+ import { type PdfFontProvider } from '../fontCache';
4
+ import { type ResourceBytesProvider } from './renderResourceBlock';
5
+ export interface RenderToPdfOptions {
6
+ fontProvider: PdfFontProvider;
7
+ pageNegative?: boolean;
8
+ /** Emit a PDF outline tree (bookmarks) so readers can jump between
9
+ * headings. Defaults to true. */
10
+ outlines?: boolean;
11
+ /** Force every colour in the rendered output through the given PDF colour
12
+ * space. Defaults to `'rgb'` (pdf-lib's native output). */
13
+ colorSpace?: PdfColorSpace;
14
+ /** Resolver for resource binary bytes by `fileId`. The bytes are sniffed:
15
+ * bitmaps embed as images, SVG markup is emitted as vector paths (or
16
+ * rasterised in the browser when it uses unsupported features), and a
17
+ * single-page PDF (`Resource.svg.pdfFileId` print masters) is embedded
18
+ * verbatim. When omitted, resource images are drawn as placeholders. */
19
+ resourceBytes?: ResourceBytesProvider;
20
+ /** Emit an accessible, tagged PDF (PDF/UA-1 oriented): logical structure
21
+ * tree, alt text on figures, document language and title, decoration
22
+ * flagged as artifacts. Defaults to true. */
23
+ accessible?: boolean;
24
+ /** Called as the render advances: after the fonts and resources are
25
+ * embedded, after every page, and before the file is written. */
26
+ onProgress?: (progress: RenderProgress) => void;
27
+ /** Rasterises an SVG the vector subset cannot draw. Defaults to the
28
+ * document's `Image` + canvas; a worker must supply one (its host's). */
29
+ rasterizeSvg?: SvgRasterizer;
30
+ }
31
+ export interface RenderProgress {
32
+ phase: 'prepare' | 'pages' | 'save';
33
+ /** Pages rendered so far, and how many there are. */
34
+ pages: number;
35
+ totalPages: number;
36
+ }
37
+ export type { PdfFontProvider };
38
+ export type { ResourceBytesProvider } from './renderResourceBlock';
39
+ /**
40
+ * Render one document — or a book, as the documents of its chapters in
41
+ * order, each laid out with the continuation of the ones before it — into
42
+ * one PDF. The chapters share fonts, embedded resources and the structure
43
+ * tree; page indices in contents links are book-absolute already, so they
44
+ * resolve across chapters.
45
+ */
46
+ export declare function renderToPdf(input: VDTDocument | VDTDocument[], options: RenderToPdfOptions): Promise<Uint8Array>;
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pdf-backend/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE7E,OAAO,KAAK,EAAqB,aAAa,EAAY,WAAW,EAAW,MAAM,SAAS,CAAC;AAEhG,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAqB/D,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AAK/B,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,eAAe,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;sCACkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;gEAC4D;IAC5D,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B;;;;6EAIyE;IACzE,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC;;kDAE8C;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;sEACkE;IAClE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;IAChD;8EAC0E;IAC1E,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACpC,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,YAAY,EAAE,eAAe,EAAE,CAAC;AAChC,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAyLnE;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,WAAW,GAAG,WAAW,EAAE,EAClC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA6ErB"}
@@ -0,0 +1,227 @@
1
+ import { PDFDocument, BlendMode, } from 'pdf-lib';
2
+ import fontkit from '@pdf-lib/fontkit';
3
+ import { computePageTextExtent, dimensionToPx } from 'postext';
4
+ import { FontCache } from '../fontCache';
5
+ import { colorFromHex, fillRectPx, makeScale, popClip, pushClipRect, whiteColor, } from './primitives';
6
+ import { collectFontStrings } from './fontHelpers';
7
+ import { computeContentArea, renderBaselineGrid, renderColumnRule, renderCutLines, } from './pageDecorations';
8
+ import { renderBlock } from './blockRender';
9
+ import { renderHeaderFooterSlot } from './headerFooter';
10
+ import { addOutlines } from './outlines';
11
+ import { addPageLabels } from './pageLabels';
12
+ import { preloadResourceImages, } from './renderResourceBlock';
13
+ import { LinkRegistry } from './links';
14
+ import { StructTree, tagArtifact } from './tagging';
15
+ import { StructureFlow } from './structureFlow';
16
+ /** BCP 47 tag of a postext locale (`/Lang`). */
17
+ function languageTag(locale) {
18
+ if (!locale)
19
+ return undefined;
20
+ const [lang, region] = locale.split('-');
21
+ return region ? `${lang}-${region.toUpperCase()}` : lang;
22
+ }
23
+ /** Title of the document for the PDF metadata: the declared title, else the
24
+ * first heading (PDF/UA-1 §7.1 requires one). */
25
+ function documentTitle(docs) {
26
+ const declared = docs[0]?.metadata?.title?.trim();
27
+ if (declared)
28
+ return declared;
29
+ let best;
30
+ for (const doc of docs) {
31
+ for (const block of doc.blocks) {
32
+ if (block.type !== 'heading')
33
+ continue;
34
+ if (!best || (block.headingLevel ?? 1) < (best.headingLevel ?? 1))
35
+ best = block;
36
+ if ((best.headingLevel ?? 1) === 1)
37
+ break;
38
+ }
39
+ if (best && (best.headingLevel ?? 1) === 1)
40
+ break;
41
+ }
42
+ const text = best?.lines.map((l) => l.text).join(' ').replace(/\s+/g, ' ').trim();
43
+ if (text)
44
+ return best?.numberPrefix && !text.startsWith(best.numberPrefix) ? `${best.numberPrefix} ${text}` : text;
45
+ return 'Document';
46
+ }
47
+ /** The heading element an opener band's text belongs to: the part title on
48
+ * a part-divider page, else the `span: 'page'` heading the band replaced
49
+ * (hidden in its column), else a plain paragraph. */
50
+ function openerTextElem(page, structure) {
51
+ let elem;
52
+ return () => {
53
+ if (elem)
54
+ return elem;
55
+ if (page.partInfo)
56
+ return (elem = structure.partHeading());
57
+ for (const col of page.columns) {
58
+ for (const block of col.blocks) {
59
+ if (block.hidden && block.type === 'heading')
60
+ return (elem = structure.blockElem(block));
61
+ }
62
+ }
63
+ return (elem = structure.tree.root.child('P'));
64
+ };
65
+ }
66
+ function renderPage(pdfDoc, vdtPage, doc, fontCache, pageNegative, colorSpace, resourceCtx, tree) {
67
+ const scale = makeScale(doc.config.page.dpi);
68
+ const pageWidthPt = vdtPage.width * scale;
69
+ const pageHeightPt = vdtPage.height * scale;
70
+ const page = pdfDoc.addPage([pageWidthPt, pageHeightPt]);
71
+ const ctx = { page, pageHeightPt, scale, colorSpace, tags: tree?.beginPage(page) };
72
+ const structure = resourceCtx.structure;
73
+ // Background: full page white first (cut-mark area stays white), then the
74
+ // trim+bleed area filled with the configured page background colour.
75
+ tagArtifact(ctx, { type: 'Background' });
76
+ fillRectPx(ctx, 0, 0, vdtPage.width, vdtPage.height, whiteColor(colorSpace));
77
+ const bgHex = doc.config.page.backgroundColor.hex;
78
+ const trimOff = doc.trimOffset;
79
+ if (bgHex && bgHex !== 'transparent') {
80
+ const bleedPx = trimOff > 0
81
+ ? dimensionToPx(doc.config.page.cutLines.bleed, doc.config.page.dpi)
82
+ : 0;
83
+ fillRectPx(ctx, trimOff - bleedPx, trimOff - bleedPx, vdtPage.width - (trimOff - bleedPx) * 2, vdtPage.height - (trimOff - bleedPx) * 2, colorFromHex(bgHex, colorSpace));
84
+ }
85
+ tagArtifact(ctx, { type: 'Layout' });
86
+ if (doc.config.page.baselineGrid.enabled) {
87
+ // Bound the grid to the page's actual text: from the first text line to
88
+ // the last (mirrors the canvas backend). Pages with no text draw no grid.
89
+ const textExtent = computePageTextExtent(vdtPage);
90
+ if (textExtent) {
91
+ const contentArea = computeContentArea(vdtPage, doc);
92
+ const gridLineWidthPx = dimensionToPx(doc.config.page.baselineGrid.lineWidth, doc.config.page.dpi);
93
+ renderBaselineGrid(ctx, contentArea, doc.baselineGrid, doc.config.page.baselineGrid.color.hex, gridLineWidthPx, textExtent);
94
+ }
95
+ }
96
+ if (doc.config.layout.columnRule.enabled && vdtPage.columns.length > 1) {
97
+ const crLineWidthPx = dimensionToPx(doc.config.layout.columnRule.lineWidth, doc.config.page.dpi);
98
+ renderColumnRule(ctx, vdtPage.columns, doc.config.layout.columnRule.color.hex, crLineWidthPx);
99
+ }
100
+ // Opener / part bands go under the columns so their backgrounds sit
101
+ // beneath the body text (mirrors the canvas backend). Their text is the
102
+ // chapter / part heading; the rest of the band is decoration.
103
+ if (vdtPage.openerBand) {
104
+ renderHeaderFooterSlot(ctx, vdtPage.openerBand, fontCache, resourceCtx.images, structure ? { text: openerTextElem(vdtPage, structure), artifact: { type: 'Layout' } } : undefined);
105
+ }
106
+ const clipOverhang = dimensionToPx({ value: 2, unit: 'pt' }, doc.config.page.dpi);
107
+ for (const col of vdtPage.columns) {
108
+ pushClipRect(ctx, col.bbox.x - clipOverhang, col.bbox.y, col.bbox.width + clipOverhang * 2, col.bbox.height);
109
+ for (const block of col.blocks) {
110
+ if (block.tocPart && block.designOverlay)
111
+ continue;
112
+ renderBlock(ctx, block, col.bbox.width, col.bbox.x, fontCache, resourceCtx);
113
+ }
114
+ popClip(ctx);
115
+ // A part row of the contents carries a design of its own, which may
116
+ // run past the column (a band reaching beyond the page numbers): it
117
+ // is drawn outside the column clip, like a float.
118
+ for (const block of col.blocks) {
119
+ if (block.tocPart && block.designOverlay)
120
+ renderBlock(ctx, block, col.bbox.width, col.bbox.x, fontCache, resourceCtx);
121
+ }
122
+ }
123
+ // Floated resources sit outside the column clip (a `span: 'page'` float can
124
+ // cross the gutter); they carry an absolute bbox positioned at build time.
125
+ if (vdtPage.floats) {
126
+ for (const fb of vdtPage.floats) {
127
+ renderBlock(ctx, fb, fb.bbox.width, fb.bbox.x, fontCache, resourceCtx);
128
+ }
129
+ }
130
+ // Running headers and footers are pagination artifacts.
131
+ const pagination = (subtype) => structure ? { artifact: { type: 'Pagination', subtype } } : undefined;
132
+ if (vdtPage.header)
133
+ renderHeaderFooterSlot(ctx, vdtPage.header, fontCache, resourceCtx.images, pagination('Header'));
134
+ if (vdtPage.footer)
135
+ renderHeaderFooterSlot(ctx, vdtPage.footer, fontCache, resourceCtx.images, pagination('Footer'));
136
+ // Page negative: overlay white rect with Difference blend across trim+bleed.
137
+ // Crop marks remain un-inverted (drawn afterwards).
138
+ tagArtifact(ctx, { type: 'Page' });
139
+ if (pageNegative) {
140
+ const bleedPx = trimOff > 0
141
+ ? dimensionToPx(doc.config.page.cutLines.bleed, doc.config.page.dpi)
142
+ : 0;
143
+ const invX = trimOff - bleedPx;
144
+ const invY = trimOff - bleedPx;
145
+ const invW = vdtPage.width - (trimOff - bleedPx) * 2;
146
+ const invH = vdtPage.height - (trimOff - bleedPx) * 2;
147
+ fillRectPx(ctx, invX, invY, invW, invH, whiteColor(colorSpace), BlendMode.Difference);
148
+ }
149
+ renderCutLines(ctx, vdtPage, doc);
150
+ ctx.tags?.close();
151
+ }
152
+ /**
153
+ * Render one document — or a book, as the documents of its chapters in
154
+ * order, each laid out with the continuation of the ones before it — into
155
+ * one PDF. The chapters share fonts, embedded resources and the structure
156
+ * tree; page indices in contents links are book-absolute already, so they
157
+ * resolve across chapters.
158
+ */
159
+ export async function renderToPdf(input, options) {
160
+ const docs = Array.isArray(input) ? input : [input];
161
+ const first = docs[0];
162
+ if (!first)
163
+ throw new Error('postext-pdf: nothing to render');
164
+ const totalPages = docs.reduce((n, d) => n + d.pages.length, 0);
165
+ const pdfDoc = await PDFDocument.create();
166
+ pdfDoc.registerFontkit(fontkit);
167
+ if (first.metadata?.title)
168
+ pdfDoc.setTitle(first.metadata.title);
169
+ if (first.metadata?.author)
170
+ pdfDoc.setAuthor(first.metadata.author);
171
+ pdfDoc.setCreator('postext');
172
+ pdfDoc.setProducer('postext-pdf');
173
+ const fontCache = new FontCache(pdfDoc, options.fontProvider);
174
+ for (const doc of docs)
175
+ await fontCache.preloadFontStrings(collectFontStrings(doc));
176
+ const missing = fontCache.missing();
177
+ if (missing.length > 0) {
178
+ throw new Error(`postext-pdf: failed to load font(s): ${missing.join(', ')}`);
179
+ }
180
+ const colorSpace = options.colorSpace ?? 'rgb';
181
+ // Accessible output: the structure tree the pages tag their content into.
182
+ const tree = (options.accessible ?? true)
183
+ ? new StructTree(pdfDoc, {
184
+ title: documentTitle(docs),
185
+ author: first.metadata?.author,
186
+ lang: languageTag(first.config.bodyText.hyphenation?.locale),
187
+ producer: 'postext-pdf',
188
+ creatorTool: 'postext',
189
+ })
190
+ : undefined;
191
+ // Resource images are embedded up front (async) so page rendering stays
192
+ // sync; one map for every document, so a figure reused across chapters
193
+ // is embedded once.
194
+ const resourceImages = new Map();
195
+ for (const doc of docs) {
196
+ await preloadResourceImages(pdfDoc, doc, options.resourceBytes, fontCache, options.fontProvider, resourceImages, options.rasterizeSvg);
197
+ }
198
+ options.onProgress?.({ phase: 'prepare', pages: 0, totalPages });
199
+ // Contents links carry book-absolute page indices; the registry maps them
200
+ // onto the PDF's pages from the first document's offset.
201
+ const linkRegistry = new LinkRegistry(first.pageIndexOffset ?? 0);
202
+ let rendered = 0;
203
+ for (const doc of docs) {
204
+ // Block ids restart per document: each gets its own structure flow
205
+ // (paragraph fragments, lists and callouts never span chapters).
206
+ const resourceCtx = {
207
+ images: resourceImages,
208
+ linkRegistry,
209
+ structure: tree ? new StructureFlow(tree) : undefined,
210
+ };
211
+ for (const page of doc.pages) {
212
+ renderPage(pdfDoc, page, doc, fontCache, options.pageNegative ?? false, colorSpace, resourceCtx, tree);
213
+ rendered++;
214
+ options.onProgress?.({ phase: 'pages', pages: rendered, totalPages });
215
+ }
216
+ }
217
+ // Attach inline-ref link annotations now that every destination is known.
218
+ linkRegistry.finalize(pdfDoc, tree);
219
+ if (options.outlines ?? true) {
220
+ addOutlines(pdfDoc, docs);
221
+ }
222
+ addPageLabels(pdfDoc, docs);
223
+ tree?.finalize();
224
+ options.onProgress?.({ phase: 'save', pages: rendered, totalPages });
225
+ return pdfDoc.save();
226
+ }
227
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pdf-backend/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * PDF link helpers for resource references (issue #49 §7) and the contents.
3
+ *
4
+ * Each `::resource{id=…}` embed creates a named destination keyed by the
5
+ * resource id (an `[page /XYZ left top zoom]` array pointing at the embed's
6
+ * top-left). Each inline `:ref{id=…}` becomes a `/Link` annotation whose
7
+ * rectangle covers the rendered ref text and whose `/Dest` is the matching
8
+ * destination. A row of the contents (`:::toc`) becomes a `/Link` to the
9
+ * top of the page it lists, when that page is in the document.
10
+ *
11
+ * Destinations and links can be emitted in any page order, so collection is
12
+ * two-phase: callers record destinations + pending links during page rendering,
13
+ * then call {@link finalizeLinks} after every page has been drawn to attach the
14
+ * annotations (resolving each link to its destination).
15
+ */
16
+ import { type PDFDocument, type PDFPage } from 'pdf-lib';
17
+ import type { StructElem, StructTree } from './tagging';
18
+ /** Structure bookkeeping of a link in an accessible render: the `Link`
19
+ * element wrapping the ref text, and the annotation's `/Contents`. */
20
+ export interface LinkStruct {
21
+ elem: StructElem;
22
+ contents: string;
23
+ }
24
+ export declare class LinkRegistry {
25
+ private readonly pageIndexOffset;
26
+ private dests;
27
+ private pending;
28
+ /** Physical pages before the document's first page. */
29
+ constructor(pageIndexOffset?: number);
30
+ /** Record the named destination for a resource embed (idempotent — the first
31
+ * embed of a given id wins, matching first-reference numbering). */
32
+ addDestination(resourceId: string, page: PDFPage, leftPt: number, topPt: number): void;
33
+ /** Record a pending inline-ref link to be attached at finalize. */
34
+ addLink(page: PDFPage, rect: [number, number, number, number], resourceId: string, struct?: LinkStruct): void;
35
+ /** Record a pending link to the top of a book page (a contents row). */
36
+ addPageLink(page: PDFPage, rect: [number, number, number, number], pageIndex: number, struct?: LinkStruct): void;
37
+ /** Attach all pending link annotations. Links whose destination resource was
38
+ * never embedded are skipped (they still rendered as styled text; in a
39
+ * tagged render their `Link` element dissolves into the paragraph). With
40
+ * `tree`, each annotation joins its `Link` element (`OBJR` kid,
41
+ * `/StructParent`) and carries `/Contents` (PDF/UA-1 §7.18.5). */
42
+ finalize(pdfDoc: PDFDocument, tree?: StructTree): void;
43
+ }
44
+ //# sourceMappingURL=links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/pdf-backend/links.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,OAAO,EAEb,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAExD;uEACuE;AACvE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsBD,qBAAa,YAAY;IAKX,OAAO,CAAC,QAAQ,CAAC,eAAe;IAJ5C,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,OAAO,CAAqB;IAEpC,uDAAuD;gBAC1B,eAAe,SAAI;IAEhD;yEACqE;IACrE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKtF,mEAAmE;IACnE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IAI7G,wEAAwE;IACxE,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IAIhH;;;;uEAImE;IACnE,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI;CA2DvD"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * PDF link helpers for resource references (issue #49 §7) and the contents.
3
+ *
4
+ * Each `::resource{id=…}` embed creates a named destination keyed by the
5
+ * resource id (an `[page /XYZ left top zoom]` array pointing at the embed's
6
+ * top-left). Each inline `:ref{id=…}` becomes a `/Link` annotation whose
7
+ * rectangle covers the rendered ref text and whose `/Dest` is the matching
8
+ * destination. A row of the contents (`:::toc`) becomes a `/Link` to the
9
+ * top of the page it lists, when that page is in the document.
10
+ *
11
+ * Destinations and links can be emitted in any page order, so collection is
12
+ * two-phase: callers record destinations + pending links during page rendering,
13
+ * then call {@link finalizeLinks} after every page has been drawn to attach the
14
+ * annotations (resolving each link to its destination).
15
+ */
16
+ import { PDFHexString, PDFName, PDFArray, PDFNumber, } from 'pdf-lib';
17
+ export class LinkRegistry {
18
+ pageIndexOffset;
19
+ dests = new Map();
20
+ pending = [];
21
+ /** Physical pages before the document's first page. */
22
+ constructor(pageIndexOffset = 0) {
23
+ this.pageIndexOffset = pageIndexOffset;
24
+ }
25
+ /** Record the named destination for a resource embed (idempotent — the first
26
+ * embed of a given id wins, matching first-reference numbering). */
27
+ addDestination(resourceId, page, leftPt, topPt) {
28
+ if (this.dests.has(resourceId))
29
+ return;
30
+ this.dests.set(resourceId, { page, leftPt, topPt });
31
+ }
32
+ /** Record a pending inline-ref link to be attached at finalize. */
33
+ addLink(page, rect, resourceId, struct) {
34
+ this.pending.push({ page, rect, resourceId, struct });
35
+ }
36
+ /** Record a pending link to the top of a book page (a contents row). */
37
+ addPageLink(page, rect, pageIndex, struct) {
38
+ this.pending.push({ page, rect, pageIndex, struct });
39
+ }
40
+ /** Attach all pending link annotations. Links whose destination resource was
41
+ * never embedded are skipped (they still rendered as styled text; in a
42
+ * tagged render their `Link` element dissolves into the paragraph). With
43
+ * `tree`, each annotation joins its `Link` element (`OBJR` kid,
44
+ * `/StructParent`) and carries `/Contents` (PDF/UA-1 §7.18.5). */
45
+ finalize(pdfDoc, tree) {
46
+ const context = pdfDoc.context;
47
+ const pages = pdfDoc.getPages();
48
+ // Group annotation refs per page so each page's /Annots array is written once.
49
+ const perPage = new Map();
50
+ for (const link of this.pending) {
51
+ let destArray;
52
+ if (link.pageIndex !== undefined) {
53
+ // A page outside the document (a chapter rendered on its own
54
+ // listing the whole book) gets no link.
55
+ const target = pages[link.pageIndex - this.pageIndexOffset];
56
+ if (!target) {
57
+ if (link.struct && tree)
58
+ tree.dissolve(link.struct.elem);
59
+ continue;
60
+ }
61
+ destArray = context.obj([target.ref, PDFName.of('XYZ'), null, target.getHeight(), null]);
62
+ }
63
+ else {
64
+ const dest = link.resourceId !== undefined ? this.dests.get(link.resourceId) : undefined;
65
+ if (!dest) {
66
+ if (link.struct && tree)
67
+ tree.dissolve(link.struct.elem);
68
+ continue;
69
+ }
70
+ destArray = context.obj([
71
+ dest.page.ref,
72
+ PDFName.of('XYZ'),
73
+ dest.leftPt,
74
+ dest.topPt,
75
+ null,
76
+ ]);
77
+ }
78
+ const annot = context.obj({
79
+ Type: PDFName.of('Annot'),
80
+ Subtype: PDFName.of('Link'),
81
+ Rect: context.obj(link.rect),
82
+ Border: context.obj([0, 0, 0]),
83
+ F: PDFNumber.of(4),
84
+ Dest: destArray,
85
+ });
86
+ const ref = context.nextRef();
87
+ if (link.struct && tree) {
88
+ annot.set(PDFName.of('Contents'), PDFHexString.fromText(link.struct.contents));
89
+ annot.set(PDFName.of('StructParent'), PDFNumber.of(tree.annotation(link.struct.elem, link.page, ref)));
90
+ }
91
+ context.assign(ref, annot);
92
+ const list = perPage.get(link.page);
93
+ if (list)
94
+ list.push(ref);
95
+ else
96
+ perPage.set(link.page, [ref]);
97
+ }
98
+ for (const [page, refs] of perPage) {
99
+ const existing = page.node.get(PDFName.of('Annots'));
100
+ if (existing instanceof PDFArray) {
101
+ for (const ref of refs)
102
+ existing.push(ref);
103
+ }
104
+ else {
105
+ page.node.set(PDFName.of('Annots'), context.obj(refs));
106
+ }
107
+ }
108
+ }
109
+ }
110
+ //# sourceMappingURL=links.js.map