reamkit 1.30.0 → 1.32.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 (147) hide show
  1. package/README.md +70 -24
  2. package/dist/esm/core/bidi/index.d.ts +10 -0
  3. package/dist/esm/core/bidi/index.js +18 -2
  4. package/dist/esm/core/converter/facade.js +10 -5
  5. package/dist/esm/core/converter/project.js +4 -1
  6. package/dist/esm/core/converter/ream.js +17 -6
  7. package/dist/esm/core/converter/sheet-images.d.ts +17 -0
  8. package/dist/esm/core/converter/sheet-images.js +33 -0
  9. package/dist/esm/core/crypto/offcrypto.js +1 -1
  10. package/dist/esm/core/document-model/index.d.ts +1 -1
  11. package/dist/esm/core/document-model/types.d.ts +175 -0
  12. package/dist/esm/core/drawingml/chart-geometry.d.ts +21 -8
  13. package/dist/esm/core/drawingml/chart-geometry.js +198 -108
  14. package/dist/esm/core/drawingml/chart-parser.js +73 -2
  15. package/dist/esm/core/drawingml/colors.d.ts +6 -0
  16. package/dist/esm/core/drawingml/colors.js +23 -3
  17. package/dist/esm/core/drawingml/shape-render.d.ts +3 -1
  18. package/dist/esm/core/drawingml/shape-render.js +4 -2
  19. package/dist/esm/core/font/index.d.ts +2 -0
  20. package/dist/esm/core/font/measure.d.ts +9 -0
  21. package/dist/esm/core/font/measure.js +9 -1
  22. package/dist/esm/core/font/ttf-build.d.ts +113 -0
  23. package/dist/esm/core/font/ttf-build.js +1224 -0
  24. package/dist/esm/core/font/ttf-subset.d.ts +19 -0
  25. package/dist/esm/core/font/ttf-subset.js +14 -1
  26. package/dist/esm/core/fonts/families.js +4 -2
  27. package/dist/esm/core/fonts/provider.d.ts +17 -0
  28. package/dist/esm/core/fonts/provider.js +27 -2
  29. package/dist/esm/core/fonts/remote-fonts.js +8 -1
  30. package/dist/esm/core/fonts/scripts.js +4 -2
  31. package/dist/esm/core/ir/flow.d.ts +77 -1
  32. package/dist/esm/core/ir/sheet.d.ts +43 -0
  33. package/dist/esm/core/number-format.js +16 -11
  34. package/dist/esm/core/numbering/index.d.ts +1 -1
  35. package/dist/esm/core/numbering/state.d.ts +11 -1
  36. package/dist/esm/core/numbering/state.js +10 -1
  37. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  38. package/dist/esm/core/spreadsheet-model/types.d.ts +96 -0
  39. package/dist/esm/core/style-cascade/resolver.js +39 -6
  40. package/dist/esm/core/style-cascade/types.d.ts +19 -1
  41. package/dist/esm/core/style-cascade/types.js +3 -0
  42. package/dist/esm/excel/column-bands.d.ts +39 -6
  43. package/dist/esm/excel/column-bands.js +41 -17
  44. package/dist/esm/excel/comments-parser.js +19 -0
  45. package/dist/esm/excel/conditional-format.d.ts +7 -3
  46. package/dist/esm/excel/conditional-format.js +77 -39
  47. package/dist/esm/excel/formula/context.d.ts +18 -0
  48. package/dist/esm/excel/formula/eval.js +51 -0
  49. package/dist/esm/excel/formula/index.d.ts +2 -2
  50. package/dist/esm/excel/formula/lexer.d.ts +1 -1
  51. package/dist/esm/excel/formula/lexer.js +41 -0
  52. package/dist/esm/excel/formula/parser.d.ts +25 -0
  53. package/dist/esm/excel/formula/parser.js +95 -0
  54. package/dist/esm/excel/print-model.d.ts +38 -1
  55. package/dist/esm/excel/print-model.js +522 -80
  56. package/dist/esm/excel/shared-strings-parser.d.ts +9 -0
  57. package/dist/esm/excel/shared-strings-parser.js +11 -1
  58. package/dist/esm/excel/sheet-drawing.d.ts +38 -2
  59. package/dist/esm/excel/sheet-drawing.js +47 -13
  60. package/dist/esm/excel/sheet-shape-parser.d.ts +6 -2
  61. package/dist/esm/excel/sheet-shape-parser.js +18 -6
  62. package/dist/esm/excel/sheet-to-flow.d.ts +17 -0
  63. package/dist/esm/excel/sheet-to-flow.js +434 -47
  64. package/dist/esm/excel/styles-parser.d.ts +9 -0
  65. package/dist/esm/excel/styles-parser.js +80 -26
  66. package/dist/esm/excel/table-parser.js +5 -0
  67. package/dist/esm/excel/table-style-presets.d.ts +26 -0
  68. package/dist/esm/excel/table-style-presets.js +1555 -0
  69. package/dist/esm/excel/table-style.d.ts +13 -0
  70. package/dist/esm/excel/table-style.js +75 -0
  71. package/dist/esm/excel/tint.d.ts +10 -0
  72. package/dist/esm/excel/tint.js +75 -0
  73. package/dist/esm/excel/vml-drawing.d.ts +41 -3
  74. package/dist/esm/excel/vml-drawing.js +143 -10
  75. package/dist/esm/excel/worksheet-parser.js +89 -15
  76. package/dist/esm/excel/xls/biff-reader.js +10 -2
  77. package/dist/esm/excel/xlsx-reader.js +195 -14
  78. package/dist/esm/excel/xlsx-writer.js +8 -7
  79. package/dist/esm/html/html-writer.js +202 -20
  80. package/dist/esm/index.d.ts +2 -2
  81. package/dist/esm/layout/fit-pages.d.ts +11 -0
  82. package/dist/esm/layout/fit-pages.js +86 -0
  83. package/dist/esm/layout/line-paint.d.ts +58 -0
  84. package/dist/esm/layout/line-paint.js +107 -0
  85. package/dist/esm/layout/page-doc.d.ts +61 -3
  86. package/dist/esm/layout/page-doc.js +141 -1
  87. package/dist/esm/layout/styled-layout.d.ts +55 -7
  88. package/dist/esm/layout/styled-layout.js +1154 -169
  89. package/dist/esm/layout/turned-section.d.ts +38 -0
  90. package/dist/esm/layout/turned-section.js +205 -0
  91. package/dist/esm/markdown/markdown-writer.js +17 -6
  92. package/dist/esm/pdf/styled-page-emitter.js +114 -60
  93. package/dist/esm/pdf/styled-page-renderer.js +1 -1
  94. package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
  95. package/dist/esm/pdf-reader/cff-outline.js +169 -21
  96. package/dist/esm/pdf-reader/content.d.ts +53 -0
  97. package/dist/esm/pdf-reader/content.js +9 -1
  98. package/dist/esm/pdf-reader/display.d.ts +36 -0
  99. package/dist/esm/pdf-reader/display.js +66 -1
  100. package/dist/esm/pdf-reader/document.js +5 -1
  101. package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
  102. package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
  103. package/dist/esm/pdf-reader/encodings.d.ts +8 -0
  104. package/dist/esm/pdf-reader/encodings.js +25 -3
  105. package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
  106. package/dist/esm/pdf-reader/face-outlines.js +362 -0
  107. package/dist/esm/pdf-reader/figures.d.ts +52 -0
  108. package/dist/esm/pdf-reader/figures.js +433 -0
  109. package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
  110. package/dist/esm/pdf-reader/flow-build.js +345 -26
  111. package/dist/esm/pdf-reader/font.js +321 -36
  112. package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
  113. package/dist/esm/pdf-reader/glyf-outline.js +135 -1
  114. package/dist/esm/pdf-reader/glyph-names.js +154 -1
  115. package/dist/esm/pdf-reader/layout.d.ts +93 -0
  116. package/dist/esm/pdf-reader/layout.js +1703 -214
  117. package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
  118. package/dist/esm/pdf-reader/page-numbers.js +167 -0
  119. package/dist/esm/pdf-reader/tagged.js +182 -23
  120. package/dist/esm/pdf-reader/text.d.ts +6 -3
  121. package/dist/esm/pdf-reader/text.js +98 -9
  122. package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
  123. package/dist/esm/pdf-reader/type1-outline.js +63 -8
  124. package/dist/esm/pdf-reader/vector.js +71 -1
  125. package/dist/esm/svg/svg-text.d.ts +58 -0
  126. package/dist/esm/svg/svg-text.js +322 -0
  127. package/dist/esm/svg/svg-writer.js +135 -71
  128. package/dist/esm/word/doc/doc-reader.js +6 -2
  129. package/dist/esm/word/doc/doc-text.d.ts +6 -0
  130. package/dist/esm/word/doc/doc-text.js +19 -1
  131. package/dist/esm/word/document-parser.d.ts +2 -2
  132. package/dist/esm/word/document-parser.js +11 -1
  133. package/dist/esm/word/docx-reader.js +5 -3
  134. package/dist/esm/word/docx-writer.js +218 -23
  135. package/dist/esm/word/drawing-parser.d.ts +5 -3
  136. package/dist/esm/word/drawing-parser.js +49 -8
  137. package/dist/esm/word/font-embed.d.ts +30 -0
  138. package/dist/esm/word/font-embed.js +173 -0
  139. package/dist/esm/word/font-table.d.ts +10 -0
  140. package/dist/esm/word/font-table.js +13 -1
  141. package/dist/esm/word/index.js +1 -1
  142. package/dist/esm/word/numbering-parser.d.ts +3 -1
  143. package/dist/esm/word/numbering-parser.js +2 -1
  144. package/dist/esm/word/paragraph-properties.d.ts +7 -6
  145. package/dist/esm/word/paragraph-properties.js +14 -2
  146. package/dist/esm/word/run-properties.js +26 -0
  147. package/package.json +12 -3
@@ -0,0 +1,38 @@
1
+ import { BodyElement } from '../core/document-model/index.js';
2
+ import { PageItem } from './page-doc.js';
3
+ /** The sheet a turned section prints on, and the margins it states for it. */
4
+ export interface Sheet {
5
+ readonly width: number;
6
+ readonly height: number;
7
+ readonly marginLeft: number;
8
+ readonly marginRight: number;
9
+ readonly marginTop: number;
10
+ readonly marginBottom: number;
11
+ }
12
+ /**
13
+ * One item of a turned section's body, carried from the frame the text reads
14
+ * in onto the sheet: a point `(x, y)` of the frame (top-left origin, y down)
15
+ * lands at `(sheetWidth − y, x)` — the frame's top edge is the sheet's right
16
+ * edge, and its left edge the sheet's top.
17
+ *
18
+ * @param item The item as the frame's layout placed it.
19
+ * @param sheetWidth The sheet's width, which is the frame's height.
20
+ * @returns The same item on the sheet, turned a quarter clockwise.
21
+ */
22
+ export declare function turnOntoSheet(item: PageItem, sheetWidth: number): PageItem;
23
+ /**
24
+ * A drawing anchored in a turned section, given the place in the text's frame
25
+ * from which turning the body carries it to where Word stands it: on the sheet,
26
+ * at the offsets its anchor states, upright.
27
+ *
28
+ * The anchor is resolved against the SHEET — its page, its margins and the
29
+ * bands beside them. What is measured from the text itself (a paragraph, a
30
+ * line, a column) has no place on the sheet before the text is laid out, and is
31
+ * measured from the margin instead. Only pictures and shapes are carried; a
32
+ * chart or a floating table turns with the text.
33
+ *
34
+ * @param el The body element, anchored or not.
35
+ * @param sheet The sheet the section prints on.
36
+ * @returns The element, its anchor restated in the frame and its turn backed off.
37
+ */
38
+ export declare function floatIntoFrame(el: BodyElement, sheet: Sheet): BodyElement;
@@ -0,0 +1,205 @@
1
+ import { pt } from "../core/ir/units.js";
2
+ //#region src/layout/turned-section.ts
3
+ /** A quarter turn in the 1/60000° a DrawingML rotation is stated in (§20.1.7.6). */
4
+ var QUARTER_60K = 90 * 6e4;
5
+ /**
6
+ * One item of a turned section's body, carried from the frame the text reads
7
+ * in onto the sheet: a point `(x, y)` of the frame (top-left origin, y down)
8
+ * lands at `(sheetWidth − y, x)` — the frame's top edge is the sheet's right
9
+ * edge, and its left edge the sheet's top.
10
+ *
11
+ * @param item The item as the frame's layout placed it.
12
+ * @param sheetWidth The sheet's width, which is the frame's height.
13
+ * @returns The same item on the sheet, turned a quarter clockwise.
14
+ */
15
+ function turnOntoSheet(item, sheetWidth) {
16
+ const turned = turnItem(item, sheetWidth);
17
+ const w = item.window;
18
+ return w ? {
19
+ ...turned,
20
+ window: turnBox(w.x, w.y, w.width, w.height, sheetWidth)
21
+ } : turned;
22
+ }
23
+ /** A top-left-frame box of the frame, as it stands on the sheet once turned a quarter clockwise. */
24
+ function turnBox(x, y, w, h, sheetWidth) {
25
+ return {
26
+ x: pt(sheetWidth - y - h),
27
+ y: pt(x),
28
+ width: pt(h),
29
+ height: pt(w)
30
+ };
31
+ }
32
+ function turnItem(item, sheetWidth) {
33
+ const box = (x, y, w, h) => turnBox(x, y, w, h, sheetWidth);
34
+ switch (item.type) {
35
+ case "line": {
36
+ const quarter = withinTurn((item.rotationDeg ?? 0) - 90);
37
+ const turned = quarter > 180 ? quarter - 360 : quarter;
38
+ const { rotationDeg: _flat, clip, ...rest } = item;
39
+ return {
40
+ ...rest,
41
+ originX: pt(sheetWidth - item.baselineY),
42
+ baselineY: pt(item.originX),
43
+ ...turned !== 0 ? { rotationDeg: turned } : {},
44
+ ...clip ? { clip: box(clip.x, clip.y, clip.width, clip.height) } : {}
45
+ };
46
+ }
47
+ case "image": {
48
+ const cx = item.x + item.width / 2;
49
+ const cy = item.y + item.height / 2;
50
+ const turned = withinTurn((item.rotationDeg ?? 0) + 90);
51
+ const { rotationDeg: _flat, clip, ...rest } = item;
52
+ return {
53
+ ...rest,
54
+ x: pt(sheetWidth - cy - item.width / 2),
55
+ y: pt(cx - item.height / 2),
56
+ ...turned !== 0 ? { rotationDeg: turned } : {},
57
+ ...clip ? { clip: {
58
+ paths: clip.paths,
59
+ transform: turnMatrix(clip.transform, sheetWidth)
60
+ } } : {}
61
+ };
62
+ }
63
+ case "shape": {
64
+ const shadow = item.shape.shadow;
65
+ return {
66
+ ...item,
67
+ shape: {
68
+ ...item.shape,
69
+ transform: turnMatrix(item.shape.transform, sheetWidth),
70
+ ...shadow ? { shadow: {
71
+ ...shadow,
72
+ dxPt: -shadow.dyPt,
73
+ dyPt: shadow.dxPt
74
+ } } : {}
75
+ }
76
+ };
77
+ }
78
+ case "border": return {
79
+ ...item,
80
+ ...box(item.x, item.y, item.width, item.height),
81
+ side: TURNED_SIDE[item.side]
82
+ };
83
+ case "fill": return {
84
+ ...item,
85
+ ...box(item.x, item.y, item.width, item.height)
86
+ };
87
+ }
88
+ }
89
+ /** An angle in degrees brought into one full turn, `[0, 360)`, with the float noise off. */
90
+ function withinTurn(deg) {
91
+ const t = (deg % 360 + 360) % 360;
92
+ return Math.abs(t) < 1e-9 || Math.abs(t - 360) < 1e-9 ? 0 : t;
93
+ }
94
+ /** Which edge of a box each edge becomes when the box turns a quarter clockwise. */
95
+ var TURNED_SIDE = {
96
+ top: "right",
97
+ right: "bottom",
98
+ bottom: "left",
99
+ left: "top"
100
+ };
101
+ /**
102
+ * A local→page matrix `[a b c d e f]` (x' = a·u + c·v + e, y' = b·u + d·v + f,
103
+ * page top-left, y down) followed by the turn onto the sheet.
104
+ */
105
+ function turnMatrix(m, sheetWidth) {
106
+ return [
107
+ -m[1],
108
+ m[0],
109
+ -m[3],
110
+ m[2],
111
+ sheetWidth - m[5],
112
+ m[4]
113
+ ];
114
+ }
115
+ /**
116
+ * A drawing anchored in a turned section, given the place in the text's frame
117
+ * from which turning the body carries it to where Word stands it: on the sheet,
118
+ * at the offsets its anchor states, upright.
119
+ *
120
+ * The anchor is resolved against the SHEET — its page, its margins and the
121
+ * bands beside them. What is measured from the text itself (a paragraph, a
122
+ * line, a column) has no place on the sheet before the text is laid out, and is
123
+ * measured from the margin instead. Only pictures and shapes are carried; a
124
+ * chart or a floating table turns with the text.
125
+ *
126
+ * @param el The body element, anchored or not.
127
+ * @param sheet The sheet the section prints on.
128
+ * @returns The element, its anchor restated in the frame and its turn backed off.
129
+ */
130
+ function floatIntoFrame(el, sheet) {
131
+ if (el.kind === "image" && el.image.float !== void 0) {
132
+ const { image } = el;
133
+ const float = image.float;
134
+ const placed = frameAnchor(float, image.width, image.height, sheet);
135
+ return {
136
+ ...el,
137
+ image: {
138
+ ...image,
139
+ float: placed,
140
+ rotation60k: backedOff(image.rotation60k)
141
+ }
142
+ };
143
+ }
144
+ if (el.kind === "shape" && el.shape.float !== void 0) {
145
+ const { shape } = el;
146
+ const float = shape.float;
147
+ const placed = frameAnchor(float, shape.width, shape.height, sheet);
148
+ return {
149
+ ...el,
150
+ shape: {
151
+ ...shape,
152
+ float: placed,
153
+ transform: {
154
+ ...shape.transform,
155
+ rotation60k: backedOff(shape.transform?.rotation60k)
156
+ }
157
+ }
158
+ };
159
+ }
160
+ return el;
161
+ }
162
+ /** A clockwise turn with a quarter taken off, kept in the one full turn. */
163
+ function backedOff(rotation60k) {
164
+ const full = 360 * 6e4;
165
+ return (((rotation60k ?? 0) - QUARTER_60K) % full + full) % full;
166
+ }
167
+ /**
168
+ * The anchor a drawing of `w × h` needs in the frame for the body's turn to put
169
+ * it where `float` stands it on the sheet.
170
+ */
171
+ function frameAnchor(float, w, h, sheet) {
172
+ const x = sheetX(float.posH, w, sheet);
173
+ const cx = sheetY(float.posV, h, sheet) + h / 2;
174
+ const cy = sheet.width - (x + w / 2);
175
+ const { posH: _h, posV: _v, ...rest } = float;
176
+ return {
177
+ ...rest,
178
+ posH: {
179
+ relativeFrom: "page",
180
+ offsetPt: pt(cx - w / 2)
181
+ },
182
+ posV: {
183
+ relativeFrom: "page",
184
+ offsetPt: pt(cy - h / 2)
185
+ }
186
+ };
187
+ }
188
+ /** §20.4.3.3 — the box's left edge on the sheet. */
189
+ function sheetX(h, w, sheet) {
190
+ const textRight = sheet.width - sheet.marginRight;
191
+ const [base, span] = h?.relativeFrom === "page" ? [0, sheet.width] : h?.relativeFrom === "leftMargin" ? [0, sheet.marginLeft] : h?.relativeFrom === "rightMargin" ? [textRight, sheet.marginRight] : [sheet.marginLeft, textRight - sheet.marginLeft];
192
+ if (h?.align === "center") return base + (span - w) / 2;
193
+ if (h?.align === "right") return base + span - w;
194
+ return base + (h?.offsetPt ?? 0);
195
+ }
196
+ /** §20.4.3.4 — the box's top edge on the sheet. */
197
+ function sheetY(v, h, sheet) {
198
+ const textBottom = sheet.height - sheet.marginBottom;
199
+ const [base, span] = v?.relativeFrom === "page" ? [0, sheet.height] : v?.relativeFrom === "topMargin" ? [0, sheet.marginTop] : v?.relativeFrom === "bottomMargin" ? [textBottom, sheet.marginBottom] : [sheet.marginTop, textBottom - sheet.marginTop];
200
+ if (v?.align === "center") return base + (span - h) / 2;
201
+ if (v?.align === "bottom") return base + span - h;
202
+ return base + (v?.offsetPt ?? 0);
203
+ }
204
+ //#endregion
205
+ export { floatIntoFrame, turnOntoSheet };
@@ -32,6 +32,7 @@ function writeMarkdown(flow, options = {}) {
32
32
  anchors: referencedAnchors(flow.body),
33
33
  notes: noteNumbers(flow),
34
34
  mediaNames: /* @__PURE__ */ new Map(),
35
+ drawn: /* @__PURE__ */ new Set(),
35
36
  ...flow.numbering ? { numbering: flow.numbering } : {}
36
37
  };
37
38
  if (flow.headersFooters && flow.headersFooters.size > 0) lose(ctx, "dropped", FEATURES.headersFooters, "markdown has no pages to band headers onto");
@@ -98,8 +99,14 @@ function emitBlock(out, el, ctx) {
98
99
  return;
99
100
  }
100
101
  ctx.list.length = 0;
101
- if (el.kind === "table") emitTable(out, el.table, ctx);
102
- else if (el.kind === "image") {
102
+ if (el.kind === "table") {
103
+ emitTable(out, el.table, ctx);
104
+ for (const drawing of el.table.overlay?.drawings ?? []) {
105
+ if (ctx.drawn.has(drawing)) continue;
106
+ ctx.drawn.add(drawing);
107
+ emitBlock(out, drawing, ctx);
108
+ }
109
+ } else if (el.kind === "image") {
103
110
  const img = pictureMarkdown(el.image.resource, el.image.altText, ctx);
104
111
  if (img.length > 0) out.push(img);
105
112
  } else if (el.kind === "chart") lose(ctx, "dropped", FEATURES.charts, "markdown cannot draw a chart");
@@ -275,8 +282,10 @@ function noteNumbers(flow) {
275
282
  add(r.endnoteRef, endnotes, flow.endnotes);
276
283
  add(r.commentRef, comments, flow.comments);
277
284
  }
278
- else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
279
- else if (el.kind === "shape") visitShape(el.shape);
285
+ else if (el.kind === "table") {
286
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
287
+ visit(el.table.overlay?.drawings ?? []);
288
+ } else if (el.kind === "shape") visitShape(el.shape);
280
289
  };
281
290
  visit(flow.body);
282
291
  return {
@@ -370,8 +379,10 @@ function referencedAnchors(body) {
370
379
  const visit = (els) => {
371
380
  for (const el of els) if (el.kind === "paragraph") {
372
381
  for (const r of el.paragraph.runs) if (r.anchor !== void 0) out.add(r.anchor);
373
- } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
374
- else if (el.kind === "shape") visitShape(el.shape);
382
+ } else if (el.kind === "table") {
383
+ for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
384
+ visit(el.table.overlay?.drawings ?? []);
385
+ } else if (el.kind === "shape") visitShape(el.shape);
375
386
  };
376
387
  visit(body);
377
388
  return out;
@@ -1,10 +1,10 @@
1
1
  import { dict, name, ref, stream, unicodeString } from "./objects.js";
2
2
  import { embedTtfFont } from "./cid-font.js";
3
3
  import { preparePdfEncryption } from "./encryption.js";
4
- import { reorderVisual } from "../core/bidi/algorithm.js";
4
+ import { pageLayers, paintPlan } from "../layout/page-doc.js";
5
5
  import { reverseByCodePoint } from "../core/bidi/index.js";
6
- import { GLUE_SHRINK_RATIO } from "../layout/styled-layout.js";
7
- import { paintPlan } from "../layout/page-doc.js";
6
+ import "../layout/styled-layout.js";
7
+ import { BORDER_DASHES, FAUX_ITALIC_SHEAR, computeJustifyExtra, lineVisualOrder, settableInOneMatrix, washVeil } from "../layout/line-paint.js";
8
8
  import { emitClipPath, emitVectorShape, shadowBlurLayers } from "./vector-graphics.js";
9
9
  import { buildGradientAlphaMask, buildGradientPattern, shapeBbox } from "./shading.js";
10
10
  import { warpGlyphMatrix } from "../core/drawingml/text-warp.js";
@@ -16,16 +16,6 @@ import { addSignaturePlaceholder } from "./signature.js";
16
16
  import { buildXmpPacket } from "./xmp.js";
17
17
  //#region src/pdf/styled-page-emitter.ts
18
18
  var encoder = new TextEncoder();
19
- function washVeil(wash) {
20
- if (!wash) return void 0;
21
- const alpha = 1 - wash.gain;
22
- if (!(alpha > .004)) return void 0;
23
- const offset = .5 * (1 - wash.gain) + wash.black;
24
- return {
25
- alpha: Math.min(1, alpha),
26
- grey: Math.max(0, Math.min(1, offset / alpha))
27
- };
28
- }
29
19
  /**
30
20
  * The veil operators for a washed picture, to follow its own `/Im Do`: the
31
21
  * placement matrix maps the unit square onto the picture as drawn, so the wash
@@ -268,7 +258,7 @@ function assembleStyledPdf(laid, options, doc) {
268
258
  tagFor: (structId) => builder.node(structId).type
269
259
  };
270
260
  }
271
- const { content: contentBytes, links } = emitPageContent(page, pageTagging, gradientNames, alphaStateNames, wantDuotone, gradientMaskNames);
261
+ const { content: contentBytes, links } = emitPageLayers(page, pageTagging, gradientNames, alphaStateNames, wantDuotone, gradientMaskNames);
272
262
  const contentsRef = doc.add(stream({}, contentBytes));
273
263
  const pageEntries = {
274
264
  Type: name("Page"),
@@ -611,31 +601,56 @@ function emitTaggedRuns(out, cmds, tagging, body) {
611
601
  * 24. Scaling them by the width instead drew page-borders-export-case-2.docx's
612
602
  * half-point frame as a row of specks a quarter the length it should be.
613
603
  */
614
- var BORDER_DASHES = new Map([
615
- ["dashed", [8, 2.5]],
616
- ["dashSmallGap", [3, 1]],
617
- ["dotted", [.5, 1]],
618
- ["dashDot", [
619
- 8,
620
- 2.5,
621
- 2.5,
622
- 2.5
623
- ]],
624
- ["dashDotDot", [
625
- 8,
626
- 2.5,
627
- 2.5,
628
- 2.5,
629
- 2.5,
630
- 2.5
631
- ]]
632
- ]);
633
- var FAUX_ITALIC_SHEAR = .2126;
634
604
  /** The PDF dash array for a border style; an empty string means a solid rule. */
635
605
  function dashPatternFor(style) {
636
606
  const pattern = style !== void 0 ? BORDER_DASHES.get(style) : void 0;
637
607
  return pattern ? `[${pattern.map(formatNumber).join(" ")}]` : "";
638
608
  }
609
+ /**
610
+ * A page's content stream in its layers (pageLayers): its own items, then —
611
+ * when some of them stand IN FRONT of its text (PageItemBase.over) — those,
612
+ * painted again pass by pass over what the first layer left, their own text
613
+ * last; and each group seen through a window of its own (PageItemBase.window)
614
+ * inside a clip to it. Each layer is a balanced stream of its own, so they
615
+ * simply follow one another.
616
+ */
617
+ function emitPageLayers(page, ...rest) {
618
+ const layers = pageLayers(page.commands);
619
+ if (layers.length === 1 && !layers[0].window) return emitPageContent(page, ...rest);
620
+ const parts = [];
621
+ const links = [];
622
+ for (const layer of layers) {
623
+ const emitted = emitPageContent({
624
+ ...page,
625
+ commands: layer.items
626
+ }, ...rest);
627
+ links.push(...emitted.links);
628
+ const w = layer.window;
629
+ if (!w) {
630
+ parts.push(emitted.content);
631
+ continue;
632
+ }
633
+ if (emitted.content.length === 0) continue;
634
+ const rect = [
635
+ w.x,
636
+ page.height - w.y - w.height,
637
+ w.width,
638
+ w.height
639
+ ].map(formatNumber);
640
+ parts.push(encoder.encode(`q ${rect.join(" ")} re W n`), emitted.content, encoder.encode("Q"));
641
+ }
642
+ const content = new Uint8Array(parts.reduce((sum, p) => sum + p.length, 0) + Math.max(0, parts.length - 1));
643
+ let at = 0;
644
+ parts.forEach((p, i) => {
645
+ if (i > 0) content[at++] = 10;
646
+ content.set(p, at);
647
+ at += p.length;
648
+ });
649
+ return {
650
+ content,
651
+ links
652
+ };
653
+ }
639
654
  function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneStateFor, gradientMaskNames) {
640
655
  const commands = page.commands;
641
656
  const links = [];
@@ -676,12 +691,13 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
676
691
  }
677
692
  lastStroke = strokeHex;
678
693
  }
679
- const tz = Math.round((tok.synthetic?.widthScale ?? 1) * 100);
694
+ const scale = (tok.synthetic?.widthScale ?? 1) * (tok.resolvedRun.widthScale ?? 1);
695
+ const tz = Math.round(scale * 100);
680
696
  if (tz !== lastTz) {
681
697
  out.push(`${formatNumber(tz)} Tz`);
682
698
  lastTz = tz;
683
699
  }
684
- const tc = tok.resolvedRun.letterSpacingPt ?? 0;
700
+ const tc = (tok.resolvedRun.letterSpacingPt ?? 0) / (tz > 0 ? tz / 100 : 1);
685
701
  if (tc !== lastTc) {
686
702
  out.push(`${formatNumber(tc)} Tc`);
687
703
  lastTc = tc;
@@ -888,7 +904,69 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
888
904
  lastColor = "";
889
905
  lastTc = 0;
890
906
  };
907
+ const emitTurned = (cmd) => {
908
+ if (inBT) {
909
+ out.push("ET");
910
+ inBT = false;
911
+ }
912
+ const rad = (cmd.rotationDeg ?? 0) * Math.PI / 180;
913
+ const cos = Math.cos(rad);
914
+ const sin = Math.sin(rad);
915
+ const ox = cmd.originX;
916
+ const oy = H - cmd.baselineY;
917
+ out.push("q");
918
+ out.push(`${formatNumber(cos)} ${formatNumber(sin)} ${formatNumber(-sin)} ${formatNumber(cos)} ${formatNumber(ox - ox * cos + oy * sin)} ${formatNumber(oy - ox * sin - oy * cos)} cm`);
919
+ lastFont = "";
920
+ lastFauxWidth = 0;
921
+ lastStroke = void 0;
922
+ lastTz = 100;
923
+ lastSize = -1;
924
+ lastColor = "";
925
+ lastTc = 0;
926
+ const from = links.length;
927
+ const { rotationDeg: _turned, ...flat } = cmd;
928
+ out.push("BT");
929
+ inBT = true;
930
+ emitOneLine(flat);
931
+ if (inBT) {
932
+ out.push("ET");
933
+ inBT = false;
934
+ }
935
+ out.push("Q");
936
+ lastFont = "";
937
+ lastFauxWidth = 0;
938
+ lastStroke = void 0;
939
+ lastTz = 100;
940
+ lastSize = -1;
941
+ lastColor = "";
942
+ lastTc = 0;
943
+ for (let k = from; k < links.length; k++) {
944
+ const link = links[k];
945
+ const [x0, y0, x1, y1] = link.rect;
946
+ const corners = [
947
+ [x0, y0],
948
+ [x1, y0],
949
+ [x1, y1],
950
+ [x0, y1]
951
+ ].map(([x, y]) => [ox + (x - ox) * cos - (y - oy) * sin, oy + (x - ox) * sin + (y - oy) * cos]);
952
+ const xs = corners.map((c) => c[0]);
953
+ const ys = corners.map((c) => c[1]);
954
+ links[k] = {
955
+ ...link,
956
+ rect: [
957
+ Math.min(...xs),
958
+ Math.min(...ys),
959
+ Math.max(...xs),
960
+ Math.max(...ys)
961
+ ]
962
+ };
963
+ }
964
+ };
891
965
  const emitOneLine = (cmd) => {
966
+ if (cmd.rotationDeg && cmd.warp === void 0 && !settableInOneMatrix(cmd.line)) {
967
+ emitTurned(cmd);
968
+ return;
969
+ }
892
970
  const line = cmd.line;
893
971
  const originX = cmd.originX;
894
972
  const baselineY = H - cmd.baselineY;
@@ -1351,9 +1429,6 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneS
1351
1429
  links
1352
1430
  };
1353
1431
  }
1354
- function lineVisualOrder(line) {
1355
- return reorderVisual(line.tokens.map((t) => t.bidiLevel));
1356
- }
1357
1432
  /**
1358
1433
  * The operators that put an image XObject in a box — ISO 32000-1 §8.9.5.1: the
1359
1434
  * unit square scaled to the box and moved to `(x, y)`, its bottom-left corner.
@@ -1397,27 +1472,6 @@ function rotateAboutCm(cx, cy, deg) {
1397
1472
  const s = Math.sin(rad);
1398
1473
  return `${formatNumber(c)} ${formatNumber(s)} ${formatNumber(-s)} ${formatNumber(c)} ${formatNumber(cx - cx * c + cy * s)} ${formatNumber(cy - cx * s - cy * c)} cm`;
1399
1474
  }
1400
- function computeJustifyExtra(line) {
1401
- const alignment = line.resolved.alignment;
1402
- if (alignment !== "both" && alignment !== "distribute") return 0;
1403
- if (line.noJustify === true) return 0;
1404
- if (line.isLastInParagraph && alignment !== "distribute") return 0;
1405
- let last = line.tokens.length - 1;
1406
- while (last >= 0 && line.tokens[last].kind === "text" && line.tokens[last].isSpace) last--;
1407
- let spaces = 0;
1408
- let narrowest = Infinity;
1409
- let contentWidthPt = 0;
1410
- for (let i = 0; i <= last; i++) {
1411
- const tok = line.tokens[i];
1412
- contentWidthPt += tok.widthPt;
1413
- if (!tok.isSpace) continue;
1414
- spaces++;
1415
- if (tok.widthPt < narrowest) narrowest = tok.widthPt;
1416
- }
1417
- if (spaces === 0) return 0;
1418
- const extra = (line.availableWidthPt - contentWidthPt) / spaces;
1419
- return extra < 0 ? Math.max(extra, -narrowest * GLUE_SHRINK_RATIO) : extra;
1420
- }
1421
1475
  function hexToRgb01(hex) {
1422
1476
  return [
1423
1477
  parseInt(hex.slice(0, 2), 16) / 255,
@@ -1,6 +1,6 @@
1
1
  import { PdfDocument } from "./writer.js";
2
- import { layoutStyledDocument } from "../layout/styled-layout.js";
3
2
  import "../layout/page-doc.js";
3
+ import { layoutStyledDocument } from "../layout/styled-layout.js";
4
4
  import { emitStyledPdf, emitStyledPdfEncrypted } from "./styled-page-emitter.js";
5
5
  //#region src/pdf/styled-page-renderer.ts
6
6
  /**
@@ -32,5 +32,32 @@ export declare function cffNameToGid(program: Uint8Array): Map<string, number> |
32
32
  * CID-keyed or cannot be read.
33
33
  */
34
34
  export declare function cffCidToGid(program: Uint8Array): Map<number, number> | undefined;
35
+ /**
36
+ * How far the pen moves after a name-keyed CFF program's `space`, in
37
+ * thousandths of an em — the width a writer gives the space a page never
38
+ * showed, which is the one it was typeset with.
39
+ *
40
+ * @param program The raw CFF bytes.
41
+ * @returns The advance, or `undefined` where the program has no `space` or its
42
+ * width cannot be read off the charstring.
43
+ */
44
+ export declare function cffSpaceAdvance(program: Uint8Array): number | undefined;
45
+ /**
46
+ * The embedding a CFF program's licence allows: CFF has no OS/2 table, and
47
+ * Adobe writes the OS/2 `fsType` into the Top DICT's `PostScript` string
48
+ * instead (TN 5176 §9, key 12 21) as `/FSType 8 def`.
49
+ *
50
+ * @param program The raw CFF bytes.
51
+ * @returns The value, or `undefined` where the program states none.
52
+ */
53
+ export declare function cffFsType(program: Uint8Array): number | undefined;
54
+ /**
55
+ * The `/FSType n def` a PostScript font program writes where an OpenType one
56
+ * has its OS/2 field (Adobe TN 5176 §9; a Type 1 font's `FontInfo` alike).
57
+ *
58
+ * @param text The PostScript to search.
59
+ * @returns The value, or `undefined` where the text states none.
60
+ */
61
+ export declare function postScriptFsType(text: string): number | undefined;
35
62
  /** The `CFF ` table of an OpenType wrapper, where the program is one. */
36
63
  export declare function openTypeCff(program: Uint8Array): Uint8Array | undefined;