docx
docx copied to clipboard
further support SVG to word
Can you further support SVG to word ?
At present, only the following formats are supported
I think this can be possible, but there doesn't seem to be much documentation on it
https://bugs.documentfoundation.org/show_bug.cgi?id=126084
Seem like you can make the way around. Using some module to generate SVG to base64 or buffer of jpeg or png and input to code of docxjs.
@deko-39 I assume they are discussing principal possibility to attach SVG pictures. There are quite a bit of image editors/convertors to make SVG -> png, jpg, B64, gif etc
@deko-39我认为他们正在讨论附加 SVG 图片的主要可能性。 有相当多的图像编辑器/转换器可以制作 SVG -> png、jpg、B64、gif 等
I can implement PNG, JPG and word, but what I need is to import SVG format into word
好像你可以绕过去。使用一些模块生成 SVG 到 base64 或 jpeg 或 png 的缓冲区并输入到 docxjs 的代码。
I can implement PNG, JPG and word, but what I need is to import SVG format into word
Anybody got a workaround? I'm trying to use a QR code svg as image but I'm getting an error image (cannot view the image) in the final file. Also, I'm on React.
Almost working example:
const Page = (props: Props) => {
const handleDocx = () => {
const qr = document.getElementById('qrcode')
const svgData = new XMLSerializer().serializeToString(qr);
const b64 = window.btoa(svgData);
const doc = new Document({
sections: [
{
children: [
new Paragraph({
children: [
new ImageRun({
data: Uint8Array.from(atob(b64), c =>
c.charCodeAt(0)
),
transformation: {
width: 256,
height: 256,
}
}),
],
}),
]
}
]
});
Packer.toBlob(doc).then(blob => {
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "qr.docx";
a.click();
})
}
return (
<>
<QRCode value={window.location.href} size={256} id="qrcode" />
<button onClick={handleDocx}></button>
</>
)
}
Fixed my issue by converting the svg to image and then to base64
const handleDocx = () => {
const qr = document.getElementById('qrcode')
const svgData = new XMLSerializer().serializeToString(qr);
const svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
const svgUrl = URL.createObjectURL(svgBlob);
const img = new Image();
img.src = svgUrl;
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx!.drawImage(img, 0, 0);
const canvasData = canvas.toDataURL("image/png");
const canvas64 = canvasData.replace(/^data:image\/(png|jpg);base64,/, "");
const doc = new Document({
sections: [
{
children: [
new Paragraph({
children: [
new ImageRun({
data: Uint8Array.from(atob(canvas64), c =>
c.charCodeAt(0)
),
transformation: {
width: 256,
height: 256,
}
}),
],
}),
]
}
]
});
Packer.toBlob(doc).then(blob => {
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "qr.docx";
a.click();
})
};
}
note that drag-drop SVG in word woks. but i will create a PNG rendition and add it as ref to the blip. but it will also append an extension list with an svgBlip:
<pic:blipFill>
<a:blip r:embed="rId4">
<a:extLst>
<a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
<a14:useLocalDpi val="0" xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main"/>
</a:ext>
<a:ext uri="{96DAC541-7B7A-43D3-8B79-37D633B846F1}">
<asvg:svgBlip r:embed="rId5" xmlns:asvg="http://schemas.microsoft.com/office/drawing/2016/SVG/main"/>
</a:ext>
</a:extLst>
</a:blip>
<a:stretch>
<a:fillRect/>
</a:stretch>
</pic:blipFill>
Another problem is, that the svg
needs to be added to the [content-types]
.
I used the following code to create the missing elements (given, I already have the drawing element)
class SvgBlip extends XmlComponent {
constructor(imgData) {
super('asvg:svgBlip');
this.imageData = imgData;
this.addChildElement(new XmlAttributeComponent({
'xmlns:asvg': 'http://schemas.microsoft.com/office/drawing/2016/SVG/main',
'r:embed': `rId{${imgData.fileName}}`,
}));
}
prepForXml(context) {
// add the svg data if it has a stream
if (this.imageData.stream) {
context.file.Media.addImage(this.imageData.fileName, this.imageData);
}
// add svg content type if missing
if (!context.file.contentTypes.root.find((entry) => entry.rootKey === 'Default'
&& (entry.root[0].root.extension === 'svg' || entry.root[0].root.Extension === 'svg'))) {
context.file.contentTypes.root.push(new XmlComponent('Default').addChildElement(new XmlAttributeComponent({
ContentType: 'image/svg+xml',
Extension: 'svg',
})));
}
return super.prepForXml(context);
}
}
if (data.ext === 'svg' || data.svgBuffer) {
// create a fake image run for the svg image
const ir = new ImageRun({
data: data.svgBuffer,
transformation: data.dimensions,
});
ir.imageData.fileName = data.svgKey;
const blipFill = findXMLComponent(drawing, 'wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill');
const blip = findXMLComponent(blipFill, 'a:blip');
// add svg stuff
blip
.addChildElement(new XmlComponent('a:extLst')
.addChildElement(new XmlComponent('a:ext')
.addChildElement(new XmlAttributeComponent({
uri: '{28A0092B-C50C-407E-A947-70E740481C1C}',
}))
.addChildElement(new XmlComponent('a14:useLocalDpi')
.addChildElement(new XmlAttributeComponent({
'xmlns:a14': 'http://schemas.microsoft.com/office/drawing/2010/main',
val: '0',
}))))
.addChildElement(new XmlComponent('a:ext')
.addChildElement(new XmlAttributeComponent({
uri: '{96DAC541-7B7A-43D3-8B79-37D633B846F1}',
}))
.addChildElement(new SvgBlip(ir.imageData))));
}
what I've done in my projects was converting SVGs to PNGs on the spot. The drawback is that this works on the client-side only as you need a
@tripodsan , I'm having trouble understanding your code snippet. Where do you add the if (data.ext === 'svg' || data.svgBuffer)
block?
@joshkel this is where I create the ImageRun
object:
const drawing = new Drawing(imageData, { floating: false });
....
// create picture
const pic = new ImageRun({
data: data.buffer,
transformation: data.dimensions,
});
// replace drawing
const oldDrawing = findXMLComponent(pic, 'w:drawing');
const idx = pic.root.indexOf(oldDrawing);
if (idx >= 0) {
pic.root.splice(idx, 1);
}
pic.root.push(drawing);
pic.key = data.key;
pic.imageData = imageData;
....
if (data.ext === 'svg' || data.svgBuffer) {
....
}
return pic;
Hi guys, any update on this feature? Did anyone find workaround? Thank You for this great library.
Yup, this feature is added here:
https://github.com/dolanmiu/docx/pull/2487