Automatizing 'simplify path' for a svg-file (using inkscape) - svg

I would like to automatize the inkscape command "simplify path". Concretely, I would like a command line tool which takes a svg-file as input, applies "simplify path" to all paths in the figure and saves a new (smaller) svg-file. Is this possible using inkscape? Is there a free command line tool (I'm using linux) which does the job?

UPDATE:
Since the question/answer is quite old the inkscape command line changed.
inkscape file.svg --batch-process --actions='EditSelectAll;SelectionSimplify;FileSave;FileClose'
Also see comment of Oren Ben-Kiki or Pix answer.
ORIG:
Should be possible:
http://tavmjong.free.fr/INKSCAPE/MANUAL/html/CommandLine.html
shows how to call functions of inkscape (called "verbs") from the command line. To get a list of all verbs call inkscape --verb-list on commandline. What you are looking for is SelectionSimplify.
Therefore you have to write a small script that is filtering every id out of the svg and is calling inkscape with the ids. Something like this (optimize all pathes and quit from inkscape at the end)
inkscape filename.svg --verb=EditSelectAll --verb=SelectionSimplify --verb=FileSave --verb=FileClose --verb=FileQuit

Extending from Fabian's answer, to control the threshold of the simplification function, I found I needed to make a fake home directory with a minimal preferences file containing my desired threshold. Here is a simple script I just put together.
simplify.sh:
#!/bin/bash
FILENAME=$1
THRESHOLD=$2
FAKEHOME=$(mktemp -d)
mkdir -p $FAKEHOME/.config/inkscape
cat > $FAKEHOME/.config/inkscape/preferences.xml <<EOF
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<inkscape
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1">
<group
id="options">
<group
id="simplifythreshold"
value="${THRESHOLD}" />
</group>
</inkscape>
EOF
# for Inkscape < 1.0
#HOME=$FAKEHOME inkscape $FILENAME --verb=EditSelectAll --verb=SelectionSimplify --verb=FileSave --verb=FileClose
# for Inkscape > 1.0
HOME=$FAKEHOME inkscape --with-gui --batch-process $FILENAME --verb='EditSelectAll;SelectionSimplify;FileSave'
#rm -rf $FAKEHOME

Alternative to Inkscape
I've got much better results using SVGO (reduced a file from 2.7 MB to 350 KB).
You may use this online service for individual files: https://jakearchibald.github.io/svgomg/

Related

imagemagick RAW file identify/convert - no such file or directory (tmp file)

I'm trying to use imagemagick-7 (CLI) on ubuntu to identify and convert RAW images to PNG format. I am using the dcraw binary as the delegate for identifying and converting raw images.
I updated the dng:decode delegate in delegates.xml as follows:
<delegate decode="dng:decode" command=""dcraw" "%i"" />
When I run this command: magick identify test.dng, I get the following error:
identify: unable to open image '/tmp/magick-24332a6nW8lcwejNJ.ppm': No such file or directory # error/blob.c/OpenBlob/3489.
The same error is given for magick convert. I noticed that imagemagick is generating a temporary intermediate file in my /tmp directory, which has a different name than the name it's expecting to find there. For example, it generates this file - magick-24332P6aVDePOFeCn.ppm - but is expecting the file it generated to have this name - magick-24332a6nW8lcwejNJ.ppm - which is why it's not finding it.
Note: I tested the same thing on OS X and it works perfectly fine (with the same delegates.xml configuration).
Is this a bug in imagemagick's implementation for unix systems or am I doing something wrong? Any thoughts would be greatly appreciated!
You were close the right command to use in delegate is
<delegate decode="dng:decode" command=""dcraw" -c "%i" > "%u.ppm"" />
Almost! You need to use the %o placeholder to tell the delegate manager were the output file will be written to. However the dcraw utility doesn't have in output destination options -- at least from what I can tell form the man-page. It does have a stdout option (-c), so you should be able to pipe/redirect the stream to an output location.
dcraw -c %i > %o
or in delegate XML.
<delegate decode="dng:decode" command=""dcraw" -c "%i" > "%o"" />
Update
Some tricks I've observed with custom delegations.
Use full path to binaries. Usually because I installed something outside of the systems PATH. Usually /usr/local/bin or /opt directories.
command="/usr/local/bin/dcraw ...
Use mv over pipes. If your not comfortable with debugging unix streams & such. Just do some basic copy/move command. We can rewrite the above command with something like...
dcraw %i; mv %i.ppm %o
Ignore escaping quotes until you know its working. Encapsulating arguments are important, and keep everything safe, but \"%i & "%i are hard to read.
<delegate decode="dng:decode" command="dcraw %i; mv %i.ppm %o" />
... verify things are called correctly ... then probably escape paths.
<delegate decode="dng:decode" command="dcraw "%i"; mv "%i".ppm "%o"" />
As pointed out previously, use identify -list Delegate to verify that the command is loaded correctly, and -verbose to verify that it was called correctly during runtime.
For anyone else who experiences this problem, my solution ended up being to switch from imagemagick to graphicsmagick, which is by default configured to use dcraw (of course, you need to have dcraw installed and on your PATH).
http://www.graphicsmagick.org/

Fontforge: Export a glyph to SVG with fontforge command line

How to export a glyph (from its unicode) to SVG with Fontforge command line ?
I also need to specify the font size and keep the original margins it has in the font.
You may have found your answer already, but I just had to do this with the latest build of FontForge. The old answer of this command:
fontforge -lang=ff -c 'Open($1); SelectWorthOutputting(); foreach Export("svg"); endloop;' font.ttf
From a command prompt didn't want to work on Windows10 (I assume a permission issue), but you could give it a try. A quick work-around is to do it via the GUI Execute Script.
Run FontForge (For Windows10 installed in the Program Files (x86) directory, you may need to right-click "run_fontforge.exe" --> Run As Administrator).
Open the font you want to export.
Go to File > Execute Script
Paste: SelectWorthOutputting(); foreach Export("svg"); endloop;
Select "FF" radial button.
Hit OK
It'll save to the FontForge folder (where run_fontforge.exe is located).
On Windows os (Tested on win10)
this is from inside a BATCH file:
c:\Programs\FontForge\bin\fontforge.exe -lang=ff -c "Open($1); SelectWorthOutputting(); foreach Export('%%e_%%f_%%n_%%u.eps'); endloop;" %1
this is directly on the command line:
c:\Programs\FontForge\bin\fontforge.exe -lang=ff -c "Open($1); SelectWorthOutputting(); foreach Export('%e_%f_%n_%u.eps'); endloop;" font-file.ttf
note - the color is not exported. And I don't know if it's unimplemented, or a bug.

Replace variables in an SVG document (externally defined in YAML)

Background
A few resources discuss using variables inside SVG documents, including:
Variables in SVG: Is it possible?
SVG variable text
How do I define or reference a variable in SVG?
SVG: About using <defs> and <use> with variable text values
http://www.schepers.cc/w3c/svg/params/ref.html
https://www.w3.org/TR/2009/WD-SVGParamPrimer-20090430/#Introduction
While CSS-, JavaScript-, and HTML-based solutions are great for the Web, there are other occasions where SVG is useful and it would be equally handy to have the ability to define external sources for variables.
Problem
SVG does not provide a mechanism to define reusable text that SVG-related software packages (such as Inkscape and rsvg-convert) can reuse. For example, the following would be superb:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg ...>
<input href="definitions.svg" />
...
<text ...>${variableName}</text>
</svg>
The image element can be overloaded to import an external file, but it is hackish and doesn't allow assigning text values to variable names for reuse.
Question
How would you read variable names and values from an external file on the server (e.g., a YAML file, but could be a database) and replace those variables in an SVG file prior to rendering?
Another possible solution:
Set Object Properties in Inkscape
save it, we will have something like
...
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:60px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;image-rendering:auto"
x="262.91638"
y="86.339157"
id="mytest"
sodipodi:linespacing="125%"
inkscape:label="#myvar"><desc
id="desc4150">The test object to replace with a var</desc><title
id="title4148">myobj</title><tspan
sodipodi:role="line"
id="tspan4804"
x="262.91638"
y="86.339157"
style="fill:#ffffff">sample</tspan></text>
...
then create the yaml file with the key value pairs
myvar: hello world
and parse the SVG and replace the values
#! /usr/bin/env python
import sys
from xml.dom import minidom
import yaml
yvars = yaml.load(file('drawing.yaml', 'r'))
xmldoc = minidom.parse('drawing.svg')
for s in xmldoc.getElementsByTagName('text'):
for c in s.getElementsByTagName('tspan'):
c.firstChild.replaceWholeText(yvars[s.attributes['inkscape:label'].value[1:]])
print xmldoc.toxml()
and the values will be replaced
<text id="mytest" inkscape:label="#myvar" sodipodi:linespacing="125%" style="font-style:normal;font-weight:normal;font-size:60px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;image-rendering:auto" x="262.91638" xml:space="preserve" y="86.339157"><desc id="desc4150">The test object to replace with a var</desc><title id="title4148">myobj</title>
<tspan id="tspan4804" sodipodi:role="line" style="fill:#ffffff" x="262.91638" y="86.339157">hello world</tspan></text>
One approach I have seen is using Jinja template to customize a Postscript file before converting it to PDF.
You can use the same method.
Put your SVG text file as Jinja template, and your variable in YAML.
Use Python to load the Jinja template, then applying the variable found in the YAML file
One possible solution uses the following:
How can I parse a YAML file from a Linux shell script? (see the bash script)
rpl package (available on most distros for string replacement without regex)
bash
grep, sed, and awk
The following script:
Reads variable definitions in YAML format.
Loops over all files in the current directory.
Detects whether a file has any variables defined.
Substitutes values for all variable definitions.
Runs Inkscape to convert the SVG file to a PDF.
There are a number of improvements that can be made, but for anyone looking to perform basic variable substitution within SVG documents using YAML with minimal dependencies, this ought to be a good start.
No sanitation is performed, so ensure inputs are clean prior to running this script.
#!/bin/bash
COMMAND="inkscape -z"
DEFINITIONS=../variables.yaml
# Parses YAML files.
#
# Courtesy of https://stackoverflow.com/a/21189044/59087
function parse_yaml {
local prefix=$2
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo #|tr # '\034')
sed -ne "s|^\($s\):|\1|" \
-e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
awk -F$fs '{
indent = length($1)/2;
vname[indent] = $2;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length($3) > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
}
}'
}
# Load variable definitions into this environment.
eval $(parse_yaml $DEFINITIONS )
for i in *.svg; do
INPUT=$i
OUTPUT=$i
# Replace strings in the file with values from the variable definitions.
REPLACE_INPUT=tmp-$INPUT
echo "Converting $INPUT..."
# Subsitute if there's at least one match.
if grep -q -o -m 1 -h \${.*} $INPUT; then
cp $INPUT $REPLACE_INPUT
# Loop over all the definitions in the file.
for svgVar in $(grep -oh \${.*} $INPUT); do
# Strip off ${} to get the variable name and then the value.
varName=${svgVar:2:-1}
varValue=${!varName}
# Substitute the variable name for its value.
rpl -fi "$svgVar" "$varValue" $REPLACE_INPUT > /dev/null 2>&1
done
INPUT=$REPLACE_INPUT
fi
$COMMAND $INPUT -A m_k_i_v_$OUTPUT.pdf
rm -f $REPLACE_INPUT
done
By performing a general search and replace on the SVG document, no maintenance is required on the script. Additionally, the variables can be defined anywhere in the file, not only within text blocks.

Converting svg to png with inkscape command line failing

I feel like I must be doing something silly wrong, but I just can't get this to work. This is the command I am running from cmd:
inkscape.com "C:\path\ship.svg" -e --export-png="C:\Path\ship.png" --without-gui
In return, I get:
WARNING: File path "--export-png=C:\path\ship.png" includes directory that doesn't exist.
It does exist. What am I missing?
You should have used either -e or --export-png, not both, since they mean the same thing.
But According to the docs, -e and --export-png are no longer available. You should use -o or --export-filename=FILENAME instead. And still, you can use only one of them since -o is just the shortcut for --export-filename.
inkscape "C:\path\ship.svg" -o "C:\path\ship.png"
or
inkscape "C:\path\ship.svg" --export-filename="C:\path\ship.png"
Just an update for 2021 (it should be typed in oneline - broken down for readability only)
inkscape
--export-width=128
--export-type=png
--export-filename="C:\path\ship.png" "C:\path\build.svg"
or if you want transparent PNGs, add --export-background-opacity=0 to invocation arguments:
inkscape
--export-background-opacity=0
--export-width=128
--export-type=png
--export-filename="C:\path\ship.png" "C:\path\build.svg"
In inkscape version 1.0.2 following command will work:
inkscape --export-type="png" myfile.svg --export-filename=myfile.png

Programmatically convert SVG shapes to paths (lineto, moveto)

I have an SVG file coming from Inkscape, Illustrator, or any other application. I want to convert the shapes to lineto, moveto, curveto format.
What I want is something like:
./Appname svgfile outfilewithpath
I will give the SVG file as an argument, then my application will convert the object into the respective paths.
Inkscape has a command-line interface. Use the Inkscape man page along with the verb source for reference:
The ObjectToPath verb converts an object to a path:
inkscape filename.svg --select=myobject --verb=ObjectToPath --export-plain-svg=filename_to_path.svg
The export-text-to-path argument converts text nodes to paths:
inkscape filename.svg --export-eps=filename.eps --export-text-to-path
These are related questions on how to run InkScape from Perl, Ruby, PHP or Python:
Using the Inkscape shell from perl
Run inkscape in PHP
Calling Inkscape in Python
Inkscape Merge Ruby Gem
This is what finally worked for me:
inkscape -f filename.svg --verb EditSelectAll --verb SelectionUnGroup --verb EditSelectAll --verb ObjectToPath --verb FileSave --verb FileQuit
It takes about 9 seconds to run and briefly opens the inkscape gui, which becomes the active application, so it's not ideal, but it's the only answer I could find that actually works.

Resources