Skip to content

Commit

Permalink
Implement drawSvgSync
Browse files Browse the repository at this point in the history
  • Loading branch information
MatheusrdSantos committed May 15, 2024
1 parent 9593e75 commit f0540b9
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 18 deletions.
23 changes: 22 additions & 1 deletion src/api/PDFPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import {
assertRangeOrUndefined,
assertIsOneOfOrUndefined,
} from '../utils';
import { drawSvg } from './svg';
import { drawSvg, drawSvgSync } from './svg';

/**
* Represents a single page of a [[PDFDocument]].
Expand Down Expand Up @@ -1604,6 +1604,27 @@ export default class PDFPage {
});
}

drawSvgSync(
svg: string,
options: PDFPageDrawSVGElementOptions = {},
): void {
assertIs(svg, 'svg', ['string']);
assertOrUndefined(options.x, 'options.x', ['number']);
assertOrUndefined(options.y, 'options.y', ['number']);
assertOrUndefined(options.width, 'options.width', ['number']);
assertOrUndefined(options.height, 'options.height', ['number']);
assertOrUndefined(options.images, 'options.images', [[PDFImage, 'PDFImage']])

drawSvgSync(this, svg, {
x: options.x ?? this.x,
y: options.y ?? this.y,
fonts: options.fonts,
width: options.width,
height: options.height,
images: options.images
});
}

getFont(): [PDFFont, PDFName] {
if (!this.font || !this.fontKey) {
const font = this.doc.embedStandardFont(StandardFonts.Helvetica);
Expand Down
2 changes: 2 additions & 0 deletions src/api/PDFPageOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PDFFont from './PDFFont';
import { Rotation } from './rotations';
import { FillRule, LineCapStyle, TextRenderingMode } from './operators';
import type { Space, TransformationMatrix } from '../types';
import PDFImage from './PDFImage';

interface SvgOptions {
matrix?: TransformationMatrix;
Expand Down Expand Up @@ -174,4 +175,5 @@ export interface PDFPageDrawSVGElementOptions {
height?: number;
fontSize?: number;
fonts?: { [fontName: string]: PDFFont };
images?: Record<string, PDFImage>
}
77 changes: 60 additions & 17 deletions src/api/svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export type SVGElement = HTMLElement & {
};

interface SVGElementToDrawMap {
[cmd: string]: (a: SVGElement) => Promise<void>;
[cmd: string]: (a: SVGElement) => void;
}

const combineMatrix = (
Expand Down Expand Up @@ -220,7 +220,7 @@ const runnersToPage = (
page: PDFPage,
options: PDFPageDrawSVGElementOptions,
): SVGElementToDrawMap => ({
async text(element) {
text(element) {
const anchor = element.svgAttributes.textAnchor;
const dominantBaseline = element.svgAttributes.dominantBaseline;
const text = element.text.trim().replace(/\s/g, ' ');
Expand Down Expand Up @@ -279,7 +279,7 @@ const runnersToPage = (
clipSpaces: element.svgAttributes.clipSpaces,
});
},
async line(element) {
line(element) {
page.drawLine({
start: {
x: element.svgAttributes.x1 || 0,
Expand All @@ -297,7 +297,7 @@ const runnersToPage = (
clipSpaces: element.svgAttributes.clipSpaces,
});
},
async path(element) {
path(element) {
if (!element.svgAttributes.d) return;
// See https://jsbin.com/kawifomupa/edit?html,output and
page.drawSvgPath(element.svgAttributes.d, {
Expand All @@ -319,13 +319,10 @@ const runnersToPage = (
clipSpaces: element.svgAttributes.clipSpaces,
});
},
async image(element) {
image(element) {
const { src } = element.svgAttributes;
if (!src) return;
const isPng = src.match(/\.png(\?|$)|^data:image\/png;base64/gim);
const img = isPng
? await page.doc.embedPng(src)
: await page.doc.embedJpg(src);
if (!(src && options.images?.[src])) return;
const img = options.images?.[src]!

const { x, y, width, height } = getFittingRectangle(
img.width,
Expand All @@ -344,7 +341,7 @@ const runnersToPage = (
clipSpaces: element.svgAttributes.clipSpaces,
});
},
async rect(element) {
rect(element) {
if (!element.svgAttributes.fill && !element.svgAttributes.stroke) return;
page.drawRectangle({
x: 0,
Expand All @@ -361,7 +358,7 @@ const runnersToPage = (
clipSpaces: element.svgAttributes.clipSpaces,
});
},
async ellipse(element) {
ellipse(element) {
page.drawEllipse({
x: element.svgAttributes.cx || 0,
y: -(element.svgAttributes.cy || 0),
Expand All @@ -377,7 +374,7 @@ const runnersToPage = (
clipSpaces: element.svgAttributes.clipSpaces,
});
},
async circle(element) {
circle(element) {
return runnersToPage(page, options).ellipse(element);
},
});
Expand Down Expand Up @@ -897,12 +894,12 @@ const parse = (
);
};

export const drawSvg = async (
const parseSvg = (
page: PDFPage,
svg: string,
options: PDFPageDrawSVGElementOptions,
) => {
if (!svg) return;
if (!svg) return [];
const size = page.getSize();
const firstChild = parseHtml(svg).firstChild as HTMLElement;

Expand Down Expand Up @@ -947,14 +944,49 @@ export const drawSvg = async (
options.y || 0,
];

const runners = runnersToPage(page, options);
const elements = parse(
firstChild.outerHTML,
options,
size,
baseTransformation,
);

return elements
};

export const drawSvg = async (
page: PDFPage,
svg: string,
options: PDFPageDrawSVGElementOptions
) => {
const elements = parseSvg(page, svg, options)
const runners = runnersToPage(page, options);

const parseImage = async (element: SVGElement) => {
const { src } = element.svgAttributes;
if (!src) return;
const isPng = src.match(/\.png(\?|$)|^data:image\/png;base64/gim);
const img = isPng
? await page.doc.embedPng(src)
: await page.doc.embedJpg(src);

const { x, y, width, height } = getFittingRectangle(
img.width,
img.height,
element.svgAttributes.width || img.width,
element.svgAttributes.height || img.height,
element.svgAttributes.preserveAspectRatio,
);
page.drawImage(img, {
x,
y: -y - height,
width,
height,
opacity: element.svgAttributes.fillOpacity,
matrix: element.svgAttributes.matrix,
clipSpaces: element.svgAttributes.clipSpaces,
});
}
await elements.reduce(async (prev, elt) => {
await prev;
// uncomment these lines to draw the clipSpaces
Expand Down Expand Up @@ -987,6 +1019,17 @@ export const drawSvg = async (
// thickness: 1
// })
// })
if (elt.tagName === 'image') return parseImage(elt);
return runners[elt.tagName]?.(elt);
}, Promise.resolve());
};
}

export const drawSvgSync = (
page: PDFPage,
svg: string,
options: PDFPageDrawSVGElementOptions
) => {
const elements = parseSvg(page, svg, options);
const runners = runnersToPage(page, options);
elements.forEach(elt => runners[elt.tagName]?.(elt));
}

0 comments on commit f0540b9

Please sign in to comment.