Skip to content

Commit

Permalink
fix(frontend): PDF text positioning
Browse files Browse the repository at this point in the history
  • Loading branch information
kris7t committed Nov 23, 2024
1 parent c99595e commit 4097f58
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ module.exports = {
// In typescript, some class methods implementing an inderface do not use `this`:
// https://github.com/typescript-eslint/typescript-eslint/issues/1103
'class-methods-use-this': 'off',
eqeqeq: 'error',
// Disable rules with a high performance cost.
// See https://typescript-eslint.io/linting/troubleshooting/performance-troubleshooting/
'import/default': 'off',
Expand Down
43 changes: 43 additions & 0 deletions subprojects/frontend/src/graph/export/exportDiagram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,48 @@ async function serializePNG(
});
}

/**
* Compensate for text baseline alignment as a workaround for the lack of support
* for the `dominant-baseline` SVG property for positioning text.
*
* We remove the `dominant-baseline` attribute from the already in the DOM and
* measure the vertical shift to compensate for it in the serialized SVG.
* This causes frequent DOM layout calculations, but we only do this when creating
* a PDF and make sure to restore the original layout at the end.
*
* @param svg The original SVG document currently present in the DOM.
* @param copyOfSVG The SVG document to be serialized as PDF, not in the DOM.
*/
function fixTextBaseline(svg: SVGSVGElement, copyOfSVG: SVGSVGElement): void {
const originalText = svg.querySelectorAll<SVGTextElement>(
'text[dominant-baseline]',
);
const copiedText = copyOfSVG.querySelectorAll<SVGTextElement>(
'text[dominant-baseline]',
);
copiedText.forEach((text, i) => {
const original = originalText[i];
if (original === undefined) {
return;
}
const y = parseFloat(original.getAttribute('y') ?? '');
const dominantBaseline = original.getAttribute('dominant-baseline');
if (dominantBaseline === null) {
return;
}
const bbox = original.getBBox();
let shiftedBBox: DOMRect;
try {
original.setAttribute('dominant-baseline', 'auto');
shiftedBBox = original.getBBox();
} finally {
original.setAttribute('dominant-baseline', dominantBaseline);
}
text.setAttribute('y', String(y + bbox.y - shiftedBBox.y));
text.setAttribute('dominant-baseline', 'auto');
});
}

let serializePDFCached:
| ((svg: SVGSVGElement, embedFonts: boolean) => Promise<Blob>)
| undefined;
Expand Down Expand Up @@ -386,6 +428,7 @@ export default async function exportDiagram(
);

if (settings.format === 'pdf') {
fixTextBaseline(svg, copyOfSVG);
const pdf = await serializePDF(copyOfSVG, settings);
await saveBlob(pdf, `${graph.name}.pdf`, {
id: EXPORT_ID,
Expand Down

0 comments on commit 4097f58

Please sign in to comment.