Skip to content

Instantly share code, notes, and snippets.

@OliverLeighC
Last active April 30, 2021 16:09
Show Gist options
  • Save OliverLeighC/1ff2b801a45257af97aa3ed11022a319 to your computer and use it in GitHub Desktop.
Save OliverLeighC/1ff2b801a45257af97aa3ed11022a319 to your computer and use it in GitHub Desktop.
React-PDF Svg Chart Options
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\" version=\"1.1\" class=\"highcharts-root\" style=\"font-family:Helvetica;font-size:20px;\" xmlns=\"http://www.w3.org/2000/svg\" width=\"1000\" height=\"800\" viewBox=\"0 0 1000 800\"><desc>Created with Highcharts 9.0.1</desc><defs><clipPath id=\"highcharts-hn7jjeq-91-\"><rect x=\"0\" y=\"0\" width=\"980\" height=\"737\" fill=\"none\"></rect></clipPath></defs><rect fill=\"#ffffff\" class=\"highcharts-background\" x=\"0\" y=\"0\" width=\"1000\" height=\"800\" rx=\"0\" ry=\"0\"></rect><rect fill=\"none\" class=\"highcharts-plot-background\" x=\"10\" y=\"48\" width=\"980\" height=\"737\"></rect><rect fill=\"none\" class=\"highcharts-plot-border\" data-z-index=\"1\" x=\"10\" y=\"48\" width=\"980\" height=\"737\"></rect><g class=\"highcharts-series-group\" data-z-index=\"3\"><g class=\"highcharts-series highcharts-series-0 highcharts-pie-series\" data-z-index=\"0.1\" opacity=\"1\" transform=\"translate(10,48) scale(1 1)\"><path fill=\"none\" d=\"M 490 10 A 358.5 358.5 0 1 1 489.64150005974983 10.00017924998508 M 490 368.5 A 0 0 0 1 0 490 368.5\" class=\"highcharts-empty-series\" stroke=\"#cccccc\" stroke-width=\"1\"></path></g><g class=\"highcharts-markers highcharts-series-0 highcharts-pie-series\" data-z-index=\"0.1\" opacity=\"1\" transform=\"translate(10,48) scale(1 1)\"></g></g><text x=\"500\" text-anchor=\"middle\" class=\"highcharts-title\" data-z-index=\"4\" style=\"color:#333333;font-size:18px;fill:#333333;\" y=\"24\"></text><text x=\"500\" text-anchor=\"middle\" class=\"highcharts-subtitle\" data-z-index=\"4\" style=\"color:#666666;font-size:20px;fill:#666666;\" y=\"30\">As Of Apr-2021</text><text x=\"10\" text-anchor=\"start\" class=\"highcharts-caption\" data-z-index=\"4\" style=\"color:#666666;fill:#666666;\" y=\"803\"></text><g class=\"highcharts-data-labels highcharts-series-0 highcharts-pie-series\" data-z-index=\"6\" opacity=\"1\" transform=\"translate(10,48) scale(1 1)\"></g><g class=\"highcharts-legend\" data-z-index=\"7\"><rect fill=\"none\" class=\"highcharts-legend-box\" rx=\"0\" ry=\"0\" x=\"0\" y=\"0\" width=\"8\" height=\"8\" visibility=\"hidden\"></rect><g data-z-index=\"1\"><g></g></g></g></svg>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
/* eslint-disable no-console */
import { useMemo, createElement } from "react";
import { parse, TextNode, ElementNode, RootNode } from "svg-parser";
const supportedStyleProps = [
"color",
"dominantBaseline",
"fill",
"fillOpacity",
"fillRule",
"opacity",
"stroke",
"strokeWidth",
"strokeOpacity",
"strokeLinecap",
"strokeDasharray",
"transform",
"textAnchor",
"visibility"
]
function isElementNode(node: TextNode | ElementNode): node is ElementNode {
return node.type === 'element'
}
function removeLineBreaks(text?: string | number | boolean) {
if (typeof text === 'string') {
return text.replace(/(\r\n|\n|\r)/gm, "")
}
return text;
}
// https://dev.to/qausim/convert-html-inline-styles-to-a-style-object-for-react-components-2cbi
const formatStringToCamelCase = (str: string) => {
const splitted = str.split("-");
if (splitted.length === 1) return splitted[0];
return (
splitted[0] +
splitted
.slice(1)
.map((word) => word[0].toUpperCase() + word.slice(1))
.join("")
);
};
export const getStyleObjectFromString = (str: string | null) => {
const style: any = {};
if (!str) return {};
str.split(";").forEach((el) => {
const [property, value] = el.split(":");
if (!property) return;
if (property === "cursor") return;
const formattedProperty = formatStringToCamelCase(property.trim());
if (supportedStyleProps.includes(formattedProperty)) {
style[formattedProperty] = value.trim();
}
});
return style;
};
function handleRelativePositioning(currentPosition: string | number, parentPosition?: number) {
let position = Number(currentPosition) ?? 0;
if (parentPosition) {
position += parentPosition
}
return position;
}
function getParentPosition(pos: number | string | undefined) {
if (!pos) return 0;
if (typeof pos === 'string') return Number(pos);
return pos;
}
function svgToJSXWithRelPositioning(
node: TextNode | ElementNode | string, parentX?: number, parentY?: number
): any {
if (typeof node === 'string') {
return removeLineBreaks(node);
}
if (!isElementNode(node)) {
return removeLineBreaks(node.value);
}
const elementName = node.tagName;
if (!elementName) {
console.log('NO TAG NAME: ', node);
return null;
}
let componentProps;
if (node.tagName === 'desc' || node.tagName === 'defs') return null;
if (node.properties !== undefined) {
if (node.tagName === "text" || node.tagName === "tspan") {
componentProps = {
x: handleRelativePositioning(node.properties.x, parentX),
y: handleRelativePositioning(node.properties.y, parentY),
textAnchor: node.properties['text-anchor']
}
} else {
componentProps = {
x: handleRelativePositioning(node.properties.x, parentX),
y: handleRelativePositioning(node.properties.y, parentY),
...node.properties
}
}
if (node.properties.style) {
componentProps = {
...componentProps,
style: getStyleObjectFromString(node.properties.style as string)
}
}
}
let children;
if (node.children && node.children.length > 0) {
children = node.children.map(
(childNode) =>
svgToJSXWithRelPositioning(
childNode, getParentPosition(node.properties?.x), getParentPosition(node.properties?.y)
)
)
}
return createElement(elementName.toUpperCase(), componentProps, children);
}
export const createSvgComponent = (svgXml: string) => {
if (!svgXml || svgXml === "") return null;
const svg = svgXml.replaceAll("px", "pt");
const svgElement = useMemo(() => {
const parsed: RootNode = parse(svg);
return svgToJSXWithRelPositioning(parsed.children[0], 0);
}, [svgXml]);
return svgElement;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment