docx icon indicating copy to clipboard operation
docx copied to clipboard

further support SVG to word

Open rxht opened this issue 3 years ago • 9 comments

Can you further support SVG to word ?

At present, only the following formats are supported image

rxht avatar Sep 15 '21 02:09 rxht

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

dolanmiu avatar Sep 17 '21 18:09 dolanmiu

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 avatar Sep 22 '21 07:09 deko-39

@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

anti-the-social avatar Sep 22 '21 12:09 anti-the-social

@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

rxht avatar Sep 27 '21 04:09 rxht

好像你可以绕过去。使用一些模块生成 SVG 到 base64 或 jpeg 或 png 的缓冲区并输入到 docxjs 的代码。

I can implement PNG, JPG and word, but what I need is to import SVG format into word

rxht avatar Sep 27 '21 04:09 rxht

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>
    </>
  )
}

error image

antipopp avatar Dec 07 '21 13:12 antipopp

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();
      })
    };
  }

antipopp avatar Dec 07 '21 13:12 antipopp

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))));
  }

tripodsan avatar Apr 03 '22 10:04 tripodsan

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 for it to work

GiladFeder avatar Jun 27 '22 11:06 GiladFeder

@tripodsan , I'm having trouble understanding your code snippet. Where do you add the if (data.ext === 'svg' || data.svgBuffer) block?

joshkel avatar Mar 21 '23 13:03 joshkel

@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;
  

tripodsan avatar Mar 22 '23 07:03 tripodsan

Hi guys, any update on this feature? Did anyone find workaround? Thank You for this great library.

piradon avatar Sep 11 '23 08:09 piradon

Yup, this feature is added here:

https://github.com/dolanmiu/docx/pull/2487

dolanmiu avatar Dec 31 '23 18:12 dolanmiu