web-doc 0.6.2 → 0.7.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 (188) hide show
  1. package/THIRD_PARTY_NOTICES.md +26 -1
  2. package/dist/adapters/docx-images.d.ts +13 -4
  3. package/dist/adapters/docx-images.js +3 -0
  4. package/dist/adapters/docx-paragraphs.d.ts +65 -0
  5. package/dist/adapters/docx-paragraphs.js +165 -0
  6. package/dist/adapters/docx-prepass.d.ts +17 -0
  7. package/dist/adapters/docx-prepass.js +230 -0
  8. package/dist/adapters/office.d.ts +31 -5
  9. package/dist/adapters/office.js +60 -29
  10. package/dist/adapters/pdf.d.ts +9 -0
  11. package/dist/adapters/pdf.js +10 -0
  12. package/dist/assets/pdfium/pdfium.wasm +0 -0
  13. package/dist/contracts.d.ts +47 -2
  14. package/dist/edit/assets.d.ts +23 -0
  15. package/dist/edit/assets.js +75 -0
  16. package/dist/edit/docx/elements.d.ts +6 -0
  17. package/dist/edit/docx/elements.js +90 -0
  18. package/dist/edit/docx/engine.d.ts +51 -0
  19. package/dist/edit/docx/engine.js +473 -0
  20. package/dist/edit/docx/handlers.d.ts +3 -0
  21. package/dist/edit/docx/handlers.js +15 -0
  22. package/dist/edit/docx/ids.d.ts +45 -0
  23. package/dist/edit/docx/ids.js +101 -0
  24. package/dist/edit/docx/model.d.ts +92 -0
  25. package/dist/edit/docx/model.js +339 -0
  26. package/dist/edit/docx/operations.d.ts +37 -0
  27. package/dist/edit/docx/operations.js +3 -0
  28. package/dist/edit/docx/provider.d.ts +13 -0
  29. package/dist/edit/docx/provider.js +34 -0
  30. package/dist/edit/docx/schemas.d.ts +6 -0
  31. package/dist/edit/docx/schemas.js +192 -0
  32. package/dist/edit/docx/session.d.ts +37 -0
  33. package/dist/edit/docx/session.js +426 -0
  34. package/dist/edit/docx/structure-ops.d.ts +6 -0
  35. package/dist/edit/docx/structure-ops.js +494 -0
  36. package/dist/edit/docx/style.d.ts +45 -0
  37. package/dist/edit/docx/style.js +375 -0
  38. package/dist/edit/docx/table-ops.d.ts +4 -0
  39. package/dist/edit/docx/table-ops.js +219 -0
  40. package/dist/edit/docx/text-ops.d.ts +18 -0
  41. package/dist/edit/docx/text-ops.js +458 -0
  42. package/dist/edit/docx/text.d.ts +34 -0
  43. package/dist/edit/docx/text.js +263 -0
  44. package/dist/edit/docx/types.d.ts +191 -0
  45. package/dist/edit/docx/types.js +1 -0
  46. package/dist/edit/docx/write.d.ts +50 -0
  47. package/dist/edit/docx/write.js +352 -0
  48. package/dist/edit/engine.d.ts +117 -0
  49. package/dist/edit/engine.js +1 -0
  50. package/dist/edit/history.d.ts +53 -0
  51. package/dist/edit/history.js +101 -0
  52. package/dist/edit/ooxml/names.d.ts +20 -0
  53. package/dist/edit/ooxml/names.js +61 -0
  54. package/dist/edit/ooxml/opc.d.ts +50 -0
  55. package/dist/edit/ooxml/opc.js +150 -0
  56. package/dist/edit/ooxml/package.d.ts +82 -0
  57. package/dist/edit/ooxml/package.js +233 -0
  58. package/dist/edit/ooxml/patch.d.ts +51 -0
  59. package/dist/edit/ooxml/patch.js +250 -0
  60. package/dist/edit/ooxml/transaction.d.ts +39 -0
  61. package/dist/edit/ooxml/transaction.js +316 -0
  62. package/dist/edit/ooxml/worker.d.ts +8 -0
  63. package/dist/edit/ooxml/worker.js +12 -0
  64. package/dist/edit/ooxml/writer.d.ts +21 -0
  65. package/dist/edit/ooxml/writer.js +187 -0
  66. package/dist/edit/ooxml/xml.d.ts +74 -0
  67. package/dist/edit/ooxml/xml.js +451 -0
  68. package/dist/edit/ooxml/zip.d.ts +54 -0
  69. package/dist/edit/ooxml/zip.js +280 -0
  70. package/dist/edit/operations.d.ts +19 -0
  71. package/dist/edit/operations.js +137 -0
  72. package/dist/edit/pdf/engine/compact.d.ts +6 -0
  73. package/dist/edit/pdf/engine/compact.js +442 -0
  74. package/dist/edit/pdf/engine/document.d.ts +95 -0
  75. package/dist/edit/pdf/engine/document.js +868 -0
  76. package/dist/edit/pdf/engine/elements.d.ts +45 -0
  77. package/dist/edit/pdf/engine/elements.js +313 -0
  78. package/dist/edit/pdf/engine/existing-text.d.ts +4 -0
  79. package/dist/edit/pdf/engine/existing-text.js +424 -0
  80. package/dist/edit/pdf/engine/fonts.d.ts +78 -0
  81. package/dist/edit/pdf/engine/fonts.js +466 -0
  82. package/dist/edit/pdf/engine/geometry.d.ts +36 -0
  83. package/dist/edit/pdf/engine/geometry.js +97 -0
  84. package/dist/edit/pdf/engine/handler.d.ts +21 -0
  85. package/dist/edit/pdf/engine/handler.js +108 -0
  86. package/dist/edit/pdf/engine/images.d.ts +33 -0
  87. package/dist/edit/pdf/engine/images.js +188 -0
  88. package/dist/edit/pdf/engine/layout.d.ts +23 -0
  89. package/dist/edit/pdf/engine/layout.js +177 -0
  90. package/dist/edit/pdf/engine/operations.d.ts +67 -0
  91. package/dist/edit/pdf/engine/operations.js +3 -0
  92. package/dist/edit/pdf/engine/pages.d.ts +6 -0
  93. package/dist/edit/pdf/engine/pages.js +98 -0
  94. package/dist/edit/pdf/engine/pdfium.d.ts +195 -0
  95. package/dist/edit/pdf/engine/pdfium.js +249 -0
  96. package/dist/edit/pdf/engine/shapes.d.ts +4 -0
  97. package/dist/edit/pdf/engine/shapes.js +183 -0
  98. package/dist/edit/pdf/engine/tables.d.ts +44 -0
  99. package/dist/edit/pdf/engine/tables.js +315 -0
  100. package/dist/edit/pdf/engine/text-box.d.ts +71 -0
  101. package/dist/edit/pdf/engine/text-box.js +317 -0
  102. package/dist/edit/pdf/engine/text-layout.d.ts +28 -0
  103. package/dist/edit/pdf/engine/text-layout.js +67 -0
  104. package/dist/edit/pdf/engine/transform.d.ts +5 -0
  105. package/dist/edit/pdf/engine/transform.js +137 -0
  106. package/dist/edit/pdf/provider.d.ts +35 -0
  107. package/dist/edit/pdf/provider.js +92 -0
  108. package/dist/edit/pdf/range-map.d.ts +14 -0
  109. package/dist/edit/pdf/range-map.js +107 -0
  110. package/dist/edit/pdf/schemas.d.ts +11 -0
  111. package/dist/edit/pdf/schemas.js +291 -0
  112. package/dist/edit/pdf/selection.d.ts +5 -0
  113. package/dist/edit/pdf/selection.js +152 -0
  114. package/dist/edit/pdf/session.d.ts +53 -0
  115. package/dist/edit/pdf/session.js +273 -0
  116. package/dist/edit/pdf/types.d.ts +336 -0
  117. package/dist/edit/pdf/types.js +1 -0
  118. package/dist/edit/pptx/elements.d.ts +74 -0
  119. package/dist/edit/pptx/elements.js +301 -0
  120. package/dist/edit/pptx/engine.d.ts +37 -0
  121. package/dist/edit/pptx/engine.js +466 -0
  122. package/dist/edit/pptx/geometry.d.ts +72 -0
  123. package/dist/edit/pptx/geometry.js +193 -0
  124. package/dist/edit/pptx/handler.d.ts +7 -0
  125. package/dist/edit/pptx/handler.js +91 -0
  126. package/dist/edit/pptx/handlers.d.ts +3 -0
  127. package/dist/edit/pptx/handlers.js +21 -0
  128. package/dist/edit/pptx/image-table-ops.d.ts +5 -0
  129. package/dist/edit/pptx/image-table-ops.js +233 -0
  130. package/dist/edit/pptx/model.d.ts +69 -0
  131. package/dist/edit/pptx/model.js +170 -0
  132. package/dist/edit/pptx/operations.d.ts +50 -0
  133. package/dist/edit/pptx/operations.js +11 -0
  134. package/dist/edit/pptx/provider.d.ts +17 -0
  135. package/dist/edit/pptx/provider.js +40 -0
  136. package/dist/edit/pptx/schemas.d.ts +6 -0
  137. package/dist/edit/pptx/schemas.js +220 -0
  138. package/dist/edit/pptx/session.d.ts +44 -0
  139. package/dist/edit/pptx/session.js +114 -0
  140. package/dist/edit/pptx/shape-ops.d.ts +10 -0
  141. package/dist/edit/pptx/shape-ops.js +486 -0
  142. package/dist/edit/pptx/slide-ops.d.ts +6 -0
  143. package/dist/edit/pptx/slide-ops.js +353 -0
  144. package/dist/edit/pptx/style.d.ts +31 -0
  145. package/dist/edit/pptx/style.js +181 -0
  146. package/dist/edit/pptx/text-ops.d.ts +13 -0
  147. package/dist/edit/pptx/text-ops.js +267 -0
  148. package/dist/edit/pptx/text-write.d.ts +67 -0
  149. package/dist/edit/pptx/text-write.js +293 -0
  150. package/dist/edit/pptx/text.d.ts +41 -0
  151. package/dist/edit/pptx/text.js +99 -0
  152. package/dist/edit/pptx/types.d.ts +232 -0
  153. package/dist/edit/pptx/types.js +1 -0
  154. package/dist/edit/schema.d.ts +11 -0
  155. package/dist/edit/schema.js +275 -0
  156. package/dist/edit/session.d.ts +66 -0
  157. package/dist/edit/session.js +701 -0
  158. package/dist/edit/sessions.d.ts +8 -0
  159. package/dist/edit/sessions.js +1 -0
  160. package/dist/edit/types.d.ts +276 -0
  161. package/dist/edit/types.js +1 -0
  162. package/dist/edit/worker-engine.d.ts +31 -0
  163. package/dist/edit/worker-engine.js +91 -0
  164. package/dist/fonts/THIRD_PARTY_NOTICES.md +3 -0
  165. package/dist/fonts/manifest.json +6 -1
  166. package/dist/fonts/noto-sans-latin-cyrillic.ttf +0 -0
  167. package/dist/fonts.d.ts +2 -0
  168. package/dist/fonts.js +4 -0
  169. package/dist/headless.d.ts +4 -0
  170. package/dist/headless.js +4 -0
  171. package/dist/index.d.ts +5 -0
  172. package/dist/index.js +5 -0
  173. package/dist/limits.js +3 -0
  174. package/dist/ooxml-edit-worker.d.ts +1 -0
  175. package/dist/ooxml-edit-worker.js +8 -0
  176. package/dist/pdf-edit-worker.d.ts +1 -0
  177. package/dist/pdf-edit-worker.js +35 -0
  178. package/dist/spreadsheet-viewport.d.ts +5 -0
  179. package/dist/spreadsheet-viewport.js +11 -0
  180. package/dist/ui.js +7 -0
  181. package/dist/viewer.d.ts +6 -0
  182. package/dist/viewer.js +293 -19
  183. package/dist/viewport.d.ts +13 -0
  184. package/dist/viewport.js +126 -1
  185. package/dist/worker-protocol.d.ts +39 -2
  186. package/dist/workers/ooxml-edit-worker.js +8671 -0
  187. package/dist/workers/pdf-edit-worker.js +10844 -0
  188. package/package.json +3 -3
@@ -0,0 +1,45 @@
1
+ import type { PageRect } from "../../types.js";
2
+ import type { PdfElement, PdfTextStyle } from "../types.js";
3
+ import { type PageGeometry } from "./geometry.js";
4
+ import type { Pdfium } from "./pdfium.js";
5
+ /** FPDFPageObj_GetType values. */
6
+ export declare const OBJECT_TEXT = 1;
7
+ export declare const OBJECT_PATH = 2;
8
+ export declare const OBJECT_IMAGE = 3;
9
+ /** Marked-content tag and parameter that carry web-doc's own elements. */
10
+ export declare const MARK_NAME = "WebDoc";
11
+ export declare const MARK_PARAM = "webdoc";
12
+ /** What a `WebDoc` mark says about the objects it tags. */
13
+ export interface MarkParams {
14
+ readonly kind: "textBox" | "table";
15
+ readonly id: string;
16
+ readonly [key: string]: unknown;
17
+ }
18
+ /** One PDFium page object with what the model knows about it. */
19
+ export interface ObjectRecord {
20
+ /** Element id of a plain object; objects of a marked group share their group id. */
21
+ readonly id: string;
22
+ readonly type: number;
23
+ readonly mark?: MarkParams;
24
+ }
25
+ /**
26
+ * Reads the objects of a loaded page in drawing order and turns them into
27
+ * elements: plain objects become one element each, objects tagged with the
28
+ * same `WebDoc` mark become one composite element.
29
+ */
30
+ export declare function scanPage(pdfium: Pdfium, page: number, textPage: number, pageIndex: number, geometry: PageGeometry, records: readonly ObjectRecord[]): {
31
+ readonly elements: PdfElement[];
32
+ readonly byObject: Map<number, string>;
33
+ };
34
+ /** The `WebDoc` mark of an object, if it carries a valid one. */
35
+ export declare function readMark(pdfium: Pdfium, object: number): MarkParams | undefined;
36
+ /**
37
+ * Whether a marked group still looks like what its inputs describe. Another
38
+ * tool may have moved, resized or retyped the objects while keeping the mark;
39
+ * rebuilding from stale inputs would then undo that edit silently, so such a
40
+ * group is listed as plain objects instead.
41
+ */
42
+ export declare function markIsFresh(pdfium: Pdfium, page: number, textPage: number, geometry: PageGeometry, mark: MarkParams, indexes: readonly number[]): boolean;
43
+ export declare function objectBounds(pdfium: Pdfium, object: number, geometry: PageGeometry): PageRect | undefined;
44
+ export declare function textStyle(pdfium: Pdfium, object: number): PdfTextStyle;
45
+ export declare function hex(rgba: readonly number[]): string;
@@ -0,0 +1,313 @@
1
+ import { validateSchema } from "../../schema.js";
2
+ import { tableMarkSchema, tableMemberMarkSchema, textBoxMarkSchema, } from "../schemas.js";
3
+ import { round, roundRect, unionRects, userRectToPage, } from "./geometry.js";
4
+ /** FPDFPageObj_GetType values. */
5
+ export const OBJECT_TEXT = 1;
6
+ export const OBJECT_PATH = 2;
7
+ export const OBJECT_IMAGE = 3;
8
+ /** Marked-content tag and parameter that carry web-doc's own elements. */
9
+ export const MARK_NAME = "WebDoc";
10
+ export const MARK_PARAM = "webdoc";
11
+ /** Font descriptor flag bits (PDF 32000-1 table 123). */
12
+ const FLAG_ITALIC = 1 << 6;
13
+ const FLAG_FORCE_BOLD = 1 << 18;
14
+ /**
15
+ * Reads the objects of a loaded page in drawing order and turns them into
16
+ * elements: plain objects become one element each, objects tagged with the
17
+ * same `WebDoc` mark become one composite element.
18
+ */
19
+ export function scanPage(pdfium, page, textPage, pageIndex, geometry, records) {
20
+ const { lib } = pdfium;
21
+ const byObject = new Map();
22
+ const elements = [];
23
+ const groups = new Map();
24
+ const count = Math.min(lib.FPDFPage_CountObjects(page), records.length);
25
+ for (let index = 0; index < count; index += 1) {
26
+ const object = lib.FPDFPage_GetObject(page, index);
27
+ const record = records[index];
28
+ byObject.set(object, record.id);
29
+ const element = plainElement(pdfium, object, textPage, record, pageIndex, geometry);
30
+ if (!element)
31
+ continue;
32
+ if (record.mark) {
33
+ const members = groups.get(record.id);
34
+ if (members)
35
+ members.push(element);
36
+ else {
37
+ groups.set(record.id, [element]);
38
+ elements.push(element); // placeholder keeps the group's z-order
39
+ }
40
+ }
41
+ else
42
+ elements.push(element);
43
+ }
44
+ return {
45
+ elements: elements.map((element) => {
46
+ const members = groups.get(element.id);
47
+ const record = members && records.find((entry) => entry.id === element.id);
48
+ return members && record?.mark
49
+ ? compositeElement(record.mark, members, pageIndex)
50
+ : element;
51
+ }),
52
+ byObject,
53
+ };
54
+ }
55
+ /** The `WebDoc` mark of an object, if it carries a valid one. */
56
+ export function readMark(pdfium, object) {
57
+ const { lib } = pdfium;
58
+ for (let index = 0; index < lib.FPDFPageObj_CountMarks(object); index += 1) {
59
+ const mark = lib.FPDFPageObj_GetMark(object, index);
60
+ const name = pdfium.readWideStringOut((buffer, bytes, out) => lib.FPDFPageObjMark_GetName(mark, buffer, bytes, out));
61
+ if (name !== MARK_NAME)
62
+ continue;
63
+ const raw = pdfium.readWideStringOut((buffer, bytes, out) => lib.FPDFPageObjMark_GetParamStringValue(mark, MARK_PARAM, buffer, bytes, out));
64
+ try {
65
+ const params = JSON.parse(raw);
66
+ // Marks come from files, so they pass the same checks as operations.
67
+ if (params && typeof params === "object" && isValidMark(params))
68
+ return params;
69
+ }
70
+ catch {
71
+ // A foreign or damaged mark leaves the object a plain element.
72
+ }
73
+ }
74
+ return undefined;
75
+ }
76
+ function isValidMark(params) {
77
+ switch (params.kind) {
78
+ case "textBox":
79
+ return validateSchema(params, textBoxMarkSchema, 0).length === 0;
80
+ case "table":
81
+ return (validateSchema(params, tableMarkSchema, 0).length === 0 ||
82
+ validateSchema(params, tableMemberMarkSchema, 0).length === 0);
83
+ default:
84
+ return false;
85
+ }
86
+ }
87
+ /**
88
+ * Whether a marked group still looks like what its inputs describe. Another
89
+ * tool may have moved, resized or retyped the objects while keeping the mark;
90
+ * rebuilding from stale inputs would then undo that edit silently, so such a
91
+ * group is listed as plain objects instead.
92
+ */
93
+ export function markIsFresh(pdfium, page, textPage, geometry, mark, indexes) {
94
+ const { lib } = pdfium;
95
+ const objects = indexes.map((index) => lib.FPDFPage_GetObject(page, index));
96
+ const bounds = objects
97
+ .map((object) => objectBounds(pdfium, object, geometry))
98
+ .filter((rect) => rect !== undefined);
99
+ if (bounds.length === 0)
100
+ return false;
101
+ const union = unionRects(bounds);
102
+ const drawn = normalizeText(objects
103
+ .filter((object) => lib.FPDFPageObj_GetType(object) === OBJECT_TEXT)
104
+ .map((object) => textOf(pdfium, object, textPage))
105
+ .join(" "));
106
+ const tolerance = 2;
107
+ if (mark.kind === "textBox") {
108
+ const rect = mark.rect;
109
+ const text = mark.text;
110
+ const style = mark.style;
111
+ return (drawn === normalizeText(text) &&
112
+ union.x >= rect.x - tolerance &&
113
+ union.x + union.width <= rect.x + rect.width + tolerance &&
114
+ // The first line's ink starts inside the box's first line band; the
115
+ // bottom is not checked because overflowing text runs past the box.
116
+ union.y >= rect.y - tolerance &&
117
+ union.y <= rect.y + style.fontSize * style.lineHeight + tolerance);
118
+ }
119
+ const at = mark.at;
120
+ const widths = mark.columnWidths;
121
+ const style = mark.style;
122
+ const rows = mark.rows;
123
+ const slack = tolerance + style.borderWidth;
124
+ return (drawn === normalizeText(rows.flat().join(" ")) &&
125
+ Math.abs(union.x - at.x) <= slack &&
126
+ Math.abs(union.y - at.y) <= slack &&
127
+ Math.abs(union.width - widths.reduce((sum, w) => sum + w, 0)) <= 2 * slack);
128
+ }
129
+ function normalizeText(text) {
130
+ return text.replaceAll(/\s+/g, " ").trim();
131
+ }
132
+ export function objectBounds(pdfium, object, geometry) {
133
+ const bounds = pdfium.readNumbers(4, "float", ([left, bottom, right, top]) => pdfium.lib.FPDFPageObj_GetBounds(object, left, bottom, right, top));
134
+ if (!bounds)
135
+ return undefined;
136
+ const [left, bottom, right, top] = bounds;
137
+ return roundRect(userRectToPage(geometry, left, bottom, right, top));
138
+ }
139
+ function plainElement(pdfium, object, textPage, record, pageIndex, geometry) {
140
+ const bounds = objectBounds(pdfium, object, geometry);
141
+ if (!bounds)
142
+ return undefined;
143
+ const rotation = objectRotation(pdfium, object, geometry);
144
+ const base = {
145
+ id: record.id,
146
+ pageIndex,
147
+ bounds,
148
+ ...(rotation === 0 ? {} : { rotation }),
149
+ };
150
+ switch (record.type) {
151
+ case OBJECT_TEXT:
152
+ return {
153
+ ...base,
154
+ kind: "text",
155
+ text: textOf(pdfium, object, textPage),
156
+ textStyle: textStyle(pdfium, object),
157
+ operations: TEXT_OPERATIONS,
158
+ };
159
+ case OBJECT_IMAGE:
160
+ return { ...base, kind: "image", operations: TRANSFORM_OPERATIONS };
161
+ case OBJECT_PATH:
162
+ return {
163
+ ...base,
164
+ kind: "shape",
165
+ shapeStyle: shapeStyle(pdfium, object),
166
+ operations: SHAPE_OPERATIONS,
167
+ };
168
+ default:
169
+ return { ...base, kind: "other", operations: TRANSFORM_OPERATIONS };
170
+ }
171
+ }
172
+ function compositeElement(mark, members, pageIndex) {
173
+ const bounds = roundRect(unionRects(members.map((member) => member.bounds)));
174
+ const text = members
175
+ .filter((member) => member.kind === "text" && member.text)
176
+ .map((member) => member.text)
177
+ .join("\n");
178
+ if (mark.kind === "table") {
179
+ // The head mark comes first in drawing order and carries the inputs.
180
+ const spec = mark;
181
+ const cells = spec.rows.map((row) => row.join("\t")).join("\n");
182
+ return {
183
+ id: mark.id,
184
+ kind: "table",
185
+ pageIndex,
186
+ bounds,
187
+ ...(cells.trim() ? { text: cells } : {}),
188
+ shapeStyle: {
189
+ stroke: {
190
+ color: spec.style.borderColor,
191
+ width: spec.style.borderWidth,
192
+ },
193
+ ...(spec.style.headerFill
194
+ ? { fill: { color: spec.style.headerFill } }
195
+ : {}),
196
+ },
197
+ table: { rows: spec.rows },
198
+ operations: TABLE_OPERATIONS,
199
+ };
200
+ }
201
+ const style = members.find((member) => member.textStyle)?.textStyle;
202
+ return {
203
+ id: mark.id,
204
+ kind: "textBox",
205
+ pageIndex,
206
+ bounds,
207
+ text: typeof mark.text === "string" ? mark.text : text,
208
+ ...(style ? { textStyle: style } : {}),
209
+ operations: TEXT_BOX_OPERATIONS,
210
+ };
211
+ }
212
+ const TRANSFORM_OPERATIONS = Object.freeze([
213
+ "moveElement",
214
+ "resizeElement",
215
+ "deleteElement",
216
+ ]);
217
+ const TEXT_OPERATIONS = Object.freeze([
218
+ "replaceText",
219
+ "setTextStyle",
220
+ ...TRANSFORM_OPERATIONS,
221
+ ]);
222
+ const TEXT_BOX_OPERATIONS = TEXT_OPERATIONS;
223
+ const SHAPE_OPERATIONS = Object.freeze([
224
+ "setShapeStyle",
225
+ ...TRANSFORM_OPERATIONS,
226
+ ]);
227
+ const TABLE_OPERATIONS = Object.freeze([
228
+ "setTableCell",
229
+ "moveElement",
230
+ "deleteElement",
231
+ ]);
232
+ function textOf(pdfium, object, textPage) {
233
+ return pdfium.readWideString((buffer, bytes) => pdfium.lib.FPDFTextObj_GetText(object, textPage, buffer, bytes));
234
+ }
235
+ export function textStyle(pdfium, object) {
236
+ const { lib } = pdfium;
237
+ const font = lib.FPDFTextObj_GetFont(object);
238
+ const baseName = pdfium.readUtf8String((buffer, bytes) => lib.FPDFFont_GetBaseFontName(font, buffer, bytes));
239
+ // PDFium reports the family of whatever font it loaded, which for a
240
+ // non-embedded font is its own substitute; the document's name is the
241
+ // one a host wants to show.
242
+ const family = lib.FPDFFont_GetIsEmbedded(font)
243
+ ? pdfium.readUtf8String((buffer, bytes) => lib.FPDFFont_GetFamilyName(font, buffer, bytes)) || declaredFamily(baseName)
244
+ : declaredFamily(baseName);
245
+ const flags = lib.FPDFFont_GetFlags(font);
246
+ const weight = lib.FPDFFont_GetWeight(font);
247
+ const size = pdfium.readNumbers(1, "float", ([pointer]) => lib.FPDFTextObj_GetFontSize(object, pointer))?.[0] ?? 0;
248
+ return {
249
+ fontFamily: family,
250
+ fontSize: round(size),
251
+ bold: weight >= 600 ||
252
+ (flags & FLAG_FORCE_BOLD) !== 0 ||
253
+ /bold|black|heavy/i.test(baseName),
254
+ italic: (flags & FLAG_ITALIC) !== 0 || /italic|oblique/i.test(baseName),
255
+ color: fillColor(pdfium, object) ?? "#000000",
256
+ };
257
+ }
258
+ /** "ABCDEF+Helvetica-BoldOblique" → "Helvetica"; "Arial,Bold" → "Arial". */
259
+ function declaredFamily(baseName) {
260
+ const name = baseName.replace(/^[A-Z]{6}\+/, "");
261
+ const separator = name.search(/[-,]/);
262
+ if (separator < 0)
263
+ return name;
264
+ const suffix = name.slice(separator + 1);
265
+ return /^(bold|italic|oblique|regular|roman|light|medium|black|heavy|mt|ps|,)*$/i.test(suffix.replaceAll(/[-,]/g, ""))
266
+ ? name.slice(0, separator)
267
+ : name;
268
+ }
269
+ function shapeStyle(pdfium, object) {
270
+ const { lib } = pdfium;
271
+ const mode = pdfium.readNumbers(2, "i32", ([fill, stroke]) => lib.FPDFPath_GetDrawMode(object, fill, stroke));
272
+ const [fillMode, stroked] = mode ?? [0, 0];
273
+ const width = pdfium.readNumbers(1, "float", ([pointer]) => lib.FPDFPageObj_GetStrokeWidth(object, pointer))?.[0] ?? 1;
274
+ return {
275
+ ...(stroked
276
+ ? {
277
+ stroke: {
278
+ color: strokeColor(pdfium, object) ?? "#000000",
279
+ width: round(width),
280
+ },
281
+ }
282
+ : {}),
283
+ ...(fillMode
284
+ ? { fill: { color: fillColor(pdfium, object) ?? "#000000" } }
285
+ : {}),
286
+ };
287
+ }
288
+ function fillColor(pdfium, object) {
289
+ const rgba = pdfium.readNumbers(4, "i32", ([r, g, b, a]) => pdfium.lib.FPDFPageObj_GetFillColor(object, r, g, b, a));
290
+ return rgba ? hex(rgba) : undefined;
291
+ }
292
+ function strokeColor(pdfium, object) {
293
+ const rgba = pdfium.readNumbers(4, "i32", ([r, g, b, a]) => pdfium.lib.FPDFPageObj_GetStrokeColor(object, r, g, b, a));
294
+ return rgba ? hex(rgba) : undefined;
295
+ }
296
+ export function hex(rgba) {
297
+ return `#${rgba
298
+ .slice(0, 3)
299
+ .map((channel) => Math.max(0, Math.min(255, channel)).toString(16).padStart(2, "0"))
300
+ .join("")}`;
301
+ }
302
+ /** Clockwise rotation in page space, in whole degrees, 0 when upright. */
303
+ function objectRotation(pdfium, object, geometry) {
304
+ const matrix = pdfium.readNumbers(6, "float", ([pointer]) => pdfium.lib.FPDFPageObj_GetMatrix(object, pointer));
305
+ if (!matrix)
306
+ return 0;
307
+ const [a, b] = matrix;
308
+ // User space is y-up, so a positive angle there is counter-clockwise on
309
+ // the displayed page; the page's own quarter turns add to it.
310
+ const degrees = (-Math.atan2(b, a) * 180) / Math.PI + geometry.rotation * 90;
311
+ const normalized = ((Math.round(degrees) % 360) + 360) % 360;
312
+ return normalized;
313
+ }
@@ -0,0 +1,4 @@
1
+ import type { ReplaceTextOperation, SetTextStyleOperation } from "../types.js";
2
+ import type { OperationHandler } from "./operations.js";
3
+ export declare const replaceText: OperationHandler<ReplaceTextOperation>;
4
+ export declare const setTextStyle: OperationHandler<SetTextStyleOperation>;