diff --git a/src/components/PageReader.test.tsx b/src/components/PageReader.test.tsx
index 4408041..63ec432 100644
--- a/src/components/PageReader.test.tsx
+++ b/src/components/PageReader.test.tsx
@@ -94,11 +94,8 @@ const page: OneNotePageDto = {
describe('PageReader structured content', () => {
beforeEach(() => {
- vi.stubGlobal('URL', {
- ...URL,
- createObjectURL: vi.fn(() => 'blob:local-image'),
- revokeObjectURL: vi.fn(),
- });
+ vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:local-image');
+ vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
});
it('renders semantic formatting, links, tables, and a signed image resource', async () => {
diff --git a/src/components/PageReader.tsx b/src/components/PageReader.tsx
index 418fdca..a7894d4 100644
--- a/src/components/PageReader.tsx
+++ b/src/components/PageReader.tsx
@@ -426,15 +426,20 @@ function ImageView({
function InkView({ ink }: { ink: OneNoteInkBlock }) {
const box = ink.boundingBox ?? boundingBoxFromInk(ink.strokes);
- const padding = Math.max(
- 140,
- ...ink.strokes.map((stroke) => stroke.width ?? 0)
+ const widestStroke = ink.strokes.reduce(
+ (widest, stroke) => Math.max(widest, stroke.width ?? 0),
+ 0
);
+ const padding = Math.max(140, Math.min(1_000_000, widestStroke));
const viewBox = box
? `${box.x - padding / 2} ${box.y - padding / 2} ${Math.max(box.width + padding, 1)} ${Math.max(box.height + padding, 1)}`
: '0 0 1 1';
- const width = box ? Math.max((box.width + padding) / (2540 / 96), 1) : 1;
- const height = box ? Math.max((box.height + padding) / (2540 / 96), 1) : 1;
+ const width = box
+ ? Math.min(Math.max((box.width + padding) / (2540 / 96), 1), 10_000)
+ : 1;
+ const height = box
+ ? Math.min(Math.max((box.height + padding) / (2540 / 96), 1), 10_000)
+ : 1;
return (
@@ -452,7 +457,10 @@ function InkView({ ink }: { ink: OneNoteInkBlock }) {
d={inkPath(stroke)}
fill="none"
stroke={inkColor(stroke.color)}
- strokeWidth={Math.max(stroke.width ?? 140, 1)}
+ strokeWidth={Math.min(
+ Math.max(stroke.width ?? 140, 1),
+ 1_000_000
+ )}
strokeOpacity={
stroke.transparency === undefined
? undefined
diff --git a/tests/content-model.test.ts b/tests/content-model.test.ts
index 4f34a71..a019cb0 100644
--- a/tests/content-model.test.ts
+++ b/tests/content-model.test.ts
@@ -121,6 +121,75 @@ describe('OneNote rich-content model', () => {
).toBeUndefined();
});
+ it('retains an embedded-file node as a downloadable lazy resource', () => {
+ const guid = '{22222222-2222-2222-2222-222222222222}';
+ const attachmentId = id(guid, 1);
+ const containerId = id(guid, 2);
+ const payload = Uint8Array.of(1, 2, 3, 4);
+ const attachment = object(
+ attachmentId,
+ JCID.EmbeddedFileNode,
+ {
+ ...emptyProps(),
+ objectIds: [compact(2)],
+ properties: {
+ entries: [
+ referenceEntry(PROPERTY.EmbeddedFileContainer, 'one'),
+ bytesEntry(
+ PROPERTY.EmbeddedFileName,
+ Buffer.from('report.txt', 'utf16le')
+ ),
+ ],
+ },
+ },
+ guid
+ );
+ const container = object(
+ containerId,
+ JCID.EmbeddedFileContainer,
+ emptyProps(),
+ guid
+ );
+ container.fileData = {
+ referenceKind: 'embedded',
+ dataReference: '{22222222-2222-2222-2222-222222222222}',
+ extension: '.txt',
+ blob: { source: payload, offset: 0, size: payload.length },
+ };
+ const resources = new Map();
+ const space: ObjectSpace = {
+ id: id(guid, 0),
+ roots: new Map(),
+ objects: new Map([
+ [exGuidKey(attachment.id), attachment],
+ [exGuidKey(container.id), container],
+ ]),
+ };
+
+ const blocks = parseContentObjects([attachmentId], {
+ space,
+ limits: { ...DEFAULT_ONENOTE_PARSER_LIMITS },
+ diagnostics: [],
+ resources,
+ });
+
+ expect(blocks).toEqual([
+ expect.objectContaining({
+ kind: 'attachment',
+ filename: 'report.txt',
+ size: 4,
+ resourceId: exGuidKey(containerId),
+ }),
+ ]);
+ expect(resources.get(exGuidKey(containerId))).toMatchObject({
+ kind: 'attachment',
+ extension: 'txt',
+ mediaType: 'text/plain',
+ availability: 'available',
+ size: 4,
+ });
+ });
+
it('bounds aggregate ink point expansion', () => {
const model = parseOneNoteSectionContent(fixture, {
limits: { maxInkPoints: 100 },