mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-05-27 07:36:18 +00:00
Improve exporting PDF files.
Changed strategy from rendering the SVG to a canvas and then exporting the canvas as an image, to directly exporting the SVG as an image. This results in much smaller PDF files.
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-02-21
|
||||||
|
|
||||||
|
- Improved saving PDF files. Decreased file size and increased chart size that can be saved as PDF.
|
||||||
|
|
||||||
## 2026-02-13
|
## 2026-02-13
|
||||||
|
|
||||||
- Show header information of the gedcom file on the side panel (by FrankBuchholz)
|
- Show header information of the gedcom file on the side panel (by FrankBuchholz)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {max, min} from 'd3-array';
|
import { max, min } from 'd3-array';
|
||||||
import {interpolateNumber} from 'd3-interpolate';
|
import { interpolateNumber } from 'd3-interpolate';
|
||||||
import {select, Selection} from 'd3-selection';
|
import { select, Selection } from 'd3-selection';
|
||||||
import 'd3-transition';
|
import 'd3-transition';
|
||||||
import {
|
import {
|
||||||
D3ZoomEvent,
|
D3ZoomEvent,
|
||||||
@@ -9,9 +9,9 @@ import {
|
|||||||
ZoomedElementBaseType,
|
ZoomedElementBaseType,
|
||||||
zoomTransform,
|
zoomTransform,
|
||||||
} from 'd3-zoom';
|
} from 'd3-zoom';
|
||||||
import {saveAs} from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import {useEffect, useRef} from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import {IntlShape, useIntl} from 'react-intl';
|
import { IntlShape, useIntl } from 'react-intl';
|
||||||
import {
|
import {
|
||||||
ChartHandle,
|
ChartHandle,
|
||||||
ChartInfo,
|
ChartInfo,
|
||||||
@@ -25,9 +25,9 @@ import {
|
|||||||
RelativesChart,
|
RelativesChart,
|
||||||
ChartColors as TopolaChartColors,
|
ChartColors as TopolaChartColors,
|
||||||
} from 'topola';
|
} from 'topola';
|
||||||
import {ChartColors, Ids, Sex} from './sidepanel/config/config';
|
import { ChartColors, Ids, Sex } from './sidepanel/config/config';
|
||||||
import {Media} from './util/media';
|
import { Media } from './util/media';
|
||||||
import {usePrevious} from './util/previous-hook';
|
import { usePrevious } from './util/previous-hook';
|
||||||
|
|
||||||
/** How much to zoom when using the +/- buttons. */
|
/** How much to zoom when using the +/- buttons. */
|
||||||
const ZOOM_FACTOR = 1.3;
|
const ZOOM_FACTOR = 1.3;
|
||||||
@@ -154,12 +154,29 @@ function getStrippedSvg() {
|
|||||||
return svg;
|
return svg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSvgDimensions() {
|
||||||
|
const svg = document.getElementById('chartSvg')!;
|
||||||
|
return { width: Number(svg.getAttribute('width')), height: Number(svg.getAttribute('height')) };
|
||||||
|
}
|
||||||
|
|
||||||
function getSvgContents() {
|
function getSvgContents() {
|
||||||
return new XMLSerializer().serializeToString(getStrippedSvg());
|
return new XMLSerializer().serializeToString(getStrippedSvg());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSvgContentsWithInlinedImages() {
|
async function getSvgContentsWithInlinedImages() {
|
||||||
const svg = getStrippedSvg();
|
const svg = getStrippedSvg();
|
||||||
|
|
||||||
|
// Set white background because the default background of the SVG
|
||||||
|
// is transparent, which causes issues when printing or exporting to PDF.
|
||||||
|
const svgNs = 'http://www.w3.org/2000/svg';
|
||||||
|
const rect = document.createElementNS(svgNs, 'rect');
|
||||||
|
rect.setAttribute('x', '0');
|
||||||
|
rect.setAttribute('y', '0');
|
||||||
|
rect.setAttribute('width', '100%');
|
||||||
|
rect.setAttribute('height', '100%');
|
||||||
|
rect.setAttribute('fill', 'white');
|
||||||
|
svg.prepend(rect);
|
||||||
|
|
||||||
await inlineImages(svg);
|
await inlineImages(svg);
|
||||||
return new XMLSerializer().serializeToString(svg);
|
return new XMLSerializer().serializeToString(svg);
|
||||||
}
|
}
|
||||||
@@ -186,13 +203,13 @@ export function printChart() {
|
|||||||
|
|
||||||
export async function downloadSvg() {
|
export async function downloadSvg() {
|
||||||
const contents = await getSvgContentsWithInlinedImages();
|
const contents = await getSvgContentsWithInlinedImages();
|
||||||
const blob = new Blob([contents], {type: 'image/svg+xml'});
|
const blob = new Blob([contents], { type: 'image/svg+xml' });
|
||||||
saveAs(blob, 'topola.svg');
|
saveAs(blob, 'topola.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function drawOnCanvas(): Promise<HTMLCanvasElement> {
|
async function drawOnCanvas(): Promise<HTMLCanvasElement> {
|
||||||
const contents = await getSvgContentsWithInlinedImages();
|
const contents = await getSvgContentsWithInlinedImages();
|
||||||
const blob = new Blob([contents], {type: 'image/svg+xml'});
|
const blob = new Blob([contents], { type: 'image/svg+xml' });
|
||||||
return drawImageOnCanvas(await loadImage(blob));
|
return drawImageOnCanvas(await loadImage(blob));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,14 +221,16 @@ export async function downloadPng() {
|
|||||||
|
|
||||||
export async function downloadPdf() {
|
export async function downloadPdf() {
|
||||||
// Lazy load jspdf.
|
// Lazy load jspdf.
|
||||||
const {default: jspdf} = await import('jspdf');
|
const { default: jspdf } = await import('jspdf');
|
||||||
const canvas = await drawOnCanvas();
|
|
||||||
|
const {width, height} = getSvgDimensions();
|
||||||
const doc = new jspdf({
|
const doc = new jspdf({
|
||||||
orientation: canvas.width > canvas.height ? 'l' : 'p',
|
orientation: width > height ? 'l' : 'p',
|
||||||
unit: 'pt',
|
unit: 'pt',
|
||||||
format: [canvas.width, canvas.height],
|
format: [width, height],
|
||||||
});
|
});
|
||||||
doc.addImage(canvas, 'PNG', 0, 0, canvas.width, canvas.height, 'NONE');
|
const contents = await getSvgContentsWithInlinedImages();
|
||||||
|
await doc.addSvgAsImage(contents, 0, 0, width, height);
|
||||||
doc.save('topola.pdf');
|
doc.save('topola.pdf');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +342,7 @@ class ChartWrapper {
|
|||||||
renderChart(
|
renderChart(
|
||||||
props: ChartProps,
|
props: ChartProps,
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
args: {initialRender: boolean; resetPosition: boolean} = {
|
args: { initialRender: boolean; resetPosition: boolean } = {
|
||||||
initialRender: false,
|
initialRender: false,
|
||||||
resetPosition: false,
|
resetPosition: false,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user