🐛(frontend) fix svg not rendering export pdf
The svg was not rendering in the pdf export. We overwrite the default mapping to convert the svg to png before rendering. The images could be out of the page as well, we fixed this issue by adding a maxWidth to the image.
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100">
|
||||
<circle cx="50" cy="30" r="20" fill="#3498db" />
|
||||
<polygon
|
||||
points="50,10 55,20 65,20 58,30 60,40 50,35 40,40 42,30 35,20 45,20"
|
||||
fill="#f1c40f"
|
||||
/>
|
||||
<text x="50" y="70" text-anchor="middle" fill="white">Hello svg</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 292 B |
@@ -157,11 +157,9 @@ test.describe('Doc Export', () => {
|
||||
await page.getByText('Upload image').click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(
|
||||
path.join(__dirname, 'assets/logo-suite-numerique.png'),
|
||||
);
|
||||
await fileChooser.setFiles(path.join(__dirname, 'assets/test.svg'));
|
||||
|
||||
const image = page.getByRole('img', { name: 'logo-suite-numerique.png' });
|
||||
const image = page.getByRole('img', { name: 'test.svg' });
|
||||
|
||||
await expect(image).toBeVisible();
|
||||
|
||||
|
||||
@@ -16,17 +16,18 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ag-media/react-pdf-table": "2.0.1",
|
||||
"@blocknote/core": "*",
|
||||
"@blocknote/mantine": "*",
|
||||
"@blocknote/react": "*",
|
||||
"@blocknote/xl-docx-exporter": "*",
|
||||
"@blocknote/xl-pdf-exporter": "*",
|
||||
"@blocknote/core": "0.23.2-hotfix.0",
|
||||
"@blocknote/mantine": "0.23.2-hotfix.0",
|
||||
"@blocknote/react": "0.23.2-hotfix.0",
|
||||
"@blocknote/xl-docx-exporter": "0.23.2-hotfix.0",
|
||||
"@blocknote/xl-pdf-exporter": "0.23.2-hotfix.0",
|
||||
"@gouvfr-lasuite/integration": "1.0.2",
|
||||
"@hocuspocus/provider": "2.15.2",
|
||||
"@openfun/cunningham-react": "3.0.0",
|
||||
"@react-pdf/renderer": "4.1.6",
|
||||
"@sentry/nextjs": "9.3.0",
|
||||
"@tanstack/react-query": "5.67.1",
|
||||
"canvg": "4.0.2",
|
||||
"cmdk": "1.0.4",
|
||||
"crisp-sdk-web": "1.0.25",
|
||||
"docx": "9.1.1",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/* eslint-disable jsx-a11y/alt-text */
|
||||
import { DefaultProps } from '@blocknote/core';
|
||||
import { Image, Text, View } from '@react-pdf/renderer';
|
||||
|
||||
import { DocsExporterPDF } from '../types';
|
||||
import { convertSvgToPng } from '../utils';
|
||||
|
||||
const PIXELS_PER_POINT = 0.75;
|
||||
const FONT_SIZE = 16;
|
||||
|
||||
export const blockMappingImagePDF: DocsExporterPDF['mappings']['blockMapping']['image'] =
|
||||
async (block, exporter) => {
|
||||
const blob = await exporter.resolveFile(block.props.url);
|
||||
let pngConverted: string | undefined;
|
||||
|
||||
if (blob.type.includes('svg')) {
|
||||
const svgText = await blob.text();
|
||||
pngConverted = await convertSvgToPng(svgText);
|
||||
}
|
||||
|
||||
return (
|
||||
<View wrap={false}>
|
||||
<Image
|
||||
src={pngConverted || blob}
|
||||
style={{
|
||||
width: block.props.previewWidth * PIXELS_PER_POINT,
|
||||
maxWidth: '100%',
|
||||
}}
|
||||
/>
|
||||
{caption(block.props)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
function caption(
|
||||
props: Partial<DefaultProps & { caption: string; previewWidth: number }>,
|
||||
) {
|
||||
if (!props.caption) {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
<Text
|
||||
style={{
|
||||
width: props.previewWidth
|
||||
? props.previewWidth * PIXELS_PER_POINT
|
||||
: undefined,
|
||||
fontSize: FONT_SIZE * 0.8 * PIXELS_PER_POINT,
|
||||
maxWidth: '100%',
|
||||
}}
|
||||
>
|
||||
{props.caption}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './dividerDocx';
|
||||
export * from './dividerPDF';
|
||||
export * from './headingPDF';
|
||||
export * from './imagePDF';
|
||||
export * from './paragraphPDF';
|
||||
export * from './quoteDocx';
|
||||
export * from './quotePDF';
|
||||
|
||||
@@ -3,6 +3,7 @@ import { pdfDefaultSchemaMappings } from '@blocknote/xl-pdf-exporter';
|
||||
import {
|
||||
blockMappingDividerPDF,
|
||||
blockMappingHeadingPDF,
|
||||
blockMappingImagePDF,
|
||||
blockMappingParagraphPDF,
|
||||
blockMappingQuotePDF,
|
||||
blockMappingTablePDF,
|
||||
@@ -14,6 +15,7 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = {
|
||||
blockMapping: {
|
||||
...pdfDefaultSchemaMappings.blockMapping,
|
||||
heading: blockMappingHeadingPDF,
|
||||
image: blockMappingImagePDF,
|
||||
paragraph: blockMappingParagraphPDF,
|
||||
divider: blockMappingDividerPDF,
|
||||
quote: blockMappingQuotePDF,
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
DefaultProps,
|
||||
UnreachableCaseError,
|
||||
} from '@blocknote/core';
|
||||
import { Canvg } from 'canvg';
|
||||
import { IParagraphOptions, ShadingType } from 'docx';
|
||||
|
||||
export function downloadFile(blob: Blob, filename: string) {
|
||||
@@ -17,6 +18,26 @@ export function downloadFile(blob: Blob, filename: string) {
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert SVG to PNG
|
||||
* @param svgText - The SVG text to convert
|
||||
* @returns The PNG data URL
|
||||
*/
|
||||
export async function convertSvgToPng(svgText: string) {
|
||||
// Create a canvas and render the SVG onto it
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
if (!ctx) {
|
||||
throw new Error('Canvas context is null');
|
||||
}
|
||||
|
||||
const svg = Canvg.fromString(ctx, svgText);
|
||||
await svg.render();
|
||||
|
||||
return canvas.toDataURL('image/png');
|
||||
}
|
||||
|
||||
export function docxBlockPropsToStyles(
|
||||
props: Partial<DefaultProps>,
|
||||
colors: typeof COLORS_DEFAULT,
|
||||
|
||||
Reference in New Issue
Block a user