fix: aggregate large ink groups safely

This commit is contained in:
2026-07-22 19:54:51 +02:00
parent ebcaf436cf
commit 95341ed81f
2 changed files with 37 additions and 12 deletions

View File

@@ -623,7 +623,7 @@ function parseInk(
id: exGuidKey(object.id), id: exGuidKey(object.id),
strokes: [], strokes: [],
children, children,
boundingBox: unionBoundingBoxes( boundingBox: unionInkBoundingBoxes(
children.map((child) => child.boundingBox) children.map((child) => child.boundingBox)
), ),
layout: parseLayout(object), layout: parseLayout(object),
@@ -915,18 +915,24 @@ function boundingBoxFromStrokes(
: undefined; : undefined;
} }
function unionBoundingBoxes( /** @internal Exported so the bounded aggregation path can be stress-tested. */
export function unionInkBoundingBoxes(
boxes: readonly (OneNoteInkBoundingBox | undefined)[] boxes: readonly (OneNoteInkBoundingBox | undefined)[]
): OneNoteInkBoundingBox | undefined { ): OneNoteInkBoundingBox | undefined {
const present = boxes.filter( let x = Infinity;
(box): box is OneNoteInkBoundingBox => box !== undefined let y = Infinity;
); let x2 = -Infinity;
if (present.length === 0) return undefined; let y2 = -Infinity;
const x = Math.min(...present.map((box) => box.x)); for (const box of boxes) {
const y = Math.min(...present.map((box) => box.y)); if (!box) continue;
const x2 = Math.max(...present.map((box) => box.x + box.width)); x = Math.min(x, box.x);
const y2 = Math.max(...present.map((box) => box.y + box.height)); y = Math.min(y, box.y);
return { x, y, width: x2 - x, height: y2 - y }; x2 = Math.max(x2, box.x + box.width);
y2 = Math.max(y2, box.y + box.height);
}
return Number.isFinite(x)
? { x, y, width: x2 - x, height: y2 - y }
: undefined;
} }
function parseLayout(object: StoreObject): OneNoteLayout { function parseLayout(object: StoreObject): OneNoteLayout {

View File

@@ -15,7 +15,10 @@ import {
type OneNoteTextBlock, type OneNoteTextBlock,
} from '../src/onenote/index.js'; } from '../src/onenote/index.js';
import type { ParserDiagnostic } from '../src/onenote/model/diagnostics.js'; import type { ParserDiagnostic } from '../src/onenote/model/diagnostics.js';
import { parseContentObjects } from '../src/onenote/one/content.js'; import {
parseContentObjects,
unionInkBoundingBoxes,
} from '../src/onenote/one/content.js';
import { JCID, PROPERTY } from '../src/onenote/one/constants.js'; import { JCID, PROPERTY } from '../src/onenote/one/constants.js';
import type { import type {
ExGuid, ExGuid,
@@ -209,6 +212,22 @@ describe('OneNote rich-content model', () => {
expect(parsedPoints).toBeLessThanOrEqual(100); expect(parsedPoints).toBeLessThanOrEqual(100);
}); });
it('combines a large number of child ink bounds without argument spreading', () => {
const boxes = Array.from({ length: 200_000 }, (_, index) => ({
x: index,
y: -index,
width: 2,
height: 3,
}));
expect(unionInkBoundingBoxes([undefined, ...boxes])).toEqual({
x: 0,
y: -199_999,
width: 200_001,
height: 200_002,
});
});
it('bounds retained rich text across content blocks', () => { it('bounds retained rich text across content blocks', () => {
const guid = '{33333333-3333-3333-3333-333333333333}'; const guid = '{33333333-3333-3333-3333-333333333333}';
const firstId = id(guid, 1); const firstId = id(guid, 1);