Compare commits

..

307 commits

Author SHA1 Message Date
a124f9f415 NonSci output option and InkScape extension added by Aleon 2023-01-31 11:02:40 +01:00
a1346054
0609c59676
Fix spelling (#284) 2021-08-30 19:17:00 +02:00
David H. Gutteridge
85f4b49d59 Fix a typo in README.md 2021-07-19 02:26:33 +02:00
Wolfgang Bangerth
897e3f565c Minor language edits in README.md. 2021-05-01 15:41:25 +02:00
Niels Thykier
fbf0c06e84 Avoid mutating a mutable kwarg
Signed-off-by: Niels Thykier <niels@thykier.net>
2021-02-23 20:00:20 +01:00
Niels Thykier
841ad54e7f Refactor function to avoid double negative
Signed-off-by: Niels Thykier <niels@thykier.net>
2021-02-23 20:00:20 +01:00
Niels Thykier
68c1e545da Replace global stats vars with a ScourStats object
This enables us to get rid of all the global variables.

I used the opportunity to update function names where call sites where
affected to move scour a step towards a more pythonic style in
general.

Signed-off-by: Niels Thykier <niels@thykier.net>
2021-02-23 20:00:20 +01:00
Niels Thykier
a7a16799a2 Remove some dead assignments
Signed-off-by: Niels Thykier <niels@thykier.net>
2021-02-23 20:00:20 +01:00
Niels Thykier
7b9c4ee935 Simplif loop logic
Signed-off-by: Niels Thykier <niels@thykier.net>
2021-02-23 20:00:20 +01:00
Niels Thykier
aa9796ea87 Refactor: Create a g_tag_is_unmergeable
Both `mergeSiblingGroupsWithCommonAttributes` and `removeNestedGroups`
used the same code in different forms.  Extract it into its own
function.

Signed-off-by: Niels Thykier <niels@thykier.net>
2021-02-23 20:00:20 +01:00
Patrick Storz
7a83e7148d CI: test with Python 3.9 stable 2020-11-22 15:21:13 +01:00
Patrick Storz
04bf3d79a0 Scour v0.38.2 2020-11-22 15:05:13 +01:00
Niels Thykier
b8a071f995
scour: Fix another variant of the crash from #260 (#264)
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-11-22 15:00:43 +01:00
Patrick Storz
92e64f0d7f CI: test with Python 3.9-dev and 3.10-dev 2020-09-02 19:43:58 +02:00
Patrick Storz
23835da44a Scour v0.38.1 2020-09-02 19:19:01 +02:00
Niels Thykier
f56843acc0
mergeSiblingGroupsWithCommonAttributes: Avoid creating "empty" <g>-tags (#261)
Closes: #260
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-09-02 17:03:36 +00:00
Patrick Storz
c84731e12d Scour v0.38 2020-08-06 22:35:02 +02:00
Patrick Storz
ab97a01427 anti-aliasing 2020-08-06 22:34:04 +02:00
Niels Thykier
f0788d5c0d
renameID: Fix bug when swapping two IDs
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-27 10:26:20 +00:00
Niels Thykier
9a1286132f
remapNamespacePrefix: Preserve prefix of attribute names (#255)
Preserve prefix of attribute names when copying them over to the new
node.  This fixes an unintentional rewrite of `xml:space` to `space`
that also caused scour to strip whitespace that should have been
preserved.

Closes: #239
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-10 20:18:21 +02:00
Patrick Storz
cc11ef3d5e
Optimize remove duplicate gradients (#248)
Optimize remove duplicate gradients
2020-06-09 21:32:59 +02:00
Niels Thykier
ca2b32c0b3
removeDuplicateGradients: Maintain referenced_ids
This avoids calling `findReferencedElements` more than once per
removeDuplicateGradients.  This is good for performance as
`findReferencedElements` is one of the slowest functions in scour.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:12 +00:00
Niels Thykier
3d29029c72
findReferencedElements: Use a set instead of list for tracking nodes
Except for one caller, nothing cares what kind of collection is used.
By migrating to a set, we can enable a future rewrite.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:11 +00:00
Niels Thykier
0e82b8dcad
Refactor removeDuplicateGradients to loop until it reaches a fixed point
This is commits enables a future optimization (but is not a notable
optimization in itself).

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:10 +00:00
Niels Thykier
a3f761f40c
Refactor some code out of removeDuplicateGradients
This is commits enables a future optimization (but is not a notable
optimization in itself).

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:09 +00:00
Niels Thykier
36ee0932a4
removeDuplicateGradients: Compile at most one regex per master gradient
Regex compilation is by far the most expensive part of
removeDuplicateGradients.  This commit reduces the pain a bit by
trading "many small regexes" to "few larger regexes", which avoid some
of the compilation overhead.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:08 +00:00
Niels Thykier
9e3a5f2e40
removeDuplicateGradients: Refactor how duplicates are passed around
This commit is mostly to enable the following commit to make
improvements.  It does reduce the number of duplicate getAttribute
calls by a tiny bit but it is unlikely to matter in practice.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:07 +00:00
Niels Thykier
ace24df5c3
removeDuplicateGradients: Avoid compiling regex unless we need it
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-09 16:45:05 +00:00
Patrick Storz
985cb58a26 Remove outdated comment
originally added in
  879300373f
and fixed shortly after in
  2dc788aa3f
2020-06-08 19:45:48 +02:00
Patrick Storz
1bb875192f
Optimize out some redundant code or calls to "slow" functions (#249) 2020-06-08 19:40:57 +02:00
Niels Thykier
fd2daf44b4
Avoid compiling "the same" regex multiple times
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 19:06:51 +00:00
Niels Thykier
045f1f0ad5
removeNamespacedElements: Avoid calling it twice as it is indempotent
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 19:04:43 +00:00
Niels Thykier
29a7474f74
removeNamespacedAttributes: Avoid calling it twice as it is indempotent
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 19:04:42 +00:00
Niels Thykier
528ad91418
removeUnusedDefs: Call getAttribute at most once per element
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 19:04:41 +00:00
Niels Thykier
c5362743c3
_getStyle: Avoid calling getAttribute twice for no reason
_getStyle accounted for ~8.9% (~17700) of all calls to getAttribute on
devices/hidef/secure-card.svgz file from the Oxygen icon theme.  This
commit removes this part of the dead weight.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 19:04:40 +00:00
Niels Thykier
5881890e44
removeUnreferencedElements: Remove defs before unref elements
The `removeUnusedDefs` function can take `referencedIDs` as parameter
and its work do not invalidate it.  By moving it up in
`removeUnreferencedElements` we can save a call to
`findReferencedElements` per call to `removeUnreferencedElements`.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 19:04:39 +00:00
Patrick Storz
4e489b7ea9
Perf optimized serialization (#247)
Some commits to reduce the overhead in outputting the SVG again (most of it is in serializeXML and below)
2020-06-07 20:35:33 +02:00
Niels Thykier
397ffc5529
make_well_formed: Optimize for the common case of nothing needs to be escaped
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 18:07:32 +00:00
Niels Thykier
9656569a72
serializeXML: Refactor the attribute ordering code
Rewrite the code for ordering attributes in the output and extract it
into a function.  As a side-effect, we ensure we only use the
`.item(index)` method once per attribute because it is inefficient
(see https://bugs.python.org/issue40689).

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 18:07:31 +00:00
Niels Thykier
5be6b03d7c
Serialization: Avoid creating a single-use dict in each call to make_well_formed
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 18:07:30 +00:00
Niels Thykier
21f1262bcb
Avoid creating single-use-throw-away lists for string join
There is no need to create a list of it only to discard it after a
single use with join (which gladly accepts an iterator/generator
instead).

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 18:07:29 +00:00
Niels Thykier
47e8b15315
convertColors: Fix bug in computation in how many bytes are saved (#245)
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-06-07 18:35:46 +02:00
Niels Thykier
a15acb3e4e
Rename testX.py to test_X.py to make py.test work out of the box (#181)
This rename makes py.test/py.test-3 find the test suite out of the
box.  Example command lines:

       # Running the test suite (optionally include "-v")
       $ py.test-3
       # Running the test suite with coverage enabled (and branch
       # coverage).
       $ py.test-3 --cov=scour --cov-report=html --cov-branch

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-05-17 19:55:24 +02:00
Niels Thykier
dd2155e576 Merge sibling <g> nodes with identical attributes
In some cases, gnuplot generates a very suboptimal SVG content of the
following pattern:

        <g color="black" fill="none" stroke="currentColor">
        <path d="m82.5 323.3v-4.1" stroke="#000"/>
        </g>
        <g color="black" fill="none" stroke="currentColor">
        <path d="m116.4 323.3v-4.1" stroke="#000"/>
        </g>
        ... repeated 10+ more times here ...
        <g color="black" fill="none" stroke="currentColor">
        <path d="m65.4 72.8v250.5h420v-250.5h-420z" stroke="#000"/>
        </g>

A more optimal pattern would be:

        <g color="black" fill="none" stroke="#000">
        <path d="m82.5 323.3v-4.1"/>
        <path d="m116.4 323.3v-4.1"/>
        ... 10+ more paths here ...
        <path d="m65.4 72.8v250.5h420v-250.5h-420z"/>
        </g>

This patch enables that optimization by handling the merging of two
sibling <g> entries that have identical attributes.  In the above
example that does not solve the rewrite from "currentColor" to "#000"
for the stroke attribute.  However, the existing code already handles
that automatically after the <g> elements have been merged.

This change provides comparable results to --create-groups as shown by
the following diagram while being a distinct optimization:

       +----------------------------+-------+--------+
       |           Test             | Size  |  in %  |
       +----------------------------+-------+--------+
       | baseline                   | 17961 |  100%  |
       | baseline + --create-groups | 17418 |  97.0% |
       | patched                    | 16939 |  94.3% |
       | patched + --create-groups  | 16855 |  93.8% |
       +----------------------------+-------+--------+

The image used in the size table above was generated based on the
instructions from https://bugs.debian.org/858039#10 with gnuplot 5.2
patchlevel 2.  Beyond the test-based "--create-groups", the following
scour command-line parameters were used:
      --enable-id-stripping --enable-comment-stripping \
      --shorten-ids --indent=none

Note that the baseline was scour'ed repeatedly to stablize the image
size.

Signed-off-by: Niels Thykier <niels@thykier.net>
2020-05-17 19:37:32 +02:00
Patrick Storz
40753af88a Fix whitespace handling for SVG 1.2 flowed text
See 718748ff22

Fixes https://github.com/scour-project/scour/issues/235
2020-05-17 17:33:50 +02:00
Patrick Storz
f65ca60809 Fix deprecation warning 2020-05-17 17:10:26 +02:00
Patrick Storz
4fe2655f86
Merge pull request #187 from nthykier/fix-gh-186-shorten-id-recycle-used-ids
Enable shortenIDs to recycle existing IDs
2020-05-17 16:48:18 +02:00
Niels Thykier
58b75c314a Add test case for #198/#202
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-05-17 16:29:08 +02:00
Niels Thykier
6846e0c9ee Preserve xhref:href attr when collapsing referenced gradients
Closes: #198
Closes: #202
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-05-17 16:29:08 +02:00
Niels Thykier
f61b4d36d6 Add test case for #203
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-05-17 16:13:45 +02:00
Niels Thykier
09a656287d Avoid picking an id-less gradient to replace one with an id
Closes: #203
Signed-off-by: Niels Thykier <niels@thykier.net>
2020-05-17 16:13:45 +02:00
Patrick Storz
695676e3a5 Run tests with Python 3.7 / 3.8 2020-05-17 16:03:06 +02:00
Eduard Braun
049264eba6 Scour v0.37 2018-07-04 19:16:55 +02:00
Eduard Braun
5ccba31ff9 Update HISTORY.md 2018-07-04 19:05:25 +02:00
Patrick Storz
718748ff22
Merge pull request #199 from Ede123/newline_handling
Several improvements for handling whitespace including newlines, especially in text nodes
2018-07-03 22:56:36 +02:00
Eduard Braun
651694a6c0 Add unittests for whitespace handling in text node
Also expand/fix the test for line endings
2018-07-03 22:53:05 +02:00
Eduard Braun
703122369e Strip newlines from text nodes and be done with it
Follow the spec "blindly" as it turns out covering all the border
and getting reasonably styled output is just to cumbersome.
This way at least scour output is consistent and it also saves us
some bytes (a lot in some cases as we do not indent <tspan>s etc.
anymore)
2018-07-02 22:14:14 +02:00
Eduard Braun
2200f8dc81 temp 2018-07-02 01:05:54 +02:00
Eduard Braun
e1c2699f07 Improve whitespace handling in text content elements
SVG specifies special logic for handling whitespace, see
   https://www.w3.org/TR/SVG/text.html#WhiteSpace
by implementing it we can even shave off some unneeded bytes here
and there (e.g. consecutive spaces).

Unfortunately handling of newlines by renderers is inconsistent:
Sometimes they are replaced by a single space, sometimes they
are removed in the output.
As we can not know the expected behavior work around this by keeping
newlines inside text content elements intact.

Fixes #160.
2018-07-01 20:19:58 +02:00
Eduard Braun
7d28f5e051 Improve handling of newlines
Previously we added way to many and removed empty lines afterwards
(potentially destructive if xml:space="preserve")

Also adds proper indentation for comment nodes
2018-07-01 19:48:18 +02:00
Eduard Braun
06ea23d0e1 fix typo 2018-07-01 13:52:51 +02:00
Patrick Storz
8c95d950af
Merge pull request #192 from nthykier/gh-189-order-vs-SVGLength
Work around an exception in removeDefaultAttributeValue() caused by some rarely used filter attributes that allow an optional second value which SVGLength does not handle properly
2018-06-30 19:03:15 +02:00
Patrick Storz
5d579f8927
Also special-case baseFrequency and add 'radius 2018-06-30 18:58:36 +02:00
Eduard Braun
3c64623a12 Discontinue official support for Python 3.3
(testing failed due to wheel now requiring Python >= 3.4)

Also run flake8 in latest Python 3.6
(3.7 is not supported on Travis yet)
2018-06-29 19:29:09 +02:00
Patrick Storz
9f4a707bb7
Merge pull request #178 from nthykier/gh-163-path-rewrite
Correct handling of "m0 0" vs. "z" commands
2018-06-29 19:11:53 +02:00
Niels Thykier
8a2892b458 Avoid crashing on stdDeviation attribute
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-21 06:39:08 +00:00
Niels Thykier
c504891bd7 test: Use number-optional-number variant of kernelUnitLength
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-21 06:19:38 +00:00
Tobias Oberstein
47f918e696
Merge pull request #191 from nthykier/gh-190-optimizeTransform-IndexError
Avoid crashing on "scale(1)" (short for "scale(1, 1)")
2018-04-18 19:25:48 +02:00
Niels Thykier
18e57cddae Avoid crashing on "scale(1)" (short for "scale(1, 1)")
The scale function on the transform attribute has a short form, where
only the first argument is used.  But optimizeTransform would always
assume that there were two when checking for the identity scale.

Closes: #190
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-18 05:41:35 +00:00
Niels Thykier
a459d629c1 removeDefaultAttributeValue: Special-case order attribute
Scour tried to handle "order" attribute as a SVGLength.  However, the
"order" attribute *can* consist of two integers according to the
[SVG 1.1 Specification] and SVGLength is not designed to handle that.

With this change, we now pretend that "order" is a string, which side
steps this issue.

[SVG 1.1 Specification]: https://www.w3.org/TR/SVG11/single-page.html#filters-feConvolveMatrixElementOrderAttribute

Closes: #189
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-17 19:48:37 +00:00
Niels Thykier
039022ee9d shortenID: Improve tracking of optimal ID lengths
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-16 18:52:12 +00:00
Niels Thykier
e25b0dae73 Remove a (now) unused parameter to renameID
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-15 17:36:07 +00:00
Niels Thykier
91503c6d7e renameID: Replace referencedIDs with referringNodes
This change pushes the responsibility of updating referencedIDs to its
callers where needed.  The only caller of renameIDs is shortenIDs and
that works perfectly fine without updating its copy of referencedIDs.
In shortenIDs, we need to be able to lookup which nodes referenced the
"original ID" (and not the "new ID").

While shortenIDs *could* update referencedIDs so it remained valid, it
is extra complexity for no gain.  As an example of this complexity,
imagine if two or more IDs are "rotated" like so:

      Original IDs: a, bb, ccc, dddd
      Mapping:
        dddd -> ccc
        ccc  -> bb
        bb   -> a
        a    -> dddd

While doable within reasonable performance, we do not need to support
it at the moment, so there is no reason to handle that complexity.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-15 17:35:05 +00:00
Niels Thykier
d6406a3470 shortenIDs: Avoid pointless renames of IDs
With the current code, scour could do a pointless remap of an ID,
where there is no benefit in it.  Consider:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
  <rect id="a" width="80" height="50" fill="red"/>
  <rect id="b" width="80" height="50" fill="blue"/>
 </defs>
 <use xlink:href="#a"/>
 <use xlink:href="#b"/>
 <use xlink:href="#b"/>
</svg>
```

In this example, there is no point in swapping the IDs - even if "#b"
is used more often than "#a", they have the same length.  Besides a
performance win on an already scour'ed image, it also mean scour will
behave like a function with a fixed-point (i.e. scour eventually stops
altering the image).

To solve this, we no longer check whether an we find exactly the same
ID.  Instead, we look at the length of the new ID compared to the
original.  This gives us a slight complication as we can now "reserve"
a "future" ID to avoid the rename.

Thanks to Eduard "Ede_123" Braun for providing the test case.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-15 17:34:24 +00:00
Eduard Braun
8ddb7d8913 Add valid elements for 'spreadMethod' attribute
Turns out 'default_attributes_universal' is actually empty right now
so we might consider removing it altogether...
2018-04-15 18:40:06 +02:00
Eduard Braun
0ec0732447 Simplify 'default_attributes' handling a bit 2018-04-15 18:33:46 +02:00
Eduard Braun
20dcbcbe64 'default_attributes': make sure 'elements' is a list 2018-04-15 18:31:51 +02:00
Niels Thykier
1650f91ea4 Optimize removeDefaultAttributeValues
Avoid looping over DefaultAttribute(s) that are not relevant for a
given node.  This skips a lot of calls to removeDefaultAttributeValue
but more importantly, it avoids "node.nodeName not in attribute.elements"
line in removeDefaultAttributeValue.  As attribute.elements is a list, this
becomes expensive for "larger lists" (or in this case when there are a lot
of attributes).

This seems to remove about 1½-2 minutes of runtime (out of ~8) on the
1_42_polytope_7-cube.svg test case provided in #184.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-15 18:29:58 +02:00
Niels Thykier
5dc1b7a820 scour: Make optimized default_attribute data structures
There are a lot of "DefaultAttribute"s and for a given tag, most of
the "DefaultAttribute"s are not applicable.  Therefore, we create two
data structures to assist us with only dealing with the attributes
that matter.

Here there are two cases:

 * Those that always matter.  These go into
   default_attributes_unrestricted list.
 * Those that matter only based on the node name.  These go into the
   default_attributes_restricted_by_tag with the node name as key
   (with the value being a list of matching attributes).

In the next commit, we will use those for optimizing the removal of
default attributes.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-15 18:29:58 +02:00
Niels Thykier
00cf42b554 Rename function to match DEP8 conventions
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-15 16:22:00 +00:00
Niels Thykier
0254014e06 Enable shortenIDs to recycle existing IDs
This patch enables shortenIDs to remap IDs currently in use.  This is
very helpful to ensure that scour does not change been "optimal" and
"suboptimal" choices for IDs as observed in GH#186.

Closes: #186
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-04-13 21:00:35 +00:00
Eduard Braun
3283d6d5ec Simplify control point detection logic
- make controlPoints() return a consistent type like flags()
- rename the ambiguous "reduce_precision" to "is_control_point"
2018-04-08 16:48:33 +02:00
Eduard Braun
103dcc0a48
Fix handling of boolean flags in elliptical path commands (#183)
* properly parse paths without space after boolean flags (fixes #161)
* omit space after boolean flag to shave off a few bytes when not using renderer workarounds
2018-04-08 15:32:47 +02:00
Niels Thykier
ba7f4b5f18 Remove more redundant uses of .keys()
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-26 22:36:19 +02:00
Niels Thykier
f8d5af0e56 Remove now unused variable
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-26 22:36:19 +02:00
Eduard Braun
d508f59aa6 Completely remove "walltime" variable and use time.time() directly 2018-03-26 22:34:11 +02:00
Niels Thykier
b622642aa1 Simplify timer selection to always use time.time() (#175)
In python2.7 and python3.3, time.time() is sufficient accurate for our
purpose and avoids going through hoops to select the best available
function.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-26 22:30:25 +02:00
Niels Thykier
38274f75bc Implement a basic rewrite of redundant commands
This basic implementation can drop and rewrite some cases of "m0 0"
and "z" without triggering the issues experienced in #163.  It works
by analysing the path backwards and tracking "z" and "m" commands.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-11 08:33:50 +00:00
Niels Thykier
a2c94c96fb Disable the "m0 0"-optimization as it is wrong in some cases
The "m0 0" rewrite gets some cases wrong, like:

         m150 240h200m0 0 150 150v-300z

Scour rewrote that into the following
         m150 240h200l150 150v-300z

However, these two paths do not produce an identical figure at all.
The first is a line followed by a triangle while the second is a
quadrilateral.

While there are some instances we can rewrite (that scour will no
longer rewrite), these will require an analysis over multiple commands
to determine whether the rewrite is safe.  This will reappear in the
next commit.

Closes: #163
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-11 08:25:46 +00:00
Niels Thykier
6ea126d290 Gracefully handle unreferenced gradients with --keep-unreferenced-defs (#173)
Closes: #156
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-10 16:06:50 +01:00
Niels Thykier
cae0faefa0 Avoid O(n²) in removeDuplicateGradient (#171)
The original implementation of removeDuplicateGradient does O(n²)
search over all gradients to remove duplicates.  In images with many
gradients (such as [MediaWiki_logo_1.svg]), this becomes a significant
overhead as that logo has over 900 duplicated gradients.

We solve this by creating a key for each gradient based on the
attributes we use for duplication detection.  This key is generated
such that if two gradients have the same key, they are duplicates (for
our purpose) and the keys are different then the gradients are
guaranteed to be different as well.  With such a key, we can rely on a
dict to handle the duplication detection (which it does very well).

This change improves the runtime performance on [MediaWiki_logo_1.svg]
by about 25% (8m51s -> 1m56s on 5 runs).

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-03-10 15:47:29 +01:00
Niels Thykier
7115e82cb8 Fix typo in HISTORY.md 2018-03-10 13:36:49 +01:00
Niels Thykier
0776d32179 Remove an unnecessary loop
The unprotected_ids function returns all unprotected ids and
removeUnreferencedIDs removes all of them that does not appear in the
return value of findReferencedElements.

On closer observation it turns out that removeUnreferencedIDs cannot
cause nodes/IDs to become unprotected nor unreferenced (as it only
remove the "id" attribute, not the node).  With this in mind, we can
just remove the loop and save a call to all of these functions.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-20 19:46:49 +01:00
Eduard Braun
5360db86d9 Fix the last instance of "list(dict.keys())" 2018-02-17 15:59:18 +01:00
Niels Thykier
2f0b3ea362 Rewrite redundant codepatterns introduced by py2 -> py3 conversion
The automated python2 -> python3 converter creates some suboptimal
code patterns in some cases, notably in its handling of dicts.

This commit handles the following cases:

  * "if x in list(y.keys()):" => "if x in y:"

    The original code is neuters the O(1) lookup effeciency of a dict
    by turning it into a list.  This occurs a O(n) in converting it to
    a list and then another O(n) for the lookup.  When done in a loop,
    this becomes O(n * m) rather than the optimal O(m).

  * "for x in list(y.keys()):" => "for x in y:" OR "for x in list(y):"

    A dict (y in these cases) operates as an iterator over keys in the
    dict by default.  This makes the entire "list(y.keys())" dance
    redundant _in most cases_.  In a some cases, scour modifies the
    dict while iterating over it and in those cases, we need a
    "list(y)" (but not a "y.keys()").

    The benefit of this differs between python2 and python3.  In
    python3, we basically "only" avoid function call.  In python2,
    y.keys() generates a list, so here we avoid generating a
    "throw-away list".

The test suite succeed both with "python testscour.py" and "python3
testscour.py" (used 2.7.14+ and 3.6.4 from Debian testing).

On a 341kB flame-graph generated by "nytprof" (a perl profiler), this
commit changes the runtimes of scour from the range 3.39s - 3.45s to
3.27s - 3.35s making it roughly 3% faster in this case (YMMV,
particularly with different input).  The timings were recorded using
the following command line:

  time PYTHONPATH=. python3 -m scour.scour --enable-id-stripping  \
     --shorten-ids --indent=none  --enable-comment-stripping
     -i input.svg -o output.svg

This was used 5 times with and 5 times without the patch picking the
worst and best time to define the range.  The runtime test was only
preformed on python3.

All changed lines where found with:
  grep -rE ' in list[(].*[.]keys[(][)][)]:'

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-17 15:55:40 +01:00
Eduard Braun
cb093e9171 Update docstring of findReferencedElements
(to match changes in c54a7239e7)
2018-02-17 14:27:19 +01:00
Niels Thykier
c54a7239e7 Simplify the "ids" structure returned by findReferencedElements
It was a dict with a two element list a la:

  {
    "id1": [len(nodeListX), nodeListX]],
    "id2": [len(nodeListY), nodeListY]],
    ...
  }

This can trivially be simplified to:

  {
    "id1": nodeListX,
    "id2": nodeListY,
    ...
  }

The two call-sites that actually needs the length (e.g. to sort by how
often the id is used) can trivially compute that via a call to "len".

All other call sites either just need to tell if an ID is used at all
or work the nodes referencing the id (e.g. to remap the id).  The
former are unaffected by this change and the latter can now avoid a
layer of indirection.

This refactoring has negiable changes to the runtime and probably also
to memory (not tested, but it is a minor constant improvement per
referenced id).

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-17 14:11:51 +01:00
Niels Thykier
b916a189e9 Avoid recomputing findReferencedElements in removeUnusedDefs
The removeUnusedDefs function does not actually remove anything (that
is left for its callers to do).  This implies that
findReferencedElements will return the same value before, during and
after a call to removeUnusedDefs.  Therefore, we can reuse the value
from findReferencedElements when recursing into child nodes.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-17 14:11:51 +01:00
Niels Thykier
633b381d87 findReferencedElements: Handle referencingProps separately
Split the handling of referencingProps into a separate loop that calls
findReferencingProperty directly.  This saves a bunch of "make list,
join list, append to another list and eventually split text into two
elements" operations.

This gives approximately 10% faster runtimes on 341 kB flamegraph
generated by the "nytprof" Perl profiler.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-17 14:11:51 +01:00
Eduard Braun
7249ae8b0a user clearer variable names for indent type and indent depth 2018-02-17 11:59:35 +01:00
Niels Thykier
843706be39 Catch specific exception rather than anything
The bare "except" also catches exceptions like "NameError" and
"SystemExit", which we really should not catch.  In scour.py, use the
most specific exception (NotFoundErr) and in the tests just catch any
"regular" exception.

Reported by flake8.

Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-17 11:47:36 +01:00
Niels Thykier
f3d8936b5e Rename "I" to "line_prefix" to avoid flake8 E741
Signed-off-by: Niels Thykier <niels@thykier.net>
2018-02-17 11:47:36 +01:00
Michael Witten
b20a0698cc tests: Add unit tests for preservation of quotes in CSS styles
These tests will ensure that issues #21 and #56 do not return.
2017-09-03 18:07:27 +02:00
Michael Witten
0a146b7fef tests: Add unit tests for the escaping of quote characters in attribute values 2017-09-03 18:07:27 +02:00
Michael Witten
7e14cd352f scour.py: Escape quote characters in attribute values, as necessary and minimally
Either double quotes or single quotes are escaped; the choice is made
so as to minimize the length of the escaped string.
2017-09-03 18:07:27 +02:00
Michael Witten
f14784b01f scour.py: handle id' and xml:id' in the same code that handles other attributes 2017-08-26 19:11:37 +00:00
Michael Witten
fef2786c5e scour.py: minor rearrangement for the sake of clarity
There has been a minor rearrangement of the code that handles the children
of the element being serialized: The relevant `if' statement has had its
condition effectively negated and thus has also had its consequent and
alternative swapped; now, there is a very short consequent, followed by a
very long alternative, rather than a very long consequent followed by a
very short alternative.
2017-08-26 19:11:37 +00:00
Michael Witten
695a91e447 scour.py: Satisfy the identing rules of PEP8 2017-08-26 18:44:58 +02:00
Michael Witten
7ee5f9774d scour.py: Use named constants rather than literal integers for `nodeType' 2017-08-26 18:44:58 +02:00
Eduard Braun
e36cd4832a Scour v0.36 2017-08-06 04:55:59 +02:00
Eduard Braun
49f3664f82 make flake8 happier 2017-08-06 04:55:43 +02:00
Eduard Braun
53d87ed35a make flake8 happy 2017-08-06 04:47:33 +02:00
Eduard Braun
c089448bb5 Update project description and use in both, README.md and setup.py 2017-08-06 04:38:33 +02:00
Eduard Braun
e0bfad272b Update README.md
- the original page only has a link these days
- project identity should be established by now
2017-08-06 04:13:23 +02:00
Eduard Braun
992b6850c6 Update HISTORY.md 2017-08-06 03:52:20 +02:00
Eduard Braun
cc592c8e8a Improve and fix behaviour when collapsing straight paths segments (#146)
* Do not collapse straight path segments in paths that have intermediate markers (see #145). The intermediate nodes might be unnecessary for the shape of the path, but their markers would be lost.
* Collapse subpaths of moveto `m` and lineto `l` commands if they have the same direction (before we only collapsed horizontal/vertical `h`/`v` lineto commands)
* Attempt to collapse lineto `l` commands into a preceding moveto `m` command (these are then called "implicit lineto commands")
* Preserve empty path segments if they have `stroke-linecap` set to `round` or `square`. They render no visible line but a tiny dot or square.
2017-05-18 00:53:25 +02:00
Ville Skyttä
75bacbc8e6 Python 3.6 invalid escape sequence deprecation fix (#144)
(see https://docs.python.org/3/whatsnew/3.6.html#deprecated-python-behavior)
2017-05-09 22:07:06 +02:00
Ville Skyttä
62b16c11d8 Spelling fixes 2017-05-09 21:45:04 +02:00
Eduard Braun
5bfffc2ca8 Hardcode printing of "flowtext" warning to stderr
Third-party applications obviously can not handle additional output on stdout nor can they be expected to do any weird stdout/sterr redirection as we do via `options.stdout`
We probably shouldn't print anything in `scourString()` to start with unless we offer an option to disable all non-SVG output for third-party libraries to use.
2017-04-30 04:13:44 +02:00
Eduard Braun
98e3040645 Add unittest for --set-c-precision (7cb0d36d72) 2017-02-25 19:44:18 +01:00
Eduard Braun
51c1e6af23 Improve options handling for precision options
- prevent '--set-precision=0' by requiring >=1
- warn user if '--set-c-precision' > '--set-precision' instead of silently ignoring the value
- some code cleanup
2017-02-25 19:44:18 +01:00
Eduard Braun
c2a65a772e Some code refactoring 2017-02-25 19:44:18 +01:00
Eduard Braun
12237e01c8 Refactor logic to detect control points from 7cb0d36d72 and also include control points of quadratic Bézier curve commands ("q") 2017-02-25 19:44:18 +01:00
Eduard Braun
090884a70f Don't force whitespace for elliptical paths (fixes #89)
This was only required in an early draft of the SVG spec (an error that was corrected later, see [1,2])

[1] https://github.com/scour-project/scour/issues/89#issuecomment-244216600
[2] https://github.com/scour-project/scour/issues/89#issuecomment-244337118
2017-02-25 19:44:18 +01:00
Eduard Braun
2ebe9741b2 Rename a variable plus some editing of comments 2017-02-25 19:44:18 +01:00
Eduard Braun
a7e7b4c21d Cleanup options.
Also omit short option strings of advanced options for now (if we offer them again in future, they should be chosen very carefully as should the options for which we offer them)
2017-02-23 23:39:27 +01:00
pborunda
7cb0d36d72 Improve precision options for smaller output size (#131)
Add a separate precision option for curve control points (--set-c-precision)
This can considerably reduce file size with marginal effect on visual appearance.
2017-02-23 22:00:32 +01:00
Eduard Braun
ffeb76c894 Unittests: remove temporary file 'testscour_temp.svg' after running tests 2017-02-22 22:13:04 +01:00
Eduard Braun
f7d6406d38 Work around https://github.com/travis-ci/travis-ci/issues/3080 as pypy throws if 'ping' can't be executed 2017-02-22 22:07:17 +01:00
Eduard Braun
0f6d9be4e2 Add sudo: false to .travis.yml for faster execution of jobs 2017-02-19 18:06:57 +01:00
Eduard Braun
3b41d3a547 Add Python 3.6 to tests and simplify .travis.yml by using 'tox-travis' 2017-02-19 18:04:36 +01:00
Eduard Braun
0ffefcd8bb Unittests for --enable-viewboxing 2017-02-19 16:10:01 +01:00
Eduard Braun
01cb120d71 Reduce precision of lengths in viewBox
This fixes #127.
Also simplify splitting of viewBox lengths and avoiding a "FutureWarning: split() requires a non-empty pattern match" at the same time
2017-02-19 16:10:01 +01:00
Eduard Braun
3e4c8d793f Typo in unittest svg file 2017-02-19 15:19:53 +01:00
Eduard Braun
f5a61eeeb3 Even better fix for 8f87118725
(previous solution still did not work for numbers like 123.4 with precision < 3)
2017-02-19 00:57:00 +01:00
Eduard Braun
7c2e035644 Merge pull request #133 from Ede123/precision
Some fixes for `scourUnitlessLength()`
2017-02-18 19:45:51 +01:00
Eduard Braun
a69efb3a55 Add unittests for b00b374e64 and 8f87118725 2017-02-18 19:36:19 +01:00
Eduard Braun
8f87118725 Only use number representation with reduced precision if it is shorter than the initial representation.
Before it could happen that "123" was replaced with "1e3" if precision was set to 1 which is obviously not desirable.
2017-02-18 19:01:26 +01:00
Eduard Braun
b00b374e64 Fix generation of non-scientific number representation.
Before numbers often were already in scientific notation due to the str() implementation of Decimal leading to strange optimization results.
2017-02-18 18:06:09 +01:00
Eduard Braun
210c5f64ab Update HISTORY.md 2016-11-27 19:10:29 +01:00
Eduard Braun
25549b35d3 Some whitespace fixes to make newer versions of flake8 happy 2016-11-27 18:52:39 +01:00
Eduard Braun
6cf8c2b7d9 call sanitizeOptions() in start() to prevent a third-party breakage 2016-11-27 18:52:39 +01:00
Eduard Braun
c45f050fe6 Update HISTORY.md (0.35 has been released) 2016-10-25 23:16:47 +02:00
Eduard Braun
d9b369864d Reimplement generateDefaultOptions() by simply calling sanitizeOptions() 2016-09-25 19:02:19 +02:00
Eduard Braun
49cb542689 Make sanitizeOptions() work with an empty parameter list 2016-09-25 18:49:05 +02:00
Eduard Braun
285d73e5a6 Fix statistics out put for "Number of comments removed" 2016-09-25 15:44:07 +02:00
Eduard Braun
2fe7152a1e Fix logic from 4a5b924d37 (which was still wrong after 4410f91dad) 2016-09-25 15:34:28 +02:00
Eduard Braun
91ee9d2112 Merge pull request #120 from Ede123/raster_images
Fix embedding of raster images
2016-09-23 23:23:21 +02:00
Eduard Braun
de1441fd58 Exclude (system specific) absolute paths from test file and add a unittest that creates/tests absolute paths on-the-fly 2016-09-23 23:16:19 +02:00
Eduard Braun
902e112a96 Add unittests for embedding rasters (and --disable-embed-rasters) 2016-09-23 23:14:56 +02:00
Eduard Braun
8cc97601c4 scourXmlFile(): Set specified 'filename' as input filename so relative references will work 2016-09-23 22:32:32 +02:00
Eduard Braun
462460a512 Fix embedRasters() function.
It was not Python 3 compatible and usually would not have worked with local files.
2016-09-23 22:30:43 +02:00
Eduard Braun
8d20805976 Add three images (for usage with a future unittest) 2016-09-18 22:41:24 +02:00
Eduard Braun
7e2b5e43df Add CONTRIBUTING.md with some (hopefully) useful pointers 2016-09-18 22:36:21 +02:00
Eduard Braun
284beae36c Merge pull request #117 from Ede123/unittests
Add some unittests
2016-09-18 18:33:14 +02:00
Eduard Braun
45a2869a17 Add unittests for --protect-ids-_ options 2016-09-18 18:29:13 +02:00
Eduard Braun
829b630d64 Add unittests which emulate calling the scour module from command line 2016-09-18 17:13:00 +02:00
Eduard Braun
47cfb9aa0e Add unittest for --strip-xml-space 2016-09-18 17:13:00 +02:00
Eduard Braun
24c8087bd4 minor whitespace fix 2016-09-18 17:12:33 +02:00
Eduard Braun
2487f4433b Fixes to globals used for tracking statistics (#118)
- Collect globals in `scourString()` and make sure they're all properly initialized to zero. Before statistics were wrong when scouring multiple files/strings because initialization was only done once when loading the module.
- harmonize names
- adjust according to PEP 8 while at it (including leading underscore to mark as non-public)
- include one missing variable in statistics output (number of comments removed)
2016-09-18 16:23:00 +02:00
Eduard Braun
4410f91dad Fix logic in previous commit 2016-09-18 03:25:19 +02:00
Eduard Braun
4a5b924d37 Do not attempt to close stdin/stdout file objects 2016-09-18 03:07:51 +02:00
Eduard Braun
8ac344daf2 Update Makefile
- add `make` target to run unittests
- add a convenience `check` target (that runs unittests and flake8 for now)
- remove `test_error_on_flowtext` target (we have unittests for that now)
2016-09-17 17:09:35 +02:00
Eduard Braun
18970e0ba1 Update README.md
- add codecov badge
- exclusively use "shields.io" for badges (one service potentially being able to track visitors is enough)
- add title texts
2016-09-17 16:28:13 +02:00
Eduard Braun
e1e3051de0 Merge pull request #115 from Ede123/coverage 2016-09-17 15:53:10 +02:00
Eduard Braun
582c2dd9b7 Add coverage to Makefile and improve clean target 2016-09-17 02:26:13 +02:00
Eduard Braun
42dc70874c Automate coverage via Travis and Codecov 2016-09-17 02:25:32 +02:00
Eduard Braun
79390dc0e4 Add flake8 to automated tests 2016-09-16 02:03:03 +02:00
Eduard Braun
fb9ffb3dbd Merge pull request #113 from Ede123/pep8
Reformat code according to PEP 8
2016-09-15 23:05:10 +02:00
Eduard Braun
f27ad1e416 add flake8 to Makefile 2016-09-15 22:15:10 +02:00
Eduard Braun
d9198d7872 Order imports as suggested by PEP 8 2016-09-15 21:56:36 +02:00
Eduard Braun
99363c9089 Fix all issues detected by pyflakes 2016-09-15 21:31:34 +02:00
Eduard Braun
82df0d2327 More PEP 8 cleanup
(solves all issues reported by `pycodestyle`)
2016-09-15 21:02:15 +02:00
Eduard Braun
fc356815a2 Some reformatting and manually break all long lines at column 119 2016-09-15 01:54:19 +02:00
Eduard Braun
0f1974c1e2 Consistent whitespace across source files according to PEP 8
(mostly automated by using autopep8, fixes #69)
2016-09-15 00:35:13 +02:00
Tobias Oberstein
943319b710 bump version 2016-09-14 12:10:57 +02:00
Eduard Braun
9b80fc55a2 Update HISTORY.md 2016-09-11 14:10:24 +02:00
Eduard Braun
9629e87f37 Throw some line breaks in there... 2016-09-11 14:03:31 +02:00
Eduard Braun
082b579410 We don't want spaces in the serialized value of style attributes.
Add a unittest that should catch this and other issues with parsing/serializing the `style` attribute
2016-09-11 14:01:04 +02:00
Eduard Braun
b065137187 Whitespace fix 2016-09-11 01:11:36 +02:00
Eduard Braun
cd8b723fed Merge pull request #111 from Ede123/property_inheritance
Fix incorrect property inheritance
2016-09-11 00:19:38 +02:00
Eduard Braun
10e687b887 Add unittests for 1cde426009 and 641d2db08a392e4a7df20c700e1accb9cd8d1341 2016-09-11 00:16:17 +02:00
Eduard Braun
0b5bb5184c Do not remove stroke:none; if a differing value would be inherited from a parent
(fixes rest of #22)
2016-09-11 00:16:04 +02:00
Eduard Braun
0b5eab7f2f Avoid removal of some additional styles if they might be inherited by a child 2016-09-10 22:13:02 +02:00
Eduard Braun
1cde426009 Do not remove stroke-related styles if they might be inherited by a child
(partially fixes #22)
2016-09-10 22:08:49 +02:00
Eduard Braun
600ec2868c Update HISTORY.mdUpdate HISTORY.md 2016-09-06 01:49:14 +02:00
Eduard Braun
1aa5722c6a Fix conversion of cubic Bézier "curveto" commands into "shorthand/smooth curveto" commands. (#110)
When the preceeding path segment is a Bézier curve, too, the first control point of the shorthand defaults to the mirrored version of the second control point of this preceeding path segment. Scour always assumed (0,0) as the control point in this case which could result in modified path data (e.g. #91).
2016-09-06 01:43:36 +02:00
Eduard Braun
0fac95ee09 Update HISTORY.md 2016-09-05 22:46:11 +02:00
Eduard Braun
ec855211de Fix replacement of duplicate gradients if "fill/stroke" contains fallbacks (#109)
(fixes #79)
2016-09-05 22:44:55 +02:00
Eduard Braun
564367f886 Update HISTORY.md 2016-08-31 22:46:48 +02:00
Eduard Braun
25c05d99d8 Merge pull request #108 from Ede123/precision
Improve path scouring
2016-08-31 22:29:51 +02:00
Dirk Thomas
dc3f66ed0f Sort declarations in style attribute (#107) 2016-08-31 22:29:01 +02:00
Eduard Braun
0c63344ea4 Add a check to prevent we make path data longer by "optimization" 2016-08-31 07:06:42 +02:00
Eduard Braun
21e6c7491b Fix a unittest that failed due to the increased accuracy of paths after 29e005bf7b 2016-08-31 06:32:05 +02:00
Eduard Braun
29e005bf7b Improve handling of scientific vs. non-scientific notation
* Negative exponents were not handled in a reasonable way (e.g. 0.000001 remained unchanged)
* Trailing zeroes were not always properly removed
2016-08-31 06:29:01 +02:00
Eduard Braun
9e4b9d6f5e Improve behaviour of numerial precision reduction
* Previously all calculations were immediately done with the precision specified by the user, resulting in accumulation of numerical errors and in some cases very discernible abberations from the initial image (e.g. #80)
* Now all calculations are done with default precision (the module "decimal" uses a whopping 28 signifigant digits initially!) and only at the very end coordinates are rounded to the desired precision.
2016-08-31 02:20:11 +02:00
Eduard Braun
3929426a5a Add some attributes that fit into the list of the previous commit, too 2016-08-30 22:32:57 +02:00
Dirk Thomas
e76da093ea Try to always order attributes intuitively based on a list of select attributes, order other attributes alphabetical (#105) 2016-08-30 22:30:29 +02:00
Eduard Braun
bf4fc1b70e Linkify HISTORY.md 2016-08-30 00:17:33 +02:00
Eduard Braun
e57adf0c96 Fix HISTORY.md 2016-08-30 00:15:09 +02:00
Eduard Braun
fa17e6655a Update HISTORY.md 2016-08-30 00:13:37 +02:00
Eduard Braun
3b68fdacf8 Fix removal rules for the overflow attribute (#104)
(fixes #92)
2016-08-30 00:05:52 +02:00
Eitot
aa48c90d56 Add options to remove descriptive elements (#102)
* --remove-titles (removes <title>)
* --remove-descriptions (removes <desc>)
* --remove-descriptive-elements (removes <title>, <desc> and <metadata>)
2016-08-29 21:05:12 +02:00
Eduard Braun
842123a393 Fix improper comparison of numeric default attribute values with textvalues resulting in wrongly removed attributes (#101)
For example for `orient="auto"` SVGLength() returns (value=0, units=Unit.INVALID); since the default value for `orient` is zero it was removed as there was check for a valid unit.
2016-08-29 06:37:28 +02:00
Eitot
3511c05298 Remove obsolete Psyco dependency (#100) 2016-08-29 04:32:54 +02:00
Eduard Braun
beea442fce Update HISTORY.md 2016-08-29 03:39:49 +02:00
Eduard Braun
0314070363 Fix/update list of SVG presentation attributes (#99)
(fixes #94)
2016-08-29 03:30:33 +02:00
Eduard Braun
5844076258 Only attempt to group elements that the content model allows to be children of a <g> when --create-groups is specified. (#98)
(before it could happen that e.g. `tspan` elements were grouped, which is invalid; fixes issues #96 and #97)
2016-08-29 02:33:13 +02:00
Eduard Braun
419f41cb48 Add links to issues in release notes 2016-08-28 20:07:51 +02:00
Eduard Braun
c433077e6f Add release notes for all releases since 0.26 2016-08-28 19:54:46 +02:00
Eduard Braun
fbf4e13e8d Restore release notes (converted to Markdown) 2016-08-28 17:22:46 +02:00
Eduard Braun
0cbf89641a Merge pull request #70 from Ede123/default_attributes
Improve code for removal of default attributes
2016-08-27 16:26:10 +02:00
Eduard Braun
7c86de83d1 Rename default_attributes -> default_properties (that's what they are called and it makes the distinction to the other default attributes clearer)
Add some documentation / links to spec as sources
2016-08-27 16:15:47 +02:00
Eduard Braun
488c756989 removeDefaultAttributeValues(): Add a lot of new default attribute values 2016-08-27 03:40:38 +02:00
Eduard Braun
002c54b5cc removeDefaultAttributeValues(): Refactor to improve code clarity and extensibility 2016-08-26 23:45:44 +02:00
Eduard Braun
cbda5dcd86 Add unittests for #66 2016-08-26 23:45:44 +02:00
Eduard Braun
beb9823a91 Fix some erroneous removal of default attributes (fxes #66) 2016-08-26 23:45:44 +02:00
Eduard Braun
90910eaa6f Fix counting issue in statistics due to collidng variable names 2016-08-26 23:44:14 +02:00
Eduard Braun
386d5d8656 Allow elements to be found via Document.getElementById() in the minidom document returned by scourXmlFile() (#68) 2016-08-25 21:13:09 +02:00
Eduard Braun
8d6301950b Redirect informational output to stderr when SVG is output to stdout (#67)
fixes #47
2016-08-23 22:31:55 +02:00
Eduard Braun
4f23ea7a34 Print usage information if no input file was specified (and no data is available from stdin) (#65)
fixes #34
2016-08-23 21:16:14 +02:00
Eduard Braun
57f93efc89 Don't escape quotes ('/") in text nodes and attributes. (#64)
- In text nodes quotes are fine
- In attributes quotes are fine if used reciprocally.

Escaping in the latter case often causes issues, e.g. with quoted font names (#21) or inline CSS styles (#56), while it probably does not gain anything (if quotes are wrongly used in attribute names the XML is most likely invalid to start with)
2016-08-16 00:10:41 +02:00
Eduard Braun
fe2884c3e8 Don't remove unreferenced defs if --keep-unreferenced-defs is specified (#62)
* Don't remove unreferenced defs if `--keep-unreferenced-defs` is specified
(fixes #18)

* Add unittests for previous commit
2016-08-14 18:52:55 +02:00
Eduard Braun
3299f8f6e0 Also shorten unused IDs when --shorten-ids is specified 2016-08-14 17:12:48 +02:00
Eduard Braun
df142a2f22 Drop official support for Python 2.6 2016-08-13 17:15:03 +02:00
Eduard Braun
3f5c6c76c0 Sync version 2016-08-13 17:14:29 +02:00
Tobias Oberstein
06457a7461 trigger build 2016-07-30 15:21:17 +02:00
Tobias Oberstein
e41893d98f trigger build 2016-07-30 15:17:14 +02:00
Tobias Oberstein
a766a3256b force travis 2016-07-30 13:14:43 +02:00
Tobias Oberstein
c564b89d21 Merge pull request #59 from dirk-thomas/order_attributes_option
add --order-attributes option
2016-07-30 13:02:09 +02:00
Dirk Thomas
371f14786a add --order-attributes option 2016-07-29 10:22:59 -07:00
Tobias Oberstein
8a2d9d604d Merge pull request #57 from Ede123/xml_standalone
Only include "standalone" attribute if it was explicitly set to "yes"
2016-07-25 12:46:45 +02:00
Eduard Braun
f83b03707b Fix unittests for previous commit 2016-06-12 15:42:30 +02:00
Eduard Braun
84b36c7109 Only include "standalone" attribute if it was explicitly set to "yes" in input document ("no" is the default value) 2016-06-12 15:36:02 +02:00
Tobias Oberstein
1a8ece216d Merge pull request #53 from codedread/detect_flowtext
add option to check and warn or bail out on flowtext
2016-04-18 19:01:56 +02:00
Tobias Oberstein
d2faa6bffd Merge pull request #52 from Ede123/travis
Basic implementation for Travis CI
2016-04-03 09:11:06 +02:00
Eduard Braun
6bb2b35ba2 Continuous integration with Travis and tox 2016-04-02 19:30:30 +02:00
Tobias Oberstein
6a23a4cd71 add unit tests 2016-04-02 17:41:20 +02:00
Tobias Oberstein
d710fb3f6c whitespace 2016-04-02 17:40:40 +02:00
Tobias Oberstein
99dc0dfae9 fix opt groups 2016-04-02 17:11:14 +02:00
Tobias Oberstein
b14e801cb7 add option to check and warn or bail out on flowtext 2016-04-02 16:49:10 +02:00
Tobias Oberstein
70e6fb776e Merge pull request #51 from Ede123/fix1
Small fix
2016-04-02 15:22:40 +02:00
Eduard Braun
ea610e5c09 Fix regression in 77906518c0
(accidentally removed default value for  "shorten_ids_prefix")
2016-04-02 01:54:27 +02:00
Tobias Oberstein
0d6725e05d Merge pull request #46 from Ede123/options
Facilitate command line usage
2016-03-22 16:20:41 +01:00
Eduard Braun
77906518c0 Use option groups for command line arguments to achieve clearer help output 2016-02-20 18:43:43 +01:00
Eduard Braun
72c2ec8e1c Add possibility to specify input/output filename using positional arguments (e.g. 'scour input.svg output.svg') 2016-02-20 17:57:30 +01:00
Tobias Oberstein
ab90d18cb2 Merge pull request #45 from Ede123/console_output
Switch order of new/old size in console output
2016-02-19 14:08:29 +01:00
Tobias Oberstein
7c3331d6f6 Merge pull request #44 from Ede123/default_options
Mechanism for sanitizing options
2016-02-19 14:07:29 +01:00
Eduard Braun
b042c93b2c Switch order of new/old size in console output
(It already looked like a fraction, now it also yields the correct result if it's read like one)
2016-02-19 04:47:33 +01:00
Eduard Braun
cf08a72e41 Unittests: Add two tests for and simplify after e701acdc25 2016-02-19 04:40:57 +01:00
Eduard Braun
e701acdc25 Add a mechanism to sanitize options.
This simplifies usage of the Scour module while avoiding any compatibility issues that might be caused by adding/removing/renaming options.
2016-02-19 04:03:59 +01:00
Tobias Oberstein
1a9d6119e0 bump version 2016-01-29 09:26:12 +01:00
Tobias Oberstein
170f8c7baa turn down log noise; improve logging 2016-01-29 09:25:43 +01:00
Tobias Oberstein
939dd160bc scour does indeed work on py3 2016-01-15 13:17:34 +01:00
Tobias Oberstein
73ec7da13e allow installation via direct execution of setup.py from outside the package directory 2016-01-15 11:15:22 +01:00
Tobias Oberstein
07e9ec0257 add notes on how to install latest trunk 2016-01-15 11:07:29 +01:00
Tobias Oberstein
f05d73b859 cleanup readme 2016-01-15 10:53:35 +01:00
Tobias Oberstein
dccb80314c Merge pull request #37 from Eitot/sketch
Sketch support
2016-01-15 10:44:32 +01:00
Eitot
184efee16e - Initial support for Sketch
- Typos in README.md
2015-12-24 17:07:40 +01:00
Tobias Oberstein
fbf4e3ec2e Merge pull request #36 from Eitot/readme
Update README.md
2015-12-21 12:59:06 +01:00
Eitot
972812e6c7 Update README.md 2015-12-21 09:10:09 +01:00
Tobias Oberstein
232f27269a bump version for release 2015-12-10 23:39:16 +01:00
Tobias Oberstein
130a71327a Merge pull request #28 from Ede123/comments
Simplify and fix "removeComments()"
2015-12-10 23:34:59 +01:00
Tobias Oberstein
0a0a062718 Merge pull request #27 from Ede123/encoding
Fix character encoding issues
2015-12-10 23:32:22 +01:00
Eduard Braun
c698522c28 Simplify and fix "removeComments()"
* The separate treatment of comments at the documentElement's level is not necessary - they have a parent (as tested in Python 3.5.0 and 2.7.11 and 2.6.6)! It might not have worked before due to a typo - note the "if isinstance(element,...)" and "len(element.data)" which should both refer to "subelement" instead - or a bug in very old versions of Python).
* Fix the iteration over childNodes (i.e. replace "for subelement in element.childNodes:" with ""for subelement in element.childNodes[:]:". We have to create a copy of the list to iterate over, otherwise we'd be iterating over a list as we change it which leads to unpredictable results.

Fixes #25
2015-12-10 22:51:27 +01:00
Eduard Braun
946ca3ce4a Unittests: Add a test for proper decoding of ISO 8859-15 2015-12-09 21:32:18 +01:00
Eduard Braun
8984e550b0 Read from stdin in binary mode an let XML parser deal with encoding. Also write to stdout in binary mode as the output is already encoded. 2015-12-09 00:30:16 +01:00
Eduard Braun
4eade69201 Open input file in binary mode an let XML parser deal with encoding.
Fixes #26
2015-12-08 23:38:06 +01:00
Jeff Schiller
1a6ff29c14 Merge pull request #24 from Ede123/unittests
Import unittests from old repository
2015-12-08 08:23:30 -08:00
Eduard Braun
32e7e5517f Unittests: Account for b979fe19e5 (fix one test and add two more)
- Unused XML namespace declarations *are supposed* to be removed
- XML namespace declarations that are used as prefix for elements/attributes *must not* be removed
2015-12-07 00:33:08 +01:00
Eduard Braun
ab1aa0e2f8 Fix a bug with "points" attribute of "polyline/polygon" starting with a negative number.
In this case "len(nums) = 0" and "nums[len(nums)-1]" triggered an "IndexError: list index out of range"
2015-12-06 23:47:56 +01:00
Eduard Braun
f0c69b827e Unittests: Sync options with current Scour
(fixes yet two other errors when running tests)
2015-12-06 23:07:06 +01:00
Eduard Braun
2293e1bd4a Fix regex for list of points splitting: Expect at least one whitespace character or comma between coords.
The previous regex would have allowed zero length matches and therefore could have split the string after every character - luckily this was not (yet) correctly implemented in Python. Since Python 3.5 a warning is raised informing of this problem. Future versions will correctly split also at zero length matches.

See also note on https://docs.python.org/3/library/re.html#re.split
2015-12-06 22:53:03 +01:00
Eduard Braun
c54c5e5d7e Unittests: Fix warnings
- Replace deprecated assertion-aliases
- Make sure opened files are properly closed after usage
2015-12-06 22:13:21 +01:00
Eduard Braun
320bdda6e9 Unittests: Run python-modernize 2015-12-06 21:20:05 +01:00
Eduard Braun
391ff77659 Unittests: Fix import paths 2015-12-06 20:10:12 +01:00
Eduard Braun
3b7e8a0091 Restore unittests from history 2015-12-06 19:59:06 +01:00
Tobias Oberstein
ebccae8cad Merge pull request #17 from Ede123/compat_fix
Compatibility fix for Python 2.6
2015-11-26 22:56:48 +01:00
Eduard Braun
6254548582 Compatibility fix for Python 2.6
(NamedNodeList seems not to have had the iterkeys method back then)
2015-11-21 23:29:59 +01:00
Tobias Oberstein
46a2ada361 Merge pull request #14 from Ede123/namespaces
Remove unused XML namespace declarations
2015-11-17 23:18:46 +01:00
Eduard Braun
532f664001 Minor fix in console output (superfluous space) 2015-11-17 23:09:25 +01:00
Eduard Braun
89f4b687f2 Statistics: count also attributes removed from root SVG element 2015-11-17 22:49:50 +01:00
Eduard Braun
67bacc2f23 Remove metadata right away
(i.e. before checking for unused XML namespaces; otherwise the relevant namespaces used for metadata would not yet be unused)
2015-11-17 22:46:50 +01:00
Eduard Braun
b979fe19e5 Remove unused XML namespace declarations 2015-11-17 22:30:23 +01:00
Tobias Oberstein
302f7f7477 use absolute imports 2015-11-16 20:36:12 +01:00
Tobias Oberstein
d3b29a68da fix links; only upload source dist 2015-11-16 18:12:56 +01:00
Tobias Oberstein
ec7385c995 update readme 2015-11-16 18:08:02 +01:00
Tobias Oberstein
e903475289 cleanups; bump version 2015-11-16 18:05:35 +01:00
Tobias Oberstein
27d5cd3881 Merge pull request #13 from Ede123/options
Add some options to optimize pretty-printing
2015-11-16 16:39:58 +01:00
Eduard Braun
7e36be4aaa Add an option to strip the xml:space="preserve" attribute from the root SVG element
This attribute is added by at least one popular vector graphics editor thwarting our efforts to pretty-print the output.
2015-11-15 19:26:39 +01:00
Eduard Braun
e21e362353 Add an option to suppress output of line breaks
(obviously this also disables indentation)
2015-11-15 18:44:17 +01:00
Eduard Braun
18266ca1ec Add an option to set number of spaces (or tabs used for indentation) 2015-11-15 18:14:03 +01:00
Tobias Oberstein
a5b09e3824 add notice of repo move 2015-10-17 17:29:47 +02:00
Tobias Oberstein
03f099a26e Merge pull request #8 from levisaya/master
Python 3 Updates
2015-05-20 19:25:25 +02:00
Andy Levisay
d1c66cc75b Python 3 Updates
Ran python-modernizer, this fixed most of the Python 3
incompatibilities.

Had to manually rework the iteration in svg_regex and svg_transform;
.next doesn't exist in Python 3. Wrapped the builtin next function in a
partial for equivalent functionality.
2015-04-28 15:26:55 -05:00
Tobias Oberstein
85dff51cc9 fix ingoring of additional args when invoked from scons; bump version 2014-08-05 12:47:33 +02:00
Tobias Oberstein
26a360a00c add option to ingore unknown cmd line opts; bump version 2014-07-26 17:20:47 +02:00
Tobias Oberstein
43ee078ee6 Merge pull request #3 from flosse/master
fixed version string
2014-03-10 00:22:47 +01:00
Tobias Oberstein
e01fac82a4 Merge pull request #2 from flosse/keep-unreferenced-defs
added option to keep elements within defs
2014-03-10 00:22:27 +01:00
Tobias Oberstein
e7332f6704 Merge pull request #5 from mdxs/patch-1
Small typo fix in doc string of parseListOfPoints
2014-03-10 00:20:05 +01:00
mdxs
f7165f66a9 Small typo fix in doc string of parseListOfPoints 2014-03-09 23:44:06 +01:00
Markus Kohlhase
9bb929d91e added option to keep elements within defs 2014-01-12 17:10:02 +01:00
Markus Kohlhase
edcaeba905 fixed version string 2014-01-12 16:56:56 +01:00
Tobias Oberstein
c27ca4db8a Merge pull request #1 from flosse/id-prefix
support custom id prefixes
2014-01-10 14:52:15 -08:00
Markus Kohlhase
ae4d9303f1 support custom id prefixes 2014-01-10 23:42:02 +01:00
Tobias Oberstein
3353500845 allow direct calling of module 2013-10-26 17:47:42 +02:00
Tobias Oberstein
8dc6137373 whitespace cleanup 2013-10-26 16:43:39 +02:00
158 changed files with 9138 additions and 2966 deletions

30
.travis.yml Normal file
View file

@ -0,0 +1,30 @@
sudo: false
language: python
python:
- pypy
- 2.7
- 3.4
- 3.5
- 3.6
- 3.7
- 3.8
- 3.9
- 3.10-dev
install:
- pip install tox-travis codecov
script:
- tox
matrix:
fast_finish: true
include:
- python: 3.9
env:
- TOXENV=flake8
after_success:
- coverage combine && codecov

35
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,35 @@
# Contributing
Contributions to Scour are welcome, feel free to create a pull request!
In order to be able to merge your PR as fast as possible please try to stick to the following guidelines.
> _**TL;DR** (if you now what you're doing) Always run [`make check`](https://github.com/scour-project/scour/blob/master/Makefile) before creating a PR to check for common problems._
## Code Style
The Scour project tries to follow the coding conventions described in [PEP 8 - The Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/). While there are some inconsistencies in existing code (e.g. with respect to naming conventions and the usage of globals), new code should always abide by the standard.
To quickly check for common mistakes you can use [`flake8`](https://pypi.python.org/pypi/flake8). Our [Makefile](https://github.com/scour-project/scour/blob/master/Makefile) has a convenience target with the correct options:
```Makefile
make flake8
```
## Unit Tests
In order to check functionality of Scour and prevent any regressions in existing code a number of tests exist which use the [`unittest`](https://docs.python.org/library/unittest.html) unit testing framework which ships with Python. You can quickly run the tests by using the [Makefile](https://github.com/scour-project/scour/blob/master/Makefile) convenience target:
```Makefile
make test
```
These tests are run automatically on all PRs using [TravisCI](https://travis-ci.org/scour-project/scour) and have to pass at all times! When you add new functionality you should always include suitable tests with your PR (see [`test_scour.py`](https://github.com/scour-project/scour/blob/master/test_scour.py)).
### Coverage
To ensure that all possible code conditions are covered by a test you can use [`coverage`](https://pypi.python.org/pypi/coverage). The [Makefile](https://github.com/scour-project/scour/blob/master/Makefile) convenience target automatically creates an HTML report in `htmlcov/index.html`:
```Makefile
make coverage
```
These reports are also created automatically by our TravisCI builds and are accessible via [Codecov](https://codecov.io/gh/scour-project/scour)

347
HISTORY.md Normal file
View file

@ -0,0 +1,347 @@
# Release Notes for Scour
## Version 0.38.2 (2020-11-22)
* Fix another regression caused by new feature to merge sibling groups ([#260](https://github.com/scour-project/scour/issues/260))
## Version 0.38.1 (2020-09-02)
* Fix regression caused by new feature to merge sibling groups ([#260](https://github.com/scour-project/scour/issues/260))
## Version 0.38 (2020-08-06)
* Fix issue with dropping xlink:href attribute when collapsing referenced gradients ([#206](https://github.com/scour-project/scour/pull/206))
* Fix issue with dropping ID while de-duplicating gradients ([#207](https://github.com/scour-project/scour/pull/207))
* Improve `--shorten-ids` so it re-maps IDs that are already used in the document if they're shorter ([#187](https://github.com/scour-project/scour/pull/187))
* Fix whitespace handling for SVG 1.2 flowed text ([#235](https://github.com/scour-project/scour/issues/235))
* Improvement: Merge sibling `<g>` nodes with identical attributes ([#208](https://github.com/scour-project/scour/pull/208))
* Improve performance of XML serialization ([#247](https://github.com/scour-project/scour/pull/247))
* Improve performance of gradient de-duplication ([#248](https://github.com/scour-project/scour/pull/248))
* Some general performance improvements ([#249](https://github.com/scour-project/scour/pull/249))
## Version 0.37 (2018-07-04)
* Fix escaping of quotes in attribute values. ([#152](https://github.com/scour-project/scour/pull/152))
* A lot of performance improvements making processing significantly faster in many cases. ([#167](https://github.com/scour-project/scour/pull/167), [#169](https://github.com/scour-project/scour/pull/169), [#171](https://github.com/scour-project/scour/pull/171), [#185](https://github.com/scour-project/scour/pull/185))
* Fix exception when removing duplicated gradients while `--keep-unreferenced-defs` is used ([#173](https://github.com/scour-project/scour/pull/173))
* Remove some illegal optimizations of `m0 0` sub-path commands ([#178](https://github.com/scour-project/scour/pull/178))
* Fix and improve handling of boolean flags in elliptical arc path commands ([#183](https://github.com/scour-project/scour/pull/183))
* Fix exception when shorthand transform `scale(1)` with single number is used ([#191](https://github.com/scour-project/scour/pull/191))
* Fix exception when using two-number forms of the filter attributes `baseFrequency`, `order`, `radius` and `stdDeviation` ([#192](https://github.com/scour-project/scour/pull/192))
* Improve whitespace handling in text nodes fixing an issue where scouring added spaces in error and reducing file size in many cases ([#199](https://github.com/scour-project/scour/pull/199))
* Drop official support for Python 3.3. (While it will probably continue to work for a while compatibility is not guaranteed anymore. If you continue to use Scour with Python 3.3 and should find/fix any compatibility issues pull requests are welcome, though.)
## Version 0.36 (2017-08-06)
* Fix embedding of raster images which was broken in most cases and did not work at all in Python 3. ([#120](https://github.com/scour-project/scour/issues/120))
* Some minor fixes for statistics output.
* Greatly improve the algorithm to reduce numeric precision.
* Precision was not properly reduced for some numbers.
* Only use reduced precision if it results in a shorter string representation, otherwise preserve full precision in output (e.g. use "123" instead of "1e2" when precision is set to 1).
* Reduce precision of lengths in `viewBox` ([#127](https://github.com/scour-project/scour/issues/127))
* Add option `--set-c-precision` which allows to set a reduced numeric precision for control points.<br/>Control points determine how a path is bent in between two nodes and are less sensitive to a reduced precision than the position coordinates of the nodes themselves. This option can be used to save a few additional bytes without affecting visual appearance negatively.
* Fix: Unnecessary whitespace was not stripped from elliptical paths. ([#89](https://github.com/scour-project/scour/issues/89))
* Improve and fix functionality to collapse straight paths segments. ([#146](https://github.com/scour-project/scour/issues/146))
* Collapse subpaths of moveto `m` and lineto `l`commands if they have the same direction (before we only collapsed horizontal/vertical `h`/`v` lineto commands).
* Attempt to collapse lineto `l` commands into a preceding moveto `m` command (these are then called "implicit lineto commands")
* Do not collapse straight path segments in paths that have intermediate markers. ([#145](https://github.com/scour-project/scour/issues/145))
* Preserve empty path segments if they have `stroke-linecap` set to `round` or `square`. They render no visible line but a tiny dot or square.
## Version 0.35 (2016-09-14)
* Drop official support for Python 2.6. (While it will probably continue to work for a while compatibility is not guaranteed anymore. If you continue to use Scour with Python 2.6 and should find/fix any compatibility issues pull requests are welcome, though.)
* Fix: Unused IDs were not shortened when `--shorten-ids` was used. ([#19](https://github.com/scour-project/scour/issues/62))
* Fix: Most elements were still removed from `<defs>` when `--keep-unreferenced-defs` was used. ([#62](https://github.com/scour-project/scour/issues/62))
* Improve escaping of single/double quotes ('/") in attributes. ([#64](https://github.com/scour-project/scour/issues/64))
* Print usage information if no input file was specified (and no data is available from `stdin`). ([#65](https://github.com/scour-project/scour/issues/65))
* Redirect informational output to `stderr` when SVG is output to `stdout`. ([#67](https://github.com/scour-project/scour/issues/67))
* Allow elements to be found via `Document.getElementById()` in the minidom document returned by scourXmlFile(). ([#68](https://github.com/scour-project/scour/issues/68))
* Improve code to remove default attribute values and add a lot of new default values. ([#70](https://github.com/scour-project/scour/issues/70))
* Fix: Only attempt to group elements that the content model allows to be children of a `<g>` when `--create-groups` is specified. ([#98](https://github.com/scour-project/scour/issues/98))
* Fix: Update list of SVG presentation attributes allowing more styles to be converted to attributes and remove two entries (`line-height` and `visibility`) that were actually invalid. ([#99](https://github.com/scour-project/scour/issues/99))
* Add three options that work analogous to `--remove-metadata` (removes `<metadata>` elements) ([#102](https://github.com/scour-project/scour/issues/102))
* `--remove-titles` (removes `<title>` elements)
* `--remove-descriptions` (removes `<desc>` elements)
* `--remove-descriptive-elements` (removes all of the descriptive elements, i.e. `<title>`, `<desc>` and `<metadata>`)
* Fix removal rules for the `overflow` attribute. ([#104](https://github.com/scour-project/scour/issues/104))
* Improvement: Automatically order all attributes ([#105](https://github.com/scour-project/scour/issues/105)), as well as `style` declarations ([#107](https://github.com/scour-project/scour/issues/107)) allowing for a constant output across multiple runs of Scour. Before order could change arbitrarily.
* Improve path scouring. ([#108](https://github.com/scour-project/scour/issues/108))<br>Notably Scour performs all calculations with enhanced precision now, guaranteeing maximum accuracy when optimizing path data. Numerical precision is reduced as a last step of the optimization according to the `--precision` option.
* Fix replacement of removed duplicate gradients if the `fill`/`stroke` properties contained a fallback. ([#109](https://github.com/scour-project/scour/issues/109))
* Fix conversion of cubic Bézier "curveto" commands into "shorthand/smooth curveto" commands. ([#110](https://github.com/scour-project/scour/issues/110))
* Fix some issues due to removal of properties without considering inheritance rules. ([#111](https://github.com/scour-project/scour/issues/111))
## Version 0.34 (2016-07-25)
* Add a function to sanitize an arbitrary Python object containing options for Scour as attributes (usage: `Scour.sanitizeOptions(options)`).<br>This simplifies usage of the Scour module by other scripts while avoiding any compatibility issues that might arise when options are added/removed/renamed in Scour. ([#44](https://github.com/scour-project/scour/issues/44))
* Input/output file can now be specified as positional arguments (e.g. `scour input.svg output.svg`). ([#46](https://github.com/scour-project/scour/issues/46))
* Improve `--help` output by intuitively arranging options in groups. ([#46](https://github.com/scour-project/scour/issues/46))
* Add option `--error-on-flowtext` to raise an exception whenever a non-standard `<flowText>` element is found (which is only supported in Inkscape). If this option is not specified a warning will be shown. ([#53](https://github.com/scour-project/scour/issues/53))
* Automate tests with continuous integration via Travis. ([#52](https://github.com/scour-project/scour/issues/52))
## Version 0.33 (2016-01-29)
* Add support for removal of editor data of Sketch. ([#37](https://github.com/scour-project/scour/issues/37))
* Add option `--verbose` (or `-v`) to show detailed statistics after running Scour. By default only a single line containing the most important information is output now.
## Version 0.32 (2015-12-10)
* Add functionality to remove unused XML namespace declarations from the `<svg>` root element. ([#14](https://github.com/scour-project/scour/issues/14))
* Restore unittests which were lost during move to GitHub. ([#24](https://github.com/scour-project/scour/issues/24))
* Fix a potential regex matching issue in `points` attribute of `<polygon>` and `<polyline>` elements. ([#24](https://github.com/scour-project/scour/issues/24))
* Fix a crash with `points` attribute of `<polygon>` and `<polyline>` starting with a negative number. ([#24](https://github.com/scour-project/scour/issues/24))
* Fix encoding issues when input file contained unicode characters. ([#27](https://github.com/scour-project/scour/issues/27))
* Fix encoding issues when using `stding`/`stdout` as input/output. ([#27](https://github.com/scour-project/scour/issues/27))
* Fix removal of comments. If a node contained multiple comments usually not all of them were removed. ([#28](https://github.com/scour-project/scour/issues/28))
## Version 0.31 (2015-11-16)
* Ensure Python 3 compatibility. ([#8](https://github.com/scour-project/scour/issues/8))
* Add option `--nindent` to set the number of spaces/tabs used for indentation (defaults to 1). ([#13](https://github.com/scour-project/scour/issues/13))
* Add option `--no-line-breaks` to suppress output of line breaks and indentation altogether. ([#13](https://github.com/scour-project/scour/issues/13))
* Add option `--strip-xml-space` which removes the specification of `xml:space="preserve"` on the `<svg>` root element which would otherwise disallow Scour to make any whitespace changes in output. ([#13](https://github.com/scour-project/scour/issues/13))
## Version 0.30 (2014-08-05)
* Fix ignoring of additional args when invoked from scons.
## Version 0.29 (2014-07-26)
* Add option `--keep-unreferenced-defs` to preserve elements in `<defs>` that are not referenced and would be removed otherwise. ([#2](https://github.com/scour-project/scour/issues/2))
* Add option to ignore unknown cmd line opts.
## Version 0.28 (2014-01-12)
* Add option `--shorten-ids-prefix` which allows to add a custom prefix to all shortened IDs. ([#1](https://github.com/scour-project/scour/issues/1))
## Version 0.27 (2013-10-26)
* Allow direct calling of the Scour module.
## Version 0.26 (2013-10-22)
* Re-release of Scour 0.26, re-packaged as a Python module [available from PyPI](https://pypi.python.org/pypi/scour) (Thanks to [Tobias Oberstet](https://github.com/oberstet)!).
* Development moved to GitHub (https://github.com/scour-project/scour).
## Version 0.26 (2011-05-09)
* Fix [Bug 702423](https://bugs.launchpad.net/scour/+bug/702423) to function well in the presence of multiple identical gradients and `--disable-style-to-xml`.
* Fix [Bug 722544](https://bugs.launchpad.net/scour/+bug/722544) to properly optimize transformation matrices. Also optimize more things away in transformation specifications. (Thanks to Johan Sundstr&ouml;m for the patch.)
* Fix [Bug 616150](https://bugs.launchpad.net/scour/+bug/616150) to run faster using the `--create-groups` option.
* Fix [Bug 708515](https://bugs.launchpad.net/scour/+bug/562784) to handle raster embedding better in the presence of file:// URLs.
* Fix [Bug 714717](https://bugs.launchpad.net/scour/+bug/714717) to avoid deleting renderable CurveTo commands in paths, which happen to end where they started.
* Per [Bug 714727](https://bugs.launchpad.net/scour/+bug/714727) and [Bug 714720](https://bugs.launchpad.net/scour/+bug/714720), Scour now deletes text attributes, including "text-align", from elements and groups of elements that only contain shapes. (Thanks to Jan Thor for the patches.)
* Per [Bug 714731](https://bugs.launchpad.net/scour/+bug/714731), remove the default value of more SVG attributes. (Thanks to Jan Thor for the patch.)
* Fix [Bug 717826](https://bugs.launchpad.net/scour/+bug/717826) to emit the correct line terminator (CR LF) in optimized SVG content on the version of Scour used in Inkscape on Windows.
* Fix [Bug 734933](https://bugs.launchpad.net/scour/+bug/734933) to avoid deleting renderable LineTo commands in paths, which happen to end where they started, if their stroke-linecap property has the value "round".
* Fix [Bug 717254](https://bugs.launchpad.net/scour/+bug/717254) to delete `<defs>` elements that become empty after unreferenced element removal. (Thanks to Jan Thor for the patch.)
* Fix [Bug 627372](https://bugs.launchpad.net/scour/+bug/627372) to future-proof the parameter passing between Scour and Inkscape. (Thanks to Bernd Feige for the patch.)
* Fix [Bug 638764](https://bugs.launchpad.net/scour/+bug/638764), which crashed Scour due to [Python Issue 2531](http://bugs.python.org/issue2531) regarding floating-point handling in ArcTo path commands. (Thanks to [Walther](https://launchpad.net/~walther-md) for investigating this bug.)
* Per [Bug 654759](https://bugs.launchpad.net/scour/+bug/654759), enable librsvg workarounds by default in Scour.
* Added ID change and removal protection options per [bug 492277](https://bugs.launchpad.net/scour/+bug/492277): `--protect-ids-noninkscape`, `--protect-ids-prefix`, `--protect-ids-list`. (Thanks to Jan Thor for this patch.)
## Version 0.25 (2010-07-11)
* Fix [Bug 541889](https://bugs.launchpad.net/scour/+bug/541889) to parse polygon/polyline points missing whitespace/comma separating a negative value. Always output points attributes as comma-separated.
* Fix [Bug 519698](https://bugs.launchpad.net/scour/+bug/519698) to properly parse move commands that have line segments.
* Fix [Bug 577940](https://bugs.launchpad.net/scour/+bug/577940) to include stroke-dasharray into list of style properties turned into XML attributes.
* Fix [Bug 562784](https://bugs.launchpad.net/scour/+bug/562784), typo in Inkscape description
* Fix [Bug 603988](https://bugs.launchpad.net/scour/+bug/603988), do not commonize attributes if the element is referenced elsewhere.
* Fix [Bug 604000](https://bugs.launchpad.net/scour/+bug/604000), correctly remove default overflow attributes.
* Fix [Bug 603994](https://bugs.launchpad.net/scour/+bug/603994), fix parsing of `<style>` element contents when a CDATA is present
* Fix [Bug 583758](https://bugs.launchpad.net/scour/+bug/583758), added a bit to the Inkscape help text saying that groups aren't collapsed if IDs are also not stripped.
* Fix [Bug 583458](https://bugs.launchpad.net/scour/+bug/583458), another typo in the Inkscape help tab.
* Fix [Bug 594930](https://bugs.launchpad.net/scour/+bug/594930), In a `<switch>`, require one level of `<g>` if there was a `<g>` in the file already. Otherwise, only the first subelement of the `<g>` is chosen and rendered.
* Fix [Bug 576958](https://bugs.launchpad.net/scour/+bug/576958), "Viewbox option doesn't work when units are set", when renderer workarounds are disabled.
* Added many options: `--remove-metadata`, `--quiet`, `--enable-comment-stripping`, `--shorten-ids`, `--renderer-workaround`.
## Version 0.24 (2010-02-05)
* Fix [Bug 517064](https://bugs.launchpad.net/scour/+bug/517064) to make XML well-formed again
* Fix [Bug 503750](https://bugs.launchpad.net/scour/+bug/503750) fix Inkscape extension to correctly pass `--enable-viewboxing`
* Fix [Bug 511186](https://bugs.launchpad.net/scour/+bug/511186) to allow comments outside of the root `<svg>` node
## Version 0.23 (2010-01-04)
* Fix [Bug 482215](https://bugs.launchpad.net/scour/+bug/482215) by using os.linesep to end lines
* Fix unittests to run properly in Windows
* Removed default scaling of image to 100%/100% and creating a viewBox. Added `--enable-viewboxing` option to explicitly turn that on
* Fix [Bug 503034](https://bugs.launchpad.net/scour/+bug/503034) by only removing children of a group if the group itself has not been referenced anywhere else in the file
## Version 0.22 (2009-11-09)
* Fix [Bug 449803](https://bugs.launchpad.net/scour/+bug/449803) by ensuring input and output filenames differ.
* Fix [Bug 453737](https://bugs.launchpad.net/scour/+bug/453737) by updated Inkscape's scour extension with a UI
* Fix whitespace collapsing on non-textual elements that had xml:space="preserve"
* Fix [Bug 479669](https://bugs.launchpad.net/scour/+bug/479669) to handle empty `<style>` elements.
## Version 0.21 (2009-09-27)
* Fix [Bug 427309](https://bugs.launchpad.net/scour/+bug/427309) by updated Scour inkscape extension file to include yocto_css.py
* Fix [Bug 435689](https://bugs.launchpad.net/scour/+bug/435689) by properly preserving whitespace in XML serialization
* Fix [Bug 436569](https://bugs.launchpad.net/scour/+bug/436569) by getting `xlink:href` prefix correct with invalid SVG
## Version 0.20 (2009-08-31)
* Fix [Bug 368716](https://bugs.launchpad.net/scour/+bug/368716) by implementing a really tiny CSS parser to find out if any style element have rules referencing gradients, filters, etc
* Remove unused attributes from parent elements
* Fix a bug with polygon/polyline point parsing if there was whitespace at the end
## Version 0.19 (2009-08-13)
* Fix XML serialization bug: `xmlns:XXX` prefixes not preserved when not in default namespace
* Fix XML serialization bug: remapping to default namespace was not actually removing the old prefix
* Move common attributes to ancestor elements
* Fix [Bug 412754](https://bugs.launchpad.net/scour/+bug/401628): Elliptical arc commands must have comma/whitespace separating the coordinates
* Scour lengths for svg x,y,width,height,*opacity,stroke-width,stroke-miterlimit
## Version 0.18 (2009-08-09)
* Remove attributes of gradients if they contain default values
* Reduce bezier/quadratic (c/q) segments to their shorthand equivalents (s/t)
* Move to a custom XML serialization such that `id`/`xml:id` is printed first (Thanks to Richard Hutch for the suggestion)
* Added `--indent` option to specify indentation type (default='space', other options: 'none', 'tab')
## Version 0.17 (2009-08-03)
* Only convert to #RRGGBB format if the color name will actually be shorter
* Remove duplicate gradients
* Remove empty q,a path segments
* Scour polyline coordinates just like path/polygon
* Scour lengths from most attributes
* Remove redundant SVG namespace declarations and prefixes
## Version 0.16 (2009-07-30)
* Fix [Bug 401628](https://bugs.launchpad.net/scour/+bug/401628): Keep namespace declarations when using `--keep-editor-data` (Thanks YoNoSoyTu!)
* Remove trailing zeros after decimal places for all path coordinates
* Use scientific notation in path coordinates if that representation is shorter
* Scour polygon coordinates just like path coordinates
* Add XML prolog to scour output to ensure valid XML, added `--strip-xml-prolog` option
## Version 0.15 (2009-07-05)
* added `--keep-editor-data` command-line option
* Fix [Bug 395645](https://bugs.launchpad.net/scour/+bug/395645): Keep all identified children inside a defs (Thanks Frederik!)
* Fix [Bug 395647](https://bugs.launchpad.net/scour/+bug/395647): Do not remove closepath (Z) path segments
## Version 0.14 (2009-06-10)
* Collapse adjacent commands of the same type
* Convert straight curves into line commands
* Eliminate last segment in a polygon
* Rework command-line argument parsing
* Fix bug in embedRasters() caused by new command-line parsing
* added `--disable-embed-rasters` command-line option
## Version 0.13 (2009-05-19)
* properly deal with `fill="url(&quot;#foo&quot;)"`
* properly handle paths with more than 1 pair of coordinates in the first Move command
* remove font/text styles from shape elements (font-weight, font-size, line-height, etc)
* remove -inkscape-font-specification styles
* added `--set-precision` argument to set the number of significant digits (defaults to 5 now)
* collapse consecutive h,v coords/segments that go in the same direction
## Version 0.12 (2009-05-17)
* upgraded enthought's path parser to handle scientific notation in path coordinates
* convert colors to #RRGGBB format
* added option to disable color conversion
## Version 0.11 (2009-04-28)
* convert gradient stop offsets from percentages to float
* convert gradient stop offsets to integers if possible (0 or 1)
* fix bug in line-to-hv conversion
* handle non-ASCII characters (Unicode)
* remove empty line or curve segments from path
* added option to prevent style-to-xml conversion
* handle compressed svg (svgz) on the input and output
* added total time taken to the report
* Removed XML pretty printing because of [this problem](http://ronrothman.com/public/leftbraned/xml-dom-minidom-toprettyxml-and-silly-whitespace/).
## Version 0.10 (2009-04-27)
* Remove path with empty d attributes
* Sanitize path data (remove unnecessary whitespace)
* Convert from absolute to relative path data
* Remove trailing zeroes from path data
* Limit to no more than 6 digits of precision
* Remove empty line segments
* Convert lines to horiz/vertical line segments where possible
* Remove some more default styles (`display:none`, `visibility:visible`, `overflow:visible`,
`marker:none`)
## Version 0.09 (2009-04-25)
* Fix bug when removing stroke styles
* Remove gradients that are only referenced by one other gradient
* Added option to prevent group collapsing
* Prevent groups with title/desc children from being collapsed
* Remove stroke="none"
## Version 0.08 (2009-04-22)
* Remove unnecessary nested `<g>` elements
* Remove duplicate gradient stops (same offset, stop-color, stop-opacity)
* Always keep fonts inside `<defs>`, always keep ids on fonts
* made ID stripping optional (disabled by default)
## Version 0.07 (2009-04-15)
* moved all functionality into a module level function named 'scour' and began adding unit tests
* prevent metadata from being removed if they contain only text nodes
* Remove unreferenced pattern and gradient elements outside of defs
* Removal of extra whitespace, pretty printing of XML
## Version 0.06 (2009-04-13)
* Prevent error when stroke-width property value has a unit
* Convert width/height into a viewBox where possible
* Convert all referenced rasters into base64 encoded URLs if the files can be found
## Version 0.05 (2009-04-07)
* Removes unreferenced elements in a `<defs>`
* Removes all inkscape, sodipodi, adobe elements
* Removes all inkscape, sodipodi, adobe attributes
* Remove all unused namespace declarations on the document element
* Removes any empty `<defs>`, `<metadata>`, or `<g>` elements
* Style fix-ups:
* Fixes any style properties like this: `style="fill: url(#linearGradient1000) rgb(0, 0, 0);"`
* Removes any style property of: `opacity: 1;`
* Removes any stroke properties when `stroke=none` or `stroke-opacity=0` or `stroke-width=0`
* Removes any fill properties when `fill=none` or `fill-opacity=0`
* Removes all fill/stroke properties when `opacity=0`
* Removes any `stop-opacity: 1`
* Removes any `fill-opacity: 1`
* Removes any `stroke-opacity: 1`
* Convert style properties into SVG attributes

View file

@ -1,15 +1,39 @@
all: clean install
install:
python setup.py install
python3 setup.py install
clean:
rm -rf build
rm -rf dist
rm -rf scour.egg-info
rm -rf .tox
rm -f .coverage*
rm -rf htmlcov
find . -name "*.pyc" -type f -exec rm -f {} \;
find . -name "*__pycache__" -type d -prune -exec rm -rf {} \;
publish: clean
python setup.py register
python setup.py sdist upload
python setup.py bdist_egg upload
python setup.py bdist_wininst upload
python3 setup.py register
python3 setup.py sdist upload
check: test flake8
test:
python3 test_scour.py
test_version:
PYTHONPATH=. python3 -m scour.scour --version
test_help:
PYTHONPATH=. python3 -m scour.scour --help
flake8:
flake8 --max-line-length=119
coverage:
coverage run --source=scour test_scour.py
coverage html
coverage report

View file

@ -1,52 +1,72 @@
# Scour
Scour is a Python module that takes an input SVG and outputs a cleaner,
more concise SVG file. The goal is that authors will use this script after
editing the file in a GUI editor such as Inkscape or Adobe Illustrator.
[![PyPI](https://img.shields.io/pypi/v/scour.svg)](https://pypi.python.org/pypi/scour "Package listing on PyPI")
[![Build status](https://img.shields.io/travis/scour-project/scour.svg)](https://travis-ci.org/scour-project/scour "Build status (via TravisCI)")
[![Codecov](https://img.shields.io/codecov/c/github/scour-project/scour.svg)](https://codecov.io/gh/scour-project/scour "Code coverage (via Codecov)")
Scour was started as a vehicle for me to learn Python. In addition, the goal
is to reduce the amount of time I spend in cleaning up files I find on sites
such as openclipart.org
---
Ideas are pulled from three places:
Scour is an SVG optimizer/cleaner written in Python that reduces the size of scalable vector graphics by optimizing structure and removing unnecessary data.
* my head
* Sam Ruby's SVG Tidy script: http://intertwingly.net/code/svgtidy/svgtidy.rb
* Inkscape's proposal for a 'cleaned SVG': http://wiki.inkscape.org/wiki/index.php/Save_Cleaned_SVG
It can be used to create streamlined vector graphics suitable for web deployment, publishing/sharing or further processing.
Regards,
The goal of Scour is to output a file that renders identically at a fraction of the size by removing a lot of redundant information created by most SVG editors. Optimization options are typically lossless but can be tweaked for more aggressive cleaning.
Jeff Schiller, 2009-04-06
Scour is open-source and licensed under [Apache License 2.0](https://github.com/codedread/scour/blob/master/LICENSE).
codedread@gmail.com
Scour was originally developed by Jeff "codedread" Schiller and Louis Simard in in 2010.
The project moved to GitLab in 2013 an is now maintained by Tobias "oberstet" Oberstein and Patrick "Ede_123" Storz.
http://blog.codedread.com/
This fork was created by Alexander Olsson ([alex@aleon.se](mailto:alex@aleon.se?subject=Scour)) at Aleon Apps.
http://www.codedread.com/scour/
## Installation
Scour requires [Python](https://www.python.org) 2.7 or 3.4+. Further, for installation, [pip](https://pip.pypa.io) should be used.
To install this fork:
```console
sudo make
```
## Extension
Place the modified extension files in the Inkscape extension directory
```console
sudo cp extension/* /usr/share/inkscape/extensions/
```
## Usage
Standard:
scour -i mysvg.svg -o mysvg_opt.svg
```console
scour -i input.svg -o output.svg
```
Better (this works in IE which needs Viewbox):
Better (for older versions of Internet Explorer):
scour -i mysvg.svg -o mysvg_opt.svg --enable-viewboxing
```console
scour -i input.svg -o output.svg --enable-viewboxing
```
Maximum:
Maximum scrubbing:
scour -i mysvg.svg -o mysvg_opt.svg --enable-viewboxing --enable-id-stripping \
--enable-comment-stripping --shorten-ids --indent=none
```console
scour -i input.svg -o output.svg --enable-viewboxing --enable-id-stripping \
--enable-comment-stripping --shorten-ids --indent=none
```
Maximum + Compress:
Maximum scrubbing and a compressed SVGZ file:
scour -i mysvg.svg -o mysvg_opt.svgz --enable-viewboxing --enable-id-stripping \
--enable-comment-stripping --shorten-ids --indent=none
```console
scour -i input.svg -o output.svgz --enable-viewboxing --enable-id-stripping \
--enable-comment-stripping --shorten-ids --indent=none
```
## Notes
Remove scientific notation from path data:
Packaging from [sources](http://www.codedread.com/scour/) retrieved on 2013/20/22:
```console
scour -i input.svg -o output.svgz --nonsci-output
```
* done by Tavendo GmbH, Tobias Oberstein
* license same as upstream (Apache 2.0)

132
extension/output_scour.inx Normal file
View file

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Optimized SVG Output</name>
<id>org.inkscape.output.scour_inkscape</id>
<param name="tab" type="notebook">
<page name="Options" gui-text="Options">
<param gui-text="Number of significant digits for coordinates:"
gui-description="Specifies the number of significant digits that should be output for coordinates. Note that significant digits are *not* the number of decimals but the overall number of digits in the output. For example if a value of &quot;3&quot; is specified, the coordinate 3.14159 is output as 3.14 while the coordinate 123.675 is output as 124."
name="set-precision" type="int" min="1">5</param>
<spacer/>
<param gui-text="Shorten color values"
gui-description="Convert all color specifications to #RRGGBB (or #RGB where applicable) format."
name="simplify-colors" type="bool">true</param>
<param gui-text="Convert CSS attributes to XML attributes"
gui-description="Convert styles from style tags and inline style=&quot;&quot; declarations into XML attributes."
name="style-to-xml" type="bool">true</param>
<spacer/>
<param gui-text="Collapse groups"
gui-description="Remove useless groups, promoting their contents up one level. Requires &quot;Remove unused IDs&quot; to be set."
name="group-collapsing" type="bool">true</param>
<param gui-text="Create groups for similar attributes"
gui-description="Create groups for runs of elements having at least one attribute in common (e.g. fill-color, stroke-opacity, ...)."
name="create-groups" type="bool">true</param>
<spacer/>
<param gui-text="Keep editor data"
gui-description="Don't remove editor-specific elements and attributes. Currently supported: Inkscape, Sodipodi and Adobe Illustrator."
name="keep-editor-data" type="bool">false</param>
<param gui-text="Remove scientific notation"
gui-description="Remove scientific notation from path data."
name="nonsci-output" type="bool">false</param>
<param gui-text="Keep unreferenced definitions"
gui-description="Keep element definitions that are not currently used in the SVG"
name="keep-unreferenced-defs" type="bool">false</param>
<spacer/>
<param gui-text="Work around renderer bugs"
gui-description="Works around some common renderer bugs (mainly libRSVG) at the cost of a slightly larger SVG file."
name="renderer-workaround" type="bool">true</param>
</page>
<page name="Output" gui-text="SVG Output">
<label appearance="header">Document options</label>
<param gui-text="Remove the XML declaration"
gui-description="Removes the XML declaration (which is optional but should be provided, especially if special characters are used in the document) from the file header."
name="strip-xml-prolog" type="bool">false</param>
<param gui-text="Remove metadata"
gui-description="Remove metadata tags along with all the contained information, which may include license and author information, alternate versions for non-SVG-enabled browsers, etc."
name="remove-metadata" type="bool">false</param>
<param gui-text="Remove comments"
gui-description="Remove all XML comments from output."
name="enable-comment-stripping" type="bool">false</param>
<param gui-text="Embed raster images"
gui-description="Resolve external references to raster images and embed them as Base64-encoded data URLs."
name="embed-rasters" type="bool">true</param>
<param gui-text="Enable viewboxing"
gui-description="Set page size to 100%/100% (full width and height of the display area) and introduce a viewBox specifying the drawings dimensions."
name="enable-viewboxing" type="bool">false</param>
<spacer/>
<label appearance="header">Pretty-printing</label>
<param gui-text="Format output with line-breaks and indentation"
gui-description="Produce nicely formatted output including line-breaks. If you do not intend to hand-edit the SVG file you can disable this option to bring down the file size even more at the cost of clarity."
name="line-breaks" type="bool">true</param>
<param gui-text="Indentation characters:"
gui-description="The type of indentation used for each level of nesting in the output. Specify &quot;None&quot; to disable indentation. This option has no effect if &quot;Format output with line-breaks and indentation&quot; is disabled."
name="indent" type="optiongroup" appearance="combo">
<option value="space">Space</option>
<option value="tab">Tab</option>
<option context="Indent" value="none">None</option>
</param>
<param gui-text="Depth of indentation:"
gui-description="The depth of the chosen type of indentation. E.g. if you choose &quot;2&quot; every nesting level in the output will be indented by two additional spaces/tabs."
name="nindent" type="int">1</param>
<param gui-text="Strip the &quot;xml:space&quot; attribute from the root SVG element"
gui-description="This is useful if the input file specifies &quot;xml:space='preserve'&quot; in the root SVG element which instructs the SVG editor not to change whitespace in the document at all (and therefore overrides the options above)."
name="strip-xml-space" type="bool">false</param>
</page>
<page name="IDs" gui-text="IDs">
<param gui-text="Remove unused IDs"
gui-description="Remove all unreferenced IDs from elements. Those are not needed for rendering."
name="enable-id-stripping" type="bool">true</param>
<spacer/>
<param gui-text="Shorten IDs"
gui-description="Minimize the length of IDs using only lowercase letters, assigning the shortest values to the most-referenced elements. For instance, &quot;linearGradient5621&quot; will become &quot;a&quot; if it is the most used element."
name="shorten-ids" type="bool">false</param>
<param gui-text="Prefix shortened IDs with:"
gui-description="Prepend shortened IDs with the specified prefix."
name="shorten-ids-prefix" type="string"></param>
<spacer/>
<param gui-text="Preserve manually created IDs not ending with digits"
gui-description="Descriptive IDs which were manually created to reference or label specific elements or groups (e.g. #arrowStart, #arrowEnd or #textLabels) will be preserved while numbered IDs (as they are generated by most SVG editors including Inkscape) will be removed/shortened."
name="protect-ids-noninkscape" type="bool">true</param>
<param gui-text="Preserve the following IDs:"
gui-description="A comma-separated list of IDs that are to be preserved."
name="protect-ids-list" type="string"></param>
<param gui-text="Preserve IDs starting with:"
gui-description="Preserve all IDs that start with the specified prefix (e.g. specify &quot;flag&quot; to preserve &quot;flag-mx&quot;, &quot;flag-pt&quot;, etc.)."
name="protect-ids-prefix" type="string"></param>
</page>
<page name="About" gui-text="About">
<hbox>
<image>output_scour.svg</image>
<spacer/>
<vbox>
<spacer/>
<label>Optimized SVG Output is provided by</label>
<label appearance="header" indent="1">Scour - An SVG Scrubber</label>
<spacer/>
<label>For details please refer to</label>
<label appearance="url" indent="1">https://github.com/scour-project/scour</label>
</vbox>
</hbox>
<spacer size="expand"/>
<hbox>
<label>This version of the extension is designed for</label>
<label>Scour 0.31+</label>
</hbox>
<param name="scour-version" type="string" gui-hidden="true">0.31</param> <!-- this parameter is checked programmatically in the extension to show a warning -->
<param gui-text="Show warnings for older versions of Scour"
name="scour-version-warn-old" type="bool">true</param>
</page>
</param>
<output>
<extension>.svg</extension>
<mimetype>image/svg+xml</mimetype>
<filetypename>Optimized SVG (*.svg)</filetypename>
<filetypetooltip>Scalable Vector Graphics</filetypetooltip>
</output>
<script>
<command location="inx" interpreter="python">output_scour.py</command>
</script>
</inkscape-extension>

100
extension/output_scour.py Normal file
View file

@ -0,0 +1,100 @@
#!/usr/bin/env python
"""
Run the scour module on the svg output.
"""
import inkex
from inkex.localization import inkex_gettext as _
try:
from packaging.version import Version
except ImportError:
raise inkex.DependencyError(
_(
"""Failed to import module 'packaging'.
Please make sure it is installed (e.g. using 'pip install packaging'
or 'sudo apt-get install python3-packaging') and try again.
"""
)
)
try:
import scour
from scour.scour import scourString
except ImportError:
raise inkex.DependencyError(
_(
"""Failed to import module 'scour'.
Please make sure it is installed (e.g. using 'pip install scour'
or 'sudo apt-get install python3-scour') and try again.
"""
)
)
class ScourInkscape(inkex.OutputExtension):
"""Scour Inkscape Extension"""
# Scour options
def add_arguments(self, pars):
pars.add_argument("--tab")
pars.add_argument("--simplify-colors", type=inkex.Boolean, dest="simple_colors")
pars.add_argument("--style-to-xml", type=inkex.Boolean)
pars.add_argument(
"--group-collapsing", type=inkex.Boolean, dest="group_collapse"
)
pars.add_argument("--create-groups", type=inkex.Boolean, dest="group_create")
pars.add_argument("--enable-id-stripping", type=inkex.Boolean, dest="strip_ids")
pars.add_argument("--shorten-ids", type=inkex.Boolean)
pars.add_argument("--shorten-ids-prefix")
pars.add_argument("--embed-rasters", type=inkex.Boolean)
pars.add_argument(
"--keep-unreferenced-defs", type=inkex.Boolean, dest="keep_defs"
)
pars.add_argument("--keep-editor-data", type=inkex.Boolean)
pars.add_argument("--nonsci-output", type=inkex.Boolean)
pars.add_argument("--remove-metadata", type=inkex.Boolean)
pars.add_argument("--strip-xml-prolog", type=inkex.Boolean)
pars.add_argument("--set-precision", type=int, dest="digits")
pars.add_argument("--indent", dest="indent_type")
pars.add_argument("--nindent", type=int, dest="indent_depth")
pars.add_argument("--line-breaks", type=inkex.Boolean, dest="newlines")
pars.add_argument(
"--strip-xml-space", type=inkex.Boolean, dest="strip_xml_space_attribute"
)
pars.add_argument("--protect-ids-noninkscape", type=inkex.Boolean)
pars.add_argument("--protect-ids-list")
pars.add_argument("--protect-ids-prefix")
pars.add_argument("--enable-viewboxing", type=inkex.Boolean)
pars.add_argument(
"--enable-comment-stripping", type=inkex.Boolean, dest="strip_comments"
)
pars.add_argument("--renderer-workaround", type=inkex.Boolean)
# options for internal use of the extension
pars.add_argument("--scour-version")
pars.add_argument("--scour-version-warn-old", type=inkex.Boolean)
def save(self, stream):
# version check if enabled in options
if self.options.scour_version_warn_old:
scour_version = scour.__version__
scour_version_min = self.options.scour_version
if Version(scour_version) < Version(scour_version_min):
raise inkex.AbortExtension(
f"""
The extension 'Optimized SVG Output' is designed for Scour {scour_version_min} or later but you're
using the older version Scour {scour_version}.
Note: You can permanently disable this message on the 'About' tab of the extension window."""
)
del self.options.scour_version
del self.options.scour_version_warn_old
# do the scouring
stream.write(scourString(self.svg.tostring(), self.options).encode("utf8"))
if __name__ == "__main__":
ScourInkscape().run()

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -1,22 +1,19 @@
###############################################################################
##
## Copyright (C) 2013 Tavendo GmbH
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
#
# Copyright (C) 2010 Jeff Schiller, 2010 Louis Simard, 2013-2015 Tavendo GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###############################################################################
import scour
import svg_regex
import svg_transform
import yocto_css
__version__ = u'0.38.2'

File diff suppressed because it is too large Load diff

28
scour/stats.py Normal file
View file

@ -0,0 +1,28 @@
class ScourStats(object):
__slots__ = (
'num_elements_removed',
'num_attributes_removed',
'num_style_properties_fixed',
'num_bytes_saved_in_colors',
'num_ids_removed',
'num_comments_removed',
'num_style_properties_fixed',
'num_rasters_embedded',
'num_path_segments_removed',
'num_points_removed_from_polygon',
'num_bytes_saved_in_path_data',
'num_bytes_saved_in_colors',
'num_bytes_saved_in_comments',
'num_bytes_saved_in_ids',
'num_bytes_saved_in_lengths',
'num_bytes_saved_in_transforms',
)
def __init__(self):
self.reset()
def reset(self):
# Set all stats to 0
for attr in self.__slots__:
setattr(self, attr, 0)

View file

@ -41,15 +41,22 @@ Out[4]: [('M', [(0.60509999999999997, 0.5)])]
In [5]: svg_parser.parse('M 100-200') # Another edge case
Out[5]: [('M', [(100.0, -200.0)])]
"""
from __future__ import absolute_import
import re
from decimal import *
from decimal import Decimal, getcontext
from functools import partial
# Sentinel.
class _EOF(object):
def __repr__(self):
return 'EOF'
EOF = _EOF()
lexicon = [
@ -69,6 +76,7 @@ class Lexer(object):
http://www.gooli.org/blog/a-simple-lexer-in-python/
"""
def __init__(self, lexicon):
self.lexicon = lexicon
parts = []
@ -91,6 +99,7 @@ class Lexer(object):
break
yield (EOF, None)
svg_lexer = Lexer(lexicon)
@ -145,140 +154,148 @@ class SVGPathParser(object):
def parse(self, text):
""" Parse a string of SVG <path> data.
"""
next = self.lexer.lex(text).next
token = next()
return self.rule_svg_path(next, token)
gen = self.lexer.lex(text)
next_val_fn = partial(next, *(gen,))
token = next_val_fn()
return self.rule_svg_path(next_val_fn, token)
def rule_svg_path(self, next, token):
def rule_svg_path(self, next_val_fn, token):
commands = []
while token[0] is not EOF:
if token[0] != 'command':
raise SyntaxError("expecting a command; got %r" % (token,))
rule = self.command_dispatch[token[1]]
command_group, token = rule(next, token)
command_group, token = rule(next_val_fn, token)
commands.append(command_group)
return commands
def rule_closepath(self, next, token):
def rule_closepath(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
return (command, []), token
def rule_moveto_or_lineto(self, next, token):
def rule_moveto_or_lineto(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
coordinates = []
while token[0] in self.number_tokens:
pair, token = self.rule_coordinate_pair(next, token)
pair, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair)
return (command, coordinates), token
def rule_orthogonal_lineto(self, next, token):
def rule_orthogonal_lineto(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
coordinates = []
while token[0] in self.number_tokens:
coord, token = self.rule_coordinate(next, token)
coord, token = self.rule_coordinate(next_val_fn, token)
coordinates.append(coord)
return (command, coordinates), token
def rule_curveto3(self, next, token):
def rule_curveto3(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
coordinates = []
while token[0] in self.number_tokens:
pair1, token = self.rule_coordinate_pair(next, token)
pair2, token = self.rule_coordinate_pair(next, token)
pair3, token = self.rule_coordinate_pair(next, token)
pair1, token = self.rule_coordinate_pair(next_val_fn, token)
pair2, token = self.rule_coordinate_pair(next_val_fn, token)
pair3, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair1)
coordinates.extend(pair2)
coordinates.extend(pair3)
return (command, coordinates), token
def rule_curveto2(self, next, token):
def rule_curveto2(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
coordinates = []
while token[0] in self.number_tokens:
pair1, token = self.rule_coordinate_pair(next, token)
pair2, token = self.rule_coordinate_pair(next, token)
pair1, token = self.rule_coordinate_pair(next_val_fn, token)
pair2, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair1)
coordinates.extend(pair2)
return (command, coordinates), token
def rule_curveto1(self, next, token):
def rule_curveto1(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
coordinates = []
while token[0] in self.number_tokens:
pair1, token = self.rule_coordinate_pair(next, token)
pair1, token = self.rule_coordinate_pair(next_val_fn, token)
coordinates.extend(pair1)
return (command, coordinates), token
def rule_elliptical_arc(self, next, token):
def rule_elliptical_arc(self, next_val_fn, token):
command = token[1]
token = next()
token = next_val_fn()
arguments = []
while token[0] in self.number_tokens:
rx = Decimal(token[1]) * 1
if rx < Decimal("0.0"):
raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
token = next()
token = next_val_fn()
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
ry = Decimal(token[1]) * 1
if ry < Decimal("0.0"):
raise SyntaxError("expecting a nonnegative number; got %r" % (token,))
token = next()
token = next_val_fn()
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
axis_rotation = Decimal(token[1]) * 1
token = next()
if token[1] not in ('0', '1'):
token = next_val_fn()
if token[1][0] not in ('0', '1'):
raise SyntaxError("expecting a boolean flag; got %r" % (token,))
large_arc_flag = Decimal(token[1]) * 1
large_arc_flag = Decimal(token[1][0]) * 1
token = next()
if token[1] not in ('0', '1'):
if len(token[1]) > 1:
token = list(token)
token[1] = token[1][1:]
else:
token = next_val_fn()
if token[1][0] not in ('0', '1'):
raise SyntaxError("expecting a boolean flag; got %r" % (token,))
sweep_flag = Decimal(token[1]) * 1
sweep_flag = Decimal(token[1][0]) * 1
token = next()
if len(token[1]) > 1:
token = list(token)
token[1] = token[1][1:]
else:
token = next_val_fn()
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
x = Decimal(token[1]) * 1
token = next()
token = next_val_fn()
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
y = Decimal(token[1]) * 1
token = next()
token = next_val_fn()
arguments.extend([rx, ry, axis_rotation, large_arc_flag, sweep_flag, x, y])
return (command, arguments), token
def rule_coordinate(self, next, token):
def rule_coordinate(self, next_val_fn, token):
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
x = getcontext().create_decimal(token[1])
token = next()
token = next_val_fn()
return x, token
def rule_coordinate_pair(self, next, token):
def rule_coordinate_pair(self, next_val_fn, token):
# Inline these since this rule is so common.
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
x = getcontext().create_decimal(token[1])
token = next()
token = next_val_fn()
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
y = getcontext().create_decimal(token[1])
token = next()
token = next_val_fn()
return [x, y], token

View file

@ -56,15 +56,22 @@ Multiple transformations are supported:
In [12]: svg_transform_parser.parse('translate(30 -30) rotate(36)')
Out[12]: [('translate', [30.0, -30.0]), ('rotate', [36.0])]
"""
from __future__ import absolute_import
import re
from decimal import *
from decimal import Decimal
from functools import partial
from six.moves import range
# Sentinel.
class _EOF(object):
def __repr__(self):
return 'EOF'
EOF = _EOF()
lexicon = [
@ -86,6 +93,7 @@ class Lexer(object):
http://www.gooli.org/blog/a-simple-lexer-in-python/
"""
def __init__(self, lexicon):
self.lexicon = lexicon
parts = []
@ -108,6 +116,7 @@ class Lexer(object):
break
yield (EOF, None)
svg_lexer = Lexer(lexicon)
@ -145,88 +154,90 @@ class SVGTransformationParser(object):
def parse(self, text):
""" Parse a string of SVG transform="" data.
"""
next = self.lexer.lex(text).next
gen = self.lexer.lex(text)
next_val_fn = partial(next, *(gen,))
commands = []
token = next()
token = next_val_fn()
while token[0] is not EOF:
command, token = self.rule_svg_transform(next, token)
commands.append(command)
command, token = self.rule_svg_transform(next_val_fn, token)
commands.append(command)
return commands
def rule_svg_transform(self, next, token):
def rule_svg_transform(self, next_val_fn, token):
if token[0] != 'command':
raise SyntaxError("expecting a transformation type; got %r" % (token,))
command = token[1]
rule = self.command_dispatch[command]
token = next()
token = next_val_fn()
if token[0] != 'coordstart':
raise SyntaxError("expecting '('; got %r" % (token,))
numbers, token = rule(next, token)
numbers, token = rule(next_val_fn, token)
if token[0] != 'coordend':
raise SyntaxError("expecting ')'; got %r" % (token,))
token = next()
token = next_val_fn()
return (command, numbers), token
def rule_1or2numbers(self, next, token):
def rule_1or2numbers(self, next_val_fn, token):
numbers = []
# 1st number is mandatory
token = next()
number, token = self.rule_number(next, token)
token = next_val_fn()
number, token = self.rule_number(next_val_fn, token)
numbers.append(number)
# 2nd number is optional
number, token = self.rule_optional_number(next, token)
number, token = self.rule_optional_number(next_val_fn, token)
if number is not None:
numbers.append(number)
return numbers, token
def rule_1number(self, next, token):
def rule_1number(self, next_val_fn, token):
# this number is mandatory
token = next()
number, token = self.rule_number(next, token)
token = next_val_fn()
number, token = self.rule_number(next_val_fn, token)
numbers = [number]
return numbers, token
def rule_1or3numbers(self, next, token):
def rule_1or3numbers(self, next_val_fn, token):
numbers = []
# 1st number is mandatory
token = next()
number, token = self.rule_number(next, token)
token = next_val_fn()
number, token = self.rule_number(next_val_fn, token)
numbers.append(number)
# 2nd number is optional
number, token = self.rule_optional_number(next, token)
number, token = self.rule_optional_number(next_val_fn, token)
if number is not None:
# but, if the 2nd number is provided, the 3rd is mandatory.
# we can't have just 2.
numbers.append(number)
number, token = self.rule_number(next, token)
number, token = self.rule_number(next_val_fn, token)
numbers.append(number)
return numbers, token
def rule_6numbers(self, next, token):
def rule_6numbers(self, next_val_fn, token):
numbers = []
token = next()
token = next_val_fn()
# all numbers are mandatory
for i in xrange(6):
number, token = self.rule_number(next, token)
for i in range(6):
number, token = self.rule_number(next_val_fn, token)
numbers.append(number)
return numbers, token
def rule_number(self, next, token):
def rule_number(self, next_val_fn, token):
if token[0] not in self.number_tokens:
raise SyntaxError("expecting a number; got %r" % (token,))
x = Decimal(token[1]) * 1
token = next()
token = next_val_fn()
return x, token
def rule_optional_number(self, next, token):
def rule_optional_number(self, next_val_fn, token):
if token[0] not in self.number_tokens:
return None, token
else:
x = Decimal(token[1]) * 1
token = next()
token = next_val_fn()
return x, token

View file

@ -48,25 +48,29 @@
# | DASHMATCH | FUNCTION S* any* ')'
# | '(' S* any* ')' | '[' S* any* ']' ] S*;
def parseCssString(str):
rules = []
# first, split on } to get the rule chunks
chunks = str.split('}')
for chunk in chunks:
# second, split on { to get the selector and the list of properties
bits = chunk.split('{')
if len(bits) != 2: continue
rule = {}
rule['selector'] = bits[0].strip()
# third, split on ; to get the property declarations
bites = bits[1].strip().split(';')
if len(bites) < 1: continue
props = {}
for bite in bites:
# fourth, split on : to get the property name and value
nibbles = bite.strip().split(':')
if len(nibbles) != 2: continue
props[nibbles[0].strip()] = nibbles[1].strip()
rule['properties'] = props
rules.append(rule)
return rules
rules = []
# first, split on } to get the rule chunks
chunks = str.split('}')
for chunk in chunks:
# second, split on { to get the selector and the list of properties
bits = chunk.split('{')
if len(bits) != 2:
continue
rule = {}
rule['selector'] = bits[0].strip()
# third, split on ; to get the property declarations
bites = bits[1].strip().split(';')
if len(bites) < 1:
continue
props = {}
for bite in bites:
# fourth, split on : to get the property name and value
nibbles = bite.strip().split(':')
if len(nibbles) != 2:
continue
props[nibbles[0].strip()] = nibbles[1].strip()
rule['properties'] = props
rules.append(rule)
return rules

128
setup.py
View file

@ -1,51 +1,87 @@
###############################################################################
##
## Copyright (C) 2013 Tavendo GmbH
##
## Licensed under the Apache License, Version 2.0 (the "License");
## you may not use this file except in compliance with the License.
## You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing, software
## distributed under the License is distributed on an "AS IS" BASIS,
## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
## See the License for the specific language governing permissions and
## limitations under the License.
##
#
# Copyright (C) 2013-2014 Tavendo GmbH
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###############################################################################
from setuptools import setup, find_packages
import os
import re
setup (
name = 'scour',
version = '0.26',
description = 'Scour SVG Optimizer',
long_description = open("README.md").read(),
license = 'Apache License 2.0',
author = 'Jeff Schiller',
author_email = 'codedread@gmail.com',
url = 'http://blog.codedread.com/',
platforms = ('Any'),
install_requires = [],
packages = find_packages(),
zip_safe = True,
entry_points = {
'console_scripts': [
'scour = scour.scour:run'
]},
classifiers = ["License :: OSI Approved :: Apache Software License",
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Internet",
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Pre-processors",
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
"Topic :: Utilities"],
keywords = 'svg optimizer'
from setuptools import find_packages, setup
LONGDESC = """
Scour is an SVG optimizer/cleaner that reduces the size of scalable
vector graphics by optimizing structure and removing unnecessary data.
It can be used to create streamlined vector graphics suitable for web
deployment, publishing/sharing or further processing.
The goal of Scour is to output a file that renders identically at a
fraction of the size by removing a lot of redundant information created
by most SVG editors. Optimization options are typically lossless but can
be tweaked for more aggressive cleaning.
Website
- http://www.codedread.com/scour/ (original website)
- https://github.com/scour-project/scour (today)
Authors:
- Jeff Schiller, Louis Simard (original authors)
- Tobias Oberstein (maintainer)
- Patrick Storz (maintainer)
"""
VERSIONFILE = os.path.join(os.path.dirname(os.path.realpath(__file__)), "scour", "__init__.py")
verstrline = open(VERSIONFILE, "rt").read()
VSRE = r"^__version__ = u['\"]([^'\"]*)['\"]"
mo = re.search(VSRE, verstrline, re.M)
if mo:
verstr = mo.group(1)
else:
raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
setup(
name='scour',
version=verstr,
description='Scour SVG Optimizer',
# long_description = open("README.md").read(),
long_description=LONGDESC,
license='Apache License 2.0',
author='Jeff Schiller',
author_email='codedread@gmail.com',
url='https://github.com/scour-project/scour',
platforms=('Any'),
install_requires=['six>=1.9.0'],
packages=find_packages(),
zip_safe=True,
entry_points={
'console_scripts': [
'scour = scour.scour:run'
]},
classifiers=["License :: OSI Approved :: Apache Software License",
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Topic :: Internet",
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Pre-processors",
"Topic :: Multimedia :: Graphics :: Graphics Conversion",
"Topic :: Utilities"],
keywords='svg optimizer'
)

57
test_css.py Executable file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Test Harness for Scour
#
# Copyright 2010 Jeff Schiller
#
# This file is part of Scour, http://www.codedread.com/scour/
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
import unittest
from scour.yocto_css import parseCssString
class Blank(unittest.TestCase):
def runTest(self):
r = parseCssString('')
self.assertEqual(len(r), 0, 'Blank string returned non-empty list')
self.assertEqual(type(r), type([]), 'Blank string returned non list')
class ElementSelector(unittest.TestCase):
def runTest(self):
r = parseCssString('foo {}')
self.assertEqual(len(r), 1, 'Element selector not returned')
self.assertEqual(r[0]['selector'], 'foo', 'Selector for foo not returned')
self.assertEqual(len(r[0]['properties']), 0, 'Property list for foo not empty')
class ElementSelectorWithProperty(unittest.TestCase):
def runTest(self):
r = parseCssString('foo { bar: baz}')
self.assertEqual(len(r), 1, 'Element selector not returned')
self.assertEqual(r[0]['selector'], 'foo', 'Selector for foo not returned')
self.assertEqual(len(r[0]['properties']), 1, 'Property list for foo did not have 1')
self.assertEqual(r[0]['properties']['bar'], 'baz', 'Property bar did not have baz value')
if __name__ == '__main__':
unittest.main()

2796
test_scour.py Executable file

File diff suppressed because it is too large Load diff

31
tox.ini Normal file
View file

@ -0,0 +1,31 @@
[tox]
envlist =
pypy
py27
py34
py35
py36
py37
py38
py39
py310
flake8
[testenv]
deps =
six
coverage
commands =
scour --version
coverage run --parallel-mode --source=scour test_scour.py
[testenv:flake8]
deps =
flake8
commands =
flake8 --max-line-length=119

45
unittests/adobe.svg Normal file
View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:x="http://ns.adobe.com/Extensibility/1.0/"
xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"
xmlns:graph="http://ns.adobe.com/Graphs/1.0/"
xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
xmlns:f="http://ns.adobe.com/Flows/1.0/"
xmlns:ir="http://ns.adobe.com/ImageReplacement/1.0/"
xmlns:custom="http://ns.adobe.com/GenericCustomNamespace/1.0/"
xmlns:xpath="http://ns.adobe.com/XPath/1.0/"
xmlns:ok="A.namespace.we.want.left.in"
i:viewOrigin="190.2959 599.1841" i:rulerOrigin="0 0" i:pageBounds="0 792 612 0">
<x:foo>bar</x:foo>
<i:foo>bar</i:foo>
<graph:foo>bar</graph:foo>
<a:foo>bar</a:foo>
<f:foo>bar</f:foo>
<ir:foo>bar</ir:foo>
<custom:foo>bar</custom:foo>
<xpath:foo>bar</xpath:foo>
<variableSets xmlns="http://ns.adobe.com/Variables/1.0/">
<variableSet varSetName="binding1" locked="none">
<variables/>
<v:sampleDataSets xmlns="http://ns.adobe.com/GenericCustomNamespace/1.0/" xmlns:v="http://ns.adobe.com/Variables/1.0/"/>
</variableSet>
</variableSets>
<sfw xmlns="http://ns.adobe.com/SaveForWeb/1.0/">
<slices/>
<sliceSourceBounds y="191.664" x="190.296" width="225.72" height="407.52" bottomLeftOrigin="true"/>
</sfw>
<rect width="300" height="200" fill="green"
x:baz="1"
i:baz="1"
graph:baz="1"
a:baz="1"
f:baz="1"
ir:baz="1"
custom:baz='1'
xpath:baz="1"
xmlns:v="http://ns.adobe.Variables/1.0/"
v:baz="1"
xmlns:sfw="http://ns.adobe.com/SaveForWeb/1.0/"
sfw:baz="1"
ok:baz="1" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path style="fill-rule:evenodd;stroke-linecap:butt;stroke-width:1.00;stroke:#000" d="m1,1z"/>
<path style="fill-rule:nonzero;stroke-linecap:butt;stroke:#000" d="m1,1z"/>
<g style="stroke:#f00;marker:none">
<path style="marker-start:none;fill-rule:evenodd;stroke-linecap:butt" d="m1,1z"/>
<path style="fill-rule:nonzero" d="m1,1z"/>
<g style="fill:#f0f;text-anchor:stop;fill-rule:evenodd;stroke-linecap:round;marker:url(#nirvana)">
<path style="marker-start:none;fill-rule:evenodd;stroke-linecap:butt" d="m1,1z"/>
<path style="color:#000;fill-rule:nonzero;" d="m1,1z"/>
<path d="m1,1z"/>
</g>
<g style="fill:#f0f;text-anchor:stop;fill-rule:evenodd;stroke-linecap:round;marker:url(#nirvana)">
<path style="marker-start:none;fill-rule:evenodd;stroke-linecap:butt" d="m1,1z"/>
<path style="color:#000;fill-rule:nonzero;" d="m1,1z"/>
</g>
<g style="text-anchor:stop;fill-rule:nonzero;marker:none;stroke-linecap:butt">
<path style="marker-start:none;fill-rule:evenodd;stroke-linecap:butt" d="m1,1z"/>
<path style="fill-rule:nonzero;" d="m1,1z"/>
<path d="m1,1z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

6
unittests/cdata.svg Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<script type="application/ecmascript"><![CDATA[
alert('pb&j');
]]></script>
</svg>

After

Width:  |  Height:  |  Size: 184 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="blue" />
<stop offset="1" stop-color="yellow" />
</linearGradient>
<radialGradient id="g2" xlink:href="#g1" cx="50%" cy="50%" r="30%" gradientUnits="objectBoundingBox"/>
</defs>
<rect fill="url(#g2)" width="200" height="200"/>
</svg>

After

Width:  |  Height:  |  Size: 507 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="blue" />
<stop offset="1" stop-color="yellow" />
</linearGradient>
<radialGradient id="g2" xlink:href="#g1" cx="100" cy="100" r="70"/>
<radialGradient id="g3" xlink:href="#g2" cx="100" cy="100" r="70"/>
</defs>
<rect fill="url(#g1)" width="200" height="200"/>
<rect fill="url(#g3)" width="200" height="200" y="200"/>
</svg>

After

Width:  |  Height:  |  Size: 599 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="grad1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" spreadMethod="reflect" gradientTransform="matrix(1,2,3,4,5,6)">
<stop offset="0" stop-color="blue" />
<stop offset="1" stop-color="yellow" />
</linearGradient>
<radialGradient id="grad2" xlink:href="#grad1" cx="100" cy="100" r="70"/>
</defs>
<rect fill="url(#grad2)" width="200" height="200"/>
</svg>

After

Width:  |  Height:  |  Size: 547 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="210" height="210">
<path stroke="yellow" fill="red" d="M100,100 L200.12345,200.12345 C215,205 185,195 200.12345,200.12345 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 281 B

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="dot">
<circle r="5px"/>
</marker>
</defs>
<!-- h/v commands should be collapsed into a single h/v commands -->
<path d="m0 0h10 20"/>
<path d="m0 0v10 20"/>
<path d="m0 0h10 0.5v10 0.5"/>
<!-- h/v commands should not be collapsed if they have different direction -->
<path d="m0 0h10 -1v10 -1"/>
<!-- h/v commands should also be collapsed if only start/end markers are present -->
<path d="m0 0h10 20" marker-start="url(#dot)" marker-end="url(#dot)"/>
<path d="m0 0h10 20" style="marker-start:url(#dot);marker-end:url(#dot)"/>
<!-- h/v commands should be preserved if intermediate markers are present -->
<path d="m0 0h10 20" marker="url(#dot)"/>
<path d="m0 0h10 20" marker-mid="url(#dot)"/>
<path d="m0 0h10 20" style="marker:url(#dot)"/>
<path d="m0 0h10 20" style="marker-mid:url(#dot)"/>
<!-- all consecutive lineto commands pointing into the sam direction
should be collapsed into a single (implicit if possible) lineto command -->
<path d="m 0 0 l 10 20 0.25 0.5 l 0.75 1.5 l 5 10 0.2 0.4 l 3 6 0.8 1.6 l 0 1 l 1 2 9 18"/>
<!-- must not be collapsed (same slope, but different direction) -->
<path d="m 0 0 10 10 -20 -20 l 10 10 -20 -20"/>
<!-- first parameter pair of a moveto subpath must not be collapsed as it's not drawn on canvas -->
<path d="m0 0 1 2 m 1 2 1 2l 1 2 m 1 2 1 2 1 2"/>
<!-- real world example of straight path with multiple nodes -->
<path d="m 6.3227953,7.1547422 10.6709787,5.9477588 9.20334,5.129731 22.977448,12.807101 30.447251,16.970601 7.898986,4.402712"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="0">
<stop offset="0.5" stop-color="rgb(50.0%, 0%, .0%)" />
</linearGradient>
<solidColor id="c1" solid-color="lightgoldenrodyellow"/>
</defs>
<rect id="rect" width="100" height="100" fill="rgb(15,16,17)" stroke="darkgrey" />
<circle id="circle" cx="100" cy="100" r="30" fill="url(#g1)" stroke="url(#c1)" />
<ellipse id="ellipse" cx="100" cy="100" rx="30" ry="30" style="fill:#ffffff" fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 589 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- Oh look a comment -->
<!-- generated by foobar version 20120503 -->
<!-- And another -->
<svg xmlns="http://www.w3.org/2000/svg">
<!-- This comment is meant to test whether removing a comment before <svg>
messes up removing comments thereafter -->
<!-- And this one is meant to test whether iteration works correctly in
<svg> as well as the document element -->
</svg>

After

Width:  |  Height:  |  Size: 448 B

6
unittests/comments.svg Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" ?>
<!-- Empty -->
<!-- Comment #2 -->
<svg xmlns="http://www.w3.org/2000/svg">
</svg>
<!-- After -->

After

Width:  |  Height:  |  Size: 121 B

View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="g">
<rect width="200" height="100" fill="#0f0"/>
<rect width="200" height="100" fill="#0f0"/>
<rect width="200" height="100" fill="#0f0"/>
<circle id="e" r="20" fill="#0f0"/>
</g>
<use xlink:href="#e" />
</svg>

After

Width:  |  Height:  |  Size: 324 B

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g1">
<stop offset="0" stop-color="red"/>
<stop offset="1" stop-color="blue"/>
</linearGradient>
<linearGradient id="g2">
<stop offset="0" stop-color="green"/>
<stop offset="1" stop-color="yellow"/>
</linearGradient>
</defs>
<style type="text/css"><![CDATA[
rect {
stroke: red;
stroke-width: 10;
fill:url(#g1)
}
]]></style>
<style type="text/css">.circ { fill: none; stroke: url("#g2"); stroke-width: 15 }</style>
<rect height="300" width="300"/>
<circle class="circ" cx="350" cy="350" r="40"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<title>This is a title element with only text node children</title>
<desc>This is a desc element with only text node children</desc>
<metadata>This is a metadata element with only text node children</metadata>
</svg>

After

Width:  |  Height:  |  Size: 319 B

7
unittests/doctype.svg Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- comment -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"/>

After

Width:  |  Height:  |  Size: 350 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="blue" />
<stop offset="1" stop-color="yellow" />
</linearGradient>
<radialGradient id="g2" xlink:href="#g1" cx="100" cy="100" r="70"/>
<radialGradient id="g3" xlink:href="#g1" cx="100" cy="100" r="70"/>
</defs>
<rect fill="url(#g2)" width="200" height="200"/>
<rect fill="url(#g3)" width="200" height="200" y="200"/>
</svg>

After

Width:  |  Height:  |  Size: 599 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<rect fill="red" width="100" height="100" />
</svg>

After

Width:  |  Height:  |  Size: 149 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="sea-gradient" x1="0" x2="0" y1="0" y2="1">
<stop offset="0%" stop-color="lightgrey"/>
<stop offset="0.5" stop-color="darkgrey"/>
<stop offset="50%" stop-color="darkgrey"/>
<stop offset="100%" stop-color="white"/>
</linearGradient>
</defs>
<rect style="fill: url(#sea-gradient) rgb(0, 0, 0);" y="0" x="0" height="100" width="100"/>
</svg>

After

Width:  |  Height:  |  Size: 497 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="lingrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="red" />
<stop offset="0.5" stop-color="blue" />
<stop offset="0.5" stop-color="blue" />
<stop offset="1.0" stop-color="green" />
</linearGradient>
<radialGradient id="radgrad">
<stop offset="0" stop-color="red" />
<stop offset="0.5" stop-color="blue" />
<stop offset="0.5" stop-color="blue" />
<stop offset="1.0" stop-color="green" />
</radialGradient>
</defs>
<rect width="300" height="300" fill="url(#lingrad)" />
<rect width="300" height="300" fill="url(#radgrad)" />
</svg>

After

Width:  |  Height:  |  Size: 740 B

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="duplicate-one" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="blue" />
<stop offset="1" stop-color="yellow" />
</linearGradient>
<linearGradient id="duplicate-two" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="blue" />
<stop offset="1" stop-color="yellow" />
</linearGradient>
</defs>
<rect style="fill: url(#duplicate-one)" width="200" height="200"/>
<rect style="fill: url(#duplicate-two)" width="200" height="200" y="200"/>
<rect style="fill: url(#duplicate-two) #fff" width="200" height="200" y="200"/>
</svg>

After

Width:  |  Height:  |  Size: 788 B

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg">
<title></title>
<desc></desc>
<metadata></metadata>
</svg>

After

Width:  |  Height:  |  Size: 106 B

7
unittests/empty-g.svg Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<g/>
<g transform="translate(10,10)">
<rect width="300" height="200" fill="green" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 203 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" ?>
<svg xmlns="http://www.w3.org/2000/svg">
<style id="style1" />
</svg>

After

Width:  |  Height:  |  Size: 97 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="ISO-8859-15" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<desc>áèîäöüߤ¦¨´¸¼½¾</desc>
</svg>

After

Width:  |  Height:  |  Size: 139 B

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<desc id="hello">Hello in many languages:
ar: أهلا
bn: হ্যালো
el: Χαίρετε
en: Hello
hi: नमस्ते
iw: שלום
ja: こんにちは
km: ជំរាបសួរ
ml: ഹലോ
ru: Здравствуйте
ur: ہیلو
zh: 您好</desc>
<desc id="common">“”—…°©®™•½¼¾⅓⅔†‡µ¢£€«»♠♣♥♦¿<EFBFBD></desc>
<desc id="math">:-×÷±∞π∅≤≥≠≈∧∨∩∪∈∀∃∄∑∏←↑→↓↔↕↖↗↘↙↺↻⇒⇔</desc>
<desc id="supersub">⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁽⁾ⁿⁱ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎</desc>
</svg>

After

Width:  |  Height:  |  Size: 731 B

8
unittests/entities.svg Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
a="'"
b='"'
c="''&quot;"
d='""&apos;'
e='&apos;&apos;""'
/>

After

Width:  |  Height:  |  Size: 144 B

5
unittests/fill-none.svg Normal file
View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path style="fill: none; fill-rule: nonzero; fill-opacity: 0.5;" d="M 7.7592046,36.982095 C 7.8831049,40.873696 7.8339808,45.305308 7.8339808,49.436888 Z" />
<path style="fill: black; fill-rule: evenodd; fill-opacity: 0.5;" d="M 7.7592046,36.982095 C 7.8831049,40.873696 7.8339808,45.305308 7.8339808,49.436888 Z" />
</svg>

After

Width:  |  Height:  |  Size: 430 B

View file

@ -0,0 +1,66 @@
<?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://creativecommons.org/ns#"
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="flowtext-less.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="350"
inkscape:cy="520"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1559"
inkscape:window-height="876"
inkscape:window-x="41"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<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 />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="142.85715"
y="638.07648"
id="text2997"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2999"
x="142.85715"
y="638.07648">abcd</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

78
unittests/flowtext.svg Normal file
View file

@ -0,0 +1,78 @@
<?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://creativecommons.org/ns#"
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="Neues Dokument 1">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="350"
inkscape:cy="520"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="936"
inkscape:window-height="631"
inkscape:window-x="41"
inkscape:window-y="24"
inkscape:window-maximized="0" />
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<flowRoot
xml:space="preserve"
id="flowRoot2985"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"><flowRegion
id="flowRegion2987"><rect
id="rect2989"
width="480"
height="262.85715"
x="45.714287"
y="218.07646" /></flowRegion><flowPara
id="flowPara2991">sfdadasd</flowPara><flowPara
id="flowPara2993">asdasd</flowPara><flowPara
id="flowPara2995">adsa</flowPara></flowRoot> <text
xml:space="preserve"
style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="142.85715"
y="638.07648"
id="text2997"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2999"
x="142.85715"
y="638.07648">abcd</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<rect style="font-size:20px" width="100" height="100" />
</svg>

After

Width:  |  Height:  |  Size: 162 B

View file

@ -0,0 +1,31 @@
<svg xmlns="http://www.w3.org/2000/svg">
<title xmlns:mytitle="http://example.org/mytitle">
<mytitle:title>This is an example SVG file</mytitle:title>
<mytitle:desc>Unit test for Scour's --remove-titles option</mytitle:desc>
</title>
<desc xmlns:mydesc="http://example.org/mydesc">
<mydesc:title>This is an example SVG file</mydesc:title>
<mydesc:para>Unit test for Scour's
<mydesc:emph>--remove-descriptions</mydesc:emph> option</mydesc:para>
</desc>
<metadata>
<rdf:RDF
xmlns:rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs = "http://www.w3.org/2000/01/rdf-schema#"
xmlns:dc = "http://purl.org/dc/elements/1.1/" >
<rdf:Description about="http://example.org/myfoo"
dc:title="MyFoo"
dc:description="Unit test for Scour's --remove-metadata option"
dc:publisher="No One"
dc:date="2010-06-09"
dc:format="image/svg+xml"
dc:language="en" >
<dc:creator>
<rdf:Bag>
<rdf:li>No One</rdf:li>
</rdf:Bag>
</dc:creator>
</rdf:Description>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<linearGradient id="grad1" x1="0" y1='0' x2='100%' y2='0.0' gradientUnits="objectBoundingBox" spreadMethod="pad">
<stop offset="0" stop-color="black"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="grad1b" x2='1.0' gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="black"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="grad1c" x2='1' gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="black"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<radialGradient id="grad2" xlink:href="#grad1" cx="50%" cy="0.5" r="50%" fx="50%" fy="0.5"/>
<rect width="100" height="100" fill="url(#grad1)"/>
<rect width="100" height="100" fill="url(#grad1b)"/>
<rect width="100" height="100" fill="url(#grad1c)"/>
<rect width="50" height="50" fill="url(#grad2)"/>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<rect fill="red" stroke="blue" x="0" y="0" width="4" height="4" />
<rect fill="red" stroke="blue" x="8" y="0" width="4" height="4" />
<rect fill="red" stroke="blue" x="16" y="0" width="4" height="4" />
</svg>

After

Width:  |  Height:  |  Size: 317 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg height="100" width="100" xmlns="http://www.w3.org/2000/svg" >
<text>
<tspan x="10" y="30" style="font-family:sans-serif">text1</tspan>
<tspan x="10" y="50" style="font-family:sans-serif">text2</tspan>
<tspan x="10" y="70" style="font-family:sans-serif">text3</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 350 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<rect fill="green" stroke="blue" x="0" y="0" width="4" height="4" />
<rect fill="yellow" stroke="red" x="8" y="0" width="4" height="4" />
<rect fill="blue" stroke="red" x="16" y="0" width="4" height="4" />
</svg>

After

Width:  |  Height:  |  Size: 321 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" viewBox="0 0 141.732 141.732" xml:space="preserve">
<g>
<g clip-path="url(#SVGID_2_)">
<path d="M1,1" fill="#fdebc8" stroke="#000" stroke-width=".5" stroke-miterlimit="10"/>
</g>
<g clip-path="url(#SVGID_2_)">
<g>
<path opacity=".5" clip-path="url(#SVGID_4_)" fill="#fff" d="M1,1"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 528 B

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<desc>Produced by GNUPLOT 5.2 patchlevel 8</desc>
<rect width="900" height="600" fill="none"/>
<g color="black" fill="none">
<path d="m88.5 564h9m777.5 0h-9" stroke="#000"/>
<g transform="translate(80.2,567.9)" fill="#000" font-family="Arial" font-size="12" text-anchor="end">
<text><tspan font-family="Arial">0</tspan></text>
</g>
</g>
<g color="black" fill="none">
<path d="m88.5 473h9m777.5 0h-9" stroke="#000"/>
<g transform="translate(80.2,476.9)" fill="#000" font-family="Arial" font-size="12" text-anchor="end">
<text><tspan font-family="Arial">5000</tspan></text>
</g>
</g>
<g color="black" fill="none">
<path d="m88.5 382h9m777.5 0h-9" stroke="#000"/>
<g transform="translate(80.2,385.9)" fill="#000" font-family="Arial" font-size="12" text-anchor="end">
<text><tspan font-family="Arial">10000</tspan></text>
</g>
</g>
<g color="black" fill="none">
<path d="m88.5 291h9m777.5 0h-9" stroke="#000"/>
<g transform="translate(80.2,294.9)" fill="#000" font-family="Arial" font-size="12" text-anchor="end">
<text><tspan font-family="Arial">15000</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:example="http://www.example.com/olfactory-feedback/0.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<switch>
<foreignObject requiredExtensions="http://www.example.com/olfactory-feedback/0.1" x="0" y="0" width="1" height="1">
<example:odor xlink:href="#odor1423">
</example:odor>
</foreignObject>
<g id="dsfargeg">
<rect width="300" height="200" fill="green" />
<circle cx="200" cy="100" r="50" fill="yellow" />
</g>
</switch>
<defs>
<example:odor id="odor1423" fill="grape"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 609 B

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:example="http://www.example.com/olfactory-feedback/0.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<switch>
<foreignObject requiredExtensions="http://www.example.com/olfactory-feedback/0.1" x="0" y="0" width="1" height="1">
<example:odor xlink:href="#odor1423">
</example:odor>
</foreignObject>
<g>
<rect width="300" height="200" fill="green" />
<circle cx="200" cy="100" r="50" fill="yellow" />
</g>
</switch>
<defs>
<example:odor id="odor1423" fill="grape"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 595 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<g>
<title>Group 1</title>
<rect width="300" height="200" fill="green" />
<circle cx="200" cy="100" r="50" fill="yellow" />
</g>
<g>
<desc>Group 1</desc>
<rect width="300" height="200" fill="green" />
<circle cx="200" cy="100" r="50" fill="yellow" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 365 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" height="110" width="100">
<text id="text1" x="10" y="20">Text 1</text>
<text id="text2" x="10" y="40">Text 2</text>
<text id="text3" x="10" y="60">Text 3</text>
<text id="text_custom" x="10" y="80">Text custom</text>
<text id="my_text1" x="10" y="100">My text</text>
</svg>

After

Width:  |  Height:  |  Size: 358 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" id="boo">
<defs>
<title id="title1">Fooey</title>
<rect id='r1' />
<linearGradient id="Polka_Dot_Pattern">
<stop offset="0.5" stop-color="blue" id="stop1234"/>
</linearGradient>
</defs>
<rect id='r2' fill="url(#Polka_Dot_Pattern)" />
</svg>

After

Width:  |  Height:  |  Size: 348 B

12
unittests/ids.svg Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" height="100" width="100" viewBox="0 0 100 100">
<defs>
<linearGradient id="linearGradient1">
<stop offset="0"/>
<stop offset="1" stop-color="blue"/>
</linearGradient>
</defs>
<g id="layer1">
<rect id="rect1" height="50" width="50" x="10" y="10" fill="url(#linearGradient1)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 425 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<g>
<linearGradient id="g1">
<stop offset="0" stop-color="blue"/>
<stop offset="1" stop-color="red"/>
</linearGradient>
</g>
</defs>
<rect fill="url(#g1)" width="100" height="100" />
</svg>

After

Width:  |  Height:  |  Size: 305 B

7
unittests/inkscape.svg Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:foo="http://www.inkscape.org/namespaces/inkscape"
inkscape:version="0.46" version="1.0">
<inkscape:perspective inkscape:vp_x="0 : 526.18109 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="744.09448 : 526.18109 : 1" inkscape:persp3d-origin="372.04724 : 350.78739 : 1" id="perspective3104"/>
<rect width="300" height="200" fill="green" inkscape:collect="always" foo:bar="1"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

2
unittests/minimal.svg Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"/>

After

Width:  |  Height:  |  Size: 81 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<g id="grampa" fill-opacity="0.4">
<g stroke-opacity="0.8">
<rect fill="#0F0" stroke="#0F0" stroke-width="5" width="100" height="300"/>
<rect fill="#0F0" width="200" height="100" />
</g>
<circle fill="#0F0" stroke="0F0" cx="50" cy="50" r="20" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 368 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<g fill-opacity="0.4">
<rect fill="#0F0" stroke="#0F0" stroke-width="5" width="100" height="300"/>
<rect fill="#0F0" width="200" height="100" />
<circle fill="#0F0" stroke="0F0" cx="50" cy="50" r="20" />
</g>
<text>Hello
<tspan font-style="italic">World!</tspan>
Goodbye
<tspan font-style="italic">Cruel World!</tspan>
</text>
</svg>

After

Width:  |  Height:  |  Size: 436 B

14
unittests/nested-defs.svg Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<defs>
<linearGradient id="linearGradient662">
<stop style="stop-color:#000000;stop-opacity:1" offset="0"/>
<stop style="stop-color:#0000ff;stop-opacity:1" offset="1"/>
</linearGradient>
</defs>
<linearGradient x1="120.22393" y1="156.07137" x2="119.33763" y2="162.87338" id="linearGradient3211" xlink:href="#linearGradient662" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0.789103,0,0,1.131232,0.366124,452.0845)"/>
</defs>
<path d="m50 50h50v50h-50z" fill="url(#linearGradient3211)"/>
</svg>

After

Width:  |  Height:  |  Size: 732 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<g>
<g transform="translate(50,50)">
<rect width="300" height="200" fill="green" />
<circle cx="200" cy="100" r="50" fill="yellow" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 253 B

50
unittests/newlines.svg Normal file
View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
>
<!-- this file has pretty messed up formatting --> <rect width="100" height="100"/>
<rect width="100" height="100"/>
<rect width="100" height="100"/>
<rect width="100" height="100"/>
<rect width="100" height="100"/>
<rect width="100" height="100"/>
<!-- we have mixed newline
characters, carriage returns and both of them
as well as tabs and spaces
-->
<rect width="100" height="100"/><rect width="100" height="100"/> <rect width="100" height="100"/>
<rect width="100" height="100"/> <rect width="100" height="100"/> <rect width="100" height="100"/>
<rect width="100" height="100"/> <rect width="100" height="100"/>
</svg>
<!-- OMG, really? -->

After

Width:  |  Height:  |  Size: 889 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"
viewBox="0.1 -0.333 125 125" xml:space="preserve">
<g id="Layer_1_2_">
<path d="M24.599,16.542v27.584l-4.994-1.033L0,60.671l52.687,63.612l51.361-40.729l20.844-18.664c0,0-23.527-4.871-27.404-5.673
c0-4.4,0-47.951,0-47.951L43.071,0L24.599,16.542z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 415 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<marker id="m1" orient="auto">
<rect width="200" height="100"/>
</marker>
<marker id="m2" orient="0">
<rect width="200" height="100"/>
</marker>
</defs>
<line x2="100" y2="100" style="marker-start:url(#m1);marker-end:url(#m2)" stroke="#000" />
</svg>

After

Width:  |  Height:  |  Size: 414 B

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<marker id="m1" style="overflow:visible">
<rect width="200" height="100"/>
</marker>
<marker id="m2" style="overflow:hidden">
<rect width="200" height="100"/>
</marker>
</defs>
<line x2="100" y2="100" style="marker-start:url(#m1);marker-end:url(#m2)" stroke="#000" />
</svg>

After

Width:  |  Height:  |  Size: 438 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="overflow:visible">
<svg style="overflow:hidden">
<line x2="100" y2="100" stroke="#000" />
</svg>
<svg style="overflow:visible">
<line x2="100" y2="100" stroke="#000" />
</svg>
</svg>

After

Width:  |  Height:  |  Size: 344 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path d="M 385,201 V181 l100,-50 h20 C 505.43501,223.44223 659.42238,164.82405 714.32160,-0.0015300000 C 649.90356,227.13187 497.48814,312.46353 371.30643,277.40123 C 245.12472,242.33893 157.17674,250.88268 121.69357,12.440270 C 211.69357,149.44027 323.87473,190.08578 385.88362,201.47812 z " fill="blue"/>
</svg>

After

Width:  |  Height:  |  Size: 416 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<path id="path1" d="m10,100c50-50,50,50,100,0,50-50,50,50,100,0" fill="none" stroke="blue" stroke-width="5"/>
<path id="path2a" d="m200,200c0,0 200,100 200,0" fill="none" stroke="red" stroke-width="5"/>
<path id="path2b" d="m0,300s200-100 200,0c0,0 200,100 200,0" fill="none" stroke="green" stroke-width="5"/>
</svg>

After

Width:  |  Height:  |  Size: 455 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path d="m100 100 l200 100 m0 0z" />
<path d="m100 100 v200 m0 0 100 100z" />
<path d="m100 100 v200 m0 0m0 0 2-1-2 1z" />
<path d="m100 100 v200 m0 0 3-5-5 3m0 0 2-1-2 1z" />
<path d="m100 100 v200 m0 0 3-5-5 3zm0 0 2-1-2 1z" />
</svg>

After

Width:  |  Height:  |  Size: 310 B

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="-100 -50 300 150" xmlns="http://www.w3.org/2000/svg">
<path d="m0 0a100 50 0 0 0 100 50" fill="none" stroke="#000"/>
<path d="m0 0a100 50 0 0 0100 50" fill="none" stroke="green"/>
<path d="m0 0a100 50 0 00 100 50" fill="none" stroke="blue"/>
<path d="m0 0a100 50 0 00100 50" fill="none" stroke="red"/>
</svg>

After

Width:  |  Height:  |  Size: 370 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path stroke="#000" d="M100,100,100,200 M300,100,100,100 M300,200,300,100" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 193 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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: 203 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="210" height="210">
<path stroke="yellow" fill="red" d="M100,100 L200.12345,200.12345 C215,205 185,195 200.12,200.12 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 275 B

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path d="m 1.11 2.22 3.33 4.44
M 0 0
C 1.11 2.22 3.33 4.44 5.55 6.66
c 1.11 2.22 3.33 4.44 5.55 6.66 1.11 2.22 3.33 4.44 5.55 6.66
M 0 0
S 1.11 2.22 3.33 4.44
s 1.11 2.22 3.33 4.44 1.11 2.22 3.33 4.44
M 0 0
Q 1.11 2.22 3.33 4.44
q 1.11 2.22 3.33 4.44 1.11 2.22 3.33 4.44" />
</svg>

After

Width:  |  Height:  |  Size: 463 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path id="p0" d="M 100.0000001 99.9999999 h100.01 v123456789.123456789 h-100 z" />
<path id="p1" d="m 1 21 321 4321 54321 654321 " />
<path id="p2" d="m 1.0 21.0 321.0 4321.0 54321.0 654321.0" />
<path id="p3" d="m 01 021 0321 04321 054321 0654321 " />
<path id="p4" d="m -1 -21 -321 -4321 -54321 -654321 " />
<path id="p6" d="m 123.456 101.001 -123.456 -101.001" />
</svg>

After

Width:  |  Height:  |  Size: 517 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
<path d="m10,100q50-50,100,0,50,50,100,0" fill="none" stroke="blue" stroke-width="5"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
<path d="M 300. 100 h-50-1.5E+2
l100.00000,2000.0E-01 z" fill="red" />
</svg>

After

Width:  |  Height:  |  Size: 222 B

4
unittests/path-sn.svg Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1781 1142">
<path d="m 0,0 l 2.e-4,0 z"/>
</svg>

After

Width:  |  Height:  |  Size: 159 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="5 0 3 3">
<path stroke="blue" stroke-width="0.01" fill="none" d="M5.81,0 H5.91000" />
</svg>

After

Width:  |  Height:  |  Size: 199 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<path d="M300.0000, -100.1 z" fill="red" />
</svg>

After

Width:  |  Height:  |  Size: 163 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="5 0 3 3">
<path d="M10000,0" />
</svg>

After

Width:  |  Height:  |  Size: 145 B

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path id="none" d="m0 0 0 0"/>
<path id="attr_butt" d="m0 0 0 0" stroke-linecap="butt"/>
<path id="attr_round" d="m0 0 0 0" stroke-linecap="round"/>
<path id="attr_square" d="m0 0 0 0" stroke-linecap="square"/>
<path id="style_butt" d="m0 0 0 0" style="stroke-linecap:butt"/>
<path id="style_round" d="m0 0 0 0" style="stroke-linecap:round"/>
<path id="style_square" d="m0 0 0 0" style="stroke-linecap:square"/>
</svg>

After

Width:  |  Height:  |  Size: 522 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="#000" d="M10,10h100v100h-100z"/>
</svg>

After

Width:  |  Height:  |  Size: 162 B

View file

@ -0,0 +1,4 @@
<?xml version='1.0' encoding='utf-8'?>
<svg xmlns="http://www.w3.org/2000/svg">
<polygon points="-100,-100,100-100,100-100-100,-100-100,200" />
</svg>

After

Width:  |  Height:  |  Size: 156 B

View file

@ -0,0 +1,4 @@
<?xml version='1.0' encoding='utf-8'?>
<svg xmlns="http://www.w3.org/2000/svg">
<polygon points="100,-100,100-100,100-100-100,-100-100,200" />
</svg>

After

Width:  |  Height:  |  Size: 155 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<polygon fill="blue" points="10000,50" />
</svg>

After

Width:  |  Height:  |  Size: 146 B

5
unittests/polygon.svg Normal file
View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<polygon fill="blue" points="50,50 150,50 150,150 50,150 +5e1,500.00e-1" />
<polygon fill="green" points="200,50 300,50 300,150 200,150" />
</svg>

After

Width:  |  Height:  |  Size: 245 B

View file

@ -0,0 +1,4 @@
<?xml version='1.0' encoding='utf-8'?>
<svg xmlns="http://www.w3.org/2000/svg">
<polyline points="-100,-100,100-100,100-100-100,-100-100,200" />
</svg>

After

Width:  |  Height:  |  Size: 157 B

View file

@ -0,0 +1,4 @@
<?xml version='1.0' encoding='utf-8'?>
<svg xmlns="http://www.w3.org/2000/svg">
<polyline points="100,-100,100-100,100-100-100,-100-100,200" />
</svg>

After

Width:  |  Height:  |  Size: 156 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<polyline fill="blue" points="10000,50" />
</svg>

After

Width:  |  Height:  |  Size: 147 B

11
unittests/protection.svg Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path id="path1" d="m1,1z"/>
<path id="path2" d="m1,1z"/>
<path id="path3" d="m1,1z"/>
<path id="path4" d="m1,1z"/>
<path id="p:mypath1" d="m1,1z"/>
<path id="p_mypath1" d="m1,1z"/>
<path id="otherpath" d="m1,1z"/>
<use xlink:href="#path1"/>
</svg>

After

Width:  |  Height:  |  Size: 403 B

10
unittests/quot-in-url.svg Normal file
View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#0F0" />
<stop offset="1" stop-color="#00F"/>
</linearGradient>
</defs>
<rect width="100" height="100" fill="url(&quot;#g&quot;)"/>
</svg>

After

Width:  |  Height:  |  Size: 372 B

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg">
<style>use[id="t"] {font-size: small}</style>
<text id="t" style="font-family:'Times New Roman'"/>
</svg>

After

Width:  |  Height:  |  Size: 188 B

Some files were not shown because too many files have changed in this diff Show more