Vectory
Purpose
Vectory is a Ruby gem that performs pairwise vector image conversions for common vector image formats (EPS, PS, EMF, SVG).
Vectory shall give you a glorious vectory over EPS files.
Installation
Prerequisites
Vectory relies on the following software to be installed:
|
Note
|
Inkscape 1.3.1+ does not work properly with EPS/PS on Windows. To avoid this issue, the 1.3.0 version of Inkscape can be used. |
Gem install
gem install vectoryOr include it in your gemspec.
Usage
$ vectory [-o {output-file-name}] -f {format} {input-file-name}Where,
format-
the desired output format (one of:
svg,eps,ps,emf) input-file-name-
file path to the input file
output-file-name-
file path to the desired output file (with the file extension) (default: writes to a current directory by the input filename with an extension changed to a desired format)
Using the Ruby library
Some examples:
Take EMF as a path to a file and return SVG as a string:
path = "path/to/file.emf"
Vectory::Emf.from_path(path).to_svg.contentTake EPS as a string and return EMF as a path to a file:
# NOTE: content is shortened for readability
content = "%!PS-Adobe-3.0 EPSF-3.0\n ... %%Trailer"
Vectory::Eps.from_content(content).to_emf.write.pathTake SVG as a datauri and return EMF as a datauri:
# NOTE: datauri is shortened for readability
uri = "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0 ... GRkYiLz48L3N2Zz4="
Vectory::Datauri.new(uri).to_vector.to_emf.to_uri.contentWhat is supported?
There are several vector classes which support conversion between each other:
Vectory::Eps
Vectory::Ps
Vectory::Emf
Vectory::SvgEach of them can be instantiated in several ways:
Vectory::Eps.from_path("images/img.eps")
Vectory::Eps.from_content("%!PS-Adobe-3.0...")
Vectory::Eps.from_datauri("data:image/svg+xml;base64,PHN2 ... 2Zz4=")
Vectory::Eps.from_node(Nokogiri::XML(
"<image mimetype="application/postscript" alt="3">
%!PS-Adobe-3.0 EPSF-3.0 ...
</image>"
).child)Converting to other formats:
Vectory::Eps.from_content(content).to_ps
Vectory::Eps.from_content(content).to_emf
Vectory::Eps.from_content(content).to_svgSeveral ways of getting content of an object:
Vectory::Eps.from_content(content).to_svg.content
Vectory::Eps.from_content(content).to_svg.to_uri.content # as datauri
Vectory::Eps.from_content(content).to_svg.write.pathDatauri
Also there is the Vectory::Datauri class which represents vectory images in
the datauri format.
Convert an SVG datauri to a plain SVG:
# NOTE: datauri is shortened for readability
uri = "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0 ... GRkYiLz48L3N2Zz4="
Vectory::Datauri.new(uri).to_vector.contentConvert an EPS file to its datauri representation:
eps = Vectory::Eps.from_path("img.eps")
Vectory::Datauri.from_vector(eps).contentThere is also a simplified API for this case:
Vectory::Eps.from_path("img.eps").to_uri.contentSVG mapping (for the metanorma project)
Vectory can integrate SVG files into XML or HTML, respecting internal id and link references. It supports ID disambiguation for multi-document and multi-svgmap scenarios.
Basic usage
xml_string = Vectory::SvgMapping.from_path("doc.xml").to_xmlID suffixing for uniqueness
When processing multiple documents or multiple svgmaps within a document, SVG IDs must be unique to avoid conflicts. Vectory applies two types of suffixes:
- ID suffix (Cross-document uniqueness)
-
An optional suffix derived from document or container identity (e.g.,
_ISO_17301-1_2016). This provides uniqueness across documents or collections. - Index suffix (Multi-svgmap uniqueness)
-
Automatically applied based on the svgmap’s position in the document (0, 1, 2, …). Formatted as a 9-digit zero-padded number (e.g.,
_000000000,_000000001). This provides uniqueness when a document contains multiple svgmaps.
Original ID: fig1
With ID suffix only: fig1_ISO_17301-1_2016
With both suffixes: fig1_ISO_17301-1_2016_000000000Usage with ID suffix
mapping = Vectory::SvgMapping.new(doc, "", id_suffix: "_ISO_17301-1_2016")
xml_string = mapping.call.to_xmlInput format
The input XML must support the svgmap tag with link mapping.
<svgmap id="_4072bdcb-5895-4821-b636-5795b96787cb">
<figure><image src="action_schemaexpg1.svg"/></figure>
<target href="mn://action_schema">
<xref target="ref1">Computer</xref>
</target>
<target href="http://www.example.com">
<link target="http://www.example.com">Phone</link>
</target>
</svgmap><?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style type="text/css">
#Layer_1 { fill:none }
svg[id = 'Layer_1'] { fill:none }
.st0{fill:none;stroke:#000000;stroke-miterlimit:10;}
</style>
<a xlink:href="mn://action_schema" xlink:dummy="Layer_1">
<rect x="123.28" y="273.93" class="st0" width="88.05" height="41.84"/>
</a>
<a xlink:href="mn://basic_attribute_schema" >
<rect x="324.69" y="450.52" class="st0" width="132.62" height="40.75"/>
</a>
<a xlink:href="mn://support_resource_schema" >
<rect x="324.69" y="528.36" class="st0" width="148.16" height="40.75"/>
</a>
</svg>Output format
<figure>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'
id='Layer_1_000000001' x='0px' y='0px' viewBox='0 0 595.28 841.89'>
<style> #Layer_1_000000001 { fill:none }</style>
<a xlink:href='#ref1' xlink:dummy='Layer_1_000000001'>
<rect x='123.28' y='273.93' class='st0' width='88.05' height='41.84'/>
</a>
<a xlink:href='mn://basic_attribute_schema'>
<rect x='324.69' y='450.52' class='st0' width='132.62' height='40.75'/>
</a>
<a xlink:href='mn://support_resource_schema'>
<rect x='324.69' y='528.36' class='st0' width='148.16' height='40.75'/>
</a>
</svg>
</figure>Inline SVG support
SVG can also be provided inline within the svgmap:
<svgmap id="_60dadf08-48d4-4164-845c-b4e293e00abd">
<figure>
<svg xmlns='http://www.w3.org/2000/svg' id='Layer_1'>
<a href="mn://action_schema">
<rect x="123.28" y="273.93" class="st0"/>
</a>
</svg>
</figure>
<target href="mn://action_schema">
<xref target="ref1">Computer</xref>
</target>
</svgmap>Data URI support
Images can be provided as data URIs:
<svgmap id="_60dadf08-48d4-4164-845c-b4e293e00abd">
<figure>
<image src='data:image/svg+xml;base64,PD94...'/>
</figure>
<target href="href1.htm">
<xref target="ref1">Computer</xref>
</target>
</svgmap>File system operations
An image object contains information where it is written. It can be obtained
with the #path API:
vector = Vectory::Eps.from_path("img.eps")
vector.pathBefore the first write it raises the NotWrittenToDiskError error:
vector.path # => raise NotWrittenToDiskErrorAfter writing it returns a path of the image on a disk:
vector.write
vector.path # => "/tmp/xxx/yyy"By default it writes to a temporary directory but it can be changed by providing an argument with a desired path:
vector.write("images/img.eps")
vector.path # => "images/img.eps"Since an image can be initially read from a disk, it also keeps an initial path. To avoid accidental overwrite, this path is used only for read-only purposes.
vector.initial_path # => "storage/images/img.eps"Additional properties
The following additional properties are supported:
Datauri#mime
Datauri#height
Datauri#width
Vector (Eps, Ps, Svg, Emf)
Vector#mime
Vector#size
Vector#file_size
Vector#height
Vector#widthDevelopment
Regenerating test fixtures
Some test fixtures are generated by external tools (Ghostscript, Inkscape, cairo). When these tools are updated, the reference files may need to be regenerated.
To regenerate all test fixtures:
bundle exec rake regenerate_fixturesThis will update the following files:
-
spec/examples/ps2eps/ref.eps- PS to EPS conversion reference -
spec/examples/ps2svg/ref.svg- PS to SVG conversion reference -
spec/examples/svg/doc2-ref.xml- SVG mapping reference
|
Note
|
After regenerating fixtures, review the changes to ensure they are expected before committing. |
Releasing
Releasing is done automatically with GitHub Actions. Just bump and tag with
gem-release.
For a patch release (0.0.x) use:
gem bump --version patch --tag --pushFor a minor release (0.x.0) use:
gem bump --version minor --tag --pushContributing
Bug reports and pull requests are welcome on GitHub at: