替换 SVG 文档中的变量(在 YAML 中外部定义)
Replace variables in an SVG document (externally defined in YAML)
背景
一些资源讨论了在 SVG 文档中使用变量,包括:
- Variables in SVG: Is it possible?
- 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
虽然基于 CSS-、JavaScript- 和 HTML 的解决方案非常适合 Web,但在其他情况下 SVG 也很有用,并且同样方便能够为变量定义外部源。
问题
SVG 不提供一种机制来定义 SVG 相关软件包(例如 Inkscape and rsvg-convert)可以重用的可重用文本。例如,以下将是极好的:
<?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>
image element 可以重载以导入外部文件,但它是 hackish,并且不允许将文本值分配给变量名以供重用。
问题
您将如何从服务器上的外部文件(例如,YAML 文件,但也可以是数据库)中读取变量名称和值,并在渲染之前替换 SVG 文件中的这些变量?
一种可能的解决方案如下:
- How can I parse a YAML file from a Linux shell script? (see the bash script)
- rpl package(在大多数发行版中都可用,无需正则表达式即可替换字符串)
- bash
- grep、sed 和 awk
以下脚本:
- 读取 YAML 格式的变量定义。
- 循环遍历当前目录中的所有文件。
- 检测文件是否定义了任何变量。
- 替换所有变量定义的值。
- 运行 Inkscape 将 SVG 文件转换为 PDF。
可以进行许多改进,但对于希望使用 YAML 以最小依赖性在 SVG 文档中执行基本变量替换的任何人来说,这应该是一个好的开始。
不执行清理,因此确保输入在 运行 此脚本之前是干净的。
#!/bin/bash
COMMAND="inkscape -z"
DEFINITIONS=../variables.yaml
# Parses YAML files.
#
# Courtesy of
function parse_yaml {
local prefix=
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '4')
sed -ne "s|^\($s\):||" \
-e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s$|$fs$fs|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s$|$fs$fs|p" |
awk -F$fs '{
indent = length()/2;
vname[indent] = ;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length() > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, , );
}
}'
}
# 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
通过对 SVG 文档执行一般搜索和替换,无需对脚本进行维护。此外,变量可以在文件中的任何位置定义,而不仅仅是在 text
块内。
另一种可能的解决方案:
在 Inkscape
中设置 对象属性
保存它,我们会有类似的东西
...
<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>
...
然后使用键值对创建 yaml 文件
myvar: hello world
并解析 SVG 并替换值
#! /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()
并且值将被替换
<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>
我见过的一种方法是在将 Postscript 文件转换为 PDF 之前使用 Jinja 模板自定义它。
你可以用同样的方法。
将您的 SVG 文本文件作为 Jinja 模板,并将您的变量放入 YAML。
使用Python加载Jinja模板,然后应用在YAML文件中找到的变量
背景
一些资源讨论了在 SVG 文档中使用变量,包括:
- Variables in SVG: Is it possible?
- 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
虽然基于 CSS-、JavaScript- 和 HTML 的解决方案非常适合 Web,但在其他情况下 SVG 也很有用,并且同样方便能够为变量定义外部源。
问题
SVG 不提供一种机制来定义 SVG 相关软件包(例如 Inkscape and rsvg-convert)可以重用的可重用文本。例如,以下将是极好的:
<?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>
image element 可以重载以导入外部文件,但它是 hackish,并且不允许将文本值分配给变量名以供重用。
问题
您将如何从服务器上的外部文件(例如,YAML 文件,但也可以是数据库)中读取变量名称和值,并在渲染之前替换 SVG 文件中的这些变量?
一种可能的解决方案如下:
- How can I parse a YAML file from a Linux shell script? (see the bash script)
- rpl package(在大多数发行版中都可用,无需正则表达式即可替换字符串)
- bash
- grep、sed 和 awk
以下脚本:
- 读取 YAML 格式的变量定义。
- 循环遍历当前目录中的所有文件。
- 检测文件是否定义了任何变量。
- 替换所有变量定义的值。
- 运行 Inkscape 将 SVG 文件转换为 PDF。
可以进行许多改进,但对于希望使用 YAML 以最小依赖性在 SVG 文档中执行基本变量替换的任何人来说,这应该是一个好的开始。
不执行清理,因此确保输入在 运行 此脚本之前是干净的。
#!/bin/bash
COMMAND="inkscape -z"
DEFINITIONS=../variables.yaml
# Parses YAML files.
#
# Courtesy of
function parse_yaml {
local prefix=
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '4')
sed -ne "s|^\($s\):||" \
-e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s$|$fs$fs|p" \
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s$|$fs$fs|p" |
awk -F$fs '{
indent = length()/2;
vname[indent] = ;
for (i in vname) {if (i > indent) {delete vname[i]}}
if (length() > 0) {
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, , );
}
}'
}
# 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
通过对 SVG 文档执行一般搜索和替换,无需对脚本进行维护。此外,变量可以在文件中的任何位置定义,而不仅仅是在 text
块内。
另一种可能的解决方案:
在 Inkscape
中设置 对象属性保存它,我们会有类似的东西
...
<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>
...
然后使用键值对创建 yaml 文件
myvar: hello world
并解析 SVG 并替换值
#! /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()
并且值将被替换
<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>
我见过的一种方法是在将 Postscript 文件转换为 PDF 之前使用 Jinja 模板自定义它。
你可以用同样的方法。
将您的 SVG 文本文件作为 Jinja 模板,并将您的变量放入 YAML。
使用Python加载Jinja模板,然后应用在YAML文件中找到的变量