0

I've got a large number of SVG files that link to external resources. Those resources are image files (usually .jpg).

The SVG spec also allows this image data to be embedded within the SVG. Is there a way to batch-convert these SVG files to embed the image data?

1
  • I see from the comment on the answer below that you built your own python code to do the job. Have you considered a) Self answering and marking the answer as correct & b) possibly open sourcing your solution for others to use by either publishing the code in your answer or linking to it on github or some such? – Steve Barnes Nov 28 '20 at 8:41
1

If you're into Javascript: Maybe the NodeJS app juice in conjunction with the web-resource-inliner module can do the job?

Juice has a web client interface, but in there the option to fetch remote resources seems to be disabled. So you need to install the software and try for yourself, on the command-line. The online demo is insufficient.

1
  • 1
    I ended up building a Python program to do this. – Hobbes Nov 26 '20 at 12:17
1

Using the LEADTOOLS SDK you could complete the task of embedding the images into the SVG file. I would recommend checking out the LEADTOOLS SDK below is a complete code snippet for generating an SVG document by embedding a RasterImage into a basic SVG document using a data URI in a .NET C# implementation. As a disclaimer I do work for this vender which is a paid for library. https://www.leadtools.com/support/forum/posts/t12616-

public static void EmbedInSvg(RasterImage image, string outputFile)
{
    // Generate the header information
    var sb = new StringBuilder();
    sb.AppendLine("<?xml version=\"1.0\" standalone=\"no\"?>");
    sb.AppendLine(
        "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" " +
        "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
    );
    sb.AppendLine(string.Format(
        "<svg width=\"{0}px\" height=\"{1}px\" version=\"1.1\" " +
        "xmlns=\"http://www.w3.org/2000/svg\" " +
        "xmlns:xlink=\"http://www.w3.org/1999/xlink\">",
        image.Width, image.Height
    ));

    // Convert to a base64 data URI
    string dataURI;
    using (var codecs = new RasterCodecs())
    using (var ms = new MemoryStream())
    {
        // SVG only required to support JPEG and PNG (or other SVG)
        RasterImageFormat format;
        switch (image.OriginalFormat)
        {
            case RasterImageFormat.Jpeg:
            case RasterImageFormat.Jpeg411:
            case RasterImageFormat.Jpeg422:
            case RasterImageFormat.JpegCmyk:
            case RasterImageFormat.JpegCmyk411:
            case RasterImageFormat.JpegCmyk422:
            case RasterImageFormat.JpegLab:
            case RasterImageFormat.JpegLab411:
            case RasterImageFormat.JpegLab422:
            case RasterImageFormat.JpegRgb:
                format = image.OriginalFormat;
                break;
            case RasterImageFormat.Png:
            default:
                format = RasterImageFormat.Png;
                break;
        }
        codecs.Save(image, ms, format, 0);
        dataURI = string.Format(
            "data:{0};base64,{1}",
            RasterCodecs.GetMimeType(format),
            Convert.ToBase64String(ms.ToArray())
        );
    }

    // Insert the image tag
    sb.AppendLine(string.Format(
        "\t<image x=\"0\" y=\"0\" width=\"{0}px\" " +
        "height=\"{1}px\" xlink:href=\"{2}\" />",
        image.Width, image.Height, dataURI
    ));

    // Finish the file
    sb.AppendLine("</svg>");

    // Save
    File.WriteAllText(outputFile, sb.ToString());
}
0

This is what I ended up doing: I made a batch file that calls Python. Drop a series of .svg files onto this batch file and the script is called for each of them.

python "%~dp0\Convert linked SVG to embedded.py" %* 

Python script:

from lxml import etree
import os
import base64
from io import BytesIO
import sys

dirname = os.path.dirname(sys.argv[1])
logname = dirname + "\ConvertSVG.log"
logfile = open(logname, "w")
print(logname)

for arg in sys.argv[1:]:
    fileOpen = open(arg, "r+b")
    tree1 = etree.parse(fileOpen)
    root = tree1.getroot()
    logfile.write("Convert file: ")
    logfile.write(arg)
    for image in root.xpath('//a:image', namespaces={'a': 'http://www.w3.org/2000/svg', 'x': 'http://www.w3.org/1999/xlink'}):
        att1=image.attrib
        extfile = att1["{http://www.w3.org/1999/xlink}href"]
        with open(extfile, "rb") as image_file:
            encoded_string = "data:image/jpg;base64," + str(base64.b64encode(image_file.read()), 'utf-8')
            image.set("{http://www.w3.org/1999/xlink}href", encoded_string)
    fileOpen.seek(0)
    fileOpen.write(etree.tostring(tree1, pretty_print=True, xml_declaration=True))
    fileOpen.truncate()
    fileOpen.close()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.