React-pdf: Feature - expose the raw doc object to Canvas's paint callback (or a new element to do the same)

Created on 11 Aug 2020  路  3Comments  路  Source: diegomura/react-pdf

I'd like to use svg-to-pdfkit to render an SVG into a PDF I'm building. It requires a handle to the PDFKit document, which I don't see an obvious way to get. The closest we get is the Canvas element's paint callback, which receives the painter object. However, this isn't sufficiently powerful for svg-to-pdfkit's purposes.

I'd like to either have the paint callback pass the root object as an available parameter:

paint(painter, width, height, root)

Or I'd like a new element, like , which would require a render callback to which the this.root.instance handle would be passed, and it would hand off the rendering work to the implementor (in this case, svg-to-pdfkit).

Or, just taking the shortcut even further, an <SVGToPDFKit svg={...} width={...} height={...} /> element which wraps up the whole kit and kaboodle. If we went that route, we could make svg-to-pdfkit a peer dependency, so it's not shipped by default.

If any of these are appropriate paths forward, I'd be happy to submit a PR implementing the desired solution.

new feature

Most helpful comment

https://github.com/cheald/react-pdf/commit/453e8a57bcbf8080559fb270d9c1b258ec7900c8

This is the simplest change necessary to enable this workflow. svg-to-pdfkit works like a champ. I now just have an component, like so:

type AspectRatioPosition =
  | "xMaxYMax"
  | "xMaxYMid"
  | "xMaxYMin"
  | "xMidYMax"
  | "xMidYMid"
  | "xMidYMin"
  | "xMinYMax"
  | "xMinYMid"
  | "xMinYMin";

type AspectRatioBehavior = "meet" | "slice";

type SVGIsh = string | React.ReactNode | SVGImageElement;

function svgishToString(svg: SVGIsh) {
  if (React.isValidElement(svg)) {
    return ReactDOMServer.renderToStaticMarkup(svg);
  } else if (typeof svg === "string") {
    return svg;
  } else {
    return (svg as SVGImageElement).innerHTML;
  }
}

function Svg({
  svg,
  children,
  width,
  height,
  assumePt = true,
  aspectRatio = ["xMidYMid", "meet"],
  ...props
}: Omit<ReactPDF.CanvasProps, "paint"> & {
  children?: SVGIsh;
  svg?: SVGIsh;
  width?: number | string;
  height?: number | string;
  assumePt?: boolean;
  aspectRatio: [AspectRatioPosition, AspectRatioBehavior];
}) {
  const source = React.useMemo(() => svgishToString(children || svg), [
    children,
    svg,
  ]);

  return (
    <Canvas
      style={{ height, width }}
      {...props}
      paint={(_, width, height, root) => {
        SVGToPDFKit(root, source, 0, 0, {
          width,
          height,
          assumePt,
          preserveAspectRatio: aspectRatio.join(" "),
        });
      }}
    />
  );
}

I have to have the SVG already available (either as source, as a React component building the SVG, or as an SVG element DOM node). Pulling remote SVGs will need a new react-pdf element which can do the async/await stuff that React doesn't like to do, as it appears that react-pdf doesn't like to re-render the PDF on a React tree change.

All 3 comments

https://github.com/cheald/react-pdf/commit/453e8a57bcbf8080559fb270d9c1b258ec7900c8

This is the simplest change necessary to enable this workflow. svg-to-pdfkit works like a champ. I now just have an component, like so:

type AspectRatioPosition =
  | "xMaxYMax"
  | "xMaxYMid"
  | "xMaxYMin"
  | "xMidYMax"
  | "xMidYMid"
  | "xMidYMin"
  | "xMinYMax"
  | "xMinYMid"
  | "xMinYMin";

type AspectRatioBehavior = "meet" | "slice";

type SVGIsh = string | React.ReactNode | SVGImageElement;

function svgishToString(svg: SVGIsh) {
  if (React.isValidElement(svg)) {
    return ReactDOMServer.renderToStaticMarkup(svg);
  } else if (typeof svg === "string") {
    return svg;
  } else {
    return (svg as SVGImageElement).innerHTML;
  }
}

function Svg({
  svg,
  children,
  width,
  height,
  assumePt = true,
  aspectRatio = ["xMidYMid", "meet"],
  ...props
}: Omit<ReactPDF.CanvasProps, "paint"> & {
  children?: SVGIsh;
  svg?: SVGIsh;
  width?: number | string;
  height?: number | string;
  assumePt?: boolean;
  aspectRatio: [AspectRatioPosition, AspectRatioBehavior];
}) {
  const source = React.useMemo(() => svgishToString(children || svg), [
    children,
    svg,
  ]);

  return (
    <Canvas
      style={{ height, width }}
      {...props}
      paint={(_, width, height, root) => {
        SVGToPDFKit(root, source, 0, 0, {
          width,
          height,
          assumePt,
          preserveAspectRatio: aspectRatio.join(" "),
        });
      }}
    />
  );
}

I have to have the SVG already available (either as source, as a React component building the SVG, or as an SVG element DOM node). Pulling remote SVGs will need a new react-pdf element which can do the async/await stuff that React doesn't like to do, as it appears that react-pdf doesn't like to re-render the PDF on a React tree change.

I would really like this feature added as well, I think a separate SvgRef element or property on the Svg element would be the nicest approach, but would be happy to just have a raw doc object too.

2nd up this feature.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

idrisadetunmbi picture idrisadetunmbi  路  3Comments

kishaningithub picture kishaningithub  路  4Comments

jbrat picture jbrat  路  3Comments

brandly picture brandly  路  3Comments

foureyedraven picture foureyedraven  路  3Comments