A few resources discuss using variables inside SVG documents, including:
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.
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.
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?
One possible solution uses the following:
The following script:
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.