Coder Social home page Coder Social logo

svg_utils's Introduction

https://readthedocs.org/projects/svgutils/badge/?version=latest

Python-based SVG editor

This is an utility package that helps to edit and concatenate SVG files. It is especially directed at scientists preparing final figures for submission to journal. So far it supports arbitrary placement and scaling of svg figures and adding markers, such as labels.

See the blog post for a short tutorial.

The full documentation is available here.

Install

From PyPI

You can install svgutils from Python Package Index (PyPI) using the pip3 utility:

pip3 install svgutils --user

Note that the pip3 will attempt to install lxml library if it is not already installed. For the installation to be sucessful, you need development libraries of libxml2 and libxslt1. On Ubuntu and other Debian-derived Linux distributions you can install them via:

sudo apt-get install libxml2-dev libxslt-dev

From Conda

Installing svgutils from the conda-forge channel can be achieved by adding conda-forge to your channels with:

conda config --add channels conda-forge

You can install svgutils from conda-forge channel:

conda install svgutils

If you don't want to add the channel to your configuration, you can specify it at the time of installation:

conda install svgutils -c conda-forge

From sources

To install system-wide (needs administrator privilages):

python3 setup.py install

To install locally (do not forget to add $HOME/python/lib/python3.6/site-packages/ to your Python path):

python3 setup.py install --user

License

The package is distributed under MIT license (see LICENSE file for information).

Related packages

svg_stack is a similar package that layouts multiple SVG files automatically (in a Qt-style).

svgmanip a related library that aims for a simple API with the ability to export to PNG accurately

cairosvg a command-line SVG to PNG converter for Python 3.4+

svglib a pure-Python library for reading and converting SVG

Authors

Bartosz Telenczuk ([email protected])

svg_utils's People

Contributors

abhisht51 avatar bradjc avatar brghena avatar btel avatar crazypython avatar cscreator avatar dependabot[bot] avatar erickmr19 avatar esc avatar fkrecinic avatar hartwork avatar jam-iko avatar khushishikhu avatar malfatti avatar neurodroid avatar samuelba avatar schlpbch avatar shervinsahba avatar sjmf avatar technic avatar willwade avatar ziererf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

svg_utils's Issues

scale_xy() from compose not working

I need advice or an example for the method scale_xy() from svgutils.compose.

when I try to use it like this i get the following error:

sc.Figure("12cm", "12cm",
sc.Panel(sc.SVG("./test1.svg").scale_xy(0.558, 0.559)),
).save("./ergebnis.svg")

ERRORS:
Argument 'element' has incorrect type (expected lxml.etree._Element, got NoneType)

also the fallback that should be scaling within on axis doesnt work as well, it brings up the following error:
TypeError: must be real number, not str

I used a simple .scale_xy(1) as number, not even something with a fractional number..

actually i'm done with it :/ and can not find the clue...

if someone would help out or say that i'm trying it right that would be nice.

greetings,
Philipp

why is "moveto" actually "moveby"

Hi,

I'm trying to open a svg file as a template and position objects programmatically.

SVGTest

I expected a new file with the rectangle and the circle but not the triangle (that worked), where circle and rectangle are placed at the same coordinate in the top left corner (that didn't work).

instead, when looking to the resulting svg i can see that elements get the "translate()" transformation, which is move by 20 to the right and 20 to bottom.

import svgutils.transform as sg
from svgutils.compose import Unit
import sys


ovWdth = Unit('1080px')
ovHght = Unit('768px')

fig = sg.SVGFigure(ovWdth,ovHght) 

#load template
tmpl = sg.fromfile('SVGTest.svg')


#get objects
boxObj = tmpl.find_id('rect846')
circObj = tmpl.find_id('path1076')

print(boxObj)

#move objects
boxObj.moveto(20,20)
circObj.moveto(20,20)

#append to figure
fig.append([boxObj, circObj])

#save figure
fig.save('SVGTest_Output.svg')

0.3.1 wheel upload "svgutils-0.3.1-py3-none-any.whl" of 2010-01-04 is not 0.3.1 and breaks existing software

Hi!

I'm a happy user of svgutils with my CLI tool xiangqi-setup; thank you for developing and sharing it as Open Source (or Software Libre) 🙏

I have an explicit svgutils==0.3.1 in my setup.py file (due to API incompatible changes on Git master after 0.3.1, which I accept and which are not my concern here). Today I noticed that CI started failing with 0.3.1 and it turns out that a new wheel svgutils-0.3.1-py3-none-any.whl was upload for 0.3.1 that contains post-0.3.1 code, see https://pypi.org/project/svgutils/0.3.1/#files. I'll be working on migrating my code to 0.3.2 shortly but I can only fix it for future releases. Could you delete that wheel file please to unbreak past and current releases of existing software depending on svgutils==0.3.1? Thanks in advance! 🙏

Best, Sebastian

improve documentation

What

Documentation is essential for every project. Read through the docs and find any errors, undocumented functions and maybe add more tutorials, examples

How

the docs are available at https://svgutils.readthedocs.io/en/latest/index.html, go through them and if you want to modify anything check click on "edit on github" in the upper right corner and edit the file directly through github interface. you don't even need to clone the repository locally, most things can be done from the browser.

if you haven't used sphinx and restructured text check out the tutorials:

https://www.sphinx-doc.org/en/master/usage/quickstart.html

https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html

add annotation primitives to compose module

What

svg_utils is useful to add annotations to existing SVG images (or even bitmaps). For now, we are supporting a few annotation primitives (like lines, texts), but it would be nice to add a few more.

How

  • add new classes in compose.py and transform.py files that implement Circles, Arrows, Rectangles, comics text cloud etc., you can follow the example of Text primitive
  • add unit tests
  • add a tutorial in the docs on annotating with SVG

for more info about SVG check out the mozilla tutorial:

https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial

https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial

add code of conduct

What

To nurish supportive community, we need a inclusive code of coduct and a way to enforce it.

How

  • look for existing code of conducts from thriving open source communities
  • describe a way of enforcing the code of conduct and the first contact person in case of any issues
  • add CODE_OF_CONDUCT.rst file and add it to the repository

Workaround mpl units bug not necessary

Thanks a lot for this really useful package!

Just want to report that the workaround mpl units bug, i.e. to set the size of the SVG in pixel instead of points, seems to be unnecessary. In this matplotlib commit, the units were changed on purpose.

Indeed, in the current version the resulting SVG (e.g. when opened in Inkscape) does not have the size that I specified as a figsize in matplotlib. Undoing the workaround, e.g. using

w_svg, h_svg = svg_mpl.get_size()
svg_mpl.set_size((w_svg+'pt', h_svg+'pt'))

yields a SVG where the size agrees with the figsize.

I assume there was a good reason to introduce the workaround so I am posting this as an issue here. Of course I would be willing to create a PR.

validate generate svg files with html5validator

What

We need to make sure that SVG files generated with svg_utils are compliant with the SVG standard. To do that, we could use an existing validator tool such as:

https://github.com/svenkreiss/html5validator

This should be integrated with the unit tests and continous integration (CI) system

How

You will need to:

  • make sure that we can use htm5validator to validate the SVG files (start with the ones in examples/files director)
  • validate the svg in unit tests, for example, you might generate several SVG files with svg_utils and add check them with the validator
  • integrate this with the CI - check the html5 instructions and travis.yml file

note that you can (and probably should) split these tasks into a few PRs

save method creates a blank svg

The following code outputs the desired plot in Jupyter Lab, but the saved graphic is blank.

sc.Figure("18cm", "9cm",
    sc.Panel(
       sc.SVG("fig1"),
       sc.Text("A", 8, 18, size=14, font='sans')
    ),
    sc.Panel(
       sc.SVG("fig2"),
       sc.Text("B", 8, 18, size=14, font='sans')
    )
).save('test')

`compose` creates empty figure

OS: Ubuntu 20.04 LTS
python: 3.8

Setup

I installed within a conda environment with:

sudo apt-get install libxml2-dev libxslt-dev
pip3 install svgutils --user

Problem

#!/usr/bin/env python
#coding=utf-8

from svgutils.compose import *
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(1, figsize=(4,4))
ax.plot(np.sin(np.linspace(0,2.*np.pi)), np.cos(np.linspace(0,2.*np.pi)))
fig.savefig('cover.svg')

Figure("16cm", "6.5cm", 
    Panel(SVG('cover.svg')),
).save("composed.svg")

cover.svg looks correct, but composed.svg is a blank canvas. Interestingly, it works when using svgutils.transform

Quick fix

Downgrading to v0.3.1 works:

pip install svgutils==0.3.1

Is there anything I am doing obviously wrong or is this a bug?

[0.3.3] Started producing SVG without width, height and viewBox

Hi @btel ,

due to my rendering regression tests in xiangqi-setup I noticed that things broke when trying to upgrade to svgutils 0.3.3. What happens is that the width, height, and viewBox parameters are no longer included in the written SVG file. Here's what that looks like in Inkscape: The dotted line is where the viewbox was with svgutils 0.3.2, the solid frame is where the new defacto viewbox is:

svgutils_0_3_3_viewbox

Here's a diff of before and after, with regard to the SVG output.

--- tests/actual-board-a4_blank_2cm_margin_BEFORE.svg   2021-02-10 11:22:04.300605915 +0100
+++ tests/actual-board-a4_blank_2cm_margin_AFTER.svg    2021-02-10 11:21:49.446494230 +0100
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='ASCII' standalone='yes'?>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="248.03149580999997px" viewBox="0 0 248.03149580999997 350.78740120838086" height="350.78740120838086px">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
   <g>
     <g transform="translate(0, 0) scale(0.33333333299287476 0.33333333299287476) "><defs id="defs4"/>
   <sodipodi:namedview xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.7" inkscape:cx="359.38222" inkscape:cy="512.51453" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1920" inkscape:window-height="1024" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1"/>

I have a feeling that #58 is causing this: .width and .height (with no underscores) are no longer set?
Is that a bug or a feature? How can I make svgutils 0.3.3 put width, height and viewBox into SVG output again?

Thanks and best, Sebastian

error when svg contains unicode

heres's error log:

UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-15-49ac8e8f02dd> in <module>
      7 sc.Figure('8cm','8cm',
      8           sc.Panel(sc.SVG(r'NACA0012.svg')),
----> 9           sc.Panel(sc.SVG(r'CaseVerify.svg')))

c:\python37\lib\site-packages\svgutils\compose.py in __init__(self, fname)
    108     def __init__(self, fname):
    109         fname = os.path.join(CONFIG['svg.file_path'], fname)
--> 110         svg = _transform.fromfile(fname)
    111         self.root = svg.getroot().root
    112 

c:\python37\lib\site-packages\svgutils\transform.py in fromfile(fname)
    308     fig = SVGFigure()
    309     fid = open(fname)
--> 310     svg_file = etree.parse(fid)
    311     fid.close()
    312 

src\lxml\etree.pyx in lxml.etree.parse()

src\lxml\parser.pxi in lxml.etree._parseDocument()

src\lxml\parser.pxi in lxml.etree._parseFilelikeDocument()

src\lxml\parser.pxi in lxml.etree._parseDocFromFilelike()

src\lxml\parser.pxi in lxml.etree._BaseParser._parseDocFromFilelike()

src\lxml\parser.pxi in lxml.etree._ParserContext._handleParseResultDoc()

src\lxml\parser.pxi in lxml.etree._handleParseResult()

src\lxml\etree.pyx in lxml.etree._ExceptionContext._raise_if_stored()

src\lxml\parser.pxi in lxml.etree._FileReaderContext.copyToBuffer()

UnicodeDecodeError: 'gbk' codec can't decode byte 0xbc in position 4126: illegal multibyte sequence

i add encoding args to transform.py fix the problem:

309c309
<     fid = open(fname,encoding='utf-8')
---
>     fid = open(fname)

Scale around center; knowing where to move; getting coordinates, bounding boxes of paths, extracting svg path element's d attribute

Hi! I'm happy:
smile

But I want to be HaPpIeErRrR… With a bigger smile! (1.25x as big) kind of like this:
Inkscape screenshot

So I do this:

import svgutils.transform as sg

fig = sg.fromfile('smile.svg')
mouth = fig.find_id('path1069')
mouth.scale(1.25)
fig.save('smile-scale.svg')

But…
smile-scale

Now that I have your attention, I actually was able to get to a solution https://stackoverflow.com/questions/76889502/ but was wondering if I was missing options from svgutils that could have helped. In particular extracting the d attribute or calculating the bounding box of a path.

Issue reading utf-8 svg files

Unicode characters were not parsed correctly. I could fix this by reading the file in binary within transform.fromfile()

Change open(fname) to open(fname, 'rb')

add instructions for conda

what

svgutils can be installed with pip, but also with conda. For now, the docs only cover installation with pip. Add instructions for installing with conda (from conda-forge channel)

how

check PR #45 for further links on editting sphinx docs

create conda-forge package for svg_utils

What

conda-forge is a repository of packages that can be installed using conda tool:

conda install my-package-name -c conda-forge

If you ever used anaconda distirbution on your computer, you are probably familiar with conda and conda-forge

For the moment, svg_utils can be only installed via pip, but not conda.

How

You will need to submit a PR to stage-recipes repository of conda-forge organisation:

https://github.com/conda-forge/staged-recipes

For details, how to create a package, check conda-build docs, for instructions of submitting to conda-forge, check out the README in staged-recipies repository.

Figure.Tile() throws TypeError: unsupported operand type(s) for /: 'Unit' and 'int'

See Stack Overflow post by user Allan for a complete description: https://stackoverflow.com/questions/45850144/is-there-a-bug-in-svgutils-compose-module-regarding-the-arrangement-of-figures/45863869#45863869

Here is a one-liner that reproduces the problem:

from svgutils import *
Figure( "21cm", "29.7cm",
    Text("A"),
    Text("B"),
).tile(1,2)

I propose the following fix in function svgutils.compose.Figure.tile():

dx = self.width.to('px').value/ncols
dy = self.height.to('px').value/nrows

Style of svg tag gets discarded.

I reduced a file generated by Batik to the following minimal example:

<svg style="stroke:black" width="1000" height="1000" >
<line style="fill:none" x1="900" x2="100" y1="100" y2="900" />
</svg>

As interpreted by Inkscape, this contains a visible line. I tried to use this as follows:

import svgutils.compose as sc
sc.Figure( "16in", "16in", sc.SVG("input.svg") ).save("output.svg")

This produces an output.svg with the following content:

<?xml version='1.0' encoding='ASCII' standalone='yes'?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="16.0in" viewBox="0 0 16.0in 16.0in" height="16.0in">
  <g>
    <g><line style="fill:none" x1="900" x2="100" y1="100" y2="900"/>
</g>
  </g>
</svg>

Here, the line is not visible anymore since the stroke:black style has gone.

I am not sure whether this is a problem with Batik not producing standard-conforming SVGs or a problem with SVG utils.

This issue seems related to it, but the issue as well as the answer are somewhat cryptic.

merge the svg_compositor branch

Not sure where else to comment, but I just wanted to say I am using the svg_compositor branch with some sucess. I have only encountered one problem - matplotlib emits svg figures with 'pt' (not px/cm) dimensions, which break the Unit() class.

Thanks a lot for your software!

Units improvements

from #13

matplotlib generates figures with those attached (relevant parts)

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Created with matplotlib (http://matplotlib.org/) -->
<svg height="144pt" version="1.1" viewBox="0 0 144 144" width="144pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <defs>
  <style type="text/css">
*{stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:100000;}
  </style>
 </defs>
 <g id="figure_1">
  <g id="patch_1">
   <path d="M 0 144 

the pt unit is unable to be parsed.

I also have to monkeypatch the Units class and fix a few other things

    from svgutils.compose import Unit, SVG, Image, Figure, Text, CONFIG
    # monkey patch so we can use upstream branch
    Unit.per_inch = {'px': 90, 'cm': 2.54, 'mm':25.4}

and

    # sometimes inkscape writes the source svg with px units, sometimes with mm, sometimes with cm, and
    try:
        wpx = str(Unit(tmpl.width).to('px'))
    except AttributeError:
        # sometimes with no units
        wpx = '%dpx' % float(tmpl.width)
    try: hpx = str(Unit(tmpl.height).to('px'))
    except AttributeError: hpx = '%dpx' % float(tmpl.height)

    # and now we know that the string is 123.456px we have to remove the px because the library regex only
    # handles integers
    wpx = str(int(float(wpx[:-2]))) + 'px'
    hpx = str(int(float(hpx[:-2]))) + 'px'

    Figure(wpx, hpx, *bits).save(args.dest)

sorry about the mess, this is pulled out of bits of code for generating figures for a paper

Plans for dropping support for end-of-life Python 2.7 and 3.5?

Hi!

The oldest version of Python that has not reached end of life as of today is Python 3.6. The svgutils code base currently supports 2.7 and 3.5 officially and in practice. I have seen people have vastly different opinions on which versions of Python should be supported by a project in 2021, before. So I would just like to hear what the current plan or approach about end-of-life Python is, and then see how and if I can help.
A benefit of dropping support for old versions, is that we could e.g. start using f-strings and type hints in this code base if we wanted, in general.

Best, Sebastian

"copy()" deepcopys "self.root" not "self", leading to unexpected behaviour not in line with documentation

Like the title says, copying e.g. a svgutils.transform.GroupElement returns a lxml.etree._Element, not a new GroupElement. That means that if you try to copy an Element instead of reading it from disk again, the function does not return a copy of the Element, like the documentation says.

Honestly, I don't understand how this could ever be the desired behavior. This way, if you want to include the same SVG twice on two different spots of the figure, you need to pass the result of copy to the appropriate constructor before you can move it.

So I propose that either the documentation should reflect this, or the behaviour should be changed to be in line with what you would naturally expect.

SVGFigure does not set width and height element if created directly

Width and height aren't correctly set in the XML if transform.SVGFigure is created directly:

import svgutils
svgutils.transform.SVGFigure("10cm", "16cm").to_str()

prints

b'<?xml version=\'1.0\' encoding=\'ASCII\' standalone=\'yes\'?>\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"/>\n'

This code is used in the tutorial:

fig = sg.SVGFigure("16cm", "6.5cm")

Therefore I expect that this is not an intended behavior.

Issues with SVGFigure Width and Height not being set

In the following code, I wanted to create an sag with a size of 1000 by 1000. Stepping through the code the values of the width and the height were not set because they generated an exception in the Width and Height Setters. This resulted in the width and the height not being set.

Please note that I tried many combinations of values for width and height.

# create an empty image
unit_fig = svgutils.transform.SVGFigure(width=1000.0, height=1000.0)

When the code reach the setter, the line self._width = value.value would fail.

def width(self, value):
    **self._width = value.value**
    self.root.set("width", str(value))
    self.root.set("viewBox", "0 0 %s %s" % (self._width, self._height))

I replaced self._width = value. This fixed my problem.

robert

Unknown expected types for SVGFigure parameters

The recently released 0.3.2 has resulted in the following (truncated) traceback:

  File "/usr/local/miniconda/lib/python3.7/site-packages/niworkflows/viz/utils.py", line 360, in compose_view
    fig = SVGFigure(width, heights[:nsvgs].sum())
  File "/usr/local/miniconda/lib/python3.7/site-packages/svgutils/transform.py", line 242, in __init__
    self._width = width.value
AttributeError: 'numpy.int64' object has no attribute 'value'

Looking at the docs for SVGFigure, a numeric value seems appropriate. I'm not sure what sort of object with a .value attribute is expected to fix our invocation, so currently I'm pinning 0.3.1.

Issue introduced in #27.

Vague error when no height or width tag is in the svg

When exporting an svg from Adobe Illustrator, by default, "responsive" is ticked and no width or height tag is set. In that case, a vague error is thrown. I think it would really help a new user if there was a check and an appropriate message if the svg has no width or height tag.

import svgutils.compose as sc
sc.SVG(os.path.join(file_directory, 'face_border resp.svg'))

File "[main]", line 55, in
sc.SVG(os.path.join(file_directory, 'face_border resp.svg'))
File "C:\Users\keith\miniconda3\lib\site-packages\svgutils\compose.py", line 122, in init
if svg.width.endswith("%"):
AttributeError: 'NoneType' object has no attribute 'endswith'

Thanks!

Update PIP package

The pip package for svg_utils was last updated over a year ago, and there have been a number of helpful commits since then. Is there anything holding back another release?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.