scour-project / scour

Scour - An SVG Optimizer / Cleaner

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature request: call scour from Python code

rgoubet opened this issue · comments

I'm sure it must be possible to call scour, along with the various command-line flags, within Python code, which is interesting to optimize svg files generated by a Python program. From what I can tell, the XML string is passed to the scourString function, but I can't make out how flags are passed as options.

It's probably a matter of documentation, but, IMO, it would be worth adding a note on this and not limit scour's usage to the command line.

I think I found a way to call scour from Python code:

from scour import scour
import sys

sys.argv = ["-i", "input.svg", "-o", "output.svg"]
scour.run()

The run function uses optparse in the background, which reads the command line arguments directly.

Yes, of course you can always call the script itself, but it would be much clearer if one could use the scourString method itself.

At least, this seems to work to optimize an XML string (i.e. without a need for an SVG file on disk):

from scour import scour

def minify_svg(xml_string):
    opts = scour.sanitizeOptions(
        {
            "digits": 5,
            "quiet": False,
            "verbose": False,
            "cdigits": -1,
            "simple_colors": True,
            "style_to_xml": True,
            "group_collapse": True,
            "group_create": False,
            "keep_editor_data": False,
            "keep_defs": False,
            "renderer_workaround": True,
            "strip_xml_prolog": False,
            "remove_titles": True,
            "remove_descriptions": True,
            "remove_metadata": True,
            "remove_descriptive_elements": True,
            "strip_comments": True,
            "embed_rasters": True,
            "enable_viewboxing": True,
            "indent_type": "none",
            "indent_depth": 1,
            "newlines": True,
            "strip_xml_space_attribute": False,
            "strip_ids": True,
            "shorten_ids": True,
            "shorten_ids_prefix": "",
            "protect_ids_noninkscape": False,
            "protect_ids_list": None,
            "protect_ids_prefix": None,
            "error_on_flowtext": False,
        }
    )
    return scour.scourString(xml_string, opts)

All options in the opts dictionary are probably not necessary (I understand that the sanitizeOptions function sets the default option values), but I'd need to run trial and error with each of them to understand their meaning compared to the command line options.

Scour with an "official" module API would be nice, yes. +1
Calling it as a subprocess from another Python app feels so suboptimal…

This works fine for me:

    scour_options_dict = {'strip_comments': True, 'indent_type': 'none', 'shorten_ids': True, 'enable_viewboxing': True, 'strip_ids': True, 'strip_xml_prolog': True, 'remove_metadata': True}
    scour_options = SimpleNamespace(**scour_options_dict)
    svg_compressed = scourString(svg, scour_options)

The only real problem I can see with that is that it's undocumented, and the names of the keys are not exactly the same as the names of the command line options, so in theory they could change.

I additionally imported the parse_args function.
You can give the command line arguments as list of string to that function.
The function then returns a valid options-dictionary.

from scour.scour import scourString
from scour.scour import sanitizeOptions as sanitizeScourOptions
from scour.scour import parse_args as parseScourArgs

def Optimize(sourcesvg):
    scouroptions = parseScourArgs([
        "--enable-id-stripping",
        "--enable-comment-stripping",
        "--shorten-ids",
        "--indent=none",
        "--no-line-breaks"])
    scouroptions = sanitizeScourOptions(scouroptions)
    optimizedsvg = scourString(sourcesvg, scouroptions)
    return optimizedsvg

(Source)

Of course, as long as this is not an official and documented feature to use Scour functions inside other Python tools, our code can break with any update. 😄

Another usecase is for a web server, one especially does not want the overhead/complexity writing to disk (multiple workers) and reading from disk. You generally want to keep everything in memory, in which case an API would be great.

I additionally imported the parse_args function. You can give the command line arguments as list of string to that function. The function then returns a valid options-dictionary.

from scour.scour import scourString
from scour.scour import sanitizeOptions as sanitizeScourOptions
from scour.scour import parse_args as parseScourArgs

def Optimize(sourcesvg):
    scouroptions = parseScourArgs([
        "--enable-id-stripping",
        "--enable-comment-stripping",
        "--shorten-ids",
        "--indent=none",
        "--no-line-breaks"])
    scouroptions = sanitizeScourOptions(scouroptions)
    optimizedsvg = scourString(sourcesvg, scouroptions)
    return optimizedsvg

(Source)

Of course, as long as this is not an official and documented feature to use Scour functions inside other Python tools, our code can break with any update. smile

This is propably the most resilent solution so far, nice one!