initial source import (v0.26) and packaging
This commit is contained in:
parent
f3a7507d82
commit
f89b6bbf6a
11 changed files with 4134 additions and 37 deletions
37
.gitignore
vendored
37
.gitignore
vendored
|
|
@ -1,36 +1,5 @@
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
*.sublime-workspace
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Packages
|
|
||||||
*.egg
|
|
||||||
*.egg-info
|
|
||||||
dist
|
|
||||||
build
|
build
|
||||||
eggs
|
dist
|
||||||
parts
|
*.egg-info
|
||||||
bin
|
|
||||||
var
|
|
||||||
sdist
|
|
||||||
develop-eggs
|
|
||||||
.installed.cfg
|
|
||||||
lib
|
|
||||||
lib64
|
|
||||||
__pycache__
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
.coverage
|
|
||||||
.tox
|
|
||||||
nosetests.xml
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
|
|
||||||
# Mr Developer
|
|
||||||
.mr.developer.cfg
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
|
|
|
||||||
201
LICENSE
Normal file
201
LICENSE
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
||||||
9
Makefile
Normal file
9
Makefile
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
all: clean install
|
||||||
|
|
||||||
|
install:
|
||||||
|
python setup.py install
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build
|
||||||
|
rm -rf dist
|
||||||
|
rm -rf scour.egg-info
|
||||||
34
README.md
34
README.md
|
|
@ -1,4 +1,32 @@
|
||||||
scour
|
# Scour
|
||||||
=====
|
|
||||||
|
|
||||||
Scour - an SVG scrubber
|
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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
* 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
|
||||||
|
|
||||||
|
Regards,
|
||||||
|
|
||||||
|
Jeff Schiller, 2009-04-06
|
||||||
|
|
||||||
|
codedread@gmail.com
|
||||||
|
|
||||||
|
http://blog.codedread.com/
|
||||||
|
|
||||||
|
http://www.codedread.com/scour/
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Packaging from [sources](http://www.codedread.com/scour/) retrieved on 2013/20/22:
|
||||||
|
|
||||||
|
* done by Tavendo GmbH, Tobias Oberstein
|
||||||
|
* license same as upstream (Apache 2.0)
|
||||||
|
|
|
||||||
21
scour.sublime-project
Normal file
21
scour.sublime-project
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"folders":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Scour",
|
||||||
|
"path": ".",
|
||||||
|
"folder_exclude_patterns": ["*.egg-info", "build", "dist"],
|
||||||
|
"file_exclude_patterns": ["*.pyc", "*.pyo", "*.pyd"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"default_encoding": "UTF-8",
|
||||||
|
"detect_indentation": false,
|
||||||
|
"ensure_newline_at_eof_on_save": true,
|
||||||
|
"tab_size": 3,
|
||||||
|
"translate_tabs_to_spaces": true,
|
||||||
|
"trim_trailing_white_space_on_save": true,
|
||||||
|
"use_tab_stops": true
|
||||||
|
}
|
||||||
|
}
|
||||||
22
scour/__init__.py
Normal file
22
scour/__init__.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
###############################################################################
|
||||||
|
##
|
||||||
|
## 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.
|
||||||
|
##
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import scour
|
||||||
|
import svg_regex
|
||||||
|
import svg_transform
|
||||||
|
import yocto_css
|
||||||
3206
scour/scour.py
Normal file
3206
scour/scour.py
Normal file
File diff suppressed because it is too large
Load diff
285
scour/svg_regex.py
Normal file
285
scour/svg_regex.py
Normal file
|
|
@ -0,0 +1,285 @@
|
||||||
|
# This software is OSI Certified Open Source Software.
|
||||||
|
# OSI Certified is a certification mark of the Open Source Initiative.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006, Enthought, Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
# list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
# * Neither the name of Enthought, Inc. nor the names of its contributors may
|
||||||
|
# be used to endorse or promote products derived from this software without
|
||||||
|
# specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
""" Small hand-written recursive descent parser for SVG <path> data.
|
||||||
|
|
||||||
|
|
||||||
|
In [1]: from svg_regex import svg_parser
|
||||||
|
|
||||||
|
In [3]: svg_parser.parse('M 10,20 30,40V50 60 70')
|
||||||
|
Out[3]: [('M', [(10.0, 20.0), (30.0, 40.0)]), ('V', [50.0, 60.0, 70.0])]
|
||||||
|
|
||||||
|
In [4]: svg_parser.parse('M 0.6051.5') # An edge case
|
||||||
|
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)])]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from decimal import *
|
||||||
|
|
||||||
|
|
||||||
|
# Sentinel.
|
||||||
|
class _EOF(object):
|
||||||
|
def __repr__(self):
|
||||||
|
return 'EOF'
|
||||||
|
EOF = _EOF()
|
||||||
|
|
||||||
|
lexicon = [
|
||||||
|
('float', r'[-+]?(?:(?:[0-9]*\.[0-9]+)|(?:[0-9]+\.?))(?:[Ee][-+]?[0-9]+)?'),
|
||||||
|
('int', r'[-+]?[0-9]+'),
|
||||||
|
('command', r'[AaCcHhLlMmQqSsTtVvZz]'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Lexer(object):
|
||||||
|
""" Break SVG path data into tokens.
|
||||||
|
|
||||||
|
The SVG spec requires that tokens are greedy. This lexer relies on Python's
|
||||||
|
regexes defaulting to greediness.
|
||||||
|
|
||||||
|
This style of implementation was inspired by this article:
|
||||||
|
|
||||||
|
http://www.gooli.org/blog/a-simple-lexer-in-python/
|
||||||
|
"""
|
||||||
|
def __init__(self, lexicon):
|
||||||
|
self.lexicon = lexicon
|
||||||
|
parts = []
|
||||||
|
for name, regex in lexicon:
|
||||||
|
parts.append('(?P<%s>%s)' % (name, regex))
|
||||||
|
self.regex_string = '|'.join(parts)
|
||||||
|
self.regex = re.compile(self.regex_string)
|
||||||
|
|
||||||
|
def lex(self, text):
|
||||||
|
""" Yield (token_type, str_data) tokens.
|
||||||
|
|
||||||
|
The last token will be (EOF, None) where EOF is the singleton object
|
||||||
|
defined in this module.
|
||||||
|
"""
|
||||||
|
for match in self.regex.finditer(text):
|
||||||
|
for name, _ in self.lexicon:
|
||||||
|
m = match.group(name)
|
||||||
|
if m is not None:
|
||||||
|
yield (name, m)
|
||||||
|
break
|
||||||
|
yield (EOF, None)
|
||||||
|
|
||||||
|
svg_lexer = Lexer(lexicon)
|
||||||
|
|
||||||
|
|
||||||
|
class SVGPathParser(object):
|
||||||
|
""" Parse SVG <path> data into a list of commands.
|
||||||
|
|
||||||
|
Each distinct command will take the form of a tuple (command, data). The
|
||||||
|
`command` is just the character string that starts the command group in the
|
||||||
|
<path> data, so 'M' for absolute moveto, 'm' for relative moveto, 'Z' for
|
||||||
|
closepath, etc. The kind of data it carries with it depends on the command.
|
||||||
|
For 'Z' (closepath), it's just None. The others are lists of individual
|
||||||
|
argument groups. Multiple elements in these lists usually mean to repeat the
|
||||||
|
command. The notable exception is 'M' (moveto) where only the first element
|
||||||
|
is truly a moveto. The remainder are implicit linetos.
|
||||||
|
|
||||||
|
See the SVG documentation for the interpretation of the individual elements
|
||||||
|
for each command.
|
||||||
|
|
||||||
|
The main method is `parse(text)`. It can only consume actual strings, not
|
||||||
|
filelike objects or iterators.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, lexer=svg_lexer):
|
||||||
|
self.lexer = lexer
|
||||||
|
|
||||||
|
self.command_dispatch = {
|
||||||
|
'Z': self.rule_closepath,
|
||||||
|
'z': self.rule_closepath,
|
||||||
|
'M': self.rule_moveto_or_lineto,
|
||||||
|
'm': self.rule_moveto_or_lineto,
|
||||||
|
'L': self.rule_moveto_or_lineto,
|
||||||
|
'l': self.rule_moveto_or_lineto,
|
||||||
|
'H': self.rule_orthogonal_lineto,
|
||||||
|
'h': self.rule_orthogonal_lineto,
|
||||||
|
'V': self.rule_orthogonal_lineto,
|
||||||
|
'v': self.rule_orthogonal_lineto,
|
||||||
|
'C': self.rule_curveto3,
|
||||||
|
'c': self.rule_curveto3,
|
||||||
|
'S': self.rule_curveto2,
|
||||||
|
's': self.rule_curveto2,
|
||||||
|
'Q': self.rule_curveto2,
|
||||||
|
'q': self.rule_curveto2,
|
||||||
|
'T': self.rule_curveto1,
|
||||||
|
't': self.rule_curveto1,
|
||||||
|
'A': self.rule_elliptical_arc,
|
||||||
|
'a': self.rule_elliptical_arc,
|
||||||
|
}
|
||||||
|
|
||||||
|
# self.number_tokens = set(['int', 'float'])
|
||||||
|
self.number_tokens = list(['int', 'float'])
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def rule_svg_path(self, next, 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)
|
||||||
|
commands.append(command_group)
|
||||||
|
return commands
|
||||||
|
|
||||||
|
def rule_closepath(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
return (command, []), token
|
||||||
|
|
||||||
|
def rule_moveto_or_lineto(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
coordinates = []
|
||||||
|
while token[0] in self.number_tokens:
|
||||||
|
pair, token = self.rule_coordinate_pair(next, token)
|
||||||
|
coordinates.extend(pair)
|
||||||
|
return (command, coordinates), token
|
||||||
|
|
||||||
|
def rule_orthogonal_lineto(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
coordinates = []
|
||||||
|
while token[0] in self.number_tokens:
|
||||||
|
coord, token = self.rule_coordinate(next, token)
|
||||||
|
coordinates.append(coord)
|
||||||
|
return (command, coordinates), token
|
||||||
|
|
||||||
|
def rule_curveto3(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
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)
|
||||||
|
coordinates.extend(pair1)
|
||||||
|
coordinates.extend(pair2)
|
||||||
|
coordinates.extend(pair3)
|
||||||
|
return (command, coordinates), token
|
||||||
|
|
||||||
|
def rule_curveto2(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
coordinates = []
|
||||||
|
while token[0] in self.number_tokens:
|
||||||
|
pair1, token = self.rule_coordinate_pair(next, token)
|
||||||
|
pair2, token = self.rule_coordinate_pair(next, token)
|
||||||
|
coordinates.extend(pair1)
|
||||||
|
coordinates.extend(pair2)
|
||||||
|
return (command, coordinates), token
|
||||||
|
|
||||||
|
def rule_curveto1(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
coordinates = []
|
||||||
|
while token[0] in self.number_tokens:
|
||||||
|
pair1, token = self.rule_coordinate_pair(next, token)
|
||||||
|
coordinates.extend(pair1)
|
||||||
|
return (command, coordinates), token
|
||||||
|
|
||||||
|
def rule_elliptical_arc(self, next, token):
|
||||||
|
command = token[1]
|
||||||
|
token = next()
|
||||||
|
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()
|
||||||
|
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()
|
||||||
|
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'):
|
||||||
|
raise SyntaxError("expecting a boolean flag; got %r" % (token,))
|
||||||
|
large_arc_flag = Decimal(token[1]) * 1
|
||||||
|
|
||||||
|
token = next()
|
||||||
|
if token[1] not in ('0', '1'):
|
||||||
|
raise SyntaxError("expecting a boolean flag; got %r" % (token,))
|
||||||
|
sweep_flag = Decimal(token[1]) * 1
|
||||||
|
|
||||||
|
token = next()
|
||||||
|
if token[0] not in self.number_tokens:
|
||||||
|
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||||
|
x = Decimal(token[1]) * 1
|
||||||
|
|
||||||
|
token = next()
|
||||||
|
if token[0] not in self.number_tokens:
|
||||||
|
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||||
|
y = Decimal(token[1]) * 1
|
||||||
|
|
||||||
|
token = next()
|
||||||
|
arguments.extend([rx, ry, axis_rotation, large_arc_flag, sweep_flag, x, y])
|
||||||
|
|
||||||
|
return (command, arguments), token
|
||||||
|
|
||||||
|
def rule_coordinate(self, next, 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()
|
||||||
|
return x, token
|
||||||
|
|
||||||
|
|
||||||
|
def rule_coordinate_pair(self, next, 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()
|
||||||
|
if token[0] not in self.number_tokens:
|
||||||
|
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||||
|
y = getcontext().create_decimal(token[1])
|
||||||
|
token = next()
|
||||||
|
return [x, y], token
|
||||||
|
|
||||||
|
|
||||||
|
svg_parser = SVGPathParser()
|
||||||
233
scour/svg_transform.py
Normal file
233
scour/svg_transform.py
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# SVG transformation list parser
|
||||||
|
#
|
||||||
|
# Copyright 2010 Louis Simard
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
""" Small recursive descent parser for SVG transform="" data.
|
||||||
|
|
||||||
|
|
||||||
|
In [1]: from svg_transform import svg_transform_parser
|
||||||
|
|
||||||
|
In [3]: svg_transform_parser.parse('translate(50, 50)')
|
||||||
|
Out[3]: [('translate', [50.0, 50.0])]
|
||||||
|
|
||||||
|
In [4]: svg_transform_parser.parse('translate(50)')
|
||||||
|
Out[4]: [('translate', [50.0])]
|
||||||
|
|
||||||
|
In [5]: svg_transform_parser.parse('rotate(36 50,50)')
|
||||||
|
Out[5]: [('rotate', [36.0, 50.0, 50.0])]
|
||||||
|
|
||||||
|
In [6]: svg_transform_parser.parse('rotate(36)')
|
||||||
|
Out[6]: [('rotate', [36.0])]
|
||||||
|
|
||||||
|
In [7]: svg_transform_parser.parse('skewX(20)')
|
||||||
|
Out[7]: [('skewX', [20.0])]
|
||||||
|
|
||||||
|
In [8]: svg_transform_parser.parse('skewY(40)')
|
||||||
|
Out[8]: [('skewX', [20.0])]
|
||||||
|
|
||||||
|
In [9]: svg_transform_parser.parse('scale(2 .5)')
|
||||||
|
Out[9]: [('scale', [2.0, 0.5])]
|
||||||
|
|
||||||
|
In [10]: svg_transform_parser.parse('scale(.5)')
|
||||||
|
Out[10]: [('scale', [0.5])]
|
||||||
|
|
||||||
|
In [11]: svg_transform_parser.parse('matrix(1 0 50 0 1 80)')
|
||||||
|
Out[11]: [('matrix', [1.0, 0.0, 50.0, 0.0, 1.0, 80.0])]
|
||||||
|
|
||||||
|
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])]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from decimal import *
|
||||||
|
|
||||||
|
|
||||||
|
# Sentinel.
|
||||||
|
class _EOF(object):
|
||||||
|
def __repr__(self):
|
||||||
|
return 'EOF'
|
||||||
|
EOF = _EOF()
|
||||||
|
|
||||||
|
lexicon = [
|
||||||
|
('float', r'[-+]?(?:(?:[0-9]*\.[0-9]+)|(?:[0-9]+\.?))(?:[Ee][-+]?[0-9]+)?'),
|
||||||
|
('int', r'[-+]?[0-9]+'),
|
||||||
|
('command', r'(?:matrix|translate|scale|rotate|skew[XY])'),
|
||||||
|
('coordstart', r'\('),
|
||||||
|
('coordend', r'\)'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Lexer(object):
|
||||||
|
""" Break SVG path data into tokens.
|
||||||
|
|
||||||
|
The SVG spec requires that tokens are greedy. This lexer relies on Python's
|
||||||
|
regexes defaulting to greediness.
|
||||||
|
|
||||||
|
This style of implementation was inspired by this article:
|
||||||
|
|
||||||
|
http://www.gooli.org/blog/a-simple-lexer-in-python/
|
||||||
|
"""
|
||||||
|
def __init__(self, lexicon):
|
||||||
|
self.lexicon = lexicon
|
||||||
|
parts = []
|
||||||
|
for name, regex in lexicon:
|
||||||
|
parts.append('(?P<%s>%s)' % (name, regex))
|
||||||
|
self.regex_string = '|'.join(parts)
|
||||||
|
self.regex = re.compile(self.regex_string)
|
||||||
|
|
||||||
|
def lex(self, text):
|
||||||
|
""" Yield (token_type, str_data) tokens.
|
||||||
|
|
||||||
|
The last token will be (EOF, None) where EOF is the singleton object
|
||||||
|
defined in this module.
|
||||||
|
"""
|
||||||
|
for match in self.regex.finditer(text):
|
||||||
|
for name, _ in self.lexicon:
|
||||||
|
m = match.group(name)
|
||||||
|
if m is not None:
|
||||||
|
yield (name, m)
|
||||||
|
break
|
||||||
|
yield (EOF, None)
|
||||||
|
|
||||||
|
svg_lexer = Lexer(lexicon)
|
||||||
|
|
||||||
|
|
||||||
|
class SVGTransformationParser(object):
|
||||||
|
""" Parse SVG transform="" data into a list of commands.
|
||||||
|
|
||||||
|
Each distinct command will take the form of a tuple (type, data). The
|
||||||
|
`type` is the character string that defines the type of transformation in the
|
||||||
|
transform data, so either of "translate", "rotate", "scale", "matrix",
|
||||||
|
"skewX" and "skewY". Data is always a list of numbers contained within the
|
||||||
|
transformation's parentheses.
|
||||||
|
|
||||||
|
See the SVG documentation for the interpretation of the individual elements
|
||||||
|
for each transformation.
|
||||||
|
|
||||||
|
The main method is `parse(text)`. It can only consume actual strings, not
|
||||||
|
filelike objects or iterators.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, lexer=svg_lexer):
|
||||||
|
self.lexer = lexer
|
||||||
|
|
||||||
|
self.command_dispatch = {
|
||||||
|
'translate': self.rule_1or2numbers,
|
||||||
|
'scale': self.rule_1or2numbers,
|
||||||
|
'skewX': self.rule_1number,
|
||||||
|
'skewY': self.rule_1number,
|
||||||
|
'rotate': self.rule_1or3numbers,
|
||||||
|
'matrix': self.rule_6numbers,
|
||||||
|
}
|
||||||
|
|
||||||
|
# self.number_tokens = set(['int', 'float'])
|
||||||
|
self.number_tokens = list(['int', 'float'])
|
||||||
|
|
||||||
|
def parse(self, text):
|
||||||
|
""" Parse a string of SVG transform="" data.
|
||||||
|
"""
|
||||||
|
next = self.lexer.lex(text).next
|
||||||
|
commands = []
|
||||||
|
token = next()
|
||||||
|
while token[0] is not EOF:
|
||||||
|
command, token = self.rule_svg_transform(next, token)
|
||||||
|
commands.append(command)
|
||||||
|
return commands
|
||||||
|
|
||||||
|
def rule_svg_transform(self, next, token):
|
||||||
|
if token[0] != 'command':
|
||||||
|
raise SyntaxError("expecting a transformation type; got %r" % (token,))
|
||||||
|
command = token[1]
|
||||||
|
rule = self.command_dispatch[command]
|
||||||
|
token = next()
|
||||||
|
if token[0] != 'coordstart':
|
||||||
|
raise SyntaxError("expecting '('; got %r" % (token,))
|
||||||
|
numbers, token = rule(next, token)
|
||||||
|
if token[0] != 'coordend':
|
||||||
|
raise SyntaxError("expecting ')'; got %r" % (token,))
|
||||||
|
token = next()
|
||||||
|
return (command, numbers), token
|
||||||
|
|
||||||
|
def rule_1or2numbers(self, next, token):
|
||||||
|
numbers = []
|
||||||
|
# 1st number is mandatory
|
||||||
|
token = next()
|
||||||
|
number, token = self.rule_number(next, token)
|
||||||
|
numbers.append(number)
|
||||||
|
# 2nd number is optional
|
||||||
|
number, token = self.rule_optional_number(next, token)
|
||||||
|
if number is not None:
|
||||||
|
numbers.append(number)
|
||||||
|
|
||||||
|
return numbers, token
|
||||||
|
|
||||||
|
def rule_1number(self, next, token):
|
||||||
|
# this number is mandatory
|
||||||
|
token = next()
|
||||||
|
number, token = self.rule_number(next, token)
|
||||||
|
numbers = [number]
|
||||||
|
return numbers, token
|
||||||
|
|
||||||
|
def rule_1or3numbers(self, next, token):
|
||||||
|
numbers = []
|
||||||
|
# 1st number is mandatory
|
||||||
|
token = next()
|
||||||
|
number, token = self.rule_number(next, token)
|
||||||
|
numbers.append(number)
|
||||||
|
# 2nd number is optional
|
||||||
|
number, token = self.rule_optional_number(next, 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)
|
||||||
|
numbers.append(number)
|
||||||
|
|
||||||
|
return numbers, token
|
||||||
|
|
||||||
|
def rule_6numbers(self, next, token):
|
||||||
|
numbers = []
|
||||||
|
token = next()
|
||||||
|
# all numbers are mandatory
|
||||||
|
for i in xrange(6):
|
||||||
|
number, token = self.rule_number(next, token)
|
||||||
|
numbers.append(number)
|
||||||
|
return numbers, token
|
||||||
|
|
||||||
|
def rule_number(self, next, token):
|
||||||
|
if token[0] not in self.number_tokens:
|
||||||
|
raise SyntaxError("expecting a number; got %r" % (token,))
|
||||||
|
x = Decimal(token[1]) * 1
|
||||||
|
token = next()
|
||||||
|
return x, token
|
||||||
|
|
||||||
|
def rule_optional_number(self, next, token):
|
||||||
|
if token[0] not in self.number_tokens:
|
||||||
|
return None, token
|
||||||
|
else:
|
||||||
|
x = Decimal(token[1]) * 1
|
||||||
|
token = next()
|
||||||
|
return x, token
|
||||||
|
|
||||||
|
|
||||||
|
svg_transform_parser = SVGTransformationParser()
|
||||||
72
scour/yocto_css.py
Normal file
72
scour/yocto_css.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# yocto-css, an extremely bare minimum CSS parser
|
||||||
|
#
|
||||||
|
# Copyright 2009 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.
|
||||||
|
|
||||||
|
# In order to resolve Bug 368716 (https://bugs.launchpad.net/scour/+bug/368716)
|
||||||
|
# scour needed a bare-minimum CSS parser in order to determine if some elements
|
||||||
|
# were still referenced by CSS properties.
|
||||||
|
|
||||||
|
# I looked at css-py (a CSS parser built in Python), but that library
|
||||||
|
# is about 35k of Python and requires ply to be installed. I just need
|
||||||
|
# something very basic to suit scour's needs.
|
||||||
|
|
||||||
|
# yocto-css takes a string of CSS and tries to spit out a list of rules
|
||||||
|
# A rule is an associative array (dictionary) with the following keys:
|
||||||
|
# - selector: contains the string of the selector (see CSS grammar)
|
||||||
|
# - properties: contains an associative array of CSS properties for this rule
|
||||||
|
|
||||||
|
# TODO: need to build up some unit tests for yocto_css
|
||||||
|
|
||||||
|
# stylesheet : [ CDO | CDC | S | statement ]*;
|
||||||
|
# statement : ruleset | at-rule;
|
||||||
|
# at-rule : ATKEYWORD S* any* [ block | ';' S* ];
|
||||||
|
# block : '{' S* [ any | block | ATKEYWORD S* | ';' S* ]* '}' S*;
|
||||||
|
# ruleset : selector? '{' S* declaration? [ ';' S* declaration? ]* '}' S*;
|
||||||
|
# selector : any+;
|
||||||
|
# declaration : property S* ':' S* value;
|
||||||
|
# property : IDENT;
|
||||||
|
# value : [ any | block | ATKEYWORD S* ]+;
|
||||||
|
# any : [ IDENT | NUMBER | PERCENTAGE | DIMENSION | STRING
|
||||||
|
# | DELIM | URI | HASH | UNICODE-RANGE | INCLUDES
|
||||||
|
# | 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
|
||||||
51
setup.py
Normal file
51
setup.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
###############################################################################
|
||||||
|
##
|
||||||
|
## 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.
|
||||||
|
##
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
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'
|
||||||
|
)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue