替换SVG文档中的变量(在YAML中外部定义)

时间:2017-08-31 02:23:06

标签: linux bash svg yaml string-interpolation

背景

一些资源讨论在SVG文档中使用变量,包括:

虽然基于CSS,JavaScript和HTML的解决方案非常适合Web,但在其他情况下,SVG很有用,并且能够为变量定义外部源也同样方便。

问题

SVG没有提供定义SVG相关软件包(例如Inkscapersvg-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文件中的这些变量?

3 个答案:

答案 0 :(得分:2)

另一种可能的解决方案:

在Inkscape中设置对象属性

enter image description here

保存它,我们会有类似的东西

...
<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>

答案 1 :(得分:1)

一种可能的解决方案使用以下方法:

以下脚本:

  1. 以YAML格式读取变量定义。
  2. 循环遍历当前目录中的所有文件。
  3. 检测文件是否定义了任何变量。
  4. 替换所有变量定义的值。
  5. 运行Inkscape将SVG文件转换为PDF。
  6. 可以进行许多改进,但是对于希望使用具有最小依赖性的YAML在SVG文档中执行基本变量替换的任何人来说,这应该是一个良好的开端。

    不执行任何卫生设施,因此在运行此脚本之前请确保输入是干净的。

    #!/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
    

    通过在SVG文档上执行常规搜索和替换,脚本无需维护。此外,变量可以在文件中的任何位置定义,而不仅仅在text块内。

答案 2 :(得分:1)

我见过的一种方法是使用Jinja模板自定义Postscript文件,然后再将其转换为PDF格式。

您可以使用相同的方法。

将您的SVG文本文件作为Jinja模板,将您的变量放在YAML中。

使用Python加载Jinja模板,然后应用YAML文件中找到的变量