Version 0.10 not complete! Remove empty path segments. Convert lines to H/V where possible. Remove some default styles.

This commit is contained in:
JSCHILL1 2009-04-27 18:20:16 -05:00
parent 19ed9e57a4
commit 3dfb1f8068
7 changed files with 183 additions and 14 deletions

7
crunch.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
mkdir $1
for FILE in `ls fulltests`
do
./scour.py -i fulltests/$FILE -o $1/$FILE >> $1/report.txt
done

58
fulltests/acid.svg Normal file
View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 128 102" id="svg2" sodipodi:version="0.32" inkscape:version="0.44" sodipodi:docbase="C:\CARLOS\Iconos_danger" sodipodi:docname="Danger corrosive.svg" version="1.0">
<defs id="defs4"/>
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="3.541725" inkscape:cx="57.766086" inkscape:cy="64.728905" inkscape:document-units="px" inkscape:current-layer="layer1" inkscape:window-width="1005" inkscape:window-height="960" inkscape:window-x="65" inkscape:window-y="103" objecttolerance="50" inkscape:object-bbox="false" inkscape:object-points="false" inkscape:object-nodes="false" inkscape:object-paths="false" inkscape:grid-bbox="false" inkscape:guide-bbox="false" height="128px" width="128px"/>
<metadata id="metadata7">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title>Sign corrosive</dc:title>
<dc:date>11/09/2006</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>h0us3s</dc:title>
</cc:Agent>
</dc:creator>
<dc:language>es</dc:language>
<dc:subject>
<rdf:Bag>
<rdf:li>Inkscape</rdf:li>
<rdf:li>Sign Hazard</rdf:li>
<rdf:li>Warning</rdf:li>
</rdf:Bag>
</dc:subject>
<cc:license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
</cc:Work>
<cc:License rdf:about="http://web.resource.org/cc/PublicDomain">
<cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
<cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
<cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
</cc:License>
</rdf:RDF>
</metadata>
<g inkscape:groupmode="layer" id="layer2" inkscape:label="1" style="display: inline;"/>
<g inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" style="display: inline;">
<g id="g3141" transform="translate(80.7205)"/>
<g id="g2426" transform="matrix(1.8, 0, 0, 1.8, -556.831, -272.25)">
<g transform="translate(-44.2566, -9.44048)" id="g7773">
<path sodipodi:nodetypes="cccc" id="path3231" d="M 389.99972,195.83771 C 389.72419,196.39685 389.40719,196.92749 389.33619,197.62724 C 389.78242,198.21164 390.25083,197.99758 390.72357,197.62724 C 390.62417,196.95979 390.36703,196.37121 389.99972,195.83771 z " style="fill: black; fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 1px; stroke-linecap: butt; stroke-linejoin: miter; stroke-opacity: 1;"/>
<g id="use4588" transform="translate(-225.268, -211.795)">
<path transform="matrix(0.738729, 0, 0, 0.738729, 168.607, 57.13)" d="M 602.84375,430.03125 C 601.57099,430.22839 600.45214,430.98062 599.8125,432.09375 L 564.1875,493.8125 C 563.44278,495.10948 563.4307,496.72518 564.1875,498.03125 C 564.94459,499.33791 566.35217,500.15742 567.875,500.15625 L 639.125,500.15625 C 640.64787,500.15742 642.0554,499.33783 642.8125,498.03125 C 643.56932,496.72517 643.55721,495.10947 642.8125,493.8125 L 607.1875,432.09375 C 606.31182,430.56876 604.57693,429.75973 602.84375,430.03125 z " id="path2277" style="overflow: visible; marker: none; opacity: 1; color: black; fill: rgb(255, 240, 0); fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 9.147; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;" xlink:href="#path3422" inkscape:original="M 602.84375 430.03125 C 601.57099 430.22839 600.45214 430.98062 599.8125 432.09375 L 564.1875 493.8125 C 563.44278 495.10948 563.4307 496.72518 564.1875 498.03125 C 564.94459 499.33791 566.35217 500.15742 567.875 500.15625 L 639.125 500.15625 C 640.64787 500.15742 642.0554 499.33783 642.8125 498.03125 C 643.56932 496.72517 643.55721 495.10947 642.8125 493.8125 L 607.1875 432.09375 C 606.31182 430.56876 604.57693 429.75973 602.84375 430.03125 z " inkscape:radius="0" sodipodi:type="inkscape:offset" inkscape:href="#path3422"/>
<path id="path2279" d="M 614.1875,376.09375 C 613.67863,376.17256 613.24534,376.47931 613,376.90625 L 586.5,422.8125 C 586.20978,423.31797 586.2089,423.96638 586.5,424.46875 C 586.79591,424.97943 587.34227,425.31295 587.9375,425.3125 L 640.9375,425.3125 C 641.53271,425.31295 642.07908,424.97944 642.375,424.46875 C 642.66608,423.96639 642.66523,423.31797 642.375,422.8125 L 615.875,376.90625 C 615.5348,376.31381 614.87688,375.98575 614.1875,376.09375 z M 614.4375,381.375 L 636.9375,421 L 591.90625,421 L 614.4375,381.375 z " style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 9.147; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;"/>
</g>
<g id="g7757" transform="translate(6.64713, 90.1886)">
<path id="path7609" d="M 385.0625,93.40625 L 384.78125,93.5 L 384.84375,93.71875 L 377.65625,95.40625 L 377.625,95.40625 C 377.3153,95.564363 377.12047,95.799829 377.03125,96.0625 C 376.94203,96.325171 376.94168,96.620371 377.03125,96.875 C 377.21038,97.384258 377.69,97.793148 378.28125,97.75 L 378.3125,97.75 L 385.4375,96.21875 L 385.5,96.5 L 385.78125,96.4375 L 385.0625,93.40625 z M 384.90625,94.03125 L 385.375,95.9375 L 384.03125,96.21875 L 377.3125,96.21875 C 377.31558,96.208621 377.30909,96.197552 377.3125,96.1875 C 377.37699,95.997623 377.501,95.814621 377.75,95.6875 L 384.90625,94.03125 z " style="fill: black; fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 0.3; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1;"/>
<path style="fill: black; fill-opacity: 1; fill-rule: evenodd; stroke: none; stroke-width: 0.3; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1;" d="M 373.98132,99.378316 L 374.2695,99.447893 L 374.22576,99.671153 L 381.53043,100.74345 L 381.56157,100.7408 C 381.88355,100.87209 382.09764,101.0902 382.2088,101.34437 C 382.31996,101.59853 382.34533,101.89264 382.27766,102.15394 C 382.14233,102.67655 381.69909,103.12462 381.10631,103.13173 L 381.07517,103.13438 L 373.84602,102.21248 L 373.80758,102.49801 L 373.52205,102.45957 L 373.98132,99.378316 z M 374.18997,99.987826 L 373.88446,101.92694 L 375.24721,102.0933 L 381.9418,101.52389 C 381.93787,101.51406 381.9434,101.50248 381.93915,101.49275 C 381.8588,101.30902 381.71973,101.13719 381.46085,101.03163 L 374.18997,99.987826 z " id="path7717"/>
<path id="rect7719" d="M 366.5625,111.25 L 366.5625,113.625 L 378.25,113.625 L 378.25,111.25 L 374.6875,111.25 C 374.6595,111.30611 374.60821,111.34841 374.59375,111.40625 C 374.57076,111.49818 374.53412,111.59088 374.46875,111.65625 C 374.42626,111.69874 374.31785,111.73246 374.28125,111.78125 C 374.26449,111.80359 374.19121,111.90625 374.15625,111.90625 C 374.07155,111.90625 373.99094,111.90625 373.90625,111.90625 C 373.82362,111.90625 373.75209,111.9203 373.6875,111.96875 C 373.63378,112.00903 373.56081,112.04445 373.53125,112.09375 C 373.48913,112.16394 373.40735,112.23163 373.375,112.3125 C 373.3611,112.34725 373.3441,112.4375 373.28125,112.4375 C 373.20759,112.4375 373.13615,112.4375 373.0625,112.4375 C 373.01858,112.4375 372.91107,112.46455 372.875,112.4375 C 372.83084,112.40438 372.7738,112.34375 372.71875,112.34375 C 372.6473,112.34375 372.56558,112.31868 372.53125,112.25 C 372.50595,112.1994 372.52733,112.14842 372.5,112.09375 C 372.47553,112.04481 372.43411,111.99325 372.40625,111.9375 C 372.37942,111.88385 372.34622,111.84375 372.28125,111.84375 C 372.22732,111.84375 372.06251,111.87147 372.0625,111.78125 C 372.0625,111.70772 372.01677,111.66085 372,111.59375 C 371.98482,111.53305 371.96875,111.48195 371.96875,111.40625 C 371.96875,111.35306 371.96875,111.3032 371.96875,111.25 L 366.5625,111.25 z " style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.3; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;"/>
<path sodipodi:nodetypes="csssc" id="path7724" d="M 374.10185,105.38045 C 374.10185,105.84256 373.78105,106.21762 373.38576,106.21762 C 372.99048,106.21762 372.66968,105.84256 372.66968,105.38045 C 372.66968,104.91833 373.32887,103.21135 373.40594,103.21135 C 373.46508,103.21135 374.10185,104.91833 374.10185,105.38045 z " style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.3; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;"/>
<path style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.3; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;" d="M 373.91435,109.69295 C 373.91435,110.15506 373.59355,110.53012 373.19826,110.53012 C 372.80298,110.53012 372.48218,110.15506 372.48218,109.69295 C 372.48218,109.23083 373.14137,107.52385 373.21844,107.52385 C 373.27758,107.52385 373.91435,109.23083 373.91435,109.69295 z " id="path7727" sodipodi:nodetypes="csssc"/>
<path sodipodi:nodetypes="csssc" id="path7729" d="M 386.8206,99.81795 C 386.8206,100.28006 386.4998,100.65512 386.10451,100.65512 C 385.70923,100.65512 385.38843,100.28006 385.38843,99.81795 C 385.38843,99.35583 386.04762,97.64885 386.12469,97.64885 C 386.18383,97.64885 386.8206,99.35583 386.8206,99.81795 z " style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.3; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;"/>
<path style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.3; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;" d="M 386.7581,105.25545 C 386.7581,105.71756 386.4373,106.09262 386.04201,106.09262 C 385.64673,106.09262 385.32593,105.71756 385.32593,105.25545 C 385.32593,104.79333 385.98512,103.08635 386.06219,103.08635 C 386.12133,103.08635 386.7581,104.79333 386.7581,105.25545 z " id="path7731" sodipodi:nodetypes="csssc"/>
<path sodipodi:nodetypes="ccccccsccsccsccccsccccsccccscccccccc" id="rect7734" d="M 387.53125,106.22656 C 387.15997,106.25121 387.09159,106.43451 387.15625,106.6875 C 387.22436,107.00481 387.39255,107.22204 387.5625,107.4375 C 388.19142,107.98518 388.90806,108.12351 389.59375,108.40625 L 389.59375,108.5625 L 387,108.5625 C 386.87639,108.91584 386.5516,109.15625 386.15625,109.15625 C 385.7591,109.15625 385.43472,108.91834 385.3125,108.5625 L 385.09375,108.5625 C 385.07706,108.81577 384.88247,109 384.625,109 C 384.36672,109 384.1405,108.81691 384.125,108.5625 L 381.75,108.5625 C 381.32154,108.5625 380.96875,108.88404 380.96875,109.3125 C 380.96875,109.74097 381.32153,110.09375 381.75,110.09375 L 388.30871,110.09375 C 388.35784,110.19792 388.43178,110.30208 388.33996,110.40625 L 380.5625,110.40625 C 380.1524,110.40625 379.8125,110.74615 379.8125,111.15625 C 379.8125,111.56635 380.15239,111.875 380.5625,111.875 L 387.5,111.875 C 387.60244,112.03158 387.60193,112.16885 387.46875,112.28125 L 381.25,112.28125 C 380.87663,112.28125 380.5625,112.59538 380.5625,112.96875 C 380.5625,113.34212 380.87664,113.625 381.25,113.625 L 388.8125,113.625 C 388.8593,113.71133 388.90747,113.875 388.8125,114 L 383.625,114 C 383.26387,114 383,114.26387 383,114.625 C 383,114.98613 383.26388,115.28125 383.625,115.28125 L 388.875,115.28125 L 388.875,115.34375 C 392.14561,115.90567 396.02366,115.95169 398.46875,114.75 L 398.46875,108.90625 C 395.79096,107.68281 392.84201,107.0559 389.875,106.46875 C 389.51658,106.50361 389.10962,106.35612 388.84375,106.53906 C 388.42022,106.19747 387.97496,106.22524 387.53125,106.22656 z " style="overflow: visible; marker: none; opacity: 1; color: black; fill: black; fill-opacity: 1; fill-rule: nonzero; stroke: none; stroke-width: 0.3; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-dasharray: none; stroke-dashoffset: 43.68; stroke-opacity: 1; visibility: visible; display: inline;"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SCOURVER="0.10" SCOURVER="0.10"
cd .. cd ..
tar cvf scour/tarballs/scour-$SCOURVER.tar scour/scour.py scour/LICENSE scour/NOTICE scour/README.txt scour/release-notes.html tar cvf scour/tarballs/scour-$SCOURVER.tar scour/scour.py scour/svg_regex.py scour/LICENSE scour/NOTICE scour/README.txt scour/release-notes.html
gzip scour/tarballs/scour-$SCOURVER.tar gzip scour/tarballs/scour-$SCOURVER.tar
cd scour cd scour

View file

@ -20,6 +20,10 @@
<li>Convert from absolute to relative path data</li> <li>Convert from absolute to relative path data</li>
<li>Remove trailing zeroes from path data</li> <li>Remove trailing zeroes from path data</li>
<li>Limit to no more than 6 digits of precision</li> <li>Limit to no more than 6 digits of precision</li>
<li>Remove empty line segments</li>
<li>Convert lines to horiz/vertical line segments where possible</li>
<li>Remove some more default styles (display:none, visibility:visible, overflow:visible,
marker:none)</li>
</ul> </ul>
</section> </section>

View file

@ -45,8 +45,8 @@
# * Put id attributes first in the serialization (or make the d attribute last) # * Put id attributes first in the serialization (or make the d attribute last)
# Next Up: # Next Up:
# - implement command-line option to output svgz
# - Remove unnecessary units of precision on attributes (use decimal: http://docs.python.org/library/decimal.html) # - Remove unnecessary units of precision on attributes (use decimal: http://docs.python.org/library/decimal.html)
# - Remove unnecessary units of precision on path coordinates
# - Convert all colors to #RRGGBB format # - Convert all colors to #RRGGBB format
# - Reduce #RRGGBB format to #RGB format when possible # - Reduce #RRGGBB format to #RGB format when possible
# https://bugs.edge.launchpad.net/ubuntu/+source/human-icon-theme/+bug/361667/ # https://bugs.edge.launchpad.net/ubuntu/+source/human-icon-theme/+bug/361667/
@ -96,6 +96,7 @@ unwanted_ns = [ NS['SODIPODI'], NS['INKSCAPE'], NS['ADOBE_ILLUSTRATOR'],
svgAttributes = [ svgAttributes = [
'clip-rule', 'clip-rule',
'display',
'fill', 'fill',
'fill-opacity', 'fill-opacity',
'fill-rule', 'fill-rule',
@ -107,7 +108,9 @@ svgAttributes = [
'font-variant', 'font-variant',
'font-weight', 'font-weight',
'line-height', 'line-height',
'marker',
'opacity', 'opacity',
'overflow',
'stop-color', 'stop-color',
'stop-opacity', 'stop-opacity',
'stroke', 'stroke',
@ -117,6 +120,7 @@ svgAttributes = [
'stroke-miterlimit', 'stroke-miterlimit',
'stroke-opacity', 'stroke-opacity',
'stroke-width', 'stroke-width',
'visibility'
] ]
def findElementById(node, id): def findElementById(node, id):
@ -187,6 +191,7 @@ numIDsRemoved = 0
numElemsRemoved = 0 numElemsRemoved = 0
numAttrsRemoved = 0 numAttrsRemoved = 0
numRastersEmbedded = 0 numRastersEmbedded = 0
numPathSegmentsReduced = 0
numBytesSavedInPathData = 0 numBytesSavedInPathData = 0
# removes all unreferenced elements except for <svg>, <font>, <metadata>, <title>, and <desc> # removes all unreferenced elements except for <svg>, <font>, <metadata>, <title>, and <desc>
@ -583,6 +588,30 @@ def repairStyle(node):
# TODO: what else? # TODO: what else?
# visibility: visible
if styleMap.has_key('visibility') :
if styleMap['visibility'] == 'visible':
del styleMap['visibility']
num += 1
# display: inline
if styleMap.has_key('display') :
if styleMap['display'] == 'inline':
del styleMap['display']
num += 1
# overflow: visible or overflow specified on element other than svg, marker, pattern
if styleMap.has_key('overflow') :
if styleMap['overflow'] == 'visible' or node.nodeName in ['svg','marker','pattern']:
del styleMap['overflow']
num += 1
# marker: none
if styleMap.has_key('marker') :
if styleMap['marker'] == 'none':
del styleMap['marker']
num += 1
# now if any of the properties match known SVG attributes we prefer attributes # now if any of the properties match known SVG attributes we prefer attributes
# over style so emit them and remove them from the style map # over style so emit them and remove them from the style map
for propName in styleMap.keys() : for propName in styleMap.keys() :
@ -609,6 +638,7 @@ def repairStyle(node):
# - parse the path data and reserialize # - parse the path data and reserialize
def cleanPath(element) : def cleanPath(element) :
global numBytesSavedInPathData global numBytesSavedInPathData
global numPathSegmentsReduced
# this gets the parser object from svg_regex.py # this gets the parser object from svg_regex.py
oldPathStr = element.getAttribute('d') oldPathStr = element.getAttribute('d')
@ -616,24 +646,37 @@ def cleanPath(element) :
# however, this parser object has some ugliness in it (lists of tuples of tuples of # however, this parser object has some ugliness in it (lists of tuples of tuples of
# numbers and booleans). we just need a list of (cmd,[numbers]): # numbers and booleans). we just need a list of (cmd,[numbers]):
# TODO: remove empty path segments
path = [] path = []
for (cmd,dataset) in pathObj: for (cmd,dataset) in pathObj:
if cmd in ['M','m','L','l','T','t']: if cmd in ['M','m','L','l','T','t']:
# one or more tuples, each containing two numbers # one or more tuples, each containing two numbers
nums = [] nums = []
for t in dataset: for t in dataset:
# only create this coord pair if it is non-zero or is an absolute Move (first cmd)
if cmd == 'M' or (t[0] != 0 or t[1] != 0):
# convert to a Decimal and ensure precision # convert to a Decimal and ensure precision
nums.append(Decimal(str(t[0])) * Decimal(1)) nums.append(Decimal(str(t[0])) * Decimal(1))
nums.append(Decimal(str(t[1])) * Decimal(1)) nums.append(Decimal(str(t[1])) * Decimal(1))
else:
numPathSegmentsReduced += 1
# only create this segment if it is not empty
if nums:
path.append( (cmd, nums) ) path.append( (cmd, nums) )
elif cmd in ['V','v','H','h']: elif cmd in ['V','v','H','h']:
# one or more numbers # one or more numbers
nums = [] nums = []
for n in dataset: for n in dataset:
if n != 0:
nums.append(Decimal(str(n))) nums.append(Decimal(str(n)))
else:
numPathSegmentsRemoved += 1
if nums:
path.append( (cmd, nums) ) path.append( (cmd, nums) )
# TODO: remove empty curve segments
elif cmd in ['C','c']: elif cmd in ['C','c']:
# one or more tuples, each containing three tuples of two numbers each # one or more tuples, each containing three tuples of two numbers each
nums = [] nums = []
@ -643,6 +686,7 @@ def cleanPath(element) :
nums.append(Decimal(str(pair[1])) * Decimal(1)) nums.append(Decimal(str(pair[1])) * Decimal(1))
path.append( (cmd, nums) ) path.append( (cmd, nums) )
# TODO: remove empty curve segments
elif cmd in ['S','s','Q','q']: elif cmd in ['S','s','Q','q']:
# one or more tuples, each containing two tuples of two numbers each # one or more tuples, each containing two tuples of two numbers each
nums = [] nums = []
@ -652,6 +696,7 @@ def cleanPath(element) :
nums.append(Decimal(str(pair[1])) * Decimal(1)) nums.append(Decimal(str(pair[1])) * Decimal(1))
path.append( (cmd, nums) ) path.append( (cmd, nums) )
# TODO: remove empty curve segments
elif cmd in ['A','a']: elif cmd in ['A','a']:
# one or more tuples, each containing a tuple of two numbers, a number, a boolean, # one or more tuples, each containing a tuple of two numbers, a number, a boolean,
# another boolean, and a tuple of two numbers # another boolean, and a tuple of two numbers
@ -674,12 +719,9 @@ def cleanPath(element) :
elif cmd in ['Z','z']: elif cmd in ['Z','z']:
path.append( (cmd, []) ) path.append( (cmd, []) )
# TODO: convert to fixed point values
# convert absolute coordinates into relative ones (start with the second subcommand # convert absolute coordinates into relative ones (start with the second subcommand
# and leave the first M as absolute) # and leave the first M as absolute)
(x,y) = path[0][1] (x,y) = path[0][1]
i = 1 i = 1
for (cmd,data) in path[1:]: for (cmd,data) in path[1:]:
# adjust abs to rel # adjust abs to rel
@ -739,7 +781,35 @@ def cleanPath(element) :
y += data[k+3] y += data[k+3]
k += 4 k += 4
# TODO: collapse adjacent H or V segments that have coords in the same direction # collapse adjacent H or V segments that have coords in the same direction
newPath = [path[0]]
for (cmd,data) in path[1:]:
if cmd == 'l':
i = 0
lineTuples = []
while i < len(data):
if data[i] == 0:
# vertical
if lineTuples:
# change the line command, then append the v and then the remaining line coords
if lineTuples: newPath.append( ('l', lineTuples) )
lineTuples = []
newPath.append( ('v', [data[i+1]]) )
numPathSegmentsReduced += 1
elif data[i+1] == 0:
if lineTuples:
# change the line command, then append the h and then the remaining line coords
if lineTuples: newPath.append( ('l', lineTuples) )
lineTuples = []
newPath.append( ('h', [data[i]]) )
numPathSegmentsReduced += 1
else:
lineTuples.append(data[i])
lineTuples.append(data[i+1])
i += 2
else:
newPath.append( (cmd, data) )
path = newPath
newPathStr = serializePath(path) newPathStr = serializePath(path)
numBytesSavedInPathData += ( len(oldPathStr) - len(newPathStr) ) numBytesSavedInPathData += ( len(oldPathStr) - len(newPathStr) )
@ -1051,6 +1121,7 @@ if __name__ == '__main__':
print ' Number of attributes removed:', numAttrsRemoved print ' Number of attributes removed:', numAttrsRemoved
print ' Number of style properties fixed:', numStylePropsFixed print ' Number of style properties fixed:', numStylePropsFixed
print ' Number of raster images embedded inline:', numRastersEmbedded print ' Number of raster images embedded inline:', numRastersEmbedded
print ' Number of path segments reduced/removed:', numPathSegmentsReduced
print ' Number of bytes saved in path data:', numBytesSavedInPathData print ' Number of bytes saved in path data:', numBytesSavedInPathData
oldsize = os.path.getsize(input.name) oldsize = os.path.getsize(input.name)
newsize = os.path.getsize(output.name) newsize = os.path.getsize(output.name)

View file

@ -460,5 +460,31 @@ class LimitPrecisionInPathData(unittest.TestCase):
self.assertEquals(path[1][1][0], 100.001, self.assertEquals(path[1][1][0], 100.001,
'Not correctly limiting precision on path data' ) 'Not correctly limiting precision on path data' )
class RemoveEmptyLineSegmentsFromPath(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/path-line-optimize.svg')
path = svg_parser.parse(doc.getElementsByTagNameNS(SVGNS, 'path')[0].getAttribute('d'))
self.assertEquals(path[4][0], 'z',
'Did not remove an empty line segment from path' )
class ChangeLineToHorizontalLineSegmentInPath(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/path-line-optimize.svg')
path = svg_parser.parse(doc.getElementsByTagNameNS(SVGNS, 'path')[0].getAttribute('d'))
self.assertEquals(path[1][0], 'h',
'Did not change line to horizontal line segment in path' )
self.assertEquals(path[1][1][0], 200.0,
'Did not calculate horizontal line segment in path correctly' )
class ChangeLineToVerticalLineSegmentInPath(unittest.TestCase):
def runTest(self):
doc = scour.scourXmlFile('unittests/path-line-optimize.svg')
path = svg_parser.parse(doc.getElementsByTagNameNS(SVGNS, 'path')[0].getAttribute('d'))
self.assertEquals(path[2][0], 'v',
'Did not change line to vertical line segment in path' )
self.assertEquals(path[2][1][0], 100.0,
'Did not calculate vertical line segment in path correctly' )
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -0,0 +1,3 @@
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
<path d="M 100,100 l200,0 l0,100 h-200 l0,0 z" fill="blue" />
</svg>

After

Width:  |  Height:  |  Size: 148 B