@stll/folio-core 0.60.0 → 0.61.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/README.md +13 -0
- package/dist/ai-edits/apply.js +139 -32
- package/dist/ai-edits/headless.d.ts +3 -0
- package/dist/ai-edits/headless.js +57 -63
- package/dist/ai-edits/note-story-inventory.d.ts +7 -0
- package/dist/ai-edits/note-story-inventory.js +11 -0
- package/dist/ai-edits/read.js +16 -9
- package/dist/ai-edits/revisionStretches.js +52 -1
- package/dist/ai-edits/table-row-column-mutations.js +6 -1
- package/dist/compare/reproducible-package.js +2 -1
- package/dist/compare/verification.js +1 -0
- package/dist/compat/eigenpal.d.ts +3 -1
- package/dist/controller/canonicalClipboard.js +2 -1
- package/dist/controller/canonicalListRendering.d.ts +4 -6
- package/dist/controller/canonicalListRendering.js +3 -157
- package/dist/controller/headerFooterEditorManager.d.ts +1 -0
- package/dist/controller/headerFooterEditorManager.js +11 -0
- package/dist/controller/hiddenEditorManager.d.ts +1 -1
- package/dist/controller/hiddenEditorManager.js +3 -1
- package/dist/controller/layoutPipeline.d.ts +3 -1
- package/dist/controller/layoutPipeline.js +13 -4
- package/dist/controller/layoutSession.d.ts +2 -1
- package/dist/display-list/build/imagePrimitives.js +3 -6
- package/dist/docx/canonicalStoryRepack.js +3 -2
- package/dist/docx/commentTextParagraph.d.ts +6 -0
- package/dist/docx/commentTextParagraph.js +15 -0
- package/dist/docx/conformance.js +6 -1
- package/dist/docx/diagramPreview.js +2 -1
- package/dist/docx/documentParser.d.ts +13 -1
- package/dist/docx/documentParser.js +1 -1
- package/dist/docx/encryption/compoundFile.d.ts +0 -8
- package/dist/docx/encryption/compoundFile.js +2 -1
- package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
- package/dist/docx/encryption/cryptoBytes.js +3 -18
- package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
- package/dist/docx/encryption/encryptionInfo.js +2 -1
- package/dist/docx/ensureParaIds.js +2 -1
- package/dist/docx/groupDrawingParser.js +6 -1
- package/dist/docx/listModelNormalization.d.ts +10 -0
- package/dist/docx/listModelNormalization.js +166 -0
- package/dist/docx/metadataPrivacy.js +3 -2
- package/dist/docx/metafileRaster.js +2 -1
- package/dist/docx/packageParts.js +6 -5
- package/dist/docx/paragraphParser.js +6 -4
- package/dist/docx/parser.js +5 -4
- package/dist/docx/preservedRunContent.js +11 -2
- package/dist/docx/removeHeaderFooterParts.js +8 -7
- package/dist/docx/replyToComment.js +3 -13
- package/dist/docx/rezip.js +88 -89
- package/dist/docx/runParser.js +12 -6
- package/dist/docx/runPropertyPresence.d.ts +11 -0
- package/dist/docx/runPropertyPresence.js +14 -0
- package/dist/docx/selectiveSave.js +15 -14
- package/dist/docx/serializer/runSerializer.js +3 -2
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
- package/dist/docx/server/boundedArchive.js +2 -1
- package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
- package/dist/docx/server/inspectDocxPackage.js +5 -1
- package/dist/docx/server/validateDocxConformance.js +2 -1
- package/dist/docx/tableParser.js +7 -4
- package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
- package/dist/docx/tableRowPropertyPresence.js +15 -0
- package/dist/docx/unzip.js +2 -1
- package/dist/docx/xmlEncoding.js +6 -5
- package/dist/fonts/fontsourceFaces.js +2 -1
- package/dist/fonts/sfnt/parse.js +6 -9
- package/dist/fonts/sfnt/subset.js +2 -1
- package/dist/fonts/sfnt/tables.js +2 -3
- package/dist/fonts/sfnt/woff.js +2 -1
- package/dist/index.d.ts +3 -1
- package/dist/internal/revisionResolutionInline.d.ts +2 -1
- package/dist/internal/revisionResolutionInline.js +22 -1
- package/dist/internal/wholeStoryRevisionResolution.js +24 -7
- package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
- package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
- package/dist/layout-engine/measure/crossRunWords.js +12 -2
- package/dist/managers/AutoSaveManager.js +2 -1
- package/dist/managers/EditorModeManager.d.ts +10 -1
- package/dist/managers/EditorModeManager.js +15 -1
- package/dist/managers/TableSelectionManager.d.ts +1 -1
- package/dist/managers/autoSaveCodec.js +2 -6
- package/dist/managers/reviewDisplayMode.d.ts +8 -0
- package/dist/managers/reviewDisplayMode.js +15 -0
- package/dist/paged-layout/pageText.d.ts +11 -2
- package/dist/paged-layout/pageText.js +8 -3
- package/dist/pdf/fonts.js +5 -14
- package/dist/pdf/images.js +3 -2
- package/dist/pdf/objects.d.ts +0 -11
- package/dist/pdf/objects.js +7 -9
- package/dist/pdf/writePdf.js +2 -1
- package/dist/prosemirror/commands/comments.js +51 -13
- package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
- package/dist/prosemirror/containerFinalParagraph.js +8 -8
- package/dist/prosemirror/conversion/fromProseDoc.js +9 -4
- package/dist/prosemirror/conversion/toProseDoc.js +4 -4
- package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
- package/dist/prosemirror/findReplaceSelection.d.ts +11 -1
- package/dist/prosemirror/findReplaceSelection.js +15 -1
- package/dist/prosemirror/markupViewProjection.d.ts +10 -1
- package/dist/prosemirror/markupViewProjection.js +21 -3
- package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
- package/dist/prosemirror/replacedAnnotations.js +71 -12
- package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
- package/dist/prosemirror/runFormattingReconciliation.js +10 -2
- package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
- package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
- package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
- package/dist/utils/base64.d.ts +1 -31
- package/dist/utils/base64.js +19 -33
- package/dist/utils/clipboard.js +5 -4
- package/dist/utils/colorResolver.js +11 -2
- package/dist/utils/findReplace.d.ts +5 -0
- package/dist/utils/replaceText.js +21 -4
- package/dist/utils/sliceText.d.ts +2 -0
- package/dist/utils/sliceText.js +2 -0
- package/dist/utils/textEncoding.d.ts +2 -0
- package/dist/utils/textEncoding.js +2 -0
- package/dist/utils/tiffConverter.d.ts +0 -9
- package/dist/utils/tiffConverter.js +2 -4
- package/package.json +51 -25
|
@@ -6,6 +6,7 @@ import "../prosemirror/extensions/nodes/SdtExtension.js";
|
|
|
6
6
|
import { canonicalFieldNode } from "../prosemirror/fieldRepresentation.js";
|
|
7
7
|
import { resolvedPreservedXmlAttrs } from "../prosemirror/preservedXmlReview.js";
|
|
8
8
|
import { continuedRunMarks } from "../prosemirror/rejoinRunCarriers.js";
|
|
9
|
+
import { commentPoint } from "../prosemirror/replacedAnnotations.js";
|
|
9
10
|
import { RUN_FORMATTING_MARK_NAMES } from "../prosemirror/runFormattingMarkNames.js";
|
|
10
11
|
import { reconstructRejectedRunFormattingMarks, restoreHistoricalRunFormatting } from "../prosemirror/runPropertyChangeResolution.js";
|
|
11
12
|
import { paragraphRunStyleContext } from "../prosemirror/runStyleFormatting.js";
|
|
@@ -158,6 +159,25 @@ const resolveInlineContent = ({ node, position, context, inheritedParagraphScope
|
|
|
158
159
|
contentChanged = true;
|
|
159
160
|
resolvedBoundaries.add(children.length);
|
|
160
161
|
}
|
|
162
|
+
if (resolved === null && child.isInline) {
|
|
163
|
+
const anchors = [];
|
|
164
|
+
for (const mark of child.marks) {
|
|
165
|
+
const id = mark.attrs["commentId"];
|
|
166
|
+
if (mark.type.name !== "comment" || !context.pointComments.delete(id)) continue;
|
|
167
|
+
anchors.push(commentPoint(child.type.schema, id));
|
|
168
|
+
}
|
|
169
|
+
if (anchors.length > 0) {
|
|
170
|
+
const fragment = Fragment.fromArray(anchors);
|
|
171
|
+
const range = context.replacementRanges.at(-1);
|
|
172
|
+
if (!range || range.from !== contentStart + offset) panic("Missing resolved inline range");
|
|
173
|
+
range.newSize = fragment.size;
|
|
174
|
+
range.slice = new Slice(fragment, 0, 0);
|
|
175
|
+
for (const anchor of anchors) {
|
|
176
|
+
children.push(anchor);
|
|
177
|
+
positions.push(contentStart + offset);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
161
181
|
if (resolved) {
|
|
162
182
|
children.push(resolved);
|
|
163
183
|
positions.push(contentStart + offset);
|
|
@@ -228,8 +248,9 @@ const coalesceReplacementRanges = (replacementRanges) => {
|
|
|
228
248
|
}
|
|
229
249
|
return coalesced;
|
|
230
250
|
};
|
|
231
|
-
const resolveInlineRevisions = ({ doc, mode, keepType, removeType, styleResolver }) => {
|
|
251
|
+
const resolveInlineRevisions = ({ pointComments, doc, mode, keepType, removeType, styleResolver }) => {
|
|
232
252
|
const context = {
|
|
253
|
+
pointComments,
|
|
233
254
|
mode,
|
|
234
255
|
keepType,
|
|
235
256
|
removeType,
|
|
@@ -6,6 +6,7 @@ import { resolveAllTableChanges } from "../prosemirror/commands/resolveAllTableC
|
|
|
6
6
|
import { resolveAllNodePropertyChangeAttrs } from "../prosemirror/commands/resolveNodePropertyChangeAttrs.js";
|
|
7
7
|
import { resolveParagraphChangeAttrs } from "../prosemirror/commands/resolveParagraphProperties.js";
|
|
8
8
|
import { rebaseParagraphRunContent } from "../prosemirror/rebaseParagraphRuns.js";
|
|
9
|
+
import { createResolvedCommentPointCollector } from "../prosemirror/replacedAnnotations.js";
|
|
9
10
|
import { expandRunFormattingCarrier } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
10
11
|
import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
|
|
11
12
|
import { paragraphRunStyleContext } from "../prosemirror/runStyleFormatting.js";
|
|
@@ -19,9 +20,14 @@ import { Fragment, Slice } from "prosemirror-model";
|
|
|
19
20
|
import { Mapping, ReplaceStep, StepMap, Transform } from "prosemirror-transform";
|
|
20
21
|
//#region src/internal/wholeStoryRevisionResolution.ts
|
|
21
22
|
const resolveProperties = ({ doc, mode, styleResolver, numbering, steps, changedRanges }) => {
|
|
23
|
+
const collector = createResolvedCommentPointCollector({
|
|
24
|
+
removes: (node) => mode === "accept" && doc.type.schema.marks["deletion"]?.isInSet(node.marks) !== void 0,
|
|
25
|
+
retiresStructure: () => mode === "accept"
|
|
26
|
+
});
|
|
22
27
|
let structural = false;
|
|
23
28
|
let runsIntoTable = false;
|
|
24
|
-
const walk = (node, position) => {
|
|
29
|
+
const walk = ({ node, position, eligibleComments = true, previous }) => {
|
|
30
|
+
const childComments = eligibleComments && collector.visit(node, position, previous);
|
|
25
31
|
if (node.isText) return node;
|
|
26
32
|
if (!runsIntoTable && !node.isTextblock) {
|
|
27
33
|
for (let index = 0; index + 1 < node.childCount; index++) if (runsIntoFollowingTable(node.child(index), node.child(index + 1), mode)) {
|
|
@@ -80,9 +86,15 @@ const resolveProperties = ({ doc, mode, styleResolver, numbering, steps, changed
|
|
|
80
86
|
}
|
|
81
87
|
const children = [];
|
|
82
88
|
let changed = false;
|
|
83
|
-
const visitChild = (child, childPosition) => {
|
|
89
|
+
const visitChild = (child, childPosition, previousChild) => {
|
|
84
90
|
const replacement = rebased.get(child) ?? child;
|
|
85
|
-
if (rebased.size === 0) return walk(
|
|
91
|
+
if (rebased.size === 0) return walk({
|
|
92
|
+
node: replacement,
|
|
93
|
+
position: childPosition,
|
|
94
|
+
eligibleComments: childComments,
|
|
95
|
+
...previousChild ? { previous: previousChild } : {}
|
|
96
|
+
});
|
|
97
|
+
if (childComments) collector.visit(child, childPosition, previousChild);
|
|
86
98
|
recordNodeResolution({
|
|
87
99
|
before: child,
|
|
88
100
|
after: replacement,
|
|
@@ -94,17 +106,21 @@ const resolveProperties = ({ doc, mode, styleResolver, numbering, steps, changed
|
|
|
94
106
|
replacement.forEach((inner, offset) => nested.push(visitChild(inner, childPosition + 1 + offset)));
|
|
95
107
|
return recreateProseNodeWithParagraphPropertySource(replacement, { content: nested });
|
|
96
108
|
};
|
|
97
|
-
resolved.forEach((child, offset) => {
|
|
98
|
-
const result = visitChild(child, position + 1 + offset);
|
|
109
|
+
resolved.forEach((child, offset, index) => {
|
|
110
|
+
const result = visitChild(child, position + 1 + offset, index > 0 ? node.maybeChild(index - 1) ?? void 0 : void 0);
|
|
99
111
|
children.push(result);
|
|
100
112
|
changed ||= result !== child;
|
|
101
113
|
});
|
|
102
114
|
return changed ? recreateProseNodeWithParagraphPropertySource(resolved, { content: children }) : resolved;
|
|
103
115
|
};
|
|
104
116
|
return {
|
|
105
|
-
resolved: walk(
|
|
117
|
+
resolved: walk({
|
|
118
|
+
node: doc,
|
|
119
|
+
position: -1
|
|
120
|
+
}),
|
|
106
121
|
structural,
|
|
107
|
-
runsIntoTable
|
|
122
|
+
runsIntoTable,
|
|
123
|
+
pointComments: new Set(collector.finish().keys())
|
|
108
124
|
};
|
|
109
125
|
};
|
|
110
126
|
const structuralReplacementMap = ({ replacements, tables, bookmarkJoins, origin }) => {
|
|
@@ -536,6 +552,7 @@ const resolveStoryRevisions = ({ doc, mode, styleResolver, numbering }) => {
|
|
|
536
552
|
});
|
|
537
553
|
const properties = propertyResult.resolved;
|
|
538
554
|
const inline = resolveInlineRevisions({
|
|
555
|
+
pointComments: propertyResult.pointComments,
|
|
539
556
|
doc: properties,
|
|
540
557
|
mode,
|
|
541
558
|
styleResolver,
|
|
@@ -9,6 +9,8 @@ import { Node } from "prosemirror-model";
|
|
|
9
9
|
* Options for the conversion.
|
|
10
10
|
*/
|
|
11
11
|
type ToFlowBlocksOptions = {
|
|
12
|
+
/** Paint comment anchors and highlights; false leaves authored marks intact. */
|
|
13
|
+
showCommentHighlights?: boolean;
|
|
12
14
|
/** Embedded families owned by this editor, never ambient process state. */
|
|
13
15
|
embeddedFontFamilyMap?: ReadonlyMap<string, string>;
|
|
14
16
|
/** Default font family. */
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { DisplayMode } from "../../managers/EditorModeManager.js";
|
|
2
3
|
import { FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, FlowBlock, FootnoteContent, Measure, Page } from "../../layout-engine/types.js";
|
|
3
4
|
import { ToFlowBlocksOptions } from "./flowConversionShared.js";
|
|
4
5
|
import "./toFlowBlocks.js";
|
|
5
6
|
//#region src/layout-bridge/convert/footnoteLayout.d.ts
|
|
6
7
|
type MeasureBlocksFn = (blocks: FlowBlock[], contentWidth: number) => Measure[];
|
|
7
8
|
type ConvertFootnoteOptions = Pick<ToFlowBlocksOptions, "embeddedFontFamilyMap"> & {
|
|
9
|
+
/** Whether comment highlights are painted. Defaults to true. */
|
|
10
|
+
showCommentHighlights?: boolean;
|
|
11
|
+
markupView?: DisplayMode;
|
|
8
12
|
styles?: document_d_exports.StyleDefinitions | null;
|
|
9
13
|
numbering?: document_d_exports.NumberingDefinitions;
|
|
10
14
|
theme?: document_d_exports.Theme | null;
|
|
@@ -2,6 +2,8 @@ import { measureParagraph } from "../../layout-engine/measure/measureParagraph.j
|
|
|
2
2
|
import { layoutTextBoxContent } from "../../layout-engine/measure/textBoxParagraphLayout.js";
|
|
3
3
|
import { DEFAULT_TEXTBOX_MARGINS, FOOTNOTE_ENTRY_MARGIN_BOTTOM, FOOTNOTE_FALLBACK_LINE_HEIGHT, FOOTNOTE_SEPARATOR_HEIGHT, isFloatingImageRun, resolveTableWidthPx } from "../../layout-engine/types.js";
|
|
4
4
|
import { footnoteToProseDoc } from "../../prosemirror/conversion/toProseDoc.js";
|
|
5
|
+
import { projectStoryMarkupView } from "../../prosemirror/markupViewProjection.js";
|
|
6
|
+
import { remapMarkupViewBlocks } from "./markupViewFlow.js";
|
|
5
7
|
import { collectNoteRefs } from "./noteReferences.js";
|
|
6
8
|
import { toFlowBlocks } from "./toFlowBlocks.js";
|
|
7
9
|
//#region src/layout-bridge/convert/footnoteLayout.ts
|
|
@@ -204,7 +206,7 @@ function convertNoteStoryToFlowBlocks(content, options, referenceMarkText) {
|
|
|
204
206
|
if (options.theme !== void 0) proseOptions.theme = options.theme;
|
|
205
207
|
if (options.numbering !== void 0) proseOptions.numbering = options.numbering;
|
|
206
208
|
const pmDoc = footnoteToProseDoc(content, proseOptions);
|
|
207
|
-
const flowOptions = {};
|
|
209
|
+
const flowOptions = { showCommentHighlights: options.showCommentHighlights !== false && options.markupView !== "no-markup" && options.markupView !== "original" };
|
|
208
210
|
if (options.defaultSize !== void 0) flowOptions.defaultSize = options.defaultSize;
|
|
209
211
|
if (options.styles) flowOptions.styles = options.styles;
|
|
210
212
|
if (options.theme !== void 0) flowOptions.theme = options.theme;
|
|
@@ -215,10 +217,18 @@ function convertNoteStoryToFlowBlocks(content, options, referenceMarkText) {
|
|
|
215
217
|
if (options.justificationCompatibility) flowOptions.justificationCompatibility = options.justificationCompatibility;
|
|
216
218
|
if (options.tableIndentCompatibility) flowOptions.tableIndentCompatibility = options.tableIndentCompatibility;
|
|
217
219
|
if (options.automaticHyphenation) flowOptions.automaticHyphenation = options.automaticHyphenation;
|
|
218
|
-
const
|
|
220
|
+
const projection = projectStoryMarkupView({
|
|
221
|
+
doc: pmDoc,
|
|
222
|
+
markupView: options.markupView ?? "all-markup",
|
|
223
|
+
styles: options.styles,
|
|
224
|
+
numbering: options.numbering
|
|
225
|
+
});
|
|
226
|
+
const viewDoc = projection.type === "resolved" ? projection.doc : pmDoc;
|
|
227
|
+
const hasReferenceMark = containsNoteReferenceMark(viewDoc);
|
|
219
228
|
if (hasReferenceMark && referenceMarkText !== void 0) flowOptions.noteReferenceMarkText = referenceMarkText;
|
|
229
|
+
const converted = toFlowBlocks(viewDoc, flowOptions);
|
|
220
230
|
return {
|
|
221
|
-
flowBlocks: preserveAuthoredFootnoteTerminalParagraph(
|
|
231
|
+
flowBlocks: preserveAuthoredFootnoteTerminalParagraph(projection.type === "resolved" ? remapMarkupViewBlocks(converted, projection) : converted),
|
|
222
232
|
hasReferenceMark
|
|
223
233
|
};
|
|
224
234
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
import { DisplayMode } from "../../managers/EditorModeManager.js";
|
|
2
3
|
import { FlowBlock, HeaderFooterContent, ImageRun, ImageRunPosition, Measure, PageMargins } from "../../layout-engine/types.js";
|
|
3
4
|
import { ToFlowBlocksOptions } from "./flowConversionShared.js";
|
|
4
5
|
import "./toFlowBlocks.js";
|
|
@@ -40,6 +41,10 @@ declare function calculateHeaderFooterMarginPushBounds(blocks: FlowBlock[], meas
|
|
|
40
41
|
*/
|
|
41
42
|
declare function calculateHeaderFooterBodyTopClearance(blocks: FlowBlock[], flowHeight: number, metrics: HeaderFooterMetrics): number | undefined;
|
|
42
43
|
type ConvertHeaderFooterOptions = {
|
|
44
|
+
/** Review projection applied before header/footer measurement. Defaults to All Markup. */
|
|
45
|
+
markupView?: DisplayMode;
|
|
46
|
+
/** Whether comment highlights are painted. Defaults to true. */
|
|
47
|
+
showCommentHighlights?: boolean;
|
|
43
48
|
styles?: document_d_exports.StyleDefinitions | null;
|
|
44
49
|
numbering?: document_d_exports.NumberingDefinitions;
|
|
45
50
|
theme?: document_d_exports.Theme | null;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { cloneParagraphWithPropertySource } from "../../docx/paragraphPropertySource.js";
|
|
2
2
|
import { isFloatingImageRun, isFloatingTextBoxBlock, isTextWrappingFloatingImageRun } from "../../layout-engine/types.js";
|
|
3
3
|
import { headerFooterToProseDoc } from "../../prosemirror/conversion/toProseDoc.js";
|
|
4
|
+
import { projectStoryMarkupView } from "../../prosemirror/markupViewProjection.js";
|
|
4
5
|
import { emuToPixels } from "../../utils/units.js";
|
|
6
|
+
import { remapMarkupViewBlocks } from "./markupViewFlow.js";
|
|
5
7
|
import { toFlowBlocks } from "./toFlowBlocks.js";
|
|
6
8
|
//#region src/layout-bridge/convert/headerFooterLayout.ts
|
|
7
9
|
const DETACHED_WATERMARK_HOST = Symbol.for("stll.detachedWatermarkHost");
|
|
@@ -341,19 +343,7 @@ function convertHeaderFooterToContent(headerFooter, contentWidth, metrics, optio
|
|
|
341
343
|
if (options.styles) proseDocOptions.styles = options.styles;
|
|
342
344
|
if (options.theme !== void 0) proseDocOptions.theme = options.theme;
|
|
343
345
|
if (options.numbering !== void 0) proseDocOptions.numbering = options.numbering;
|
|
344
|
-
|
|
345
|
-
const flowOptions = {};
|
|
346
|
-
if (options.defaultSize !== void 0) flowOptions.defaultSize = options.defaultSize;
|
|
347
|
-
if (options.styles) flowOptions.styles = options.styles;
|
|
348
|
-
if (options.theme !== void 0) flowOptions.theme = options.theme;
|
|
349
|
-
if (options.embeddedFontFamilyMap !== void 0) flowOptions.embeddedFontFamilyMap = options.embeddedFontFamilyMap;
|
|
350
|
-
if (options.fontAlternates !== void 0) flowOptions.fontAlternates = options.fontAlternates;
|
|
351
|
-
if (options.defaultTabStopTwips !== void 0) flowOptions.defaultTabStopTwips = options.defaultTabStopTwips;
|
|
352
|
-
if (options.lineBreakRules) flowOptions.lineBreakRules = options.lineBreakRules;
|
|
353
|
-
if (options.justificationCompatibility) flowOptions.justificationCompatibility = options.justificationCompatibility;
|
|
354
|
-
if (options.tableIndentCompatibility) flowOptions.tableIndentCompatibility = options.tableIndentCompatibility;
|
|
355
|
-
if (options.automaticHyphenation) flowOptions.automaticHyphenation = options.automaticHyphenation;
|
|
356
|
-
return finalizeHeaderFooterContent(toFlowBlocks(pmDoc, flowOptions), contentWidth, metrics, options);
|
|
346
|
+
return convertHeaderFooterPmDocToContent(headerFooter.watermarkBlockIndex === void 0 ? headerFooterToProseDoc(headerFooter.content, proseDocOptions) : headerFooterToProseDocWithDetachedWatermarkHost(headerFooter, proseDocOptions), contentWidth, metrics, options);
|
|
357
347
|
}
|
|
358
348
|
/**
|
|
359
349
|
* Same as {@link convertHeaderFooterToContent}, but sourced from a live
|
|
@@ -368,7 +358,7 @@ function convertHeaderFooterToContent(headerFooter, contentWidth, metrics, optio
|
|
|
368
358
|
*/
|
|
369
359
|
function convertHeaderFooterPmDocToContent(pmDoc, contentWidth, metrics, options) {
|
|
370
360
|
if (!pmDoc || pmDoc.content.size === 0) return;
|
|
371
|
-
const flowOptions = {};
|
|
361
|
+
const flowOptions = { showCommentHighlights: options.showCommentHighlights !== false && options.markupView !== "no-markup" && options.markupView !== "original" };
|
|
372
362
|
if (options.defaultSize !== void 0) flowOptions.defaultSize = options.defaultSize;
|
|
373
363
|
if (options.styles) flowOptions.styles = options.styles;
|
|
374
364
|
if (options.theme !== void 0) flowOptions.theme = options.theme;
|
|
@@ -379,7 +369,14 @@ function convertHeaderFooterPmDocToContent(pmDoc, contentWidth, metrics, options
|
|
|
379
369
|
if (options.justificationCompatibility) flowOptions.justificationCompatibility = options.justificationCompatibility;
|
|
380
370
|
if (options.tableIndentCompatibility) flowOptions.tableIndentCompatibility = options.tableIndentCompatibility;
|
|
381
371
|
if (options.automaticHyphenation) flowOptions.automaticHyphenation = options.automaticHyphenation;
|
|
382
|
-
|
|
372
|
+
const projection = projectStoryMarkupView({
|
|
373
|
+
doc: pmDoc,
|
|
374
|
+
markupView: options.markupView ?? "all-markup",
|
|
375
|
+
styles: options.styles,
|
|
376
|
+
numbering: options.numbering
|
|
377
|
+
});
|
|
378
|
+
const flowBlocks = toFlowBlocks(projection.type === "resolved" ? projection.doc : pmDoc, flowOptions);
|
|
379
|
+
return finalizeHeaderFooterContent(projection.type === "resolved" ? remapMarkupViewBlocks(flowBlocks, projection) : flowBlocks, contentWidth, metrics, options);
|
|
383
380
|
}
|
|
384
381
|
function horizontalImageLeft({ run, contentWidth, metrics }) {
|
|
385
382
|
const horizontal = run.position?.horizontal;
|
|
@@ -58,7 +58,8 @@ function paragraphToRuns(node, startPos, _options, pageBreaks) {
|
|
|
58
58
|
});
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
|
-
const
|
|
61
|
+
const projectedMarks = mergeProjectedInlineMarks(inheritedMarks, child.marks);
|
|
62
|
+
const effectiveMarks = _options.showCommentHighlights === false ? projectedMarks.filter((mark) => mark.type.name !== "comment") : projectedMarks;
|
|
62
63
|
if (child.type.name === "pageBreakRun") {
|
|
63
64
|
const trackedChange = extractRunFormatting(effectiveMarks, theme, fontAlternates);
|
|
64
65
|
if (trackedChange.isDeletion) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sliceText } from "../../utils/sliceText.js";
|
|
1
2
|
import { splitTrailingToken } from "../../utils/trailingText.js";
|
|
2
3
|
import { resolveEffectiveLineBreakPolicy } from "./effectiveLineBreakPolicy.js";
|
|
3
4
|
import "./lineBreakProvider.js";
|
|
@@ -47,7 +48,11 @@ function computeFollowingTextLeads(block) {
|
|
|
47
48
|
leads[index] = lead;
|
|
48
49
|
const run = block.runs[index];
|
|
49
50
|
if (!run || !isTextRun(run)) lead = void 0;
|
|
50
|
-
else if (run.text) lead =
|
|
51
|
+
else if (run.text) lead = sliceText({
|
|
52
|
+
text: run.text,
|
|
53
|
+
units: "codePoints",
|
|
54
|
+
end: 1
|
|
55
|
+
});
|
|
51
56
|
}
|
|
52
57
|
return leads;
|
|
53
58
|
}
|
|
@@ -101,6 +106,7 @@ function computeProtectedCrossRunGlueWidths(block) {
|
|
|
101
106
|
* invoked from, without the per-word text-length budget ever kicking in.
|
|
102
107
|
*/
|
|
103
108
|
const MAX_CROSS_RUN_SEGMENTS = 64;
|
|
109
|
+
const MAX_SCALAR_CODE_UNITS = 2;
|
|
104
110
|
/** Collect one lexical word continued through adjacent formatting runs. */
|
|
105
111
|
function collectCrossRunWord({ block, startRunIndex, startChar }) {
|
|
106
112
|
const segments = [];
|
|
@@ -115,7 +121,11 @@ function collectCrossRunWord({ block, startRunIndex, startChar }) {
|
|
|
115
121
|
if (remainder.length === 0) continue;
|
|
116
122
|
if (runIndex !== startRunIndex && isSpaceOrTab(remainder[0])) break;
|
|
117
123
|
const remainingBudget = 256 - text.length;
|
|
118
|
-
const boundedRemainder =
|
|
124
|
+
const boundedRemainder = sliceText({
|
|
125
|
+
text: remainder,
|
|
126
|
+
units: "codeUnits",
|
|
127
|
+
end: remainingBudget + MAX_SCALAR_CODE_UNITS
|
|
128
|
+
});
|
|
119
129
|
const firstBreak = findWordBreaks(boundedRemainder, resolveEffectiveLineBreakPolicy({
|
|
120
130
|
attrs: block.attrs,
|
|
121
131
|
run
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeUtf8 } from "../utils/textEncoding.js";
|
|
1
2
|
import { Subscribable } from "./Subscribable.js";
|
|
2
3
|
import { decodeAutoSaveEnvelope, encodeAutoSaveDocument, encodeAutoSaveEnvelopeFromDocument } from "./autoSaveCodec.js";
|
|
3
4
|
//#region src/managers/AutoSaveManager.ts
|
|
@@ -228,7 +229,7 @@ function getAutoSaveStorageSize(storageKey = DEFAULT_STORAGE_KEY) {
|
|
|
228
229
|
try {
|
|
229
230
|
const data = localStorage.getItem(storageKey);
|
|
230
231
|
if (!data) return 0;
|
|
231
|
-
return
|
|
232
|
+
return encodeUtf8(data).byteLength;
|
|
232
233
|
} catch {
|
|
233
234
|
return 0;
|
|
234
235
|
}
|
|
@@ -5,6 +5,15 @@ type EditorMode = "editing" | "suggesting" | "viewing";
|
|
|
5
5
|
/** How tracked changes render. Drives the display mode dropdown. */
|
|
6
6
|
declare const DISPLAY_MODES: readonly ["all-markup", "simple-markup", "no-markup", "original"];
|
|
7
7
|
type DisplayMode = (typeof DISPLAY_MODES)[number];
|
|
8
|
+
/** Why a user action cannot edit the displayed document. */
|
|
9
|
+
type EditorReadOnlyReason = "host-read-only" | "viewing-mode" | "review-display-mode";
|
|
10
|
+
declare const getReviewViewReadOnlyReason: (displayMode: DisplayMode) => EditorReadOnlyReason | null;
|
|
11
|
+
type EditorReadOnlyReasonOptions = {
|
|
12
|
+
editingMode: EditorMode;
|
|
13
|
+
readOnlyProp: boolean;
|
|
14
|
+
displayMode: DisplayMode;
|
|
15
|
+
};
|
|
16
|
+
declare const getEditorReadOnlyReason: ({ editingMode, readOnlyProp, displayMode }: EditorReadOnlyReasonOptions) => EditorReadOnlyReason | null;
|
|
8
17
|
/** EditorModeManager snapshot. */
|
|
9
18
|
type EditorModeSnapshot = {
|
|
10
19
|
/** Uncontrolled internal editing mode (the host's controlled prop wins over this). */
|
|
@@ -26,4 +35,4 @@ declare class EditorModeManager extends Subscribable<EditorModeSnapshot> {
|
|
|
26
35
|
setDisplayMode(mode: DisplayMode): void;
|
|
27
36
|
}
|
|
28
37
|
//#endregion
|
|
29
|
-
export { DISPLAY_MODES, DisplayMode, EditorMode, EditorModeManager, EditorModeSnapshot, isReadOnlyMode, isTrackChangesMode, toggledTrackChangesMode };
|
|
38
|
+
export { DISPLAY_MODES, DisplayMode, EditorMode, EditorModeManager, EditorModeSnapshot, EditorReadOnlyReason, getEditorReadOnlyReason, getReviewViewReadOnlyReason, isReadOnlyMode, isTrackChangesMode, toggledTrackChangesMode };
|
|
@@ -18,6 +18,20 @@ const DISPLAY_MODES = [
|
|
|
18
18
|
"no-markup",
|
|
19
19
|
"original"
|
|
20
20
|
];
|
|
21
|
+
const REVIEW_VIEW_ACCESS = {
|
|
22
|
+
"all-markup": "editable",
|
|
23
|
+
"simple-markup": "read-only",
|
|
24
|
+
"no-markup": "read-only",
|
|
25
|
+
original: "read-only"
|
|
26
|
+
};
|
|
27
|
+
const getReviewViewReadOnlyReason = (displayMode) => REVIEW_VIEW_ACCESS[displayMode] === "read-only" ? "review-display-mode" : null;
|
|
28
|
+
const getEditorReadOnlyReason = ({ editingMode, readOnlyProp, displayMode }) => {
|
|
29
|
+
const viewReason = getReviewViewReadOnlyReason(displayMode);
|
|
30
|
+
if (viewReason) return viewReason;
|
|
31
|
+
if (readOnlyProp) return "host-read-only";
|
|
32
|
+
if (editingMode === "viewing") return "viewing-mode";
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
21
35
|
/** True when the host opts into read-only OR the mode is `"viewing"`. */
|
|
22
36
|
const isReadOnlyMode = (editingMode, readOnlyProp) => readOnlyProp || editingMode === "viewing";
|
|
23
37
|
/** True when the mode is `"suggesting"`. */
|
|
@@ -47,4 +61,4 @@ var EditorModeManager = class extends Subscribable {
|
|
|
47
61
|
}
|
|
48
62
|
};
|
|
49
63
|
//#endregion
|
|
50
|
-
export { DISPLAY_MODES, EditorModeManager, isReadOnlyMode, isTrackChangesMode, toggledTrackChangesMode };
|
|
64
|
+
export { DISPLAY_MODES, EditorModeManager, getEditorReadOnlyReason, getReviewViewReadOnlyReason, isReadOnlyMode, isTrackChangesMode, toggledTrackChangesMode };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
-
import { TableAction, TableContext } from "../utils/tableOperations.js";
|
|
3
2
|
import { Subscribable } from "./Subscribable.js";
|
|
3
|
+
import { TableAction, TableContext } from "../utils/tableOperations.js";
|
|
4
4
|
import { CellCoordinates } from "./types.js";
|
|
5
5
|
//#region src/managers/TableSelectionManager.d.ts
|
|
6
6
|
/** Data attributes for table elements in the rendered DOM */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { validateFolioDocumentModel } from "../docx/modelValidation.js";
|
|
2
2
|
import { bytesToBase64 } from "../utils/base64.js";
|
|
3
|
+
import { base64ToBytes } from "../utils/textEncoding.js";
|
|
3
4
|
//#region src/managers/autoSaveCodec.ts
|
|
4
5
|
/**
|
|
5
6
|
* Version 3 renamed the model's `bidiWrapper` to `inlineWrapper` and gave it a
|
|
@@ -10,12 +11,7 @@ import { bytesToBase64 } from "../utils/base64.js";
|
|
|
10
11
|
* re-taken rather than migrated.
|
|
11
12
|
*/
|
|
12
13
|
const AUTOSAVE_FORMAT_VERSION = 3;
|
|
13
|
-
const decodeBase64 = (value) =>
|
|
14
|
-
const binary = atob(value);
|
|
15
|
-
const bytes = new Uint8Array(binary.length);
|
|
16
|
-
for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
|
|
17
|
-
return bytes.buffer;
|
|
18
|
-
};
|
|
14
|
+
const decodeBase64 = (value) => base64ToBytes(value).buffer;
|
|
19
15
|
const encodeValue = (value) => {
|
|
20
16
|
if (value === void 0) return { type: "undefined" };
|
|
21
17
|
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DisplayMode } from "./EditorModeManager.js";
|
|
2
|
+
//#region src/managers/reviewDisplayMode.d.ts
|
|
3
|
+
/** The host's reading of pending revisions; it never changes the saved document. */
|
|
4
|
+
declare const REVIEW_DISPLAY_MODES: readonly ["all-markup", "final", "original"];
|
|
5
|
+
type ReviewDisplayMode = (typeof REVIEW_DISPLAY_MODES)[number];
|
|
6
|
+
declare const reviewDisplayModeToDisplayMode: (mode: ReviewDisplayMode) => DisplayMode;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { REVIEW_DISPLAY_MODES, ReviewDisplayMode, reviewDisplayModeToDisplayMode };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/managers/reviewDisplayMode.ts
|
|
2
|
+
/** The host's reading of pending revisions; it never changes the saved document. */
|
|
3
|
+
const REVIEW_DISPLAY_MODES = [
|
|
4
|
+
"all-markup",
|
|
5
|
+
"final",
|
|
6
|
+
"original"
|
|
7
|
+
];
|
|
8
|
+
const REVIEW_DISPLAY_MODE_VIEWS = {
|
|
9
|
+
"all-markup": "all-markup",
|
|
10
|
+
final: "no-markup",
|
|
11
|
+
original: "original"
|
|
12
|
+
};
|
|
13
|
+
const reviewDisplayModeToDisplayMode = (mode) => REVIEW_DISPLAY_MODE_VIEWS[mode];
|
|
14
|
+
//#endregion
|
|
15
|
+
export { REVIEW_DISPLAY_MODES, reviewDisplayModeToDisplayMode };
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { Layout } from "../layout-engine/types.js";
|
|
2
2
|
import "../layout-engine/index.js";
|
|
3
|
+
import { MarkupViewProjection } from "../prosemirror/markupViewProjection.js";
|
|
3
4
|
import { Node } from "prosemirror-model";
|
|
4
5
|
//#region src/paged-layout/pageText.d.ts
|
|
6
|
+
type PageTextOptions = {
|
|
7
|
+
layout: Layout | null;
|
|
8
|
+
doc: Node;
|
|
9
|
+
page: number;
|
|
10
|
+
projection?: MarkupViewProjection;
|
|
11
|
+
};
|
|
5
12
|
/**
|
|
6
13
|
* The plain text of a single rendered page (1-based), read by joining each of
|
|
7
14
|
* the page's layout fragments' `[pmStart, pmEnd)` document text with a
|
|
8
|
-
* newline.
|
|
15
|
+
* newline. A review projection maps those authored ranges to the displayed
|
|
16
|
+
* document, excluding text the view hides. Fragments with no `pmStart`/`pmEnd`
|
|
17
|
+
* (e.g. a page-shell placeholder)
|
|
9
18
|
* are skipped. Returns `null` when the layout hasn't been computed yet or the
|
|
10
19
|
* page number is out of range.
|
|
11
20
|
*/
|
|
12
|
-
declare const getPageTextFromLayout: (layout
|
|
21
|
+
declare const getPageTextFromLayout: ({ layout, doc, page, projection }: PageTextOptions) => string | null;
|
|
13
22
|
//#endregion
|
|
14
23
|
export { getPageTextFromLayout };
|
|
@@ -2,18 +2,23 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* The plain text of a single rendered page (1-based), read by joining each of
|
|
4
4
|
* the page's layout fragments' `[pmStart, pmEnd)` document text with a
|
|
5
|
-
* newline.
|
|
5
|
+
* newline. A review projection maps those authored ranges to the displayed
|
|
6
|
+
* document, excluding text the view hides. Fragments with no `pmStart`/`pmEnd`
|
|
7
|
+
* (e.g. a page-shell placeholder)
|
|
6
8
|
* are skipped. Returns `null` when the layout hasn't been computed yet or the
|
|
7
9
|
* page number is out of range.
|
|
8
10
|
*/
|
|
9
|
-
const getPageTextFromLayout = (layout, doc, page) => {
|
|
11
|
+
const getPageTextFromLayout = ({ layout, doc, page, projection }) => {
|
|
10
12
|
if (!layout || !Number.isInteger(page) || page < 1) return null;
|
|
11
13
|
const layoutPage = layout.pages[page - 1];
|
|
12
14
|
if (!layoutPage) return null;
|
|
15
|
+
const viewDoc = projection?.type === "resolved" ? projection.doc : doc;
|
|
13
16
|
const fragmentTexts = [];
|
|
14
17
|
for (const fragment of layoutPage.fragments) {
|
|
15
18
|
if (typeof fragment.pmStart !== "number" || typeof fragment.pmEnd !== "number") continue;
|
|
16
|
-
|
|
19
|
+
const from = projection?.type === "resolved" ? projection.toView(fragment.pmStart, 1) : fragment.pmStart;
|
|
20
|
+
const to = projection?.type === "resolved" ? projection.toView(fragment.pmEnd, -1) : fragment.pmEnd;
|
|
21
|
+
fragmentTexts.push(viewDoc.textBetween(from, to, "\n"));
|
|
17
22
|
}
|
|
18
23
|
return fragmentTexts.join("\n");
|
|
19
24
|
};
|
package/dist/pdf/fonts.js
CHANGED
|
@@ -2,6 +2,7 @@ import { parseSfnt } from "../fonts/sfnt/parse.js";
|
|
|
2
2
|
import { subsetTrueType } from "../fonts/sfnt/subset.js";
|
|
3
3
|
import { toSfntBytes } from "../fonts/sfnt/woff.js";
|
|
4
4
|
import { needsShaping, placeRun } from "../shaping/placeRun.js";
|
|
5
|
+
import { bytesToHex, decodeText, encodeUtf16BE, encodeUtf8 } from "../utils/textEncoding.js";
|
|
5
6
|
import { pdfArray, pdfAsciiString, pdfDict, pdfFlateStream, pdfName, pdfNumber, pdfNumberArray } from "./objects.js";
|
|
6
7
|
import { Result, TaggedError, panic } from "better-result";
|
|
7
8
|
//#region src/pdf/fonts.ts
|
|
@@ -46,7 +47,7 @@ const SUBSET_TAG_LENGTH = 6;
|
|
|
46
47
|
const ALPHABET_LENGTH = 26;
|
|
47
48
|
const UPPERCASE_A = 65;
|
|
48
49
|
const WOFF_SIGNATURES = ["wOFF", "wOF2"];
|
|
49
|
-
const readTag = (bytes) =>
|
|
50
|
+
const readTag = (bytes) => decodeText(Uint8Array.of(bytes[0] ?? 0, bytes[1] ?? 0, bytes[2] ?? 0, bytes[3] ?? 0), { encoding: "binary" });
|
|
50
51
|
/**
|
|
51
52
|
* Six uppercase letters derived from the face and the exact glyph set, so the
|
|
52
53
|
* tag is stable across runs and two different subsets of one family do not
|
|
@@ -148,18 +149,8 @@ const buildWinAnsiTable = () => {
|
|
|
148
149
|
return table;
|
|
149
150
|
};
|
|
150
151
|
const WIN_ANSI_BY_CODE_POINT = buildWinAnsiTable();
|
|
151
|
-
const utf16BeHexOf = (text) =>
|
|
152
|
-
const utf16BeHex = (codePoint) =>
|
|
153
|
-
const SUPPLEMENTARY_START = 65536;
|
|
154
|
-
const SURROGATE_HALF_BITS = 10;
|
|
155
|
-
const LOW_SURROGATE_MASK = 1023;
|
|
156
|
-
const HIGH_SURROGATE_BASE = 55296;
|
|
157
|
-
const LOW_SURROGATE_BASE = 56320;
|
|
158
|
-
const hex = (unit) => unit.toString(16).padStart(4, "0").toUpperCase();
|
|
159
|
-
if (codePoint < SUPPLEMENTARY_START) return hex(codePoint);
|
|
160
|
-
const offset = codePoint - SUPPLEMENTARY_START;
|
|
161
|
-
return hex(HIGH_SURROGATE_BASE + (offset >> SURROGATE_HALF_BITS)) + hex(LOW_SURROGATE_BASE + (offset & LOW_SURROGATE_MASK));
|
|
162
|
-
};
|
|
152
|
+
const utf16BeHexOf = (text) => bytesToHex(encodeUtf16BE(text)).toUpperCase();
|
|
153
|
+
const utf16BeHex = (codePoint) => utf16BeHexOf(String.fromCodePoint(codePoint));
|
|
163
154
|
/** Entries per `beginbfchar` block; the CMap syntax caps this at 100. */
|
|
164
155
|
const BFCHAR_BLOCK_SIZE = 100;
|
|
165
156
|
const toUnicodeCMap = (glyphToText) => {
|
|
@@ -290,7 +281,7 @@ const emitFont = (document, plan) => {
|
|
|
290
281
|
fontFileKey: font.isCff ? "FontFile3" : "FontFile2",
|
|
291
282
|
fontFileRef
|
|
292
283
|
}));
|
|
293
|
-
const toUnicodeRef = document.add(pdfFlateStream([],
|
|
284
|
+
const toUnicodeRef = document.add(pdfFlateStream([], encodeUtf8(toUnicodeCMap(plan.glyphToText))));
|
|
294
285
|
const descendantRef = document.add(pdfDict([
|
|
295
286
|
["Type", pdfName("Font")],
|
|
296
287
|
["Subtype", pdfName(font.isCff ? "CIDFontType0" : "CIDFontType2")],
|
package/dist/pdf/images.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeText } from "../utils/textEncoding.js";
|
|
1
2
|
import { pdfArray, pdfHexString, pdfName, pdfNumber } from "./objects.js";
|
|
2
3
|
import { Result, TaggedError } from "better-result";
|
|
3
4
|
import { inflateSync } from "node:zlib";
|
|
@@ -63,7 +64,7 @@ const readPngChunks = (bytes) => {
|
|
|
63
64
|
const CHUNK_CRC_BYTES = 4;
|
|
64
65
|
while (cursor + CHUNK_HEADER_BYTES <= bytes.length) {
|
|
65
66
|
const length = view.getUint32(cursor);
|
|
66
|
-
const type =
|
|
67
|
+
const type = decodeText(bytes.subarray(cursor + 4, cursor + 8), { encoding: "binary" });
|
|
67
68
|
const dataStart = cursor + CHUNK_HEADER_BYTES;
|
|
68
69
|
const dataEnd = dataStart + length;
|
|
69
70
|
if (dataEnd > bytes.length) return Result.err(new PdfImageError({ message: `PNG chunk ${type} runs past the end` }));
|
|
@@ -538,7 +539,7 @@ const decodeJpeg = (bytes) => {
|
|
|
538
539
|
const length = view.getUint16(cursor + 2);
|
|
539
540
|
const segment = cursor + 4;
|
|
540
541
|
const frame = JPEG_FRAME_MODES[marker];
|
|
541
|
-
if (marker === APP14 && length >= 12) adobe =
|
|
542
|
+
if (marker === APP14 && length >= 12) adobe = decodeText(bytes.subarray(segment, segment + 5), { encoding: "binary" }) === "Adobe";
|
|
542
543
|
if (frame !== void 0) {
|
|
543
544
|
if (!frame.dctDecodable) return Result.err(new PdfImageError({ message: `JPEG ${frame.name} frame (marker 0xff${marker.toString(16)}) has no /DCTDecode equivalent` }));
|
|
544
545
|
const height = view.getUint16(segment + 1);
|
package/dist/pdf/objects.d.ts
CHANGED
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
//#region src/pdf/objects.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* The PDF object model: values, serialization, the cross-reference table and
|
|
4
|
-
* the trailer.
|
|
5
|
-
*
|
|
6
|
-
* Everything a backend writes into a PDF file passes through here, which is
|
|
7
|
-
* what makes byte-for-byte determinism enforceable rather than aspirational:
|
|
8
|
-
* numbers go through one formatter, dictionaries carry their entries as an
|
|
9
|
-
* ordered list (never an object or a `Map` whose iteration order depends on
|
|
10
|
-
* how it was built), and object numbers come from one allocator in call
|
|
11
|
-
* order.
|
|
12
|
-
*/
|
|
13
2
|
/** The one number formatter. Every number in the file is emitted through it. */
|
|
14
3
|
declare const formatNumber: (value: number) => string;
|
|
15
4
|
type PdfRef = {
|
package/dist/pdf/objects.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bytesToHex, encodeBinaryString, encodeUtf16BE, encodeUtf8 } from "../utils/textEncoding.js";
|
|
1
2
|
import { panic } from "better-result";
|
|
2
3
|
import { deflateSync } from "node:zlib";
|
|
3
4
|
//#region src/pdf/objects.ts
|
|
@@ -94,7 +95,10 @@ const pdfFlateStream = (entries, data) => pdfStream(pdfDict([...entries, ["Filte
|
|
|
94
95
|
const NAME_REGULAR = /^[A-Za-z0-9._\-+]$/u;
|
|
95
96
|
const encodeName = (value) => {
|
|
96
97
|
let out = "/";
|
|
97
|
-
for (const
|
|
98
|
+
for (const byte of encodeUtf8(value)) {
|
|
99
|
+
const char = String.fromCharCode(byte);
|
|
100
|
+
out += NAME_REGULAR.test(char) ? char : `#${byte.toString(16).padStart(2, "0").toUpperCase()}`;
|
|
101
|
+
}
|
|
98
102
|
return out;
|
|
99
103
|
};
|
|
100
104
|
const encodeAsciiString = (value) => {
|
|
@@ -108,11 +112,7 @@ const encodeAsciiString = (value) => {
|
|
|
108
112
|
}
|
|
109
113
|
return `${out})`;
|
|
110
114
|
};
|
|
111
|
-
const encodeTextString = (value) => {
|
|
112
|
-
let hex = "FEFF";
|
|
113
|
-
for (let index = 0; index < value.length; index += 1) hex += value.charCodeAt(index).toString(16).padStart(4, "0").toUpperCase();
|
|
114
|
-
return `<${hex}>`;
|
|
115
|
-
};
|
|
115
|
+
const encodeTextString = (value) => `<FEFF${bytesToHex(encodeUtf16BE(value)).toUpperCase()}>`;
|
|
116
116
|
const createByteSink = () => {
|
|
117
117
|
const chunks = [];
|
|
118
118
|
let length = 0;
|
|
@@ -122,9 +122,7 @@ const createByteSink = () => {
|
|
|
122
122
|
};
|
|
123
123
|
return {
|
|
124
124
|
writeAscii: (text) => {
|
|
125
|
-
|
|
126
|
-
for (let index = 0; index < text.length; index += 1) bytes[index] = text.charCodeAt(index) & 255;
|
|
127
|
-
writeBytes(bytes);
|
|
125
|
+
writeBytes(encodeBinaryString(text));
|
|
128
126
|
},
|
|
129
127
|
writeBytes,
|
|
130
128
|
offset: () => length,
|
package/dist/pdf/writePdf.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getShaper } from "../shaping/shaper.js";
|
|
2
|
+
import { encodeUtf8 } from "../utils/textEncoding.js";
|
|
2
3
|
import { collectResources, renderContentStream } from "./contentStream.js";
|
|
3
4
|
import { prepareFonts } from "./fonts.js";
|
|
4
5
|
import { decodeImage } from "./images.js";
|
|
@@ -288,7 +289,7 @@ const writePdf = async (list, options) => {
|
|
|
288
289
|
fonts: byFontIndex
|
|
289
290
|
});
|
|
290
291
|
const resources = collectResources(parts);
|
|
291
|
-
const contentRef = document.add(pdfFlateStream([],
|
|
292
|
+
const contentRef = document.add(pdfFlateStream([], encodeUtf8(renderContentStream(parts, resources))));
|
|
292
293
|
const annotationRefs = [];
|
|
293
294
|
for (const link of page.links) {
|
|
294
295
|
const annotation = buildLinkAnnotation({
|