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.
- package/THIRD_PARTY_NOTICES.md +26 -1
- package/dist/adapters/docx-images.d.ts +13 -4
- package/dist/adapters/docx-images.js +3 -0
- package/dist/adapters/docx-paragraphs.d.ts +65 -0
- package/dist/adapters/docx-paragraphs.js +165 -0
- package/dist/adapters/docx-prepass.d.ts +17 -0
- package/dist/adapters/docx-prepass.js +230 -0
- package/dist/adapters/office.d.ts +31 -5
- package/dist/adapters/office.js +60 -29
- package/dist/adapters/pdf.d.ts +9 -0
- package/dist/adapters/pdf.js +10 -0
- package/dist/assets/pdfium/pdfium.wasm +0 -0
- package/dist/contracts.d.ts +47 -2
- package/dist/edit/assets.d.ts +23 -0
- package/dist/edit/assets.js +75 -0
- package/dist/edit/docx/elements.d.ts +6 -0
- package/dist/edit/docx/elements.js +90 -0
- package/dist/edit/docx/engine.d.ts +51 -0
- package/dist/edit/docx/engine.js +473 -0
- package/dist/edit/docx/handlers.d.ts +3 -0
- package/dist/edit/docx/handlers.js +15 -0
- package/dist/edit/docx/ids.d.ts +45 -0
- package/dist/edit/docx/ids.js +101 -0
- package/dist/edit/docx/model.d.ts +92 -0
- package/dist/edit/docx/model.js +339 -0
- package/dist/edit/docx/operations.d.ts +37 -0
- package/dist/edit/docx/operations.js +3 -0
- package/dist/edit/docx/provider.d.ts +13 -0
- package/dist/edit/docx/provider.js +34 -0
- package/dist/edit/docx/schemas.d.ts +6 -0
- package/dist/edit/docx/schemas.js +192 -0
- package/dist/edit/docx/session.d.ts +37 -0
- package/dist/edit/docx/session.js +426 -0
- package/dist/edit/docx/structure-ops.d.ts +6 -0
- package/dist/edit/docx/structure-ops.js +494 -0
- package/dist/edit/docx/style.d.ts +45 -0
- package/dist/edit/docx/style.js +375 -0
- package/dist/edit/docx/table-ops.d.ts +4 -0
- package/dist/edit/docx/table-ops.js +219 -0
- package/dist/edit/docx/text-ops.d.ts +18 -0
- package/dist/edit/docx/text-ops.js +458 -0
- package/dist/edit/docx/text.d.ts +34 -0
- package/dist/edit/docx/text.js +263 -0
- package/dist/edit/docx/types.d.ts +191 -0
- package/dist/edit/docx/types.js +1 -0
- package/dist/edit/docx/write.d.ts +50 -0
- package/dist/edit/docx/write.js +352 -0
- package/dist/edit/engine.d.ts +117 -0
- package/dist/edit/engine.js +1 -0
- package/dist/edit/history.d.ts +53 -0
- package/dist/edit/history.js +101 -0
- package/dist/edit/ooxml/names.d.ts +20 -0
- package/dist/edit/ooxml/names.js +61 -0
- package/dist/edit/ooxml/opc.d.ts +50 -0
- package/dist/edit/ooxml/opc.js +150 -0
- package/dist/edit/ooxml/package.d.ts +82 -0
- package/dist/edit/ooxml/package.js +233 -0
- package/dist/edit/ooxml/patch.d.ts +51 -0
- package/dist/edit/ooxml/patch.js +250 -0
- package/dist/edit/ooxml/transaction.d.ts +39 -0
- package/dist/edit/ooxml/transaction.js +316 -0
- package/dist/edit/ooxml/worker.d.ts +8 -0
- package/dist/edit/ooxml/worker.js +12 -0
- package/dist/edit/ooxml/writer.d.ts +21 -0
- package/dist/edit/ooxml/writer.js +187 -0
- package/dist/edit/ooxml/xml.d.ts +74 -0
- package/dist/edit/ooxml/xml.js +451 -0
- package/dist/edit/ooxml/zip.d.ts +54 -0
- package/dist/edit/ooxml/zip.js +280 -0
- package/dist/edit/operations.d.ts +19 -0
- package/dist/edit/operations.js +137 -0
- package/dist/edit/pdf/engine/compact.d.ts +6 -0
- package/dist/edit/pdf/engine/compact.js +442 -0
- package/dist/edit/pdf/engine/document.d.ts +95 -0
- package/dist/edit/pdf/engine/document.js +868 -0
- package/dist/edit/pdf/engine/elements.d.ts +45 -0
- package/dist/edit/pdf/engine/elements.js +313 -0
- package/dist/edit/pdf/engine/existing-text.d.ts +4 -0
- package/dist/edit/pdf/engine/existing-text.js +424 -0
- package/dist/edit/pdf/engine/fonts.d.ts +78 -0
- package/dist/edit/pdf/engine/fonts.js +466 -0
- package/dist/edit/pdf/engine/geometry.d.ts +36 -0
- package/dist/edit/pdf/engine/geometry.js +97 -0
- package/dist/edit/pdf/engine/handler.d.ts +21 -0
- package/dist/edit/pdf/engine/handler.js +108 -0
- package/dist/edit/pdf/engine/images.d.ts +33 -0
- package/dist/edit/pdf/engine/images.js +188 -0
- package/dist/edit/pdf/engine/layout.d.ts +23 -0
- package/dist/edit/pdf/engine/layout.js +177 -0
- package/dist/edit/pdf/engine/operations.d.ts +67 -0
- package/dist/edit/pdf/engine/operations.js +3 -0
- package/dist/edit/pdf/engine/pages.d.ts +6 -0
- package/dist/edit/pdf/engine/pages.js +98 -0
- package/dist/edit/pdf/engine/pdfium.d.ts +195 -0
- package/dist/edit/pdf/engine/pdfium.js +249 -0
- package/dist/edit/pdf/engine/shapes.d.ts +4 -0
- package/dist/edit/pdf/engine/shapes.js +183 -0
- package/dist/edit/pdf/engine/tables.d.ts +44 -0
- package/dist/edit/pdf/engine/tables.js +315 -0
- package/dist/edit/pdf/engine/text-box.d.ts +71 -0
- package/dist/edit/pdf/engine/text-box.js +317 -0
- package/dist/edit/pdf/engine/text-layout.d.ts +28 -0
- package/dist/edit/pdf/engine/text-layout.js +67 -0
- package/dist/edit/pdf/engine/transform.d.ts +5 -0
- package/dist/edit/pdf/engine/transform.js +137 -0
- package/dist/edit/pdf/provider.d.ts +35 -0
- package/dist/edit/pdf/provider.js +92 -0
- package/dist/edit/pdf/range-map.d.ts +14 -0
- package/dist/edit/pdf/range-map.js +107 -0
- package/dist/edit/pdf/schemas.d.ts +11 -0
- package/dist/edit/pdf/schemas.js +291 -0
- package/dist/edit/pdf/selection.d.ts +5 -0
- package/dist/edit/pdf/selection.js +152 -0
- package/dist/edit/pdf/session.d.ts +53 -0
- package/dist/edit/pdf/session.js +273 -0
- package/dist/edit/pdf/types.d.ts +336 -0
- package/dist/edit/pdf/types.js +1 -0
- package/dist/edit/pptx/elements.d.ts +74 -0
- package/dist/edit/pptx/elements.js +301 -0
- package/dist/edit/pptx/engine.d.ts +37 -0
- package/dist/edit/pptx/engine.js +466 -0
- package/dist/edit/pptx/geometry.d.ts +72 -0
- package/dist/edit/pptx/geometry.js +193 -0
- package/dist/edit/pptx/handler.d.ts +7 -0
- package/dist/edit/pptx/handler.js +91 -0
- package/dist/edit/pptx/handlers.d.ts +3 -0
- package/dist/edit/pptx/handlers.js +21 -0
- package/dist/edit/pptx/image-table-ops.d.ts +5 -0
- package/dist/edit/pptx/image-table-ops.js +233 -0
- package/dist/edit/pptx/model.d.ts +69 -0
- package/dist/edit/pptx/model.js +170 -0
- package/dist/edit/pptx/operations.d.ts +50 -0
- package/dist/edit/pptx/operations.js +11 -0
- package/dist/edit/pptx/provider.d.ts +17 -0
- package/dist/edit/pptx/provider.js +40 -0
- package/dist/edit/pptx/schemas.d.ts +6 -0
- package/dist/edit/pptx/schemas.js +220 -0
- package/dist/edit/pptx/session.d.ts +44 -0
- package/dist/edit/pptx/session.js +114 -0
- package/dist/edit/pptx/shape-ops.d.ts +10 -0
- package/dist/edit/pptx/shape-ops.js +486 -0
- package/dist/edit/pptx/slide-ops.d.ts +6 -0
- package/dist/edit/pptx/slide-ops.js +353 -0
- package/dist/edit/pptx/style.d.ts +31 -0
- package/dist/edit/pptx/style.js +181 -0
- package/dist/edit/pptx/text-ops.d.ts +13 -0
- package/dist/edit/pptx/text-ops.js +267 -0
- package/dist/edit/pptx/text-write.d.ts +67 -0
- package/dist/edit/pptx/text-write.js +293 -0
- package/dist/edit/pptx/text.d.ts +41 -0
- package/dist/edit/pptx/text.js +99 -0
- package/dist/edit/pptx/types.d.ts +232 -0
- package/dist/edit/pptx/types.js +1 -0
- package/dist/edit/schema.d.ts +11 -0
- package/dist/edit/schema.js +275 -0
- package/dist/edit/session.d.ts +66 -0
- package/dist/edit/session.js +701 -0
- package/dist/edit/sessions.d.ts +8 -0
- package/dist/edit/sessions.js +1 -0
- package/dist/edit/types.d.ts +276 -0
- package/dist/edit/types.js +1 -0
- package/dist/edit/worker-engine.d.ts +31 -0
- package/dist/edit/worker-engine.js +91 -0
- package/dist/fonts/THIRD_PARTY_NOTICES.md +3 -0
- package/dist/fonts/manifest.json +6 -1
- package/dist/fonts/noto-sans-latin-cyrillic.ttf +0 -0
- package/dist/fonts.d.ts +2 -0
- package/dist/fonts.js +4 -0
- package/dist/headless.d.ts +4 -0
- package/dist/headless.js +4 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/limits.js +3 -0
- package/dist/ooxml-edit-worker.d.ts +1 -0
- package/dist/ooxml-edit-worker.js +8 -0
- package/dist/pdf-edit-worker.d.ts +1 -0
- package/dist/pdf-edit-worker.js +35 -0
- package/dist/spreadsheet-viewport.d.ts +5 -0
- package/dist/spreadsheet-viewport.js +11 -0
- package/dist/ui.js +7 -0
- package/dist/viewer.d.ts +6 -0
- package/dist/viewer.js +293 -19
- package/dist/viewport.d.ts +13 -0
- package/dist/viewport.js +126 -1
- package/dist/worker-protocol.d.ts +39 -2
- package/dist/workers/ooxml-edit-worker.js +8671 -0
- package/dist/workers/pdf-edit-worker.js +10844 -0
- package/package.json +3 -3
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { MARK_NAME, MARK_PARAM, OBJECT_TEXT, } from "./elements.js";
|
|
2
|
+
import { displayedSize, pageToUser } from "./geometry.js";
|
|
3
|
+
import { layoutText } from "./text-layout.js";
|
|
4
|
+
import { tableSpecOf, tableText } from "./tables.js";
|
|
5
|
+
export const insertTextBox = {
|
|
6
|
+
validate(operation, context, issue) {
|
|
7
|
+
if (operation.pageIndex >= context.pageCount) {
|
|
8
|
+
issue("/pageIndex", "unknown-target", `No page ${operation.pageIndex}`);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
validateRect(operation.rect, context.geometry(operation.pageIndex), issue);
|
|
12
|
+
validateFont(fontRequest(resolveStyle(operation.style ?? {}), operation.text), context, issue);
|
|
13
|
+
},
|
|
14
|
+
apply(operation, context) {
|
|
15
|
+
const id = context.newId(operation.pageIndex);
|
|
16
|
+
const spec = {
|
|
17
|
+
kind: "textBox",
|
|
18
|
+
id,
|
|
19
|
+
rect: operation.rect,
|
|
20
|
+
text: operation.text,
|
|
21
|
+
style: resolveStyle(operation.style ?? {}),
|
|
22
|
+
};
|
|
23
|
+
const drawn = drawTextBox(context, operation.pageIndex, spec);
|
|
24
|
+
return {
|
|
25
|
+
createdIds: [id],
|
|
26
|
+
changedPages: [operation.pageIndex],
|
|
27
|
+
warnings: warningsFor(drawn, id),
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
/** `replaceText` for a text box: a rebuild from its inputs with new text. */
|
|
32
|
+
export const textBoxReplaceText = {
|
|
33
|
+
validate(operation, context, issue) {
|
|
34
|
+
const target = textBoxTarget(operation.target, context, issue);
|
|
35
|
+
if (!target)
|
|
36
|
+
return;
|
|
37
|
+
validateFont(fontRequest(target.spec.style, operation.text), context, issue);
|
|
38
|
+
},
|
|
39
|
+
apply(operation, context) {
|
|
40
|
+
const { location, spec } = textBoxTarget(operation.target, context);
|
|
41
|
+
return changed(location, rebuildTextBox(context, location, { ...spec, text: operation.text }));
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
/** `setTextStyle` for a text box: a rebuild with the merged style. */
|
|
45
|
+
export const textBoxSetTextStyle = {
|
|
46
|
+
validate(operation, context, issue) {
|
|
47
|
+
const target = textBoxTarget(operation.target, context, issue);
|
|
48
|
+
if (!target)
|
|
49
|
+
return;
|
|
50
|
+
const style = { ...target.spec.style, ...definedFields(operation.style) };
|
|
51
|
+
validateFont(fontRequest(style, target.spec.text), context, issue);
|
|
52
|
+
},
|
|
53
|
+
apply(operation, context) {
|
|
54
|
+
const { location, spec } = textBoxTarget(operation.target, context);
|
|
55
|
+
return changed(location, rebuildTextBox(context, location, {
|
|
56
|
+
...spec,
|
|
57
|
+
style: { ...spec.style, ...definedFields(operation.style) },
|
|
58
|
+
}));
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Lays the box out and inserts one text object per line, appended to the
|
|
63
|
+
* page or at `insertAt` in drawing order.
|
|
64
|
+
*/
|
|
65
|
+
export function drawTextBox(context, pageIndex, spec, insertAt) {
|
|
66
|
+
const { pdfium, measurer } = context;
|
|
67
|
+
const { lib } = pdfium;
|
|
68
|
+
const font = context.fonts.resolve(pdfium, context.document, fontRequest(spec.style, spec.text));
|
|
69
|
+
const { ascent } = measurer.metrics(font.handle, spec.style.fontSize);
|
|
70
|
+
const layout = layoutText({
|
|
71
|
+
text: spec.text,
|
|
72
|
+
width: spec.rect.width,
|
|
73
|
+
height: spec.rect.height,
|
|
74
|
+
fontSize: spec.style.fontSize,
|
|
75
|
+
lineHeight: spec.style.lineHeight,
|
|
76
|
+
align: spec.style.align,
|
|
77
|
+
ascent,
|
|
78
|
+
advance: (text) => measurer.advance(font.handle, spec.style.fontSize, text),
|
|
79
|
+
});
|
|
80
|
+
const geometry = context.geometry(pageIndex);
|
|
81
|
+
const [r, g, b] = parseColor(spec.style.color);
|
|
82
|
+
const params = JSON.stringify(spec);
|
|
83
|
+
context.withPage(pageIndex, (page) => {
|
|
84
|
+
const records = [];
|
|
85
|
+
let index = insertAt ?? -1;
|
|
86
|
+
for (const line of layout.lines) {
|
|
87
|
+
const object = lib.FPDFPageObj_CreateTextObj(context.document, font.handle, spec.style.fontSize);
|
|
88
|
+
setText(pdfium, object, line.text);
|
|
89
|
+
lib.FPDFPageObj_SetFillColor(object, r, g, b, 255);
|
|
90
|
+
placeUpright(pdfium, object, geometry, spec.rect.x + line.x, spec.rect.y + line.baseline);
|
|
91
|
+
const mark = lib.FPDFPageObj_AddMark(object, MARK_NAME);
|
|
92
|
+
lib.FPDFPageObjMark_SetStringParam(context.document, object, mark, MARK_PARAM, params);
|
|
93
|
+
if (insertAt === undefined)
|
|
94
|
+
lib.FPDFPage_InsertObject(page, object);
|
|
95
|
+
else
|
|
96
|
+
lib.FPDFPage_InsertObjectAtIndex(page, object, index++);
|
|
97
|
+
records.push({ id: spec.id, type: OBJECT_TEXT, mark: spec });
|
|
98
|
+
}
|
|
99
|
+
if (insertAt === undefined)
|
|
100
|
+
context.appendObjects(pageIndex, records);
|
|
101
|
+
else
|
|
102
|
+
context.spliceObjects(pageIndex, insertAt, 0, records);
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
overflow: layout.overflow,
|
|
106
|
+
...(font.substitution ? { substitution: font.substitution } : {}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Replaces a text box's objects with ones drawn from `spec`, at the same
|
|
111
|
+
* place in the drawing order so nothing else changes its stacking.
|
|
112
|
+
*/
|
|
113
|
+
export function rebuildTextBox(context, location, spec) {
|
|
114
|
+
const first = removeObjects(context, location);
|
|
115
|
+
return drawTextBox(context, location.pageIndex, spec, first);
|
|
116
|
+
}
|
|
117
|
+
/** Removes an element's objects from the page; returns where they started. */
|
|
118
|
+
export function removeObjects(context, location) {
|
|
119
|
+
const { lib } = context.pdfium;
|
|
120
|
+
const first = location.indexes[0];
|
|
121
|
+
context.withPage(location.pageIndex, (page) => {
|
|
122
|
+
// Highest first, so earlier indexes stay valid while removing.
|
|
123
|
+
for (const index of [...location.indexes].reverse()) {
|
|
124
|
+
const object = lib.FPDFPage_GetObject(page, index);
|
|
125
|
+
lib.FPDFPage_RemoveObject(page, object);
|
|
126
|
+
lib.FPDFPageObj_Destroy(object);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
context.spliceObjects(location.pageIndex, first, location.indexes.length, []);
|
|
130
|
+
return first;
|
|
131
|
+
}
|
|
132
|
+
/** The location and stored inputs of a text box, or the issue that stops the edit. */
|
|
133
|
+
export function textBoxTarget(target, context, issue = () => { }) {
|
|
134
|
+
const location = context.locate(target);
|
|
135
|
+
if (!location) {
|
|
136
|
+
issue("/target", "unknown-target", `No element ${target}`);
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
if (location.record.mark?.kind !== "textBox") {
|
|
140
|
+
issue("/target", "unsupported-target", "Only text boxes created by web-doc can be edited yet");
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
return { location, spec: location.record.mark };
|
|
144
|
+
}
|
|
145
|
+
export function changed(location, drawn) {
|
|
146
|
+
return {
|
|
147
|
+
createdIds: [],
|
|
148
|
+
changedPages: [location.pageIndex],
|
|
149
|
+
warnings: warningsFor(drawn, location.record.id),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function warningsFor(drawn, elementId) {
|
|
153
|
+
return [
|
|
154
|
+
...(drawn.overflow
|
|
155
|
+
? [
|
|
156
|
+
{
|
|
157
|
+
code: "fidelity-degraded",
|
|
158
|
+
message: "The text does not fit the box's height and runs past it",
|
|
159
|
+
details: { elementId },
|
|
160
|
+
},
|
|
161
|
+
]
|
|
162
|
+
: []),
|
|
163
|
+
...(drawn.substitution
|
|
164
|
+
? [
|
|
165
|
+
{
|
|
166
|
+
code: "font-substitution",
|
|
167
|
+
message: drawn.substitution,
|
|
168
|
+
details: { elementId },
|
|
169
|
+
},
|
|
170
|
+
]
|
|
171
|
+
: []),
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The fonts a batch will need: what each operation draws, read from the
|
|
176
|
+
* operation itself, from the text box it rebuilds, or from the text object
|
|
177
|
+
* it may have to replace.
|
|
178
|
+
*/
|
|
179
|
+
export function fontRequestsOf(operations, markOf, elementOf) {
|
|
180
|
+
const requests = [];
|
|
181
|
+
const spec = (id) => {
|
|
182
|
+
const mark = markOf(id);
|
|
183
|
+
return mark?.kind === "textBox"
|
|
184
|
+
? mark
|
|
185
|
+
: undefined;
|
|
186
|
+
};
|
|
187
|
+
const table = (id) => tableSpecOf(markOf(id));
|
|
188
|
+
for (const raw of operations) {
|
|
189
|
+
const operation = raw;
|
|
190
|
+
switch (operation.op) {
|
|
191
|
+
case "insertTextBox":
|
|
192
|
+
requests.push({
|
|
193
|
+
family: operation.style?.fontFamily ?? "Helvetica",
|
|
194
|
+
text: operation.text,
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
case "replaceText": {
|
|
198
|
+
const box = spec(operation.target);
|
|
199
|
+
if (box) {
|
|
200
|
+
requests.push({ family: box.style.fontFamily, text: operation.text });
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
const element = elementOf(operation.target);
|
|
204
|
+
if (element?.kind === "text")
|
|
205
|
+
requests.push({
|
|
206
|
+
family: element.textStyle?.fontFamily ?? "Helvetica",
|
|
207
|
+
text: operation.text,
|
|
208
|
+
});
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "setTextStyle": {
|
|
212
|
+
const box = spec(operation.target);
|
|
213
|
+
if (box)
|
|
214
|
+
requests.push({
|
|
215
|
+
family: operation.style.fontFamily ?? box.style.fontFamily,
|
|
216
|
+
text: box.text,
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case "resizeElement":
|
|
221
|
+
case "moveElement": {
|
|
222
|
+
const box = spec(operation.target);
|
|
223
|
+
if (box)
|
|
224
|
+
requests.push({ family: box.style.fontFamily, text: box.text });
|
|
225
|
+
const grid = table(operation.target);
|
|
226
|
+
if (grid)
|
|
227
|
+
requests.push({
|
|
228
|
+
family: grid.style.fontFamily,
|
|
229
|
+
text: tableText(grid.rows),
|
|
230
|
+
});
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case "insertTable":
|
|
234
|
+
requests.push({
|
|
235
|
+
family: operation.style?.fontFamily ?? "Helvetica",
|
|
236
|
+
text: tableText(operation.rows),
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
case "setTableCell": {
|
|
240
|
+
const grid = table(operation.target);
|
|
241
|
+
if (grid)
|
|
242
|
+
requests.push({
|
|
243
|
+
family: grid.style.fontFamily,
|
|
244
|
+
text: `${tableText(grid.rows)}\n${operation.text}`,
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
default:
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return requests;
|
|
253
|
+
}
|
|
254
|
+
export function fontRequest(style, text) {
|
|
255
|
+
return {
|
|
256
|
+
family: style.fontFamily,
|
|
257
|
+
bold: style.bold,
|
|
258
|
+
italic: style.italic,
|
|
259
|
+
text,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
export function validateFont(request, context, issue) {
|
|
263
|
+
const problem = context.fonts.problem(request);
|
|
264
|
+
if (problem)
|
|
265
|
+
issue(problem.path, problem.code, problem.message);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Positions an object so it reads upright on the displayed page: the object's
|
|
269
|
+
* origin goes to the page-space point and it turns against the page's /Rotate.
|
|
270
|
+
*/
|
|
271
|
+
export function placeUpright(pdfium, object, geometry, pageX, pageY) {
|
|
272
|
+
const [a, b, c, d] = ROTATIONS[geometry.rotation % 4];
|
|
273
|
+
const origin = pageToUser(geometry, pageX, pageY);
|
|
274
|
+
pdfium.lib.FPDFPageObj_Transform(object, a, b, c, d, origin.x, origin.y);
|
|
275
|
+
}
|
|
276
|
+
/** Counter-clockwise quarter turns in user space, which cancel the page's clockwise /Rotate. */
|
|
277
|
+
const ROTATIONS = [
|
|
278
|
+
[1, 0, 0, 1],
|
|
279
|
+
[0, 1, -1, 0],
|
|
280
|
+
[-1, 0, 0, -1],
|
|
281
|
+
[0, -1, 1, 0],
|
|
282
|
+
];
|
|
283
|
+
export function setText(pdfium, object, text) {
|
|
284
|
+
const wide = pdfium.writeWideString(text);
|
|
285
|
+
try {
|
|
286
|
+
pdfium.lib.FPDFText_SetText(object, wide);
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
pdfium.free(wide);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
export function parseColor(color) {
|
|
293
|
+
return [1, 3, 5].map((offset) => Number.parseInt(color.slice(offset, offset + 2), 16));
|
|
294
|
+
}
|
|
295
|
+
export function resolveStyle(style) {
|
|
296
|
+
return {
|
|
297
|
+
fontFamily: style.fontFamily ?? "Helvetica",
|
|
298
|
+
fontSize: style.fontSize ?? 12,
|
|
299
|
+
bold: style.bold ?? false,
|
|
300
|
+
italic: style.italic ?? false,
|
|
301
|
+
color: style.color ?? "#000000",
|
|
302
|
+
align: style.align ?? "left",
|
|
303
|
+
lineHeight: style.lineHeight ?? 1.2,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function definedFields(style) {
|
|
307
|
+
return Object.fromEntries(Object.entries(style).filter(([, value]) => value !== undefined));
|
|
308
|
+
}
|
|
309
|
+
export function validateRect(rect, geometry, issue, path = "/rect") {
|
|
310
|
+
const size = displayedSize(geometry);
|
|
311
|
+
const tolerance = 0.01;
|
|
312
|
+
if (rect.x < -tolerance ||
|
|
313
|
+
rect.y < -tolerance ||
|
|
314
|
+
rect.x + rect.width > size.width + tolerance ||
|
|
315
|
+
rect.y + rect.height > size.height + tolerance)
|
|
316
|
+
issue(path, "range", `The rectangle must lie within the ${size.width}×${size.height} pt page`);
|
|
317
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { PdfTextAlign } from "../types.js";
|
|
2
|
+
export interface LayoutInput {
|
|
3
|
+
readonly text: string;
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
readonly fontSize: number;
|
|
7
|
+
/** Multiple of the font size. */
|
|
8
|
+
readonly lineHeight: number;
|
|
9
|
+
readonly align: PdfTextAlign;
|
|
10
|
+
readonly ascent: number;
|
|
11
|
+
/** Width of a string in points at the font size. */
|
|
12
|
+
readonly advance: (text: string) => number;
|
|
13
|
+
}
|
|
14
|
+
export interface LayoutLine {
|
|
15
|
+
readonly text: string;
|
|
16
|
+
/** Offset of the line's left edge from the box's left edge. */
|
|
17
|
+
readonly x: number;
|
|
18
|
+
/** Offset of the baseline from the box's top edge. */
|
|
19
|
+
readonly baseline: number;
|
|
20
|
+
readonly width: number;
|
|
21
|
+
}
|
|
22
|
+
export interface Layout {
|
|
23
|
+
readonly lines: readonly LayoutLine[];
|
|
24
|
+
/** Height the lines need; may exceed the box. */
|
|
25
|
+
readonly height: number;
|
|
26
|
+
readonly overflow: boolean;
|
|
27
|
+
}
|
|
28
|
+
export declare function layoutText(input: LayoutInput): Layout;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export function layoutText(input) {
|
|
2
|
+
const step = input.fontSize * input.lineHeight;
|
|
3
|
+
const texts = [];
|
|
4
|
+
for (const paragraph of input.text.replaceAll("\r\n", "\n").split("\n"))
|
|
5
|
+
texts.push(...wrapParagraph(paragraph.replaceAll("\t", " "), input));
|
|
6
|
+
const lines = texts.map((text, index) => {
|
|
7
|
+
const width = input.advance(text);
|
|
8
|
+
return {
|
|
9
|
+
text,
|
|
10
|
+
x: offset(input.align, input.width, width),
|
|
11
|
+
baseline: index * step + input.ascent,
|
|
12
|
+
width,
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
const height = lines.length * step;
|
|
16
|
+
return { lines, height, overflow: height > input.height + 0.001 };
|
|
17
|
+
}
|
|
18
|
+
function wrapParagraph(paragraph, input) {
|
|
19
|
+
const lines = [];
|
|
20
|
+
let current = "";
|
|
21
|
+
const push = () => {
|
|
22
|
+
lines.push(current.trimEnd());
|
|
23
|
+
current = "";
|
|
24
|
+
};
|
|
25
|
+
// Runs of spaces travel with the line they are in; a word that does not
|
|
26
|
+
// fit moves to the next line, and one wider than the box is cut.
|
|
27
|
+
for (const token of paragraph.match(/ +|[^ ]+/g) ?? []) {
|
|
28
|
+
if (token.startsWith(" ")) {
|
|
29
|
+
current += token;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const candidate = current + token;
|
|
33
|
+
if (input.advance(candidate) <= input.width) {
|
|
34
|
+
current = candidate;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (current.trim())
|
|
38
|
+
push();
|
|
39
|
+
else
|
|
40
|
+
current = "";
|
|
41
|
+
if (input.advance(token) <= input.width) {
|
|
42
|
+
current = token;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
for (const character of token) {
|
|
46
|
+
const next = current + character;
|
|
47
|
+
if (current && input.advance(next) > input.width) {
|
|
48
|
+
push();
|
|
49
|
+
current = character;
|
|
50
|
+
}
|
|
51
|
+
else
|
|
52
|
+
current = next;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
lines.push(current.trimEnd());
|
|
56
|
+
return lines;
|
|
57
|
+
}
|
|
58
|
+
function offset(align, boxWidth, lineWidth) {
|
|
59
|
+
switch (align) {
|
|
60
|
+
case "center":
|
|
61
|
+
return Math.max(0, (boxWidth - lineWidth) / 2);
|
|
62
|
+
case "right":
|
|
63
|
+
return Math.max(0, boxWidth - lineWidth);
|
|
64
|
+
default:
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DeleteElementOperation, MoveElementOperation, ResizeElementOperation } from "../types.js";
|
|
2
|
+
import type { OperationHandler } from "./operations.js";
|
|
3
|
+
export declare const moveElement: OperationHandler<MoveElementOperation>;
|
|
4
|
+
export declare const resizeElement: OperationHandler<ResizeElementOperation>;
|
|
5
|
+
export declare const deleteElement: OperationHandler<DeleteElementOperation>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { pageToUser } from "./geometry.js";
|
|
2
|
+
import { changed, rebuildTextBox, removeObjects, validateRect, } from "./text-box.js";
|
|
3
|
+
import { changedTable, rebuildTable, tableSpecOf } from "./tables.js";
|
|
4
|
+
/*
|
|
5
|
+
* Geometry operations that apply to any element. Plain objects are moved and
|
|
6
|
+
* stretched through PDFium matrices in user space; web-doc's own composites
|
|
7
|
+
* are rebuilt from their stored inputs so the mark stays true.
|
|
8
|
+
*/
|
|
9
|
+
export const moveElement = {
|
|
10
|
+
validate(operation, context, issue) {
|
|
11
|
+
const target = anyTarget(operation.target, context, issue);
|
|
12
|
+
if (!target)
|
|
13
|
+
return;
|
|
14
|
+
if ((operation.to === undefined) === (operation.by === undefined)) {
|
|
15
|
+
issue("", "one-of", "Give exactly one of `to` and `by`");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const delta = moveDelta(operation, target.element.bounds);
|
|
19
|
+
validateRect(shifted(target.element.bounds, delta.dx, delta.dy), context.geometry(target.location.pageIndex), issue, operation.to ? "/to" : "/by");
|
|
20
|
+
},
|
|
21
|
+
apply(operation, context) {
|
|
22
|
+
const { location, element } = anyTarget(operation.target, context);
|
|
23
|
+
const { dx, dy } = moveDelta(operation, element.bounds);
|
|
24
|
+
const spec = textBoxSpec(location);
|
|
25
|
+
if (spec)
|
|
26
|
+
return changed(location, rebuildTextBox(context, location, {
|
|
27
|
+
...spec,
|
|
28
|
+
rect: shifted(spec.rect, dx, dy),
|
|
29
|
+
}));
|
|
30
|
+
const table = tableSpecOf(location.record.mark);
|
|
31
|
+
if (table)
|
|
32
|
+
return changedTable(location, rebuildTable(context, location, {
|
|
33
|
+
...table,
|
|
34
|
+
at: { x: table.at.x + dx, y: table.at.y + dy },
|
|
35
|
+
}));
|
|
36
|
+
const geometry = context.geometry(location.pageIndex);
|
|
37
|
+
const from = pageToUser(geometry, 0, 0);
|
|
38
|
+
const to = pageToUser(geometry, dx, dy);
|
|
39
|
+
transformObjects(context, location, [
|
|
40
|
+
1,
|
|
41
|
+
0,
|
|
42
|
+
0,
|
|
43
|
+
1,
|
|
44
|
+
to.x - from.x,
|
|
45
|
+
to.y - from.y,
|
|
46
|
+
]);
|
|
47
|
+
return changed(location, { overflow: false });
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export const resizeElement = {
|
|
51
|
+
validate(operation, context, issue) {
|
|
52
|
+
const target = anyTarget(operation.target, context, issue);
|
|
53
|
+
if (!target)
|
|
54
|
+
return;
|
|
55
|
+
if (target.element.kind === "table") {
|
|
56
|
+
issue("/target", "unsupported-target", "Tables cannot be resized");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
validateRect(operation.rect, context.geometry(target.location.pageIndex), issue);
|
|
60
|
+
},
|
|
61
|
+
apply(operation, context) {
|
|
62
|
+
const { location, element } = anyTarget(operation.target, context);
|
|
63
|
+
const spec = textBoxSpec(location);
|
|
64
|
+
if (spec)
|
|
65
|
+
return changed(location, rebuildTextBox(context, location, { ...spec, rect: operation.rect }));
|
|
66
|
+
const geometry = context.geometry(location.pageIndex);
|
|
67
|
+
const { bounds } = element;
|
|
68
|
+
// Scale about the top-left corner in page space, then move it into place.
|
|
69
|
+
const scaleX = operation.rect.width / bounds.width;
|
|
70
|
+
const scaleY = operation.rect.height / bounds.height;
|
|
71
|
+
const [userScaleX, userScaleY] = geometry.rotation % 2 === 0 ? [scaleX, scaleY] : [scaleY, scaleX];
|
|
72
|
+
const anchor = pageToUser(geometry, bounds.x, bounds.y);
|
|
73
|
+
transformObjects(context, location, [
|
|
74
|
+
userScaleX,
|
|
75
|
+
0,
|
|
76
|
+
0,
|
|
77
|
+
userScaleY,
|
|
78
|
+
anchor.x - userScaleX * anchor.x,
|
|
79
|
+
anchor.y - userScaleY * anchor.y,
|
|
80
|
+
]);
|
|
81
|
+
const from = pageToUser(geometry, 0, 0);
|
|
82
|
+
const to = pageToUser(geometry, operation.rect.x - bounds.x, operation.rect.y - bounds.y);
|
|
83
|
+
transformObjects(context, location, [
|
|
84
|
+
1,
|
|
85
|
+
0,
|
|
86
|
+
0,
|
|
87
|
+
1,
|
|
88
|
+
to.x - from.x,
|
|
89
|
+
to.y - from.y,
|
|
90
|
+
]);
|
|
91
|
+
return changed(location, { overflow: false });
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
export const deleteElement = {
|
|
95
|
+
validate(operation, context, issue) {
|
|
96
|
+
anyTarget(operation.target, context, issue);
|
|
97
|
+
},
|
|
98
|
+
apply(operation, context) {
|
|
99
|
+
const { location } = anyTarget(operation.target, context);
|
|
100
|
+
removeObjects(context, location);
|
|
101
|
+
return {
|
|
102
|
+
...changed(location, { overflow: false }),
|
|
103
|
+
removedIds: [operation.target],
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
/** Any element, by id, with its location; reports a missing one. */
|
|
108
|
+
function anyTarget(target, context, issue = () => { }) {
|
|
109
|
+
const location = context.locate(target);
|
|
110
|
+
const element = location && context.element(target);
|
|
111
|
+
if (!location || !element) {
|
|
112
|
+
issue("/target", "unknown-target", `No element ${target}`);
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return { location, element };
|
|
116
|
+
}
|
|
117
|
+
function textBoxSpec(location) {
|
|
118
|
+
return location.record.mark?.kind === "textBox"
|
|
119
|
+
? location.record.mark
|
|
120
|
+
: undefined;
|
|
121
|
+
}
|
|
122
|
+
function moveDelta(operation, bounds) {
|
|
123
|
+
return operation.to
|
|
124
|
+
? { dx: operation.to.x - bounds.x, dy: operation.to.y - bounds.y }
|
|
125
|
+
: { dx: operation.by.dx, dy: operation.by.dy };
|
|
126
|
+
}
|
|
127
|
+
function shifted(rect, dx, dy) {
|
|
128
|
+
return { ...rect, x: rect.x + dx, y: rect.y + dy };
|
|
129
|
+
}
|
|
130
|
+
/** Concatenates a user-space matrix onto every object of an element. */
|
|
131
|
+
function transformObjects(context, location, [a, b, c, d, e, f]) {
|
|
132
|
+
const { lib } = context.pdfium;
|
|
133
|
+
context.withPage(location.pageIndex, (page) => {
|
|
134
|
+
for (const index of location.indexes)
|
|
135
|
+
lib.FPDFPageObj_Transform(lib.FPDFPage_GetObject(page, index), a, b, c, d, e, f);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type WorkerLike } from "../../worker-client.js";
|
|
2
|
+
import type { EditEngineContext } from "../engine.js";
|
|
3
|
+
import type { PagePoint, PageRect, TextPosition, TextRange } from "../types.js";
|
|
4
|
+
import { WorkerEngineClient } from "../worker-engine.js";
|
|
5
|
+
import type { PageBitmap, PageLayout, TextLayout } from "./types.js";
|
|
6
|
+
/** The reads behind the overlay primitives, beyond the core engine interface. */
|
|
7
|
+
export interface PdfEngineReads {
|
|
8
|
+
textLayout(id: string, signal: AbortSignal): Promise<TextLayout | undefined>;
|
|
9
|
+
positionAt(pageIndex: number, point: PagePoint, signal: AbortSignal): Promise<TextPosition | undefined>;
|
|
10
|
+
rangeRects(range: TextRange, signal: AbortSignal): Promise<readonly PageRect[]>;
|
|
11
|
+
renderWithout(pageIndex: number, elementIds: readonly string[], scale: number, signal: AbortSignal): Promise<PageBitmap>;
|
|
12
|
+
pageLayout(pageIndex: number, signal: AbortSignal): Promise<PageLayout | undefined>;
|
|
13
|
+
}
|
|
14
|
+
export interface PdfEditProviderOptions {
|
|
15
|
+
readonly workerUrl?: string | URL;
|
|
16
|
+
readonly wasmUrl?: string | URL;
|
|
17
|
+
/** TrueType font for text the standard fonts cannot encode. */
|
|
18
|
+
readonly fallbackFontUrl?: string | URL;
|
|
19
|
+
/** Test hook: supplies the worker instead of the packaged script. */
|
|
20
|
+
readonly createWorker?: () => WorkerLike;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Starts the PDF edit worker for `original` and returns the engine the core
|
|
24
|
+
* drives. Loaded lazily by the PDF adapter's provider on the first `edit()`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function loadPdfEditEngine(original: Uint8Array, context: EditEngineContext, options?: PdfEditProviderOptions): Promise<PdfEditEngineClient>;
|
|
27
|
+
export declare class PdfEditEngineClient extends WorkerEngineClient implements PdfEngineReads {
|
|
28
|
+
readonly schemas: import("../types.js").OperationSchemaSet;
|
|
29
|
+
start(original: Uint8Array, options: PdfEditProviderOptions): Promise<void>;
|
|
30
|
+
textLayout(id: string, signal: AbortSignal): Promise<TextLayout | undefined>;
|
|
31
|
+
positionAt(pageIndex: number, point: PagePoint, signal: AbortSignal): Promise<TextPosition | undefined>;
|
|
32
|
+
rangeRects(range: TextRange, signal: AbortSignal): Promise<readonly PageRect[]>;
|
|
33
|
+
renderWithout(pageIndex: number, elementIds: readonly string[], scale: number, signal: AbortSignal): Promise<PageBitmap>;
|
|
34
|
+
pageLayout(pageIndex: number, signal: AbortSignal): Promise<PageLayout | undefined>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ViewerError } from "../../errors.js";
|
|
2
|
+
import { WorkerRpcClient } from "../../worker-client.js";
|
|
3
|
+
import { packageRelativeUrl, resolveAssetUrl, WorkerEngineClient, } from "../worker-engine.js";
|
|
4
|
+
import { pdfOperationSchemas } from "./schemas.js";
|
|
5
|
+
/**
|
|
6
|
+
* Starts the PDF edit worker for `original` and returns the engine the core
|
|
7
|
+
* drives. Loaded lazily by the PDF adapter's provider on the first `edit()`.
|
|
8
|
+
*/
|
|
9
|
+
export async function loadPdfEditEngine(original, context, options = {}) {
|
|
10
|
+
const worker = options.createWorker
|
|
11
|
+
? options.createWorker()
|
|
12
|
+
: createPackagedWorker(options, context);
|
|
13
|
+
const rpc = new WorkerRpcClient(worker);
|
|
14
|
+
const engine = new PdfEditEngineClient(rpc, context);
|
|
15
|
+
try {
|
|
16
|
+
await engine.start(original, options);
|
|
17
|
+
return engine;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
rpc.destroy();
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class PdfEditEngineClient extends WorkerEngineClient {
|
|
25
|
+
schemas = pdfOperationSchemas;
|
|
26
|
+
async start(original, options) {
|
|
27
|
+
const base = this.context.assetBaseUrl;
|
|
28
|
+
const init = {
|
|
29
|
+
wasmUrl: resolveAssetUrl(options.wasmUrl, base, base
|
|
30
|
+
? new URL("assets/pdfium/pdfium.wasm", base)
|
|
31
|
+
: packageRelativeUrl("../../../assets/pdfium/pdfium.wasm", import.meta.url)).href,
|
|
32
|
+
fallbackFontUrl: resolveAssetUrl(options.fallbackFontUrl, base, base
|
|
33
|
+
? new URL("fonts/noto-sans-latin-cyrillic.ttf", base)
|
|
34
|
+
: packageRelativeUrl("../../../fonts/noto-sans-latin-cyrillic.ttf", import.meta.url)).href,
|
|
35
|
+
};
|
|
36
|
+
await this.request("edit-init", init, this.context.signal);
|
|
37
|
+
const data = original.slice().buffer;
|
|
38
|
+
const fonts = (this.context.fonts ?? []).map(toWorkerFont);
|
|
39
|
+
const open = {
|
|
40
|
+
data,
|
|
41
|
+
limits: this.context.limits,
|
|
42
|
+
format: "pdf",
|
|
43
|
+
...(this.context.fileName ? { fileName: this.context.fileName } : {}),
|
|
44
|
+
...(fonts.length > 0 ? { fonts } : {}),
|
|
45
|
+
};
|
|
46
|
+
await this.request("edit-open", open, this.context.signal, [
|
|
47
|
+
data,
|
|
48
|
+
...fonts
|
|
49
|
+
.map((font) => font.source)
|
|
50
|
+
.filter((source) => source instanceof ArrayBuffer),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
textLayout(id, signal) {
|
|
54
|
+
return this.request("edit-text-layout", { id }, signal);
|
|
55
|
+
}
|
|
56
|
+
positionAt(pageIndex, point, signal) {
|
|
57
|
+
return this.request("edit-position-at", { pageIndex, point }, signal);
|
|
58
|
+
}
|
|
59
|
+
rangeRects(range, signal) {
|
|
60
|
+
return this.request("edit-range-rects", { range }, signal);
|
|
61
|
+
}
|
|
62
|
+
async renderWithout(pageIndex, elementIds, scale, signal) {
|
|
63
|
+
const bitmap = await this.request("edit-render-without", { pageIndex, elementIds, scale }, signal);
|
|
64
|
+
return { ...bitmap, data: new Uint8Array(bitmap.data) };
|
|
65
|
+
}
|
|
66
|
+
pageLayout(pageIndex, signal) {
|
|
67
|
+
return this.request("edit-page-layout", { pageIndex }, signal);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** A host font in the worker's shape: bytes are copied, URLs made absolute. */
|
|
71
|
+
function toWorkerFont(font) {
|
|
72
|
+
const source = font.source instanceof ArrayBuffer
|
|
73
|
+
? font.source.slice(0)
|
|
74
|
+
: font.source instanceof Uint8Array
|
|
75
|
+
? font.source.slice().buffer
|
|
76
|
+
: new URL(font.source, globalThis.location?.href ?? "http://localhost/")
|
|
77
|
+
.href;
|
|
78
|
+
return {
|
|
79
|
+
family: font.family,
|
|
80
|
+
weight: font.weight ?? 400,
|
|
81
|
+
style: font.style ?? "normal",
|
|
82
|
+
source,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function createPackagedWorker(options, context) {
|
|
86
|
+
if (typeof Worker === "undefined")
|
|
87
|
+
throw new ViewerError("edit-unsupported", "PDF editing requires a browser module Worker", { details: { format: "pdf", reason: "no-worker" } });
|
|
88
|
+
const url = resolveAssetUrl(options.workerUrl, context.assetBaseUrl, context.assetBaseUrl
|
|
89
|
+
? new URL("workers/pdf-edit-worker.js", context.assetBaseUrl)
|
|
90
|
+
: packageRelativeUrl("../../../workers/pdf-edit-worker.js", import.meta.url));
|
|
91
|
+
return new Worker(url, { type: "module", name: "web-doc-pdf-edit" });
|
|
92
|
+
}
|