fix: map ink transparency to SVG opacity

This commit is contained in:
2026-07-22 19:46:40 +02:00
parent 3b1777a5b3
commit 239bb3c465
4 changed files with 32 additions and 3 deletions

View File

@@ -82,6 +82,7 @@ const page: OneNotePageDto = {
], ],
width: 2, width: 2,
color: 0xff0000, color: 0xff0000,
transparency: 255,
}, },
], ],
children: [], children: [],
@@ -127,6 +128,10 @@ describe('PageReader structured content', () => {
'd', 'd',
'M 10 20 L 15 25 30 40' 'M 10 20 L 15 25 30 40'
); );
expect(document.querySelector('.onenote-ink path')).toHaveAttribute(
'stroke-opacity',
'0'
);
await waitFor(() => await waitFor(() =>
expect( expect(
screen.getByRole('img', { name: 'Embedded diagram' }) screen.getByRole('img', { name: 'Embedded diagram' })

View File

@@ -464,7 +464,7 @@ function InkView({ ink }: { ink: OneNoteInkBlock }) {
strokeOpacity={ strokeOpacity={
stroke.transparency === undefined stroke.transparency === undefined
? undefined ? undefined
: Math.max(0, Math.min(1, (255 - stroke.transparency) / 256)) : Math.max(0, Math.min(1, 1 - stroke.transparency / 255))
} }
strokeLinecap={stroke.penTip === 0 ? 'round' : 'square'} strokeLinecap={stroke.penTip === 0 ? 'round' : 'square'}
strokeLinejoin={stroke.penTip === 0 ? 'round' : 'bevel'} strokeLinejoin={stroke.penTip === 0 ? 'round' : 'bevel'}

View File

@@ -103,7 +103,7 @@ describe('OneNote export serializers', () => {
expect(document.querySelectorAll('svg.ink')).toHaveLength(1); expect(document.querySelectorAll('svg.ink')).toHaveLength(1);
expect( expect(
document.querySelector('polyline')?.getAttribute('stroke-opacity') document.querySelector('polyline')?.getAttribute('stroke-opacity')
).toBe('0.496'); ).toBe('0.498');
expect( expect(
document document
.querySelector('meta[http-equiv="Content-Security-Policy"]') .querySelector('meta[http-equiv="Content-Security-Policy"]')
@@ -111,6 +111,30 @@ describe('OneNote export serializers', () => {
).toContain("default-src 'none'"); ).toContain("default-src 'none'");
}); });
it('maps the full ink transparency byte range to SVG opacity', () => {
const snapshot = fixtureSnapshot();
const ink = snapshot.sections[0]!.pages[0]!.blocks.find(
(block) => block.kind === 'ink'
);
if (!ink || ink.kind !== 'ink') throw new Error('Ink fixture is missing');
ink.strokes[0]!.transparency = 0;
ink.strokes.push({
...ink.strokes[0]!,
points: ink.strokes[0]!.points.map((point) => ({ ...point })),
transparency: 255,
});
const document = new DOMParser().parseFromString(
serializeOneNoteSemanticHtml(snapshot),
'text/html'
);
expect(
[...document.querySelectorAll('polyline')].map((stroke) =>
stroke.getAttribute('stroke-opacity')
)
).toEqual(['1', '0']);
});
it('creates a deterministic, path-safe static notebook ZIP', () => { it('creates a deterministic, path-safe static notebook ZIP', () => {
const snapshot = fixtureSnapshot(); const snapshot = fixtureSnapshot();
const first = createOneNoteStaticNotebookZip(snapshot); const first = createOneNoteStaticNotebookZip(snapshot);

View File

@@ -573,7 +573,7 @@ function svgStroke(stroke: OneNoteInkBlock['strokes'][number]): string {
stroke.transparency !== undefined && Number.isFinite(stroke.transparency) stroke.transparency !== undefined && Number.isFinite(stroke.transparency)
? Math.max(0, Math.min(255, stroke.transparency)) ? Math.max(0, Math.min(255, stroke.transparency))
: undefined; : undefined;
const opacity = transparency === undefined ? 1 : (255 - transparency) / 256; const opacity = transparency === undefined ? 1 : 1 - transparency / 255;
if (points.length === 1) { if (points.length === 1) {
return `<circle cx="${formatNumber(points[0]!.x)}" cy="${formatNumber(points[0]!.y)}" r="${formatNumber(width / 2)}" fill="${color}" fill-opacity="${formatNumber(opacity)}"/>`; return `<circle cx="${formatNumber(points[0]!.x)}" cy="${formatNumber(points[0]!.y)}" r="${formatNumber(width / 2)}" fill="${color}" fill-opacity="${formatNumber(opacity)}"/>`;
} }