@stll/folio-core 0.25.1 → 0.25.3
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/dist/ai-edits/headless.d.ts +6 -0
- package/dist/ai-edits/headless.js +60 -7
- package/dist/ai-edits/read.d.ts +26 -1
- package/dist/ai-edits/read.js +26 -1
- package/dist/docx/rezip.d.ts +2 -0
- package/dist/docx/rezip.js +61 -58
- package/dist/docx/selectiveXmlPatch.d.ts +24 -1
- package/dist/docx/selectiveXmlPatch.js +186 -1
- package/dist/docx/xmlParser.d.ts +7 -1
- package/dist/docx/xmlParser.js +11 -4
- package/dist/layout-bridge/convert/toFlowBlocks.js +3 -4
- package/dist/layout-bridge/engine/clickToPosition.js +13 -28
- package/dist/layout-bridge/engine/hitTest.d.ts +2 -0
- package/dist/layout-bridge/engine/hitTest.js +28 -13
- package/dist/layout-bridge/engine/selectionRects.js +26 -15
- package/dist/layout-engine/index.js +6 -9
- package/dist/layout-engine/measure/hyperlinkInstance.d.ts +6 -0
- package/dist/layout-engine/measure/hyperlinkInstance.js +8 -0
- package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -1
- package/dist/layout-engine/measure/tableCellGrid.js +29 -6
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +12 -0
- package/dist/layout-engine/measure/tableInlinePlacement.js +22 -0
- package/dist/layout-engine/types.d.ts +1 -1
- package/dist/layout-painter/renderParagraph.d.ts +3 -0
- package/dist/layout-painter/renderParagraph.js +13 -19
- package/dist/layout-painter/renderTable.js +36 -74
- package/dist/prosemirror/conversion/toProseDoc.js +42 -44
- package/dist/prosemirror/extensions/marks/CommentExtension.js +1 -0
- package/dist/utils/paragraphInlineLayout.d.ts +14 -0
- package/dist/utils/paragraphInlineLayout.js +25 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WORDPROCESSINGML_NAMESPACE_URIS, findAttributeByNamespaceUri, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, parseXmlDocument } from "./xmlParser.js";
|
|
1
2
|
//#region src/docx/selectiveXmlPatch.ts
|
|
2
3
|
/**
|
|
3
4
|
* Selective XML Patch Module
|
|
@@ -317,6 +318,190 @@ function extractElementByIdAttr(xml, openLiteral, closeTag, idAttr, id) {
|
|
|
317
318
|
const offsets = findElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
|
|
318
319
|
return offsets ? xml.slice(offsets.start, offsets.end) : null;
|
|
319
320
|
}
|
|
321
|
+
const qualifiedName = (prefix, localName) => prefix.length === 0 ? localName : `${prefix}:${localName}`;
|
|
322
|
+
const collectNoteElementSyntax = (xml, elementName) => {
|
|
323
|
+
const byId = /* @__PURE__ */ new Map();
|
|
324
|
+
const root = parseXmlDocument(xml);
|
|
325
|
+
for (const element of getChildElements(root)) {
|
|
326
|
+
if (getLocalName(element.name) !== elementName || !WORDPROCESSINGML_NAMESPACE_URIS.has(getNamespaceUri(element) ?? "")) continue;
|
|
327
|
+
const idAttribute = findAttributeByNamespaceUri(element, WORDPROCESSINGML_NAMESPACE_URIS, "id");
|
|
328
|
+
if (!element.name || !idAttribute) continue;
|
|
329
|
+
const syntax = {
|
|
330
|
+
elementName: element.name,
|
|
331
|
+
idAttributeName: idAttribute.name,
|
|
332
|
+
elementPrefix: getNamespacePrefix(element.name) ?? "",
|
|
333
|
+
attributePrefix: getNamespacePrefix(idAttribute.name) ?? ""
|
|
334
|
+
};
|
|
335
|
+
const entries = byId.get(idAttribute.value);
|
|
336
|
+
if (entries) entries.push(syntax);
|
|
337
|
+
else byId.set(idAttribute.value, [syntax]);
|
|
338
|
+
}
|
|
339
|
+
return byId;
|
|
340
|
+
};
|
|
341
|
+
const syntaxLiteral = (syntax) => ({
|
|
342
|
+
openLiteral: `<${syntax.elementName}`,
|
|
343
|
+
closeTag: `</${syntax.elementName}>`,
|
|
344
|
+
idAttr: syntax.idAttributeName
|
|
345
|
+
});
|
|
346
|
+
const extractNoteElement = (xml, syntax, id) => {
|
|
347
|
+
const { openLiteral, closeTag, idAttr } = syntaxLiteral(syntax);
|
|
348
|
+
return extractElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
|
|
349
|
+
};
|
|
350
|
+
const findNoteElement = (xml, syntax, id) => {
|
|
351
|
+
const { openLiteral, closeTag, idAttr } = syntaxLiteral(syntax);
|
|
352
|
+
return findElementByIdAttr(xml, openLiteral, closeTag, idAttr, id);
|
|
353
|
+
};
|
|
354
|
+
const rewriteWordprocessingPrefixes = (xml, { source, target, sourceXmlnsDeclarations }) => {
|
|
355
|
+
let rewritten = "";
|
|
356
|
+
let quote = null;
|
|
357
|
+
let insideTag = false;
|
|
358
|
+
for (let index = 0; index < xml.length; index++) {
|
|
359
|
+
const character = xml[index];
|
|
360
|
+
if (!insideTag) {
|
|
361
|
+
rewritten += character;
|
|
362
|
+
insideTag = character === "<";
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (quote) {
|
|
366
|
+
rewritten += character;
|
|
367
|
+
if (character === quote) quote = null;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (character === "\"" || character === "'") {
|
|
371
|
+
quote = character;
|
|
372
|
+
rewritten += character;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (character === ">") {
|
|
376
|
+
insideTag = false;
|
|
377
|
+
rewritten += character;
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
const previous = xml[index - 1];
|
|
381
|
+
const isElementName = previous === "<" || previous === "/" && xml[index - 2] === "<";
|
|
382
|
+
const sourcePrefix = isElementName ? source.elementPrefix : source.attributePrefix;
|
|
383
|
+
if (sourcePrefix.length > 0 && xml.startsWith(`${sourcePrefix}:`, index)) {
|
|
384
|
+
const targetPrefix = isElementName ? target.elementPrefix : target.attributePrefix;
|
|
385
|
+
rewritten += targetPrefix.length === 0 ? "" : `${targetPrefix}:`;
|
|
386
|
+
index += sourcePrefix.length;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
rewritten += character;
|
|
390
|
+
}
|
|
391
|
+
return withXmlnsDeclarations(rewritten, sourceXmlnsDeclarations);
|
|
392
|
+
};
|
|
393
|
+
const paragraphRanges = (xml, wordPrefix) => {
|
|
394
|
+
const ranges = [];
|
|
395
|
+
const paragraphName = qualifiedName(wordPrefix, "p");
|
|
396
|
+
const openLiteral = `<${paragraphName}`;
|
|
397
|
+
const closeTag = `</${paragraphName}>`;
|
|
398
|
+
let pos = 0;
|
|
399
|
+
while (pos < xml.length) {
|
|
400
|
+
const start = xml.indexOf(openLiteral, pos);
|
|
401
|
+
if (start === -1) break;
|
|
402
|
+
if (!isXmlNameBoundary(xml[start + openLiteral.length])) {
|
|
403
|
+
pos = start + 1;
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const range = scanElementRange(xml, start, openLiteral, closeTag);
|
|
407
|
+
if (!range) break;
|
|
408
|
+
ranges.push(range);
|
|
409
|
+
pos = range.end;
|
|
410
|
+
}
|
|
411
|
+
return ranges;
|
|
412
|
+
};
|
|
413
|
+
const collectChangedNoteParaIds = (baselineXml, currentXml) => {
|
|
414
|
+
const changed = /* @__PURE__ */ new Set();
|
|
415
|
+
const baselineIds = collectParaIds(baselineXml);
|
|
416
|
+
const baselineOffsets = buildParagraphOffsetIndex(baselineXml);
|
|
417
|
+
const currentOffsets = buildParagraphOffsetIndex(currentXml);
|
|
418
|
+
for (const [id, count] of collectParaIds(currentXml)) {
|
|
419
|
+
if (count !== 1 || baselineIds.get(id) !== 1) continue;
|
|
420
|
+
const before = baselineOffsets.get(id);
|
|
421
|
+
const after = currentOffsets.get(id);
|
|
422
|
+
if (before && after && baselineXml.slice(before.start, before.end) !== currentXml.slice(after.start, after.end)) changed.add(id);
|
|
423
|
+
}
|
|
424
|
+
return changed;
|
|
425
|
+
};
|
|
426
|
+
const replaceRanges = (xml, replacements) => {
|
|
427
|
+
let result = xml;
|
|
428
|
+
for (const { start, end, newXml } of [...replacements].toSorted((a, b) => b.start - a.start)) result = result.slice(0, start) + newXml + result.slice(end);
|
|
429
|
+
return result;
|
|
430
|
+
};
|
|
431
|
+
/**
|
|
432
|
+
* Patch an existing note part from its model serialization.
|
|
433
|
+
*
|
|
434
|
+
* Dirty paragraph ids locate their owning note in the model serialization;
|
|
435
|
+
* `(note w:id, paragraph ordinal)` then locates the corresponding source XML
|
|
436
|
+
* even when the producer omitted paragraph ids. Equal-shape edits replace only
|
|
437
|
+
* dirty paragraphs. A tracked paragraph-break resolution can change that
|
|
438
|
+
* shape, so it replaces the one affected note. Separator notes, unrelated
|
|
439
|
+
* notes, and unaffected equal-shape paragraphs remain byte-exact.
|
|
440
|
+
* `replacementXml` also supplies synthesized automatic note-reference marks,
|
|
441
|
+
* which the parsed model intentionally omits.
|
|
442
|
+
*/
|
|
443
|
+
function buildPatchedNotePartXml({ originalXml, baselineXml, serializedXml, replacementXml, elementName, changedParaIds }) {
|
|
444
|
+
const currentElements = collectNoteElementSyntax(serializedXml, elementName);
|
|
445
|
+
const originalElements = collectNoteElementSyntax(originalXml, elementName);
|
|
446
|
+
const replacementElements = collectNoteElementSyntax(replacementXml, elementName);
|
|
447
|
+
const replacementXmlnsDeclarations = collectXmlnsFromOpeningTag(replacementXml);
|
|
448
|
+
const ordinalReplacements = [];
|
|
449
|
+
const serializedParaIds = collectParaIds(serializedXml);
|
|
450
|
+
const effectiveChangedParaIds = changedParaIds ?? collectChangedNoteParaIds(baselineXml, serializedXml);
|
|
451
|
+
const unroutedChangedParaIds = new Set([...effectiveChangedParaIds].filter((paraId) => serializedParaIds.has(paraId)));
|
|
452
|
+
for (const [id, currentSyntaxEntries] of currentElements) {
|
|
453
|
+
const originalSyntaxEntries = originalElements.get(id);
|
|
454
|
+
const replacementSyntaxEntries = replacementElements.get(id);
|
|
455
|
+
if (currentSyntaxEntries.length !== 1 || originalSyntaxEntries?.length !== 1 || replacementSyntaxEntries?.length !== 1) return null;
|
|
456
|
+
const currentSyntax = currentSyntaxEntries[0];
|
|
457
|
+
const originalSyntax = originalSyntaxEntries[0];
|
|
458
|
+
const replacementSyntax = replacementSyntaxEntries[0];
|
|
459
|
+
if (!currentSyntax || !originalSyntax || !replacementSyntax) return null;
|
|
460
|
+
const currentNote = extractNoteElement(serializedXml, currentSyntax, id);
|
|
461
|
+
const originalOffsets = findNoteElement(originalXml, originalSyntax, id);
|
|
462
|
+
const replacementNote = extractNoteElement(replacementXml, replacementSyntax, id);
|
|
463
|
+
if (!currentNote || !originalOffsets || !replacementNote) return null;
|
|
464
|
+
const originalNote = originalXml.slice(originalOffsets.start, originalOffsets.end);
|
|
465
|
+
const currentParagraphs = paragraphRanges(currentNote, currentSyntax.elementPrefix);
|
|
466
|
+
const originalParagraphs = paragraphRanges(originalNote, originalSyntax.elementPrefix);
|
|
467
|
+
const replacementParagraphs = paragraphRanges(replacementNote, replacementSyntax.elementPrefix);
|
|
468
|
+
const noteChangedParaIds = [...collectParaIds(currentNote).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
|
|
469
|
+
if (noteChangedParaIds.length === 0) continue;
|
|
470
|
+
if (currentParagraphs.length !== originalParagraphs.length || currentParagraphs.length !== replacementParagraphs.length) {
|
|
471
|
+
for (const paraId of noteChangedParaIds) unroutedChangedParaIds.delete(paraId);
|
|
472
|
+
ordinalReplacements.push({
|
|
473
|
+
start: originalOffsets.start,
|
|
474
|
+
end: originalOffsets.end,
|
|
475
|
+
newXml: rewriteWordprocessingPrefixes(replacementNote, {
|
|
476
|
+
source: replacementSyntax,
|
|
477
|
+
target: originalSyntax,
|
|
478
|
+
sourceXmlnsDeclarations: replacementXmlnsDeclarations
|
|
479
|
+
})
|
|
480
|
+
});
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
for (let index = 0; index < currentParagraphs.length; index++) {
|
|
484
|
+
const currentRange = currentParagraphs[index];
|
|
485
|
+
const originalRange = originalParagraphs[index];
|
|
486
|
+
const replacementRange = replacementParagraphs[index];
|
|
487
|
+
if (!currentRange || !originalRange || !replacementRange) return null;
|
|
488
|
+
const routedIds = [...collectParaIds(currentNote.slice(currentRange.start, currentRange.end)).keys()].filter((paraId) => unroutedChangedParaIds.has(paraId));
|
|
489
|
+
if (routedIds.length === 0) continue;
|
|
490
|
+
for (const paraId of routedIds) unroutedChangedParaIds.delete(paraId);
|
|
491
|
+
ordinalReplacements.push({
|
|
492
|
+
start: originalOffsets.start + originalRange.start,
|
|
493
|
+
end: originalOffsets.start + originalRange.end,
|
|
494
|
+
newXml: rewriteWordprocessingPrefixes(replacementNote.slice(replacementRange.start, replacementRange.end), {
|
|
495
|
+
source: replacementSyntax,
|
|
496
|
+
target: originalSyntax,
|
|
497
|
+
sourceXmlnsDeclarations: replacementXmlnsDeclarations
|
|
498
|
+
})
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (unroutedChangedParaIds.size > 0) return null;
|
|
503
|
+
return replaceRanges(originalXml, ordinalReplacements);
|
|
504
|
+
}
|
|
320
505
|
/**
|
|
321
506
|
* The full range of the first `<openLiteral …>…</closeTag>` element, or null.
|
|
322
507
|
* Used to locate an unkeyed sub-element (a level's `mc:AlternateContent`).
|
|
@@ -627,4 +812,4 @@ function escapeRegExp(str) {
|
|
|
627
812
|
return str.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
628
813
|
}
|
|
629
814
|
//#endregion
|
|
630
|
-
export { appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, validatePatchSafety };
|
|
815
|
+
export { appendNumberingDefs, buildParagraphOffsetIndex, buildPatchedDocumentXml, buildPatchedNotePartXml, buildPatchedNoteXml, buildPatchedNumberingXml, collectAddedNumberingDefs, collectChangedNoteParaIds, collectChangedNumberingDefs, collectParaIds, countParagraphElements, extractParagraphXml, findParagraphOffsets, isXmlNameBoundary, validatePatchSafety };
|
package/dist/docx/xmlParser.d.ts
CHANGED
|
@@ -104,6 +104,12 @@ declare const WORDPROCESSINGML_NAMESPACE_URIS: ReadonlySet<string>;
|
|
|
104
104
|
* from a foreign namespace is not accepted.
|
|
105
105
|
*/
|
|
106
106
|
declare function findChildByNamespaceUri(parent: XmlElement | null | undefined, namespaceUris: ReadonlySet<string>, localName: string): XmlElement | null;
|
|
107
|
+
type XmlAttributeMatch = {
|
|
108
|
+
name: string;
|
|
109
|
+
value: string;
|
|
110
|
+
};
|
|
111
|
+
/** Find an attribute whose prefix resolves to one of the accepted namespace URIs. */
|
|
112
|
+
declare function findAttributeByNamespaceUri(element: XmlElement | null | undefined, namespaceUris: ReadonlySet<string>, localName: string): XmlAttributeMatch | null;
|
|
107
113
|
/** Get an attribute whose prefix resolves to one of the accepted namespace URIs. */
|
|
108
114
|
declare function getAttributeByNamespaceUri(element: XmlElement | null | undefined, namespaceUris: ReadonlySet<string>, localName: string): string | null;
|
|
109
115
|
/**
|
|
@@ -338,4 +344,4 @@ declare function mergeXmlnsDeclarations(inherited: Record<string, string>, eleme
|
|
|
338
344
|
*/
|
|
339
345
|
declare function cloneWithXmlnsDeclarations(element: XmlElement, xmlnsDecls: Record<string, string>): XmlElement;
|
|
340
346
|
//#endregion
|
|
341
|
-
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, XmlElement, XmlNamespaceScope, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
|
347
|
+
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, XmlAttributeMatch, XmlElement, XmlNamespaceScope, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
package/dist/docx/xmlParser.js
CHANGED
|
@@ -208,16 +208,23 @@ function findChildByNamespaceUri(parent, namespaceUris, localName) {
|
|
|
208
208
|
for (const child of parent.elements) if (child.type === "element" && hasLocalName(child.name, localName) && namespaceUris.has(child.namespaceUri ?? "")) return child;
|
|
209
209
|
return null;
|
|
210
210
|
}
|
|
211
|
-
/**
|
|
212
|
-
function
|
|
211
|
+
/** Find an attribute whose prefix resolves to one of the accepted namespace URIs. */
|
|
212
|
+
function findAttributeByNamespaceUri(element, namespaceUris, localName) {
|
|
213
213
|
if (!element?.attributes) return null;
|
|
214
214
|
for (const [name, value] of Object.entries(element.attributes)) {
|
|
215
215
|
if (value === void 0 || getLocalName(name) !== localName) continue;
|
|
216
216
|
const prefix = getNamespacePrefix(name);
|
|
217
|
-
if (prefix !== null && namespaceUris.has(resolveNamespaceUri(element.namespaceScope, prefix) ?? "")) return
|
|
217
|
+
if (prefix !== null && namespaceUris.has(resolveNamespaceUri(element.namespaceScope, prefix) ?? "")) return {
|
|
218
|
+
name,
|
|
219
|
+
value: String(value)
|
|
220
|
+
};
|
|
218
221
|
}
|
|
219
222
|
return null;
|
|
220
223
|
}
|
|
224
|
+
/** Get an attribute whose prefix resolves to one of the accepted namespace URIs. */
|
|
225
|
+
function getAttributeByNamespaceUri(element, namespaceUris, localName) {
|
|
226
|
+
return findAttributeByNamespaceUri(element, namespaceUris, localName)?.value ?? null;
|
|
227
|
+
}
|
|
221
228
|
function hasLocalName(name, localName) {
|
|
222
229
|
if (!name) return false;
|
|
223
230
|
if (name === localName) return true;
|
|
@@ -712,4 +719,4 @@ function cloneWithXmlnsDeclarations(element, xmlnsDecls) {
|
|
|
712
719
|
return element;
|
|
713
720
|
}
|
|
714
721
|
//#endregion
|
|
715
|
-
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
|
722
|
+
export { NAMESPACES, WORDPROCESSINGML_NAMESPACE_URIS, cloneWithXmlnsDeclarations, collectXmlnsDeclarations, elementToXml, findAllDeep, findAttributeByNamespaceUri, findByFullName, findChild, findChildByLocalName, findChildByNamespaceUri, findChildren, findChildrenByLocalName, findDeep, getAttribute, getAttributeAny, getAttributeAnyPrefix, getAttributeByNamespaceUri, getAttributes, getChildElements, getLocalName, getNamespacePrefix, getNamespaceUri, getTextContent, hasChild, hasFlag, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseColorElement, parseNumberingLevelAttribute, parseNumericAttribute, parseOnOffValue, parseTableMeasurementValue, parseXml, parseXmlDocument };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
|
|
2
2
|
import { resolveDocumentGridLinePitch } from "../../docx/documentGrid.js";
|
|
3
3
|
import { formatOoxmlCounter } from "../../docx/ooxmlCounterFormatter.js";
|
|
4
|
+
import { setHyperlinkInstanceIndex } from "../../layout-engine/measure/hyperlinkInstance.js";
|
|
4
5
|
import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
|
|
5
6
|
import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
|
|
6
7
|
import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
|
|
@@ -267,6 +268,7 @@ function extractRunFormatting(marks, theme) {
|
|
|
267
268
|
const attrs = expectHyperlinkMarkAttrs(mark);
|
|
268
269
|
const link = { href: attrs.href };
|
|
269
270
|
if (attrs.tooltip !== void 0) link.tooltip = attrs.tooltip;
|
|
271
|
+
if (attrs._docxHyperlinkIndex !== void 0) setHyperlinkInstanceIndex(link, attrs._docxHyperlinkIndex);
|
|
270
272
|
formatting.hyperlink = link;
|
|
271
273
|
break;
|
|
272
274
|
}
|
|
@@ -468,10 +470,7 @@ const TOC_STYLE_ID = /^TOC\d*$/iu;
|
|
|
468
470
|
*/
|
|
469
471
|
function stripTocHyperlinkStyle(formatting) {
|
|
470
472
|
if (!formatting.hyperlink) return;
|
|
471
|
-
formatting.hyperlink =
|
|
472
|
-
...formatting.hyperlink,
|
|
473
|
-
noDefaultStyle: true
|
|
474
|
-
};
|
|
473
|
+
formatting.hyperlink.noDefaultStyle = true;
|
|
475
474
|
delete formatting.color;
|
|
476
475
|
delete formatting.underline;
|
|
477
476
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { measuredLineAdvance, measuredLineContentOffset } from "../../layout-engine/lineFlow.js";
|
|
2
2
|
import { buildRunFontStyle, findCharacterAtX } from "../../layout-engine/measure/measureHelpers.js";
|
|
3
3
|
import { measureRun } from "../../layout-engine/measure/measureProvider.js";
|
|
4
|
+
import { resolvePhysicalParagraphInlineLayout } from "../../utils/paragraphInlineLayout.js";
|
|
4
5
|
import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
|
|
5
6
|
//#region src/layout-bridge/engine/clickToPosition.ts
|
|
6
7
|
/**
|
|
@@ -120,24 +121,12 @@ function findLineAtY(measure, localY, fromLine, toLine) {
|
|
|
120
121
|
if (toLine > fromLine) return Math.min(toLine - 1, measure.lines.length - 1);
|
|
121
122
|
return null;
|
|
122
123
|
}
|
|
123
|
-
|
|
124
|
-
* Find the character position at an X coordinate within a line.
|
|
125
|
-
*
|
|
126
|
-
* Uses canvas text measurement for pixel-perfect accuracy.
|
|
127
|
-
*
|
|
128
|
-
* @param block - The paragraph block.
|
|
129
|
-
* @param line - The measured line.
|
|
130
|
-
* @param x - X coordinate relative to the line's start position.
|
|
131
|
-
* @param availableWidth - Available width for alignment calculations.
|
|
132
|
-
* @returns Character offset and PM position.
|
|
133
|
-
*/
|
|
134
|
-
function findCharacterInLine(block, line, x, availableWidth) {
|
|
124
|
+
function findCharacterInLine({ block, line, x, availableWidth, alignment }) {
|
|
135
125
|
const { pmStart, pmEnd } = computeLinePmRange(block, line);
|
|
136
126
|
if (pmStart === void 0 || pmEnd === void 0) return {
|
|
137
127
|
charOffset: 0,
|
|
138
128
|
pmPosition: block.pmStart ?? 0
|
|
139
129
|
};
|
|
140
|
-
const alignment = block.attrs?.alignment ?? "left";
|
|
141
130
|
let alignmentOffset = 0;
|
|
142
131
|
if (alignment === "center") alignmentOffset = Math.max(0, (availableWidth - line.width) / 2);
|
|
143
132
|
else if (alignment === "right") alignmentOffset = Math.max(0, availableWidth - line.width);
|
|
@@ -236,11 +225,15 @@ function clickToPositionInParagraph(fragmentHit) {
|
|
|
236
225
|
if (lineIndex === null) return null;
|
|
237
226
|
const line = paragraphMeasure.lines[lineIndex];
|
|
238
227
|
if (!line) return null;
|
|
239
|
-
const
|
|
240
|
-
const indentLeft = indent?.left ?? 0;
|
|
241
|
-
const indentRight = indent?.right ?? 0;
|
|
228
|
+
const { alignment, indentLeft, indentRight } = resolvePhysicalParagraphInlineLayout(paragraphBlock);
|
|
242
229
|
const availableWidth = Math.max(0, fragment.width - indentLeft - indentRight);
|
|
243
|
-
const { charOffset, pmPosition } = findCharacterInLine(
|
|
230
|
+
const { charOffset, pmPosition } = findCharacterInLine({
|
|
231
|
+
block: paragraphBlock,
|
|
232
|
+
line,
|
|
233
|
+
x: localX - indentLeft,
|
|
234
|
+
availableWidth,
|
|
235
|
+
alignment
|
|
236
|
+
});
|
|
244
237
|
return {
|
|
245
238
|
pmPosition,
|
|
246
239
|
charOffset,
|
|
@@ -254,7 +247,7 @@ function clickToPositionInParagraph(fragmentHit) {
|
|
|
254
247
|
* @returns PM position, or null if mapping fails.
|
|
255
248
|
*/
|
|
256
249
|
function clickToPositionInTableCell(tableCellHit) {
|
|
257
|
-
const { cellBlock, cellMeasure, cellLocalX, cellLocalY } = tableCellHit;
|
|
250
|
+
const { cellBlock, cellMeasure, cellLocalX, cellLocalY, cellContentWidth } = tableCellHit;
|
|
258
251
|
if (!cellBlock || !cellMeasure) return null;
|
|
259
252
|
return clickToPositionInParagraph({
|
|
260
253
|
fragment: {
|
|
@@ -262,7 +255,7 @@ function clickToPositionInTableCell(tableCellHit) {
|
|
|
262
255
|
blockId: cellBlock.id,
|
|
263
256
|
x: 0,
|
|
264
257
|
y: 0,
|
|
265
|
-
width:
|
|
258
|
+
width: cellContentWidth,
|
|
266
259
|
fromLine: 0,
|
|
267
260
|
toLine: cellMeasure.lines.length,
|
|
268
261
|
height: cellMeasure.totalHeight
|
|
@@ -371,21 +364,13 @@ function positionToX(block, measure, pmPosition, _fragmentWidth) {
|
|
|
371
364
|
}
|
|
372
365
|
return null;
|
|
373
366
|
}
|
|
374
|
-
const getMaxLineWidth = (lines, fallback) => {
|
|
375
|
-
let maxWidth = fallback;
|
|
376
|
-
for (const line of lines) maxWidth = Math.max(maxWidth, line.width);
|
|
377
|
-
return maxWidth;
|
|
378
|
-
};
|
|
379
367
|
/**
|
|
380
368
|
* Get the bounding rect for a PM position (for caret rendering).
|
|
381
369
|
*/
|
|
382
370
|
function getPositionRect(block, measure, pmPosition, fragmentX, fragmentY, fragmentWidth, fromLine) {
|
|
383
371
|
const result = positionToX(block, measure, pmPosition, fragmentWidth);
|
|
384
372
|
if (!result) return null;
|
|
385
|
-
const alignment = block
|
|
386
|
-
const indent = block.attrs?.indent;
|
|
387
|
-
const indentLeft = indent?.left ?? 0;
|
|
388
|
-
const indentRight = indent?.right ?? 0;
|
|
373
|
+
const { alignment, indentLeft, indentRight } = resolvePhysicalParagraphInlineLayout(block);
|
|
389
374
|
const availableWidth = Math.max(0, fragmentWidth - indentLeft - indentRight);
|
|
390
375
|
const line = measure.lines[result.lineIndex];
|
|
391
376
|
if (!line) return null;
|
|
@@ -57,6 +57,8 @@ type TableCellHit = {
|
|
|
57
57
|
cellMeasure?: ParagraphMeasure;
|
|
58
58
|
/** X position relative to cell content area. */
|
|
59
59
|
cellLocalX: number;
|
|
60
|
+
/** Width of the cell content area after physical padding. */
|
|
61
|
+
cellContentWidth: number;
|
|
60
62
|
/** Y position relative to cell content area. */
|
|
61
63
|
cellLocalY: number;
|
|
62
64
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { getHeaderRowsHeight } from "../../layout-engine/index.js";
|
|
2
2
|
import { measuredLineRangeHeight } from "../../layout-engine/lineFlow.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getTableCellContentWidth } from "../../layout-engine/measure/tableCellFloating.js";
|
|
4
|
+
import { buildTableCellGrid, buildTableCellPlacements } from "../../layout-engine/measure/tableCellGrid.js";
|
|
5
|
+
import { resolveTableCellPadding } from "../../layout-engine/types.js";
|
|
4
6
|
//#region src/layout-bridge/engine/hitTest.ts
|
|
5
7
|
/**
|
|
6
8
|
* Hit Testing Utilities
|
|
@@ -214,21 +216,33 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
|
|
|
214
216
|
const rowMeasure = tableMeasure.rows[rowIndex];
|
|
215
217
|
const row = tableBlock.rows[rowIndex];
|
|
216
218
|
if (!rowMeasure || !row) continue;
|
|
217
|
-
let colX = getTableRowLeadingWidth(row, tableMeasure.columnWidths);
|
|
218
219
|
let colIndex = -1;
|
|
220
|
+
let cellLeft = 0;
|
|
219
221
|
if (rowMeasure.cells.length === 0 || row.cells.length === 0) continue;
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
222
|
+
const cellPlacements = buildTableCellPlacements({
|
|
223
|
+
grid: buildTableCellGrid(tableBlock.rows, tableMeasure.columnWidths.length),
|
|
224
|
+
columnWidths: tableMeasure.columnWidths,
|
|
225
|
+
bidi: tableBlock.bidi === true
|
|
226
|
+
});
|
|
227
|
+
let nearestDistance = Infinity;
|
|
228
|
+
for (let c = 0; c < row.cells.length; c++) {
|
|
229
|
+
const cell = row.cells[c];
|
|
230
|
+
if (!cell) continue;
|
|
231
|
+
const placement = cellPlacements.get(cell);
|
|
232
|
+
if (!placement) continue;
|
|
233
|
+
if (localX >= placement.left && localX < placement.left + placement.width) {
|
|
223
234
|
colIndex = c;
|
|
235
|
+
cellLeft = placement.left;
|
|
224
236
|
break;
|
|
225
237
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
const distance = Math.min(Math.abs(localX - placement.left), Math.abs(localX - placement.left - placement.width));
|
|
239
|
+
if (distance < nearestDistance) {
|
|
240
|
+
nearestDistance = distance;
|
|
241
|
+
colIndex = c;
|
|
242
|
+
cellLeft = placement.left;
|
|
243
|
+
}
|
|
231
244
|
}
|
|
245
|
+
if (colIndex === -1) continue;
|
|
232
246
|
const cellMeasure = rowMeasure.cells[colIndex];
|
|
233
247
|
const cell = row.cells[colIndex];
|
|
234
248
|
if (!cellMeasure || !cell) continue;
|
|
@@ -239,8 +253,6 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
|
|
|
239
253
|
rowTop = headerHeight;
|
|
240
254
|
for (let r = tableFragment.fromRow; r < rowIndex; r++) rowTop += tableMeasure.rows[r]?.height ?? 0;
|
|
241
255
|
}
|
|
242
|
-
let colLeft = 0;
|
|
243
|
-
for (let c = 0; c < colIndex; c++) colLeft += rowMeasure.cells[c]?.width ?? 0;
|
|
244
256
|
let cellBlock;
|
|
245
257
|
let cellBlockMeasure;
|
|
246
258
|
if (cell.blocks.length > 0) {
|
|
@@ -251,7 +263,9 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
|
|
|
251
263
|
cellBlockMeasure = firstMeasure;
|
|
252
264
|
}
|
|
253
265
|
}
|
|
254
|
-
const
|
|
266
|
+
const { left: padLeft } = resolveTableCellPadding(cell);
|
|
267
|
+
const cellLocalX = localX - cellLeft - padLeft;
|
|
268
|
+
const cellContentWidth = getTableCellContentWidth(cell, cellMeasure);
|
|
255
269
|
const clipOffset = isClickOnHeader ? 0 : tableFragment.topClip ?? 0;
|
|
256
270
|
const cellLocalY = localY - rowTop + clipOffset;
|
|
257
271
|
return {
|
|
@@ -264,6 +278,7 @@ function hitTestTableCell(pageHit, blocks, measures, pagePoint) {
|
|
|
264
278
|
...cellBlock !== void 0 ? { cellBlock } : {},
|
|
265
279
|
...cellBlockMeasure !== void 0 ? { cellMeasure: cellBlockMeasure } : {},
|
|
266
280
|
cellLocalX: Math.max(0, cellLocalX),
|
|
281
|
+
cellContentWidth,
|
|
267
282
|
cellLocalY: Math.max(0, cellLocalY)
|
|
268
283
|
};
|
|
269
284
|
}
|
|
@@ -4,7 +4,9 @@ import { buildRunFontStyle } from "../../layout-engine/measure/measureHelpers.js
|
|
|
4
4
|
import { measureParagraph } from "../../layout-engine/measure/measureParagraph.js";
|
|
5
5
|
import { measureRun } from "../../layout-engine/measure/measureProvider.js";
|
|
6
6
|
import { buildTableCellFloatingZones, getTableCellContentWidth, getTableCellFloatingImages } from "../../layout-engine/measure/tableCellFloating.js";
|
|
7
|
-
import {
|
|
7
|
+
import { buildTableCellGrid, buildTableCellPlacements } from "../../layout-engine/measure/tableCellGrid.js";
|
|
8
|
+
import { resolveTableCellPadding } from "../../layout-engine/types.js";
|
|
9
|
+
import { resolvePhysicalParagraphInlineLayout } from "../../utils/paragraphInlineLayout.js";
|
|
8
10
|
import { inlineImageBoundingBox } from "../../utils/rotationBoundingBox.js";
|
|
9
11
|
import { getPageTop } from "./hitTest.js";
|
|
10
12
|
//#region src/layout-bridge/engine/selectionRects.ts
|
|
@@ -246,6 +248,7 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
246
248
|
const selFrom = Math.min(from, to);
|
|
247
249
|
const selTo = Math.max(from, to);
|
|
248
250
|
const rects = [];
|
|
251
|
+
const tableCellPlacements = /* @__PURE__ */ new WeakMap();
|
|
249
252
|
for (let pageIndex = 0; pageIndex < layout.pages.length; pageIndex++) {
|
|
250
253
|
const page = layout.pages[pageIndex];
|
|
251
254
|
const pageTopY = getPageTop(layout, pageIndex);
|
|
@@ -260,6 +263,8 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
260
263
|
const paragraphBlock = block;
|
|
261
264
|
const paragraphMeasure = measure;
|
|
262
265
|
const paragraphFragment = fragment;
|
|
266
|
+
const { alignment, indentLeft, indentRight } = resolvePhysicalParagraphInlineLayout(paragraphBlock);
|
|
267
|
+
const availableWidth = Math.max(0, fragment.width - indentLeft - indentRight);
|
|
263
268
|
const intersectingLines = findLinesInRange(paragraphBlock, paragraphMeasure, selFrom, selTo);
|
|
264
269
|
for (const { line, index } of intersectingLines) {
|
|
265
270
|
if (index < paragraphFragment.fromLine || index >= paragraphFragment.toLine) continue;
|
|
@@ -272,13 +277,8 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
272
277
|
if (!isEmptyLine && sliceFrom >= sliceTo) continue;
|
|
273
278
|
const charOffsetFrom = pmPosToCharOffset(paragraphBlock, line, sliceFrom);
|
|
274
279
|
const charOffsetTo = pmPosToCharOffset(paragraphBlock, line, sliceTo);
|
|
275
|
-
const indent = paragraphBlock.attrs?.indent;
|
|
276
|
-
const indentLeft = indent?.left ?? 0;
|
|
277
|
-
const indentRight = indent?.right ?? 0;
|
|
278
|
-
const availableWidth = Math.max(0, fragment.width - indentLeft - indentRight);
|
|
279
280
|
const startX = charOffsetToX(paragraphBlock, line, charOffsetFrom, availableWidth);
|
|
280
281
|
const endX = charOffsetToX(paragraphBlock, line, charOffsetTo, availableWidth);
|
|
281
|
-
const alignment = paragraphBlock.attrs?.alignment ?? "left";
|
|
282
282
|
let alignmentOffset = 0;
|
|
283
283
|
if (alignment === "center") alignmentOffset = Math.max(0, (availableWidth - line.width) / 2);
|
|
284
284
|
else if (alignment === "right") alignmentOffset = Math.max(0, availableWidth - line.width);
|
|
@@ -305,6 +305,15 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
305
305
|
const tableBlock = block;
|
|
306
306
|
const tableMeasure = measure;
|
|
307
307
|
const tableFragment = fragment;
|
|
308
|
+
let cellPlacements = tableCellPlacements.get(tableBlock);
|
|
309
|
+
if (!cellPlacements) {
|
|
310
|
+
cellPlacements = buildTableCellPlacements({
|
|
311
|
+
grid: buildTableCellGrid(tableBlock.rows, tableMeasure.columnWidths.length),
|
|
312
|
+
columnWidths: tableMeasure.columnWidths,
|
|
313
|
+
bidi: tableBlock.bidi === true
|
|
314
|
+
});
|
|
315
|
+
tableCellPlacements.set(tableBlock, cellPlacements);
|
|
316
|
+
}
|
|
308
317
|
const hdrCount = tableFragment.headerRowCount ?? 0;
|
|
309
318
|
let rowY = hdrCount > 0 && tableFragment.continuesFromPrev ? getHeaderRowsHeight(tableMeasure, hdrCount) : 0;
|
|
310
319
|
for (let rowIndex = tableFragment.fromRow; rowIndex < tableFragment.toRow && rowIndex < tableBlock.rows.length; rowIndex++) {
|
|
@@ -313,11 +322,12 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
313
322
|
if (!row || !rowMeasure) continue;
|
|
314
323
|
const clipTop = tableFragment.topClip ?? 0;
|
|
315
324
|
const clipBottom = tableFragment.bottomClip ?? rowMeasure.height;
|
|
316
|
-
let cellX = getTableRowLeadingWidth(row, tableMeasure.columnWidths);
|
|
317
325
|
for (let cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
|
|
318
326
|
const cell = row.cells[cellIndex];
|
|
319
327
|
const cellMeasure = rowMeasure.cells[cellIndex];
|
|
320
328
|
if (!cell || !cellMeasure) continue;
|
|
329
|
+
const placement = cellPlacements.get(cell);
|
|
330
|
+
if (!placement) continue;
|
|
321
331
|
const contentWidth = getTableCellContentWidth(cell, cellMeasure);
|
|
322
332
|
const floatingZones = buildTableCellFloatingZones(getTableCellFloatingImages(cell, cellMeasure, contentWidth), contentWidth);
|
|
323
333
|
const contentOffsetX = getCellContentOffsetX(cell);
|
|
@@ -340,6 +350,8 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
340
350
|
floatingZones,
|
|
341
351
|
paragraphYOffset: blockY
|
|
342
352
|
});
|
|
353
|
+
const { alignment, indentLeft, indentRight } = resolvePhysicalParagraphInlineLayout(paragraphBlock);
|
|
354
|
+
const availableWidth = Math.max(0, contentWidth - indentLeft - indentRight);
|
|
343
355
|
const intersectingLines = findLinesInRange(paragraphBlock, paragraphMeasure, selFrom, selTo);
|
|
344
356
|
for (const { line, index } of intersectingLines) {
|
|
345
357
|
const range = computeLinePmRange(paragraphBlock, line);
|
|
@@ -351,13 +363,16 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
351
363
|
if (!isEmptyLine && sliceFrom >= sliceTo) continue;
|
|
352
364
|
const charOffsetFrom = pmPosToCharOffset(paragraphBlock, line, sliceFrom);
|
|
353
365
|
const charOffsetTo = pmPosToCharOffset(paragraphBlock, line, sliceTo);
|
|
354
|
-
const startX = charOffsetToX(paragraphBlock, line, charOffsetFrom,
|
|
355
|
-
const endX = charOffsetToX(paragraphBlock, line, charOffsetTo,
|
|
366
|
+
const startX = charOffsetToX(paragraphBlock, line, charOffsetFrom, availableWidth);
|
|
367
|
+
const endX = charOffsetToX(paragraphBlock, line, charOffsetTo, availableWidth);
|
|
368
|
+
let alignmentOffset = 0;
|
|
369
|
+
if (alignment === "center") alignmentOffset = Math.max(0, (availableWidth - line.width) / 2);
|
|
370
|
+
else if (alignment === "right") alignmentOffset = Math.max(0, availableWidth - line.width);
|
|
356
371
|
const lineY = measuredLineContentOffset(paragraphMeasure.lines, 0, index);
|
|
357
372
|
const clippedLineY = contentOffsetY + blockY + lineY;
|
|
358
373
|
if (clippedLineY + line.lineHeight <= clipTop || clippedLineY >= clipBottom) continue;
|
|
359
374
|
rects.push({
|
|
360
|
-
x: tableFragment.x +
|
|
375
|
+
x: tableFragment.x + placement.left + contentOffsetX + indentLeft + alignmentOffset + Math.min(startX, endX),
|
|
361
376
|
y: tableFragment.y + rowY + clippedLineY - clipTop + pageTopY,
|
|
362
377
|
width: isEmptyLine ? EMPTY_PARAGRAPH_SLIVER_WIDTH : Math.max(1, Math.abs(endX - startX)),
|
|
363
378
|
height: line.lineHeight,
|
|
@@ -366,7 +381,6 @@ function selectionToRects(layout, blocks, measures, from, to) {
|
|
|
366
381
|
}
|
|
367
382
|
blockY += paragraphMeasure.totalHeight;
|
|
368
383
|
}
|
|
369
|
-
cellX += cellMeasure.width;
|
|
370
384
|
}
|
|
371
385
|
rowY += rowMeasure.height;
|
|
372
386
|
}
|
|
@@ -419,12 +433,9 @@ function getCaretPosition(layout, blocks, measures, pmPosition) {
|
|
|
419
433
|
if (range.pmStart === void 0 || range.pmEnd === void 0) continue;
|
|
420
434
|
if (pmPosition >= range.pmStart && pmPosition <= range.pmEnd) {
|
|
421
435
|
const charOffset = pmPosToCharOffset(paragraphBlock, line, pmPosition);
|
|
422
|
-
const
|
|
423
|
-
const indentLeft = indent?.left ?? 0;
|
|
424
|
-
const indentRight = indent?.right ?? 0;
|
|
436
|
+
const { alignment, indentLeft, indentRight } = resolvePhysicalParagraphInlineLayout(paragraphBlock);
|
|
425
437
|
const availableWidth = Math.max(0, fragment.width - indentLeft - indentRight);
|
|
426
438
|
const x = charOffsetToX(paragraphBlock, line, charOffset, availableWidth);
|
|
427
|
-
const alignment = paragraphBlock.attrs?.alignment ?? "left";
|
|
428
439
|
let alignmentOffset = 0;
|
|
429
440
|
if (alignment === "center") alignmentOffset = Math.max(0, (availableWidth - line.width) / 2);
|
|
430
441
|
else if (alignment === "right") alignmentOffset = Math.max(0, availableWidth - line.width);
|
|
@@ -3,6 +3,7 @@ import { resolveSectionHeaderFooterRefs } from "./headerFooterRefs.js";
|
|
|
3
3
|
import { calculateChainHeight, computeKeepNextChains, getMidChainIndices, hasKeepLines, hasPageBreakBefore } from "./keep-together.js";
|
|
4
4
|
import { measuredLineAdvance } from "./lineFlow.js";
|
|
5
5
|
import { resolveFloatingTableX } from "./measure/floatingTablePosition.js";
|
|
6
|
+
import { resolveTableInlinePlacement } from "./measure/tableInlinePlacement.js";
|
|
6
7
|
import { createPaginator } from "./paginator.js";
|
|
7
8
|
import { getParagraphFragmentPmRange } from "./paragraphFragmentRange.js";
|
|
8
9
|
import { collapseParagraphSpacing, getParagraphSpacingAfter, getParagraphSpacingBefore, isEmptyParagraph, paragraphsShareStyle, resolveEffectiveParagraphSpacingTree } from "./paragraphSpacing.js";
|
|
@@ -521,15 +522,11 @@ function layoutTable(block, measure, paginator, footnoteHeightById) {
|
|
|
521
522
|
const breakInfo = buildTableRowBreakInfo(block, measure);
|
|
522
523
|
const verticallyMergedRows = getVerticallyMergedRows(block);
|
|
523
524
|
const computeTableX = (columnIndex) => {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const leadingCellMargin = block.rows.at(0)?.cells.at(0)?.padding?.left ?? 0;
|
|
530
|
-
x -= leadingCellMargin;
|
|
531
|
-
}
|
|
532
|
-
return x;
|
|
525
|
+
const x = paginator.getColumnX(columnIndex);
|
|
526
|
+
const placement = resolveTableInlinePlacement(block);
|
|
527
|
+
if (placement.alignment === "center") return x + (paginator.columnWidth - measure.totalWidth) / 2;
|
|
528
|
+
if (placement.alignment === "right") return x + paginator.columnWidth - measure.totalWidth - placement.offset;
|
|
529
|
+
return x + placement.offset;
|
|
533
530
|
};
|
|
534
531
|
const getCurrentRowCapacity = (state = paginator.getCurrentState()) => state.rawContentBottom - state.topMargin;
|
|
535
532
|
const hasAdjacentPriorTableRows = (rowIndex, state = paginator.getCurrentState()) => {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { HyperlinkInfo } from "../types.js";
|
|
2
|
+
//#region src/layout-engine/measure/hyperlinkInstance.d.ts
|
|
3
|
+
declare const setHyperlinkInstanceIndex: (hyperlink: HyperlinkInfo, instanceIndex: number) => void;
|
|
4
|
+
declare const getHyperlinkInstanceIndex: (hyperlink: HyperlinkInfo) => number | undefined;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { getHyperlinkInstanceIndex, setHyperlinkInstanceIndex };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/layout-engine/measure/hyperlinkInstance.ts
|
|
2
|
+
const hyperlinkInstanceIndexes = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
const setHyperlinkInstanceIndex = (hyperlink, instanceIndex) => {
|
|
4
|
+
hyperlinkInstanceIndexes.set(hyperlink, instanceIndex);
|
|
5
|
+
};
|
|
6
|
+
const getHyperlinkInstanceIndex = (hyperlink) => hyperlinkInstanceIndexes.get(hyperlink);
|
|
7
|
+
//#endregion
|
|
8
|
+
export { getHyperlinkInstanceIndex, setHyperlinkInstanceIndex };
|