I originally intended vpype 1.10 to be a ‘quick-and-dirty’, bug-fix-only release but it ended up being quite substantial, so let’s dive in.
New features and improvements
Improved support for layer pen width and opacity in the viewer (#448)
- The “Pen Width” and “Pen Opacity” menus are now named “Default Pen Width” and “Default Pen Opacity”.
- The layer opacity is now used for display by default. It can be overridden by the default pen opacity by checking the “Override” item from the “Default Pen Opacity” menu.
- The layer pen width is now used for display by default as well. Likewise, it can be overridden by checking the “Override” item from the “Default Pen Width” menu.
This alone is reason to upgrade, and, if we’re being honest, it should have been done in the previous release. The display logic of the viewer is now as follows:
- By default, honor the layer’s pen width and opacity if present.
- If pen width and/or opacity is not set, revert to the value set in the menu (0.3mm / 80% by default).
- Either or both of the displayed pen width and opacity can be forced to the value in the menu using the new “Override” menu item.
It is worth noting that opacity is not a standalone layer property, but is part of its RGBA color property (
vp_color). Weirdly, vpype 1.9’s viewer would honor the base color (RGB), but not its alpha chanel. (See next feature, though.)
By the way, this article’s cover image is a screenshot of the viewer made with this command:
(Yes, this is a properly syntax-highlighted vpype command made with a custom script and Rich’s new SVG export. This is very preliminary, but, in time, something to improve on and deploy more widely in the doc and elsewhere.)
- Added the
alphacommand to set layer opacity without changing the base color (#447, #451)
While working on the viewer improvements, I realized how inconvenient it was to set an arbitrary opacity value. Using CSS color names (e.g.
color red) always sets opacity to 100% and there is no way around the hex notation for a custom value (e.g.
color #ff00007f or
color #f007). The new
alpha command fills that gap:
color red alpha 0.5.
- Added HPGL configuration for the Calcomp Artisan plotter (thanks to Andee Collard and @ithinkido) (#418)
Good news for Calcomp Artisan’s owners! Let this be a reminder that I welcome this kind of contribution. Though I’d like to, I can’t own every single type of vintage plotter! 😅
readcommand now better handles SVGs with missing
heightattribute is missing or expressed as percent, the
readcommand now attempts to use the
viewBoxattribute to set the page size, defaulting to 1000x1000px if missing. This behavior can be overridden with the
I recently came across a SVG with a
viewBox defined but no
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1191.26 1684.49">...</svg>
In such an instance, using the
read command used to default to an A4 page size, while the
vpype.read_svg() API (and friends) would default to a 1000x1000px page size. This is both inconsistent and missing the opportunity to fallback on the
viewBox. vpype now fully delegates this fallback logic to
svgelements, which does a good job at making the most of the available information. Also, if everything is missing (or
height are expressed in percents), vpype consistently falls back to 1000x1000px.
- Added the
--dont-set-dateoption to the
This one is a bit niche. vpype adds some metadata to the SVG, including the date and time at which it was generated (note the
$ vpype line 1cm 1cm 5cm 3cm layout a6 write -f svg - <?xml version="1.0" encoding="utf-8" ?> <svg xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink" baseProfile="tiny" height="14.8cm" version="1.2" viewBox="0 0 396.85039370078744 559.3700787401575" width="10.5cm"> <metadata> <rdf:RDF> <cc:Work> <dc:format>image/svg+xml</dc:format> <dc:source>vpype line 1cm 1cm 5cm 3cm layout a6 write -f svg - </dc:source> <dc:date>2022-04-07T10:15:00.532842</dc:date> </cc:Work> </rdf:RDF> </metadata> <defs/> <g fill="none" id="layer1" inkscape:groupmode="layer" inkscape:label="1" stroke="#0000ff" style="display:inline"> <line x1="122.8346" x2="274.0157" y1="241.8898" y2="317.4803"/> </g> </svg>
This is all well and good until you automate the generation of SVGs under a version control system, which is what I did for vpype-perspective’s documentation figures. A PyDoIt
dodo.py files converts any
.vpy file it finds into corresponding SVGs – basically a Python-powered, overcharged
Makefile. (This is a rather neat process which, by the way, should get its own article someday.) In this kind of setup, having a ever-changing date in the SVG yields many unwanted VCS diffs which can now be avoided using this option.
- Fixed an issue with
_nvariable was improperly set (#443)
One word: inexcusable 🙄
- Fixed an issue with
writewhere layer opacity was included in the
strokeattribute instead of using
stroke-opacity, which, although compliant, was not compatible with Inkscape (#429)
This one is Inkscape’s fault. Using
stroke-opacity is “more compatible” anyways, so it’s a good move regardless.
- Fixed an issue with
vpype --helpwhere commands from plug-ins would not be listed (#444)
I ran into an issue with Click where a sub-command plug-in using APIs from the top-level command’s package (a scheme widely used by vpype and its plug-ins) would fail because of circular imports. The workaround I used in vpype 1.9 meant that plug-ins were no longer listed in
vpype --help. This is fixed now, but this may not be the end of the story. I tried – and failed – to reproduce the original issue in a minimal demo project and I’ll have to further dig into this someday.
- Fixed a minor issue where plug-ins would be reloaded each time
vpype_cli.execute()is called (#444)
By “minor”, I mean that this amounted to a tiny performance hit for Python scripts using vpype’s
execute() API multiple times.
- Fixed a rendering inconsistency in the viewer where the ruler width could vary by one pixel depending on the OpenGL driver/GPU/OS combination (#448)
The ruler of vpype’s viewer is supposed to be 20px wide, but it turns out that either of the horizontal or the vertical one was 21px wide instead. Which one? It depends on the platform and my Intel/AMD- and M1-based laptops disagreed on the matter! 😲
It took me a while to discover that this is due to drawing lines at a 0.5px offset with respect to the pixel grid, leading to unpredictable rounding behavior. This is basically what happens when drawing horizontal or vertical lines with integer coordinates, and I wrote about it a few days ago.
You’d think that not one soul would care about this, but some of my tests are based on comparing newly rendered images of the viewer with previously-generated reference images, and those tests would fail on my new M1 Mac.
Click types provide support for property and expression substitution. They were missing from vpype 1.9 because they aren’t needed internally. Plug-ins, however, wanted them, including flow imager and my new vpype-perspective.
vpype.Document.add_to_sources()to also modify the
This will simplify the code needed to handle sources in plug-ins.
- Changed the parameter name of both
default_pen_opacity(breaking change) (#448)
override_pen_opacityboolean parameters to both
- Added a
set_date:bool = Trueargument to
- Changed the default value of
vpype.read_svg()(and friends) to
svgelementbetter handle missing
These are the API counterparts of some of the changes described before.
- Added support for Python 3.10 and dropped support for Python 3.7 (#417)
Walruses have appeared already! 🦭
- Updated code base with modern typing syntax (using pyupgrade) (#427)
I was this year old when I learned that you can use modern typing syntax (such as
list[int] | None) with older Python versions thanks to this statement:
from __future__ import annotations
I swiftly ran pyupgrade on the entire code base to bring it up to date.
- Updated the documentation template (#428)
It looks cleaner now IMO, though there is still a whole lot that could be improved.
- Updated installation instructions to use pipx (#428)
I have yet to get over how long it took me to realize this! 😳 I’m sorry for everyone who has struggled with virtual environments to install vpype!