@a3s-lab/office 0.9.2 → 0.10.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/COLLABORATION_ROADMAP.md +19 -5
- package/README.md +16 -6
- package/dist/{0~7754.js → 0~4705.js} +500 -3
- package/dist/0~document-editor.js +7 -1
- package/dist/0~work-docx-export.js +260 -13
- package/dist/0~work-docx-import.js +9 -391
- package/dist/0~work-office-diagnostics.js +6 -4
- package/dist/8928.js +325 -22
- package/dist/internal/features/work/work-document-format-change-tracking.d.ts +9 -0
- package/dist/internal/features/work/work-document-format-changes.d.ts +28 -0
- package/dist/internal/features/work/work-docx-format-change-export.d.ts +17 -0
- package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +10 -0
- package/dist/internal/features/work/work-types.d.ts +1 -1
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +20 -0
- package/package.json +5 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import jszip from "jszip";
|
|
2
|
-
import { normalizeDocumentBookmarkName, applyDocumentImageIdentityToElement, DOCUMENT_PAGE_BORDER_EDGES, createDocumentImageIdentityRegistry, normalizeDocumentColumns, normalizeDocumentTableVerticalAlign, uniqueDocumentParagraphIdentity, documentTableColumnPercentagesFromElement, documentTableCellMarginOverridesFromElement, applyDocumentTableRowIdentityToElement, normalizeDocumentBookmarksHtml, documentImageIdentityFromElement, documentTableGeometryFromElement, normalizeDocumentTableRowHeightRule, documentNoteKey, normalizeDocumentParagraphBorders, normalizeDocumentTableBorderWidth, normalizeDocumentParagraphId, normalizeDocumentTableBorderStyle, normalizeDocumentBookmarkReferencesHtml, documentCitationInstruction, parseDocumentParagraphBordersElement, documentImageWrapContourFromElement, documentImagePositionFromElement, normalizeDocumentPageChrome, documentPageMarginsForLayout, normalizeDocumentImageLayer, documentBookmarkReferenceInstruction, documentImageCropFromElement, normalizeDocumentPageBorders, documentImageLayerFromElement,
|
|
2
|
+
import { normalizeDocumentBookmarkName, applyDocumentImageIdentityToElement, DOCUMENT_PAGE_BORDER_EDGES, createDocumentImageIdentityRegistry, normalizeDocumentColumns, normalizeDocumentTableVerticalAlign, uniqueDocumentParagraphIdentity, documentTableColumnPercentagesFromElement, documentTableCellMarginOverridesFromElement, applyDocumentTableRowIdentityToElement, normalizeDocumentBookmarksHtml, documentImageIdentityFromElement, documentTableGeometryFromElement, normalizeDocumentTableRowHeightRule, documentNoteKey, normalizeDocumentParagraphBorders, normalizeDocumentTableBorderWidth, normalizeDocumentParagraphId, normalizeDocumentTableBorderStyle, normalizeDocumentBookmarkReferencesHtml, documentCitationInstruction, parseDocumentParagraphBordersElement, documentImageWrapContourFromElement, documentImagePositionFromElement, normalizeDocumentPageChrome, documentPageMarginsForLayout, normalizeDocumentImageLayer, documentBookmarkReferenceInstruction, documentImageCropFromElement, normalizeDocumentPageBorders, documentImageLayerFromElement, parseDocxThemeReference, documentCaptionLabel, documentCaptionKind, applyDocumentParagraphIdentityToElement, isContourImageLayout, normalizeDocumentImageIdentity, normalizeTableColor, documentTableBordersFromElement, documentSections, createDocumentParagraphIdentityRegistry, documentCitationTags, docxDocumentFieldKind, wrapsBesideImage, DocxThemePatchCollector, defaultDocumentImageWrapContour, normalizeDocumentParagraphIdentity, normalizeDocumentPaperSource, documentEquationFromElement, DOCUMENT_PARAGRAPH_BORDER_EDGES, parseDocumentCharacterFormatting, patchDocxThemeReferences, parseDocumentParagraphShadingElement, documentParagraphIdentityFromElement, normalizeDocumentEquation, documentTableRowIdentityFromElement, collectDocumentNotes, normalizeDocumentBookmarkNativeId, DOCUMENT_PAGE_MARGIN_KEYS, uniqueDocumentImageIdentity, documentImageLayoutFromElement, documentPageHorizontalMarginTwips, documentPageGeometryForLayout, normalizeDocumentPageMargins, documentNoteKind } from "./8928.js";
|
|
3
3
|
import { serializeUtf8Xml, directChildren, xmlNamespacePrefix, parseXml, decodeXmlBytes, descendants, resolvePartTarget, directChild, attribute as work_ooxml_package_attribute } from "./5184.js";
|
|
4
4
|
import { assignDocxNoteIds, patchDocxPageColor, normalizeDocxCommentId, assignDocxCommentThreads } from "./0~4980.js";
|
|
5
5
|
import { paragraphTabStops, mergeDocxIgnorableExtensionsAtPairs, patchDocxBibliography, XMLNS_NAMESPACE as work_docx_settings_xml_XMLNS_NAMESPACE, paragraphAlignment, paragraphSpacingOptions, xmlNamespaceUri, hasNonWhitespaceXmlText, cloneXmlElement, cssColorToHex, DocxParagraphDefaultCollapsedPatchCollector, assertXmlRoot, xmlDeclaredPrefix, paragraphIndent, domDirection, DOCX_WORDPROCESSING_NAMESPACES, paragraphBidirectional, XML_NAMESPACE as work_docx_settings_xml_XML_NAMESPACE, patchDocxParagraphDefaultCollapsed, xmlAttributeNamespace, dataBoolean, mergeDocxIgnorableExtensions, parseDocxColorSchemeMappingElement, paragraphPaginationOptions, DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES, xmlAttributeLocalName, cssFontSize, paragraphDirectionOptions, cssFontFamily } from "./0~2805.js";
|
|
@@ -2095,6 +2095,240 @@ function docxDocumentFieldRun(element, docx) {
|
|
|
2095
2095
|
if (!docxDocumentFieldKind(instruction)) return new docx.TextRun(display);
|
|
2096
2096
|
return new docx.SimpleField(instruction, display);
|
|
2097
2097
|
}
|
|
2098
|
+
const MAX_RUN_FORMATTING_CHANGE_PATCHES = 65536;
|
|
2099
|
+
const RUN_FORMATTING_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes)\.xml$/i;
|
|
2100
|
+
class DocxRunFormattingChangePatchCollector {
|
|
2101
|
+
patches = [];
|
|
2102
|
+
register(element, id) {
|
|
2103
|
+
if ('formatting' !== element.dataset.changeKind || !element.hasAttribute('data-document-change')) return null;
|
|
2104
|
+
const key = element.dataset.changeId?.trim() ?? '';
|
|
2105
|
+
const author = element.dataset.changeAuthor?.trim() ?? '';
|
|
2106
|
+
const date = normalizedRevisionDate(element.dataset.changeDate);
|
|
2107
|
+
const before = element.dataset.changeBefore ?? '';
|
|
2108
|
+
if (!key || !author || author.length > 255 || !parseDocumentCharacterFormatting(before)) throw new Error('Document contains an invalid character-formatting revision.');
|
|
2109
|
+
if (this.patches.length >= MAX_RUN_FORMATTING_CHANGE_PATCHES) throw new Error('Document exceeds the character-formatting revision limit.');
|
|
2110
|
+
const sequence = this.patches.length + 1;
|
|
2111
|
+
const start = `__A3S_WORK_FORMAT_CHANGE_START_${sequence}__`;
|
|
2112
|
+
const end = `__A3S_WORK_FORMAT_CHANGE_END_${sequence}__`;
|
|
2113
|
+
this.patches.push({
|
|
2114
|
+
start,
|
|
2115
|
+
end,
|
|
2116
|
+
id,
|
|
2117
|
+
author,
|
|
2118
|
+
date,
|
|
2119
|
+
before
|
|
2120
|
+
});
|
|
2121
|
+
return {
|
|
2122
|
+
start,
|
|
2123
|
+
end
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
async function patchDocxRunFormattingChanges(buffer, patches) {
|
|
2128
|
+
if (!patches.length) return buffer;
|
|
2129
|
+
if (patches.length > MAX_RUN_FORMATTING_CHANGE_PATCHES) throw new Error('Document exceeds the character-formatting revision limit.');
|
|
2130
|
+
const archive = await jszip.loadAsync(buffer);
|
|
2131
|
+
const byMarker = new Map();
|
|
2132
|
+
for (const patch of patches){
|
|
2133
|
+
byMarker.set(patch.start, patch);
|
|
2134
|
+
byMarker.set(patch.end, patch);
|
|
2135
|
+
}
|
|
2136
|
+
const applied = new Set();
|
|
2137
|
+
for (const entry of Object.values(archive.files)){
|
|
2138
|
+
if (entry.dir || !RUN_FORMATTING_PART_PATTERN.test(entry.name)) continue;
|
|
2139
|
+
const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
|
|
2140
|
+
const runs = descendants(document, 'r').filter((run)=>DOCX_WORDPROCESSING_NAMESPACES.has(run.namespaceURI ?? ''));
|
|
2141
|
+
const markerRuns = new Map();
|
|
2142
|
+
for (const run of runs){
|
|
2143
|
+
const marker = runMarker(run, byMarker);
|
|
2144
|
+
if (!marker) continue;
|
|
2145
|
+
const current = markerRuns.get(marker.patch) ?? {};
|
|
2146
|
+
if ('start' === marker.kind) {
|
|
2147
|
+
if (current.start) throw duplicateMarker(marker.patch.start);
|
|
2148
|
+
current.start = run;
|
|
2149
|
+
} else {
|
|
2150
|
+
if (current.end) throw duplicateMarker(marker.patch.end);
|
|
2151
|
+
current.end = run;
|
|
2152
|
+
}
|
|
2153
|
+
markerRuns.set(marker.patch, current);
|
|
2154
|
+
}
|
|
2155
|
+
let changed = false;
|
|
2156
|
+
for (const [patch, markers] of markerRuns){
|
|
2157
|
+
if (!markers.start || !markers.end) continue;
|
|
2158
|
+
const start = runs.indexOf(markers.start);
|
|
2159
|
+
const end = runs.indexOf(markers.end);
|
|
2160
|
+
if (start < 0 || end <= start + 1) throw new Error('Generated DOCX character-formatting markers are invalid.');
|
|
2161
|
+
const targets = runs.slice(start + 1, end).filter((run)=>directChildren(run, 't').length > 0);
|
|
2162
|
+
if (!targets.length) throw new Error('A character-formatting revision must mark text.');
|
|
2163
|
+
for (const run of targets)setRunFormattingChange(document, run, patch);
|
|
2164
|
+
markers.start.remove();
|
|
2165
|
+
markers.end.remove();
|
|
2166
|
+
applied.add(patch);
|
|
2167
|
+
changed = true;
|
|
2168
|
+
}
|
|
2169
|
+
if (changed) archive.file(entry.name, serializeUtf8Xml(document));
|
|
2170
|
+
}
|
|
2171
|
+
const missing = patches.filter((patch)=>!applied.has(patch));
|
|
2172
|
+
if (missing.length) throw new Error(`DOCX character-formatting revision markers were not emitted: ${missing.map((patch)=>patch.start).join(', ')}.`);
|
|
2173
|
+
return archive.generateAsync({
|
|
2174
|
+
type: 'arraybuffer'
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
function runMarker(run, patches) {
|
|
2178
|
+
const texts = directChildren(run, 't').filter((text)=>DOCX_WORDPROCESSING_NAMESPACES.has(text.namespaceURI ?? ''));
|
|
2179
|
+
if (1 !== texts.length || !work_docx_format_change_export_runHasOnlyMarkerText(run, texts[0])) return null;
|
|
2180
|
+
const value = texts[0].textContent ?? '';
|
|
2181
|
+
const patch = patches.get(value);
|
|
2182
|
+
if (!patch) return null;
|
|
2183
|
+
return {
|
|
2184
|
+
patch,
|
|
2185
|
+
kind: value === patch.start ? 'start' : 'end'
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
function work_docx_format_change_export_runHasOnlyMarkerText(run, text) {
|
|
2189
|
+
return directChildren(run).every((child)=>child === text || 'rPr' === child.localName && DOCX_WORDPROCESSING_NAMESPACES.has(child.namespaceURI ?? ''));
|
|
2190
|
+
}
|
|
2191
|
+
function setRunFormattingChange(document, run, patch) {
|
|
2192
|
+
const wordNamespace = run.namespaceURI ?? '';
|
|
2193
|
+
const prefix = run.prefix || 'w';
|
|
2194
|
+
const existing = directChildren(run, 'rPr').filter((element)=>element.namespaceURI === wordNamespace);
|
|
2195
|
+
if (existing.length > 1) throw new Error('Generated DOCX run contains duplicate properties.');
|
|
2196
|
+
const properties = existing[0] ?? insertRunProperties(document, run, wordNamespace, prefix);
|
|
2197
|
+
if (directChildren(properties, 'rPrChange').some((element)=>element.namespaceURI === wordNamespace)) throw new Error('Generated DOCX run already contains a formatting revision.');
|
|
2198
|
+
const change = wordElement(document, wordNamespace, prefix, 'rPrChange');
|
|
2199
|
+
work_docx_format_change_export_setWordAttribute(change, wordNamespace, prefix, 'id', String(patch.id));
|
|
2200
|
+
work_docx_format_change_export_setWordAttribute(change, wordNamespace, prefix, 'author', patch.author);
|
|
2201
|
+
work_docx_format_change_export_setWordAttribute(change, wordNamespace, prefix, 'date', patch.date);
|
|
2202
|
+
const before = wordElement(document, wordNamespace, prefix, 'rPr');
|
|
2203
|
+
appendFormattingProperties(document, before, wordNamespace, prefix, patch.before);
|
|
2204
|
+
change.append(before);
|
|
2205
|
+
properties.append(change);
|
|
2206
|
+
}
|
|
2207
|
+
function insertRunProperties(document, run, namespace, prefix) {
|
|
2208
|
+
const properties = wordElement(document, namespace, prefix, 'rPr');
|
|
2209
|
+
run.insertBefore(properties, run.firstChild);
|
|
2210
|
+
return properties;
|
|
2211
|
+
}
|
|
2212
|
+
function appendFormattingProperties(document, properties, namespace, prefix, serialized) {
|
|
2213
|
+
const formatting = parseDocumentCharacterFormatting(serialized);
|
|
2214
|
+
if (!formatting) throw new Error('Document contains an invalid character-formatting revision.');
|
|
2215
|
+
const byType = new Map(formatting.map((mark)=>[
|
|
2216
|
+
mark.type,
|
|
2217
|
+
mark
|
|
2218
|
+
]));
|
|
2219
|
+
appendFontProperties(document, properties, namespace, prefix, byType.get('textStyle'));
|
|
2220
|
+
appendBooleanProperty(document, properties, namespace, prefix, byType, 'bold', 'b');
|
|
2221
|
+
appendBooleanProperty(document, properties, namespace, prefix, byType, 'bold', 'bCs');
|
|
2222
|
+
appendBooleanProperty(document, properties, namespace, prefix, byType, 'italic', 'i');
|
|
2223
|
+
appendBooleanProperty(document, properties, namespace, prefix, byType, 'italic', 'iCs');
|
|
2224
|
+
if (byType.has('underline')) appendValuedProperty(document, properties, namespace, prefix, 'u', 'single');
|
|
2225
|
+
appendBooleanProperty(document, properties, namespace, prefix, byType, 'strike', 'strike');
|
|
2226
|
+
appendColorProperty(document, properties, namespace, prefix, byType.get('textStyle'));
|
|
2227
|
+
appendFontSizeProperties(document, properties, namespace, prefix, byType.get('textStyle'));
|
|
2228
|
+
appendHighlightProperty(document, properties, namespace, prefix, byType.get('highlight'));
|
|
2229
|
+
appendSnapToGridProperty(document, properties, namespace, prefix, byType.get('textStyle'));
|
|
2230
|
+
appendVerticalAlignProperty(document, properties, namespace, prefix, byType);
|
|
2231
|
+
}
|
|
2232
|
+
function appendFontProperties(document, properties, namespace, prefix, mark) {
|
|
2233
|
+
const family = firstFontFamily(mark?.attrs?.fontFamily);
|
|
2234
|
+
if (!family) return;
|
|
2235
|
+
const fonts = wordElement(document, namespace, prefix, 'rFonts');
|
|
2236
|
+
for (const name of [
|
|
2237
|
+
'ascii',
|
|
2238
|
+
'hAnsi',
|
|
2239
|
+
'eastAsia',
|
|
2240
|
+
'cs'
|
|
2241
|
+
])work_docx_format_change_export_setWordAttribute(fonts, namespace, prefix, name, family);
|
|
2242
|
+
properties.append(fonts);
|
|
2243
|
+
}
|
|
2244
|
+
function appendBooleanProperty(document, properties, namespace, prefix, marks, mark, property) {
|
|
2245
|
+
if (marks.has(mark)) properties.append(wordElement(document, namespace, prefix, property));
|
|
2246
|
+
}
|
|
2247
|
+
function appendValuedProperty(document, properties, namespace, prefix, property, value) {
|
|
2248
|
+
const element = wordElement(document, namespace, prefix, property);
|
|
2249
|
+
work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', value);
|
|
2250
|
+
properties.append(element);
|
|
2251
|
+
}
|
|
2252
|
+
function appendColorProperty(document, properties, namespace, prefix, mark) {
|
|
2253
|
+
const color = normalizedHex(mark?.attrs?.color);
|
|
2254
|
+
const theme = themeReference(mark?.attrs?.themeColor);
|
|
2255
|
+
if (!color && !theme) return;
|
|
2256
|
+
const element = wordElement(document, namespace, prefix, 'color');
|
|
2257
|
+
work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', color ?? normalizedHex(theme?.resolved) ?? '000000');
|
|
2258
|
+
if (theme) setThemeAttributes(element, namespace, prefix, theme, 'themeColor');
|
|
2259
|
+
properties.append(element);
|
|
2260
|
+
}
|
|
2261
|
+
function appendFontSizeProperties(document, properties, namespace, prefix, mark) {
|
|
2262
|
+
const halfPoints = fontSizeHalfPoints(mark?.attrs?.fontSize);
|
|
2263
|
+
if (null === halfPoints) return;
|
|
2264
|
+
appendValuedProperty(document, properties, namespace, prefix, 'sz', String(halfPoints));
|
|
2265
|
+
appendValuedProperty(document, properties, namespace, prefix, 'szCs', String(halfPoints));
|
|
2266
|
+
}
|
|
2267
|
+
function appendHighlightProperty(document, properties, namespace, prefix, mark) {
|
|
2268
|
+
const fill = normalizedHex(mark?.attrs?.color);
|
|
2269
|
+
const theme = themeReference(mark?.attrs?.themeFill);
|
|
2270
|
+
if (!fill && !theme) return;
|
|
2271
|
+
const shading = wordElement(document, namespace, prefix, 'shd');
|
|
2272
|
+
work_docx_format_change_export_setWordAttribute(shading, namespace, prefix, 'val', 'clear');
|
|
2273
|
+
work_docx_format_change_export_setWordAttribute(shading, namespace, prefix, 'color', 'auto');
|
|
2274
|
+
work_docx_format_change_export_setWordAttribute(shading, namespace, prefix, 'fill', fill ?? normalizedHex(theme?.resolved) ?? 'FFFFFF');
|
|
2275
|
+
if (theme) setThemeAttributes(shading, namespace, prefix, theme, 'themeFill');
|
|
2276
|
+
properties.append(shading);
|
|
2277
|
+
}
|
|
2278
|
+
function appendSnapToGridProperty(document, properties, namespace, prefix, mark) {
|
|
2279
|
+
const value = mark?.attrs?.wordSnapToGrid;
|
|
2280
|
+
if ('boolean' != typeof value) return;
|
|
2281
|
+
appendValuedProperty(document, properties, namespace, prefix, 'snapToGrid', value ? '1' : '0');
|
|
2282
|
+
}
|
|
2283
|
+
function appendVerticalAlignProperty(document, properties, namespace, prefix, marks) {
|
|
2284
|
+
if (marks.has("subscript")) appendValuedProperty(document, properties, namespace, prefix, 'vertAlign', "subscript");
|
|
2285
|
+
else if (marks.has("superscript")) appendValuedProperty(document, properties, namespace, prefix, 'vertAlign', "superscript");
|
|
2286
|
+
}
|
|
2287
|
+
function setThemeAttributes(element, namespace, prefix, theme, name) {
|
|
2288
|
+
work_docx_format_change_export_setWordAttribute(element, namespace, prefix, name, theme.theme);
|
|
2289
|
+
if (theme.tint) work_docx_format_change_export_setWordAttribute(element, namespace, prefix, `${name}Tint`, theme.tint);
|
|
2290
|
+
if (theme.shade) work_docx_format_change_export_setWordAttribute(element, namespace, prefix, `${name}Shade`, theme.shade);
|
|
2291
|
+
}
|
|
2292
|
+
function themeReference(value) {
|
|
2293
|
+
return 'string' == typeof value ? parseDocxThemeReference(value) : null;
|
|
2294
|
+
}
|
|
2295
|
+
function firstFontFamily(value) {
|
|
2296
|
+
if ('string' != typeof value) return null;
|
|
2297
|
+
const source = value.trim();
|
|
2298
|
+
if (!source || source.length > 255) return null;
|
|
2299
|
+
const quote = source[0];
|
|
2300
|
+
if ('"' === quote || "'" === quote) {
|
|
2301
|
+
const end = source.indexOf(quote, 1);
|
|
2302
|
+
if (end > 1) return source.slice(1, end).trim() || null;
|
|
2303
|
+
}
|
|
2304
|
+
return source.split(',')[0]?.trim() || null;
|
|
2305
|
+
}
|
|
2306
|
+
function fontSizeHalfPoints(value) {
|
|
2307
|
+
if ('string' != typeof value) return null;
|
|
2308
|
+
const match = /^(\d+(?:\.\d+)?)pt$/i.exec(value.trim());
|
|
2309
|
+
if (!match) return null;
|
|
2310
|
+
const points = Number(match[1]);
|
|
2311
|
+
if (!Number.isFinite(points) || points <= 0 || points > 512) return null;
|
|
2312
|
+
return Math.round(2 * points);
|
|
2313
|
+
}
|
|
2314
|
+
function normalizedHex(value) {
|
|
2315
|
+
if ('string' != typeof value) return null;
|
|
2316
|
+
const match = /^#?([0-9a-f]{6})$/i.exec(value.trim());
|
|
2317
|
+
return match?.[1]?.toUpperCase() ?? null;
|
|
2318
|
+
}
|
|
2319
|
+
function wordElement(document, namespace, prefix, name) {
|
|
2320
|
+
return document.createElementNS(namespace, `${prefix}:${name}`);
|
|
2321
|
+
}
|
|
2322
|
+
function work_docx_format_change_export_setWordAttribute(element, namespace, prefix, name, value) {
|
|
2323
|
+
element.setAttributeNS(namespace, `${prefix}:${name}`, value);
|
|
2324
|
+
}
|
|
2325
|
+
function normalizedRevisionDate(value) {
|
|
2326
|
+
const time = Date.parse(value ?? '');
|
|
2327
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString();
|
|
2328
|
+
}
|
|
2329
|
+
function duplicateMarker(marker) {
|
|
2330
|
+
return new Error(`Generated DOCX marker '${marker}' is duplicated.`);
|
|
2331
|
+
}
|
|
2098
2332
|
class DocxImageCropPatchCollector {
|
|
2099
2333
|
patches = [];
|
|
2100
2334
|
nextMarker = 1;
|
|
@@ -6868,7 +7102,8 @@ async function createDocxBlob(content, sourcePackage) {
|
|
|
6868
7102
|
paragraphBorderPatches: new DocxParagraphBorderPatchCollector(JSON.stringify(normalizedContent)),
|
|
6869
7103
|
paragraphDefaultCollapsedPatches: new DocxParagraphDefaultCollapsedPatchCollector(),
|
|
6870
7104
|
paragraphIdentityPatches: new DocxParagraphIdentityPatchCollector(JSON.stringify(normalizedContent)),
|
|
6871
|
-
equationPatches: new DocxEquationPatchCollector(JSON.stringify(normalizedContent))
|
|
7105
|
+
equationPatches: new DocxEquationPatchCollector(JSON.stringify(normalizedContent)),
|
|
7106
|
+
formattingChangePatches: new DocxRunFormattingChangePatchCollector()
|
|
6872
7107
|
};
|
|
6873
7108
|
const commentRecords = createDocxCommentRecords(commentThreads, docx, noteContext.commentIds);
|
|
6874
7109
|
const sections = [];
|
|
@@ -6915,7 +7150,8 @@ async function createDocxBlob(content, sourcePackage) {
|
|
|
6915
7150
|
}
|
|
6916
7151
|
});
|
|
6917
7152
|
const packed = await docx.Packer.toBlob(document);
|
|
6918
|
-
const
|
|
7153
|
+
const formattingChangesPatched = await patchDocxRunFormattingChanges(await packed.arrayBuffer(), noteContext.formattingChangePatches.patches);
|
|
7154
|
+
const noteImageRelationshipsPatched = await patchDocxNoteImageRelationships(formattingChangesPatched);
|
|
6919
7155
|
const commentMetadataPatched = await patchDocxCommentMetadata(noteImageRelationshipsPatched, commentThreads);
|
|
6920
7156
|
const numberingPatched = await patchDocxNumberingRestartRules(commentMetadataPatched, noteContext.numberingRestartRules);
|
|
6921
7157
|
const bibliographyPatched = await patchDocxBibliography(numberingPatched, normalizedContent.bibliography);
|
|
@@ -7157,7 +7393,9 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
|
|
|
7157
7393
|
'footnote' === kind ? new docx.FootnoteReferenceRun(noteId) : new docx.EndnoteReferenceRun(noteId)
|
|
7158
7394
|
];
|
|
7159
7395
|
}
|
|
7160
|
-
const
|
|
7396
|
+
const textRevisionKind = 'del' === tag ? 'deletion' : 'ins' === tag ? 'insertion' : null;
|
|
7397
|
+
const change = node.hasAttribute('data-document-change') && textRevisionKind ? docxTextRevision(node, textRevisionKind, noteContext) : revision;
|
|
7398
|
+
const formattingChange = node.hasAttribute('data-document-change') && 'formatting' === node.dataset.changeKind ? noteContext.formattingChangePatches.register(node, docxRevisionId(node, noteContext)) : null;
|
|
7161
7399
|
const commentBoundary = node.hasAttribute('data-document-comment') ? nextDocxCommentBoundary(node.dataset.commentId, noteContext) : null;
|
|
7162
7400
|
const backgroundColor = cssColorToHex(node.style.backgroundColor);
|
|
7163
7401
|
const direction = domDirection(node);
|
|
@@ -7202,7 +7440,7 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
|
|
|
7202
7440
|
const children = [];
|
|
7203
7441
|
for (const child of node.childNodes)children.push(...await visit(child, style, change));
|
|
7204
7442
|
const href = 'a' === tag ? normalizeDocumentHref(node.getAttribute('href') ?? '') : null;
|
|
7205
|
-
const
|
|
7443
|
+
const linked = href ? [
|
|
7206
7444
|
href.startsWith('#') ? new docx.InternalHyperlink({
|
|
7207
7445
|
anchor: href.slice(1),
|
|
7208
7446
|
children
|
|
@@ -7211,6 +7449,11 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
|
|
|
7211
7449
|
children
|
|
7212
7450
|
})
|
|
7213
7451
|
] : children;
|
|
7452
|
+
const result = formattingChange ? [
|
|
7453
|
+
new docx.TextRun(formattingChange.start),
|
|
7454
|
+
...linked,
|
|
7455
|
+
new docx.TextRun(formattingChange.end)
|
|
7456
|
+
] : linked;
|
|
7214
7457
|
if (!commentBoundary) return result;
|
|
7215
7458
|
return [
|
|
7216
7459
|
...commentBoundary.start ? [
|
|
@@ -7231,13 +7474,7 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
|
|
|
7231
7474
|
return runs;
|
|
7232
7475
|
}
|
|
7233
7476
|
function docxTextRevision(element, kind, context) {
|
|
7234
|
-
const
|
|
7235
|
-
let id = context.changeIds.get(key);
|
|
7236
|
-
if (!id) {
|
|
7237
|
-
id = context.nextChangeId;
|
|
7238
|
-
context.nextChangeId += 1;
|
|
7239
|
-
context.changeIds.set(key, id);
|
|
7240
|
-
}
|
|
7477
|
+
const id = docxRevisionId(element, context);
|
|
7241
7478
|
const sourceDate = element.dataset.changeDate?.trim() ?? '';
|
|
7242
7479
|
const time = Date.parse(sourceDate);
|
|
7243
7480
|
return {
|
|
@@ -7247,9 +7484,19 @@ function docxTextRevision(element, kind, context) {
|
|
|
7247
7484
|
date: Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString()
|
|
7248
7485
|
};
|
|
7249
7486
|
}
|
|
7487
|
+
function docxRevisionId(element, context) {
|
|
7488
|
+
const key = element.dataset.changeId?.trim() || `change-${context.nextChangeId}`;
|
|
7489
|
+
let id = context.changeIds.get(key);
|
|
7490
|
+
if (!id) {
|
|
7491
|
+
id = context.nextChangeId;
|
|
7492
|
+
context.nextChangeId += 1;
|
|
7493
|
+
context.changeIds.set(key, id);
|
|
7494
|
+
}
|
|
7495
|
+
return id;
|
|
7496
|
+
}
|
|
7250
7497
|
function documentHasTrackedChanges(html) {
|
|
7251
7498
|
const document = new DOMParser().parseFromString(html, 'text/html');
|
|
7252
|
-
return Boolean(document.body.querySelector('ins[data-document-change], del[data-document-change]'));
|
|
7499
|
+
return Boolean(document.body.querySelector('ins[data-document-change], del[data-document-change], span[data-document-change][data-change-kind="formatting"]'));
|
|
7253
7500
|
}
|
|
7254
7501
|
function anchoredDocumentComments(content) {
|
|
7255
7502
|
const document = new DOMParser().parseFromString(content.html, 'text/html');
|