summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm
diff options
context:
space:
mode:
Diffstat (limited to 'dviware/dvisvgm')
-rw-r--r--dviware/dvisvgm/ChangeLog356
-rw-r--r--dviware/dvisvgm/NEWS8
-rwxr-xr-xdviware/dvisvgm/configure22
-rw-r--r--dviware/dvisvgm/configure.ac4
-rw-r--r--dviware/dvisvgm/doc/dvisvgm.1134
-rw-r--r--dviware/dvisvgm/doc/dvisvgm.txt.in84
-rw-r--r--dviware/dvisvgm/src/CMapReader.cpp34
-rw-r--r--dviware/dvisvgm/src/CMapReader.hpp5
-rw-r--r--dviware/dvisvgm/src/DVIToSVGActions.cpp14
-rw-r--r--dviware/dvisvgm/src/DVIToSVGActions.hpp12
-rw-r--r--dviware/dvisvgm/src/EmSpecialHandler.cpp6
-rw-r--r--dviware/dvisvgm/src/EmSpecialHandler.hpp7
-rw-r--r--dviware/dvisvgm/src/FileFinder.cpp4
-rw-r--r--dviware/dvisvgm/src/FilePath.cpp21
-rw-r--r--dviware/dvisvgm/src/FilePath.hpp2
-rw-r--r--dviware/dvisvgm/src/PSInterpreter.cpp1
-rw-r--r--dviware/dvisvgm/src/PSInterpreter.hpp1
-rw-r--r--dviware/dvisvgm/src/PsSpecialHandler.cpp51
-rw-r--r--dviware/dvisvgm/src/PsSpecialHandler.hpp3
-rw-r--r--dviware/dvisvgm/src/SpecialActions.hpp3
-rw-r--r--dviware/dvisvgm/src/TpicSpecialHandler.cpp6
-rw-r--r--dviware/dvisvgm/src/XMLNode.hpp4
-rw-r--r--dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp33
-rw-r--r--dviware/dvisvgm/src/optimizer/GroupCollapser.hpp2
-rw-r--r--dviware/dvisvgm/src/optimizer/Makefile.am1
-rw-r--r--dviware/dvisvgm/src/optimizer/Makefile.in8
-rw-r--r--dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp2
-rw-r--r--dviware/dvisvgm/src/optimizer/TextSimplifier.cpp118
-rw-r--r--dviware/dvisvgm/src/optimizer/TextSimplifier.hpp30
-rw-r--r--dviware/dvisvgm/src/psdefs.cpp194
-rw-r--r--dviware/dvisvgm/tests/CMapReaderTest.cpp11
-rw-r--r--dviware/dvisvgm/tests/PSInterpreterTest.cpp1
32 files changed, 885 insertions, 297 deletions
diff --git a/dviware/dvisvgm/ChangeLog b/dviware/dvisvgm/ChangeLog
index c92c64aa7a..e7761560f4 100644
--- a/dviware/dvisvgm/ChangeLog
+++ b/dviware/dvisvgm/ChangeLog
@@ -1,8 +1,364 @@
+2019-11-14 Martin Gieseking <martin.gieseking@uos.de>
+
+ * NEWS, README.md:
+ updated NEWS and README
+
+2019-11-12 Martin Gieseking <martin.gieseking@uos.de>
+
+ * .appveyor.yml, .travis.yml, configure.ac, src/Doxyfile, src/version.hpp:
+ set version to 2.8.1
+
+ * doc/dvisvgm.txt.in:
+ some minor changes of the manual page
+
+2019-11-11 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/psdefs.cpp:
+ added newline at end of file
+
+2019-11-08 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/optimizer/GroupCollapser.hpp:
+ made GroupCollapser::moveAttributes() static
+
+ * doc/dvisvgm.txt.in, src/optimizer/Makefile.am,
+ src/optimizer/SVGOptimizer.cpp, src/optimizer/TextSimplifier.cpp,
+ src/optimizer/TextSimplifier.hpp:
+ added optimizer module TextSimplifier
+
+ * doc/dvisvgm.txt.in, src/XMLNode.hpp,
+ src/optimizer/AttributeExtractor.hpp, src/optimizer/TextSimplifier.cpp:
+ extended functionality of optimizer module 'simplify-text'
+- common
+ attributes of tspans are moved to the enclosing text element
+- tspans
+ without attributes are unwrapped
+
+2019-11-05 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/DVIToSVGActions.cpp, src/DVIToSVGActions.hpp,
+ src/EmSpecialHandler.cpp, src/EmSpecialHandler.hpp:
+ removed some constant initializers from constructors
+
+ * src/DVIToSVGActions.cpp, src/DVIToSVGActions.hpp,
+ src/EmSpecialHandler.cpp, src/PSInterpreter.cpp, src/PSInterpreter.hpp,
+ src/PsSpecialHandler.cpp, src/PsSpecialHandler.hpp,
+ src/SpecialActions.hpp, src/TpicSpecialHandler.cpp, src/psdefs.cpp,
+ tests/PSInterpreterTest.cpp:
+ added support for PS operator 'nulldevice'
+
+2019-10-30 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/FileFinder.cpp:
+ added PNG, JPEG, and SVG to supported formats of FileFinder
+
+ * src/PsSpecialHandler.cpp, src/PsSpecialHandler.hpp:
+ psfile: create absolute or relative paths to reference images
+depending
+ on the file location and the file path given by the user
+
+2019-10-29 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/FilePath.cpp, src/FilePath.hpp:
+ added method to check if a path is absolute or relative
+
+2019-10-27 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/CMapReader.cpp, src/CMapReader.hpp, tests/CMapReaderTest.cpp:
+ CMapReader: extracted common code; added test for 'begincidchar'
+
+2019-10-25 Martin Gieseking <martin.gieseking@uos.de>
+
+ * tests/Makefile.am, tests/create-makefile:
+ added missing gtest file to distribution
+
+ * tests/FontCacheTest.cpp:
+ remove temporary files created by FontCacheTest
+
+ * .appveyor.yml, .travis.yml, configure.ac, src/Doxyfile, src/version.hpp:
+ set version to 2.8
+
+ * NEWS:
+ updated NEWS
+
+2019-10-23 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/CMapReader.cpp, src/CMapReader.hpp:
+ CMapReader: added support for operator 'begincidchar' (patch by Akira
+ Kakuto)
+
+2019-10-20 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/DVIToSVG.cpp, src/DVIToSVG.hpp, src/DVIToSVGActions.cpp,
+ src/DVIToSVGActions.hpp, src/FilePath.cpp, src/FilePath.hpp,
+ src/HyperlinkManager.cpp, src/ImageToSVG.cpp, src/ImageToSVG.hpp,
+ src/PDFToSVG.hpp, src/SVGOutput.cpp, src/SVGOutput.hpp,
+ src/SpecialActions.hpp, tests/FilePathTest.cpp, tests/SVGOutputTest.cpp:
+ SVGOutput: renamed 'filename' to 'filepath'; changed return type to
+ FilePath
+
+2019-10-18 Martin Gieseking <martin.gieseking@uos.de>
+
+ * tests/gtest/include/gtest/gtest-death-test.h,
+ tests/gtest/include/gtest/gtest-matchers.h,
+ tests/gtest/include/gtest/gtest-message.h,
+ tests/gtest/include/gtest/gtest-param-test.h,
+ tests/gtest/include/gtest/gtest-printers.h,
+ tests/gtest/include/gtest/gtest-spi.h,
+ tests/gtest/include/gtest/gtest-test-part.h,
+ tests/gtest/include/gtest/gtest-typed-test.h,
+ tests/gtest/include/gtest/gtest.h,
+ tests/gtest/include/gtest/gtest_pred_impl.h,
+ tests/gtest/include/gtest/internal/gtest-death-test-internal.h,
+ tests/gtest/include/gtest/internal/gtest-filepath.h,
+ tests/gtest/include/gtest/internal/gtest-internal.h,
+ tests/gtest/include/gtest/internal/gtest-param-util.h,
+ tests/gtest/include/gtest/internal/gtest-port-arch.h,
+ tests/gtest/include/gtest/internal/gtest-port.h,
+ tests/gtest/include/gtest/internal/gtest-string.h,
+ tests/gtest/include/gtest/internal/gtest-type-util.h,
+ tests/gtest/src/gtest-all.cc, tests/gtest/src/gtest-death-test.cc,
+ tests/gtest/src/gtest-filepath.cc, tests/gtest/src/gtest-internal-inl.h,
+ tests/gtest/src/gtest-matchers.cc, tests/gtest/src/gtest-port.cc,
+ tests/gtest/src/gtest-printers.cc, tests/gtest/src/gtest-test-part.cc,
+ tests/gtest/src/gtest-typed-test.cc, tests/gtest/src/gtest.cc,
+ tests/gtest/src/gtest_main.cc, tests/testutil.hpp:
+ updated gtest to version 1.10
+
+ * src/PsSpecialHandler.cpp, src/PsSpecialHandler.hpp:
+ psfile: added support for PDF, JPG, PNG, and SVG files
+
+ * src/optimizer/TransformSimplifier.cpp,
+ src/optimizer/TransformSimplifier.hpp:
+ try to incorporate transformations into positional and size attributes
+
+ * src/PsSpecialHandler.cpp:
+ PS handler: minor code enhancements
+
+2019-10-17 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/PSInterpreter.cpp:
+ call GS with -dDELAYSAFER if GS version >= 9.50
+Ghostscript 9.50
+ introduced incompatible changes regarding SAFER which is now
+active by
+ default. This conflicts with DELAYBIND and WRITESYSTEMDICT, which
+ are
+necessary for dvisvgm to work
+ correctly.
+https://www.ghostscript.com/doc/9.50/Use.htm#Safer
+
+ * tests/HashFunctionTest.cpp:
+ adapted XXH128 tests to changes in xxhash 0.7.2
+
+2019-10-08 Martin Gieseking <martin.gieseking@uos.de>
+
+ * libs/xxHash/xxh3.h, libs/xxHash/xxhash.c, libs/xxHash/xxhash.h:
+ updated xxhash to version 0.7.2
+
+2019-10-06 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/EllipticalArc.cpp:
+ minor changes in EllipticalArc.cpp
+
+2019-09-19 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/PathClipper.cpp:
+ prevent warnings about uninitialized variables
+
+2019-09-17 Martin Gieseking <martin.gieseking@uos.de>
+
+ * libs/potrace/auxiliary.h, libs/potrace/bitmap.h, libs/potrace/config.h,
+ libs/potrace/curve.c, libs/potrace/curve.h, libs/potrace/decompose.c,
+ libs/potrace/decompose.h, libs/potrace/lists.h,
+ libs/potrace/potracelib.c, libs/potrace/potracelib.h,
+ libs/potrace/progress.h, libs/potrace/trace.c, libs/potrace/trace.h:
+ updated potrace to version 1.16
+
+ * src/Bitmap.cpp, src/Bitmap.hpp:
+ minor code cleanup of Bitmap class
+
+2019-09-12 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/GraphicsPath.hpp:
+ renamed misspelled variables 'largeArgFlag' to 'largeArcFlag'
+
+ * src/EllipticalArc.cpp:
+ removed #include "debug.h"
+
+2019-09-03 Martin Gieseking <martin.gieseking@uos.de>
+
+ * libs/woff2/src/glyph.cc, libs/woff2/src/normalize.cc:
+ fixed two compiler warnings in libwoff2
+regarding signed/unsigned
+ comparison
+
+2019-08-27 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/XXHashFunction.hpp, tests/HashFunctionTest.cpp:
+ simplified implementation of XXHashFunction::digestValue() and tests
+
+2019-08-26 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/StreamReader.cpp, src/StreamReader.hpp, src/VFReader.cpp,
+ tests/StreamReaderTest.cpp:
+ removed vector reference from StreamReader::readBytes()
+
+ * src/CRC32.cpp, src/CRC32.hpp, src/FontCache.cpp, src/FontCache.hpp,
+ src/HashFunction.cpp, src/HashFunction.hpp, src/Makefile.am,
+ src/StreamReader.cpp, src/StreamReader.hpp, src/StreamWriter.cpp,
+ src/StreamWriter.hpp, src/XXHashFunction.hpp, src/utility.hpp,
+ tests/CRC32Test.cpp, tests/FontCacheTest.cpp, tests/Makefile.am,
+ tests/StreamReaderTest.cpp, tests/StreamWriterTest.cpp:
+ replaced CRC32 cache checksums with XXH32 hashes
+
+2019-08-25 Martin Gieseking <martin.gieseking@uos.de>
+
+ * doc/dvisvgm.txt.in, libs/xxHash/xxhash.h, src/HashFunction.cpp,
+ src/XXHashFunction.hpp, tests/HashFunctionTest.cpp:
+ added support for the experimental 128-bit XXH3 algorithm
+
+2019-08-24 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/GraphicsPath.hpp:
+ avoid redundant sequences of closepath (z/Z) commands
+
+2019-08-19 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/GraphicsPath.hpp, tests/GraphicsPathTest.cpp:
+ added short versions of 'quadto' and 'cubicto' to GraphicsPath
+
+2019-08-18 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/TpicSpecialHandler.cpp, tests/TpicSpecialTest.cpp:
+ use class EllipticalArc to process arcs in tpic handler
+
+2019-08-15 Martin Gieseking <martin.gieseking@uos.de>
+
+ * libs/xxHash/Makefile.am, libs/xxHash/xxh3.h, libs/xxHash/xxhash.c,
+ libs/xxHash/xxhash.h:
+ updated xxhash to version 0.7.1
+
+ * src/DVIToSVG.cpp:
+ print error message on wrong page hash arguments
+
+2019-08-14 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/EllipticalArc.cpp, src/EllipticalArc.hpp, src/GraphicsPath.hpp,
+ tests/EllipticalArcTest.cpp, tests/GraphicsPathTest.cpp:
+ added GraphicsPath::approximateArcs()
+
+2019-08-13 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/Bezier.cpp, src/Bezier.hpp, src/EllipticalArc.cpp,
+ src/EllipticalArc.hpp, src/GraphicsPath.hpp, src/Makefile.am,
+ src/Pair.hpp, src/utility.cpp, src/utility.hpp,
+ tests/EllipticalArcTest.cpp, tests/Makefile.am:
+ added support of 'arcto' command to class GraphicsPath
+
+2019-08-08 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/Bezier.cpp, src/Bezier.hpp, src/PsSpecialHandler.cpp,
+ src/ShadingPatch.hpp, src/TensorProductPatch.cpp,
+ src/TensorProductPatch.hpp, src/TriangularPatch.cpp,
+ src/TriangularPatch.hpp, tests/BezierTest.cpp,
+ tests/TensorProductPatchTest.cpp, tests/TriangularPatchTest.cpp:
+ getBBox(): replaced reference parameter with return value
+
+ * src/PsSpecialHandler.cpp, src/ShadingPatch.hpp,
+ src/TensorProductPatch.cpp, src/TensorProductPatch.hpp,
+ src/TriangularPatch.cpp, src/TriangularPatch.hpp,
+ tests/TensorProductPatchTest.cpp, tests/TriangularPatchTest.cpp:
+ getBoundaryPath(): replaced reference parameter with return value
+
+2019-08-04 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/Color.cpp, src/Pair.hpp:
+ use std::hypot() to compute length of vector
+
+2019-08-01 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/MD5HashFunction.hpp:
+ removed 'explicit' from default constructor
+Work around for C++11 bug in
+ GCC 4.8:
+https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58255
+
+ * src/FontCache.cpp, src/FontEngine.cpp, src/FontWriter.cpp,
+ src/GraphicsPath.hpp, src/Pair.hpp, src/PathClipper.cpp,
+ src/TpicSpecialHandler.cpp, src/optimizer/CMakeLists.txt,
+ src/optimizer/Makefile.am, tests/FontCacheTest.cpp,
+ tests/GraphicsPathTest.cpp:
+ redesigned implementation of class GraphicsPath
+- use variant instances
+ to store path commands instead of plain structs
+- process commands in
+ visitor objects
+- removed GraphicsPath::Actions::draw() that exposed
+ implemenation details
+- simplify iterate() method
+
+2019-07-29 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/FontCache.cpp:
+ use direct string concatenation instead of temporary string stream
+
+2019-07-28 Martin Gieseking <martin.gieseking@uos.de>
+
+ * doc/Makefile.am:
+ added missing dependency to Makefile rule
+
+ * .appveyor.yml, .travis.yml, configure.ac, src/Doxyfile, src/version.hpp:
+ set version to 2.7.4
+
+ * NEWS:
+ updated NEWS
+
+2019-07-27 Martin Gieseking <martin.gieseking@uos.de>
+
+ * doc/Makefile.am:
+ moved generation of dvisvgm.sty to separate rule
+
+2019-07-26 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/Color.cpp:
+ use std::round to round color value (fixes #116)
+
+ * doc/Makefile.am, doc/dvisvgm.sty, doc/dvisvgm.txt.in,
+ doc/generate-dvisvgm-sty.xsl, doc/tweak-db-article.xsl,
+ doc/tweak-db-refentry.xsl:
+ show revision date given in doc/dvisvgm.txt on manual page
+rather than
+ the build date
+
+2019-07-25 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/TrueTypeFont.cpp:
+ call ofstream constructor with std::string instead of C string
+
+2019-07-23 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/MetafontWrapper.cpp:
+ replaced atoi() with stoi()
+
+2019-07-16 Martin Gieseking <martin.gieseking@uos.de>
+
+ * src/MetafontWrapper.cpp:
+ don't access moved string
+
+ * doc/dvisvgm.txt.in, src/dvisvgm.cpp:
+ changed cache location to $XDG_CACHE_HOME/dvisvgm
+or ~/.cache/dvisvgm if
+ $XDG_CACHE_HOME is not set (closes #112)
+
2019-07-12 Martin Gieseking <martin.gieseking@uos.de>
* .appveyor.yml, .travis.yml, configure.ac, src/Doxyfile, src/version.hpp:
set version to 2.7.3
+ * NEWS:
+ updated NEWS
+
2019-07-09 Martin Gieseking <martin.gieseking@uos.de>
* src/XMLString.cpp, src/utility.cpp, src/utility.hpp,
diff --git a/dviware/dvisvgm/NEWS b/dviware/dvisvgm/NEWS
index e59cf494a3..301a2de7a6 100644
--- a/dviware/dvisvgm/NEWS
+++ b/dviware/dvisvgm/NEWS
@@ -1,3 +1,9 @@
+dvisvgm-2.8.1 (2019-11-14)
+- added SVG optimizer module 'simplify-text'
+- added support for PS operator 'nulldevice'
+- copy absolute paths from special 'psfile' unchanged to the SVG file
+- some code refactorings
+
dvisvgm-2.8 (2019-10-25)
- added support for JPEG, PNG, PDF, and SVG files to special 'psfile'
- added support for Ghostscript 9.50
@@ -8,7 +14,7 @@ dvisvgm-2.8 (2019-10-25)
scaling components into positional and size attributes
- replaced CRC32 checksums used in cache files with XXH32 hashes
- updated potrace to version 1.16
-- updated xxHash to version 2.7.2
+- updated xxHash to version 0.7.2
- updated gtest to 1.10
- lots of code refactorings
diff --git a/dviware/dvisvgm/configure b/dviware/dvisvgm/configure
index 259c8bbde6..3cdad36f6d 100755
--- a/dviware/dvisvgm/configure
+++ b/dviware/dvisvgm/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for dvisvgm 2.8.
+# Generated by GNU Autoconf 2.69 for dvisvgm 2.8.1.
#
# Report bugs to <martin.gieseking@uos.de>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='dvisvgm'
PACKAGE_TARNAME='dvisvgm'
-PACKAGE_VERSION='2.8'
-PACKAGE_STRING='dvisvgm 2.8'
+PACKAGE_VERSION='2.8.1'
+PACKAGE_STRING='dvisvgm 2.8.1'
PACKAGE_BUGREPORT='martin.gieseking@uos.de'
PACKAGE_URL=''
@@ -1406,7 +1406,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures dvisvgm 2.8 to adapt to many kinds of systems.
+\`configure' configures dvisvgm 2.8.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1477,7 +1477,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of dvisvgm 2.8:";;
+ short | recursive ) echo "Configuration of dvisvgm 2.8.1:";;
esac
cat <<\_ACEOF
@@ -1625,7 +1625,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-dvisvgm configure 2.8
+dvisvgm configure 2.8.1
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2169,7 +2169,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by dvisvgm $as_me 2.8, which was
+It was created by dvisvgm $as_me 2.8.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2527,7 +2527,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-DATE="October 2019"
+DATE="November 2019"
ac_config_headers="$ac_config_headers config.h"
@@ -3157,7 +3157,7 @@ fi
# Define the identity of the package.
PACKAGE='dvisvgm'
- VERSION='2.8'
+ VERSION='2.8.1'
cat >>confdefs.h <<_ACEOF
@@ -20138,7 +20138,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by dvisvgm $as_me 2.8, which was
+This file was extended by dvisvgm $as_me 2.8.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -20204,7 +20204,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-dvisvgm config.status 2.8
+dvisvgm config.status 2.8.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/dviware/dvisvgm/configure.ac b/dviware/dvisvgm/configure.ac
index c091777385..6a85518935 100644
--- a/dviware/dvisvgm/configure.ac
+++ b/dviware/dvisvgm/configure.ac
@@ -4,8 +4,8 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
-AC_INIT([dvisvgm],[2.8],[martin.gieseking@uos.de])
-DATE="October 2019"
+AC_INIT([dvisvgm],[2.8.1],[martin.gieseking@uos.de])
+DATE="November 2019"
AC_CONFIG_SRCDIR(src)
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
diff --git a/dviware/dvisvgm/doc/dvisvgm.1 b/dviware/dvisvgm/doc/dvisvgm.1
index 27efd0582d..e6d6b8743f 100644
--- a/dviware/dvisvgm/doc/dvisvgm.1
+++ b/dviware/dvisvgm/doc/dvisvgm.1
@@ -2,12 +2,12 @@
.\" Title: dvisvgm
.\" Author: Martin Gieseking <martin.gieseking@uos.de>
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
-.\" Date: 2019-08-25
+.\" Date: 2019-11-12
.\" Manual: dvisvgm Manual
-.\" Source: dvisvgm 2.8
+.\" Source: dvisvgm 2.8.1
.\" Language: English
.\"
-.TH "DVISVGM" "1" "2019\-08\-25" "dvisvgm 2\&.8" "dvisvgm Manual"
+.TH "DVISVGM" "1" "2019\-11\-12" "dvisvgm 2\&.8\&.1" "dvisvgm Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@@ -545,6 +545,17 @@ is not given\&. You can use argument
to prevent that\&.
.RE
.PP
+\fBsimplify\-text\fR
+.RS 4
+If a
+\fBtext\fR
+element only contains whitespace nodes and
+\fBtspan\fR
+elements, all common inheritable attributes of the latter are moved to the enclosing text element\&. All
+\fBtspan\fR
+elements without further attributes are unwrapped\&.
+.RE
+.PP
\fBsimplify\-transform\fR
.RS 4
Tries to shorten all
@@ -664,10 +675,10 @@ provides a combined hash computed from the DVI data and the command\-line option
Since the page number isn\(cqt part of the file name by default, different DVI pages with identical contents get the same file name\&. Therefore, only the first one is converted while the others are skipped\&. To create separate files for each page, you can add the page number to the output pattern, e\&.g\&.
\fB\-\-output="%f\-%p\-%hc"\fR\&.
.sp
-By default, dvisvgm uses the XXH64 hash algorithm to compute the values provided through
+By default, dvisvgm uses the fast XXH64 hash algorithm to compute the values provided through
\fB%hd\fR
and
-\fB%hc\fR\&. It\(cqs extremely fast, and a 64\-bit hash should be sufficient for most documents with an average size of pages\&. Alternatively, XXH32 and MD5 can be used as well\&. The desired algorithm is specified by argument
+\fB%hc\fR\&. 64\-bit hashes should be sufficient for most documents with an average size of pages\&. Alternatively, XXH32 and MD5 can be used as well\&. The desired algorithm is specified by argument
\fIparams\fR
of option
\fB\-\-page\-hashes\fR\&. It takes one of the strings
@@ -1291,8 +1302,8 @@ pdfTeX user manual (\m[blue]https://ctan.org/pkg/pdftex\m[])\&.
\fBps\fR
.RS 4
The famous DVI driver
-\fIdvips\fR
-introduced its own set of specials in order to embed PostScript code into DVI files, which greatly improves the capabilities of DVI documents\&. One aim of dvisvgm is to completely evaluate all PostScript snippets and to convert as many of them as possible to SVG\&. In contrast to dvips, dvisvgm uses floating point arithmetics to compute the precise position of each graphic element, i\&.e\&. it doesn\(cqt round the coordinates\&. Therefore, the relative locations of the graphic elements may slightly differ from those computed by dvips\&.
+\fIdvips\fR (\m[blue]https://www.tug.org/texinfohtml/dvips.html\m[])
+introduced its own set of specials in order to embed PostScript code into DVI files, which greatly improves the capabilities of DVI documents\&. One aim of dvisvgm is to completely evaluate all PostScript fragments and to convert as many of them as possible to SVG\&. In contrast to dvips, dvisvgm uses floating point arithmetics to compute the precise position of each graphic element, i\&.e\&. it doesn\(cqt round the coordinates\&. Therefore, the relative locations of the graphic elements may slightly differ from those computed by dvips\&.
.sp
Since PostScript is a rather complex language, dvisvgm does not implement its own PostScript interpreter but relies on
Ghostscript (\m[blue]https://ghostscript.com\m[])
@@ -1329,66 +1340,75 @@ If you call dvisvgm with option
The TPIC special set defines instructions for drawing simple geometric objects\&. Some LaTeX packages, like eepic and tplot, use these specials to describe graphics\&.
.RE
.SH "EXAMPLES"
-.sp
-.if n \{\
+.PP
+\fBdvisvgm file\fR
.RS 4
-.\}
-.nf
-dvisvgm file
-.fi
-.if n \{\
+Converts the first page of
+\fIfile\&.dvi\fR
+to
+\fIfile\&.svg\fR\&.
.RE
-.\}
-.sp
-Converts the first page of \fIfile\&.dvi\fR to \fIfile\&.svg\fR\&.
-.sp
-.if n \{\
+.PP
+\fBdvisvgm \-p1\-5 file\fR
.RS 4
-.\}
-.nf
-dvisvgm \- < file\&.dvi
-.fi
-.if n \{\
+Converts the first five pages of
+\fIfile\&.dvi\fR
+to
+\fIfile\-1\&.svg\fR,\&...,\fIfile\-5\&.svg\fR\&.
.RE
-.\}
-.sp
-Converts the first page of \fIfile\&.dvi\fR to \fIstdin\&.svg\fR where the contents of \fIfile\&.dvi\fR is read from \fBstdin\fR\&.
-.sp
-.if n \{\
+.PP
+\fBdvisvgm \-p1\- file\fR
.RS 4
-.\}
-.nf
-dvisvgm \-z file
-.fi
-.if n \{\
+Converts all pages of
+\fIfile\&.dvi\fR
+to separate SVG files\&.
.RE
-.\}
-.sp
-Converts the first page of \fIfile\&.dvi\fR to \fIfile\&.svgz\fR with default compression level 9\&.
-.sp
-.if n \{\
+.PP
+\fBdvisvgm \-p1,3 \-O file\fR
.RS 4
-.\}
-.nf
-dvisvgm \-p5 \-z3 \-ba4\-l \-onewfile file
-.fi
-.if n \{\
+Converts the first and third page of
+\fIfile\&.dvi\fR
+to optimized SVG files\&.
.RE
-.\}
-.sp
-Converts the fifth page of \fIfile\&.dvi\fR to \fInewfile\&.svgz\fR with compression level 3\&. The bounding box is set to DIN/ISO A4 in landscape format\&.
-.sp
-.if n \{\
+.PP
+\fBdvisvgm \- < file\&.dvi\fR
.RS 4
-.\}
-.nf
-dvisvgm \-\-transform="R20,w/3,2h/5 T1cm,1cm S2,3" file
-.fi
-.if n \{\
+Converts the first page of
+\fIfile\&.dvi\fR
+to
+\fIstdin\&.svg\fR
+where the contents of
+\fIfile\&.dvi\fR
+is read from
+\fBstdin\fR\&.
+.RE
+.PP
+\fBdvisvgm \-z file\fR
+.RS 4
+Converts the first page of
+\fIfile\&.dvi\fR
+to
+\fIfile\&.svgz\fR
+with default compression level 9\&.
+.RE
+.PP
+\fBdvisvgm \-p5 \-z3 \-ba4\-l \-o newfile file\fR
+.RS 4
+Converts the fifth page of
+\fIfile\&.dvi\fR
+to
+\fInewfile\&.svgz\fR
+with compression level 3\&. The bounding box is set to DIN/ISO A4 in landscape format\&.
+.RE
+.PP
+\fBdvisvgm \-\-transform="R20,w/3,2h/5 T1cm,1cm S2,3" file\fR
+.RS 4
+Converts the first page of
+\fIfile\&.dvi\fR
+to
+\fIfile\&.svg\fR
+where three transformations are applied\&.
.RE
-.\}
-.sp
-Converts the first page of \fIfile\&.dvi\fR to \fIfile\&.svg\fR where three transformations are applied\&.
.SH "ENVIRONMENT"
.sp
dvisvgm uses the \fBkpathsea\fR library for locating the files that it opens\&. Hence, the environment variables described in the library\(cqs documentation influence the converter\&.
diff --git a/dviware/dvisvgm/doc/dvisvgm.txt.in b/dviware/dvisvgm/doc/dvisvgm.txt.in
index 02f3618a10..1b5c669252 100644
--- a/dviware/dvisvgm/doc/dvisvgm.txt.in
+++ b/dviware/dvisvgm/doc/dvisvgm.txt.in
@@ -22,7 +22,7 @@ Martin Gieseking <@PACKAGE_BUGREPORT@>
:man source: dvisvgm
:man version: @VERSION@
:man manual: dvisvgm Manual
-:revdate: 2019-08-25 20:14 +0200
+:revdate: 2019-11-12 20:21 +0100
Name
----
@@ -388,6 +388,11 @@ The following list describes the currently available optimizer modules.
of dvisvgm and was always applied by default. This behavior is retained, i.e. dvisvgm executes
this module even if option *--optimize* is not given. You can use argument 'none' to prevent that.
+ *simplify-text*;;
+ If a +text+ element only contains whitespace nodes and +tspan+ elements, all common inheritable
+ attributes of the latter are moved to the enclosing text element. All +tspan+ elements without
+ further attributes are unwrapped.
+
*simplify-transform*;;
Tries to shorten all 'transform' attributes. This module combines the transformation commands of
each attribute and decomposes the resulting transformation matrix into a sequence of basic
@@ -463,14 +468,14 @@ contents get the same file name. Therefore, only the first one is converted whil
skipped. To create separate files for each page, you can add the page number to the output pattern,
e.g. +--output="%f-%p-%hc"+.
+
-By default, dvisvgm uses the XXH64 hash algorithm to compute the values provided through +%hd+ and
-+%hc+. It's extremely fast, and a 64-bit hash should be sufficient for most documents with an average
-size of pages. Alternatively, XXH32 and MD5 can be used as well. The desired algorithm is specified
-by argument 'params' of option *--page-hashes*. It takes one of the strings +MD5+, +XXH32+, and +XXH64+,
-where the names can be given in lower case too, like +--page-hashes=md5+. Since version 0.7.1,
-xxHash provides an experimental 128-bit hash function, whose algorithm may still change with the next
-versions of the library. If the corresponding API is available, dvisvgm supports the new hash function
-and option *--page-hashes* additionally accepts the algorithm specifier +XXH128+.
+By default, dvisvgm uses the fast XXH64 hash algorithm to compute the values provided through +%hd+ and
++%hc+. 64-bit hashes should be sufficient for most documents with an average size of pages. Alternatively,
+XXH32 and MD5 can be used as well. The desired algorithm is specified by argument 'params' of option
+*--page-hashes*. It takes one of the strings +MD5+, +XXH32+, and +XXH64+, where the names can be given
+in lower case too, like +--page-hashes=md5+. Since version 0.7.1, xxHash provides an experimental 128-bit
+hash function, whose algorithm may still change with the next versions of the library. If the
+corresponding API is available, dvisvgm supports the new hash function and option *--page-hashes*
+additionally accepts the algorithm specifier +XXH128+.
+
Finally, option *--page-hashes* can take a second argument that must be separated by a comma.
Currently, only the two parameters 'list' and 'replace' are evaluated, e.g. +--page-hashes=md5,list+
@@ -800,12 +805,12 @@ information on the command syntax and semantics, see the documentation of +\pdfm
https://ctan.org/pkg/pdftex[pdfTeX user manual].
*ps*::
-The famous DVI driver 'dvips' introduced its own set of specials in order to embed PostScript code into DVI files,
-which greatly improves the capabilities of DVI documents. One aim of dvisvgm is to completely evaluate all
-PostScript snippets and to convert as many of them as possible to SVG. In contrast to dvips, dvisvgm uses
-floating point arithmetics to compute the precise position of each graphic element, i.e. it doesn't round the
-coordinates. Therefore, the relative locations of the graphic elements may slightly differ from those computed
-by dvips.
+The famous DVI driver https://www.tug.org/texinfohtml/dvips.html['dvips'] introduced its own set of specials
+in order to embed PostScript code into DVI files, which greatly improves the capabilities of DVI documents.
+One aim of dvisvgm is to completely evaluate all PostScript fragments and to convert as many of them as possible
+to SVG. In contrast to dvips, dvisvgm uses floating point arithmetics to compute the precise position of each
+graphic element, i.e. it doesn't round the coordinates. Therefore, the relative locations of the graphic elements
+may slightly differ from those computed by dvips.
+
Since PostScript is a rather complex language, dvisvgm does not implement its own PostScript interpreter
but relies on https://ghostscript.com[Ghostscript] instead. If the Ghostscript library was not linked to the
@@ -838,32 +843,29 @@ eepic and tplot, use these specials to describe graphics.
Examples
--------
-------------
-dvisvgm file
-------------
-Converts the first page of 'file.dvi' to 'file.svg'.
-
---------------------
-dvisvgm - < file.dvi
---------------------
-Converts the first page of 'file.dvi' to 'stdin.svg' where the contents of 'file.dvi'
-is read from *stdin*.
-
----------------
-dvisvgm -z file
----------------
-Converts the first page of 'file.dvi' to 'file.svgz' with default compression level 9.
-
--------------------------------------
-dvisvgm -p5 -z3 -ba4-l -onewfile file
--------------------------------------
-Converts the fifth page of 'file.dvi' to 'newfile.svgz' with compression level 3.
-The bounding box is set to DIN/ISO A4 in landscape format.
-
------------------------------------------------------
-dvisvgm --transform="R20,w/3,2h/5 T1cm,1cm S2,3" file
------------------------------------------------------
-Converts the first page of 'file.dvi' to 'file.svg' where three transformations are applied.
++dvisvgm file+::
+ Converts the first page of 'file.dvi' to 'file.svg'.
+
++dvisvgm -p1-5 file+::
+ Converts the first five pages of 'file.dvi' to 'file-1.svg',...,'file-5.svg'.
+
++dvisvgm -p1- file+::
+ Converts all pages of 'file.dvi' to separate SVG files.
+
++dvisvgm -p1,3 -O file+::
+ Converts the first and third page of 'file.dvi' to optimized SVG files.
+
++dvisvgm - < file.dvi+::
+ Converts the first page of 'file.dvi' to 'stdin.svg' where the contents of 'file.dvi' is read from *stdin*.
+
++dvisvgm -z file+::
+ Converts the first page of 'file.dvi' to 'file.svgz' with default compression level 9.
+
++dvisvgm -p5 -z3 -ba4-l -o newfile file+::
+ Converts the fifth page of 'file.dvi' to 'newfile.svgz' with compression level 3. The bounding box is set to DIN/ISO A4 in landscape format.
+
++dvisvgm --transform="R20,w/3,2h/5 T1cm,1cm S2,3" file+::
+ Converts the first page of 'file.dvi' to 'file.svg' where three transformations are applied.
[[environment]]
Environment
diff --git a/dviware/dvisvgm/src/CMapReader.cpp b/dviware/dvisvgm/src/CMapReader.cpp
index f873bded0a..6661afbbb1 100644
--- a/dviware/dvisvgm/src/CMapReader.cpp
+++ b/dviware/dvisvgm/src/CMapReader.cpp
@@ -32,10 +32,6 @@
using namespace std;
-CMapReader::CMapReader () : _inCMap(false) {
-}
-
-
/** Reads a cmap file and returns the corresponding CMap object.
* @param fname[in] name/path of cmap file
* @return CMap object representing the read data, or 0 if file could not be read */
@@ -155,37 +151,33 @@ static uint32_t parse_hexentry (InputReader &ir) {
}
-void CMapReader::op_begincidchar (InputReader &ir) {
+void CMapReader::parseCIDChars (InputReader &ir, bool isRange) {
if (!_tokens.empty() && _tokens.back().type() == Token::Type::NUMBER) {
ir.skipSpace();
int num_entries = static_cast<int>(popToken().numvalue());
while (num_entries > 0 && ir.peek() == '<') {
uint32_t first = parse_hexentry(ir);
- uint32_t cid;
+ uint32_t last = first;
+ if (isRange)
+ last = parse_hexentry(ir);
ir.skipSpace();
+ uint32_t cid;
if (!ir.parseUInt(cid))
throw CMapReaderException("invalid char entry (decimal value expected)");
- _cmap->addCIDRange(first, first, cid);
+ _cmap->addCIDRange(first, last, cid);
ir.skipSpace();
}
}
}
+
+void CMapReader::op_begincidchar (InputReader &ir) {
+ parseCIDChars(ir, false);
+}
+
+
void CMapReader::op_begincidrange (InputReader &ir) {
- if (!_tokens.empty() && _tokens.back().type() == Token::Type::NUMBER) {
- ir.skipSpace();
- int num_entries = static_cast<int>(popToken().numvalue());
- while (num_entries > 0 && ir.peek() == '<') {
- uint32_t first = parse_hexentry(ir);
- uint32_t last = parse_hexentry(ir);
- uint32_t cid;
- ir.skipSpace();
- if (!ir.parseUInt(cid))
- throw CMapReaderException("invalid range entry (decimal value expected)");
- _cmap->addCIDRange(first, last, cid);
- ir.skipSpace();
- }
- }
+ parseCIDChars(ir, true);
}
diff --git a/dviware/dvisvgm/src/CMapReader.hpp b/dviware/dvisvgm/src/CMapReader.hpp
index b39ca9874c..3e5b841e0c 100644
--- a/dviware/dvisvgm/src/CMapReader.hpp
+++ b/dviware/dvisvgm/src/CMapReader.hpp
@@ -50,13 +50,13 @@ class CMapReader {
};
public:
- CMapReader ();
std::unique_ptr<CMap> read (const std::string &fname);
std::unique_ptr<CMap> read (std::istream &is, const std::string &name);
protected:
Token popToken () {Token t=_tokens.back(); _tokens.pop_back(); return t;}
void executeOperator (const std::string &op, InputReader &ir);
+ void parseCIDChars (InputReader &ir, bool isRange);
void op_beginbfchar (InputReader &ir);
void op_beginbfrange (InputReader &ir);
void op_begincidchar (InputReader &ir);
@@ -68,11 +68,10 @@ class CMapReader {
private:
std::unique_ptr<SegmentedCMap> _cmap; ///< pointer to CMap being read
std::vector<Token> _tokens; ///< stack of tokens to be processed
- bool _inCMap; ///< operator begincmap has been executed
+ bool _inCMap=false; ///< true if operator begincmap has been executed
};
-
struct CMapReaderException : public MessageException {
explicit CMapReaderException (const std::string &msg) : MessageException(msg) {}
};
diff --git a/dviware/dvisvgm/src/DVIToSVGActions.cpp b/dviware/dvisvgm/src/DVIToSVGActions.cpp
index 3d53d1bd4d..bb8e4d6f84 100644
--- a/dviware/dvisvgm/src/DVIToSVGActions.cpp
+++ b/dviware/dvisvgm/src/DVIToSVGActions.cpp
@@ -32,14 +32,6 @@
using namespace std;
-DVIToSVGActions::DVIToSVGActions (DVIToSVG &dvisvg, SVGTree &svg)
- : _svg(svg), _dvireader(&dvisvg), _bgcolor(Color::TRANSPARENT)
-{
- _currentFontNum = -1;
- _pageCount = 0;
-}
-
-
void DVIToSVGActions::reset() {
_usedChars.clear();
_usedFonts.clear();
@@ -93,6 +85,9 @@ string DVIToSVGActions::getBBoxFormatString () const {
* @param[in] vertical true if we're in vertical mode
* @param[in] font font to be used */
void DVIToSVGActions::setChar (double x, double y, unsigned c, bool vertical, const Font &font) {
+ if (_outputLocked)
+ return;
+
// If we use SVG fonts there is no need to record all font name/char/size combinations
// because the SVG font mechanism handles this automatically. It's sufficient to
// record font names and chars. The various font sizes can be ignored here.
@@ -167,6 +162,9 @@ void DVIToSVGActions::setChar (double x, double y, unsigned c, bool vertical, co
* @param[in] height length of the vertical edges
* @param[in] width length of the horizontal edges */
void DVIToSVGActions::setRule (double x, double y, double height, double width) {
+ if (_outputLocked)
+ return;
+
// (x,y) is the lower left corner of the rectangle
auto rect = util::make_unique<XMLElement>("rect");
rect->addAttribute("x", x);
diff --git a/dviware/dvisvgm/src/DVIToSVGActions.hpp b/dviware/dvisvgm/src/DVIToSVGActions.hpp
index 88fa6028ee..e9adc63e38 100644
--- a/dviware/dvisvgm/src/DVIToSVGActions.hpp
+++ b/dviware/dvisvgm/src/DVIToSVGActions.hpp
@@ -42,7 +42,7 @@ class DVIToSVGActions : public DVIActions, public SpecialActions {
using BoxMap = std::unordered_map<std::string,BoundingBox>;
public:
- DVIToSVGActions (DVIToSVG &dvisvg, SVGTree &svg);
+ DVIToSVGActions (DVIToSVG &dvisvg, SVGTree &svg) : _svg(svg), _dvireader(&dvisvg) {}
void reset () override;
void setChar (double x, double y, unsigned c, bool vertical, const Font &f) override;
void setRule (double x, double y, double height, double width) override;
@@ -68,6 +68,9 @@ class DVIToSVGActions : public DVIActions, public SpecialActions {
void setX (double x) override {_dvireader->translateToX(x); _svg.setX(x);}
void setY (double y) override {_dvireader->translateToY(y); _svg.setY(y);}
void finishLine () override {_dvireader->finishLine();}
+ void lockOutput () override {_outputLocked = true;}
+ void unlockOutput () override {_outputLocked = false;}
+ bool outputLocked () const override {return _outputLocked;}
const SVGTree& svgTree () const override {return _svg;}
BoundingBox& bbox () override {return _bbox;}
BoundingBox& bbox (const std::string &name, bool reset=false) override;
@@ -83,12 +86,13 @@ class DVIToSVGActions : public DVIActions, public SpecialActions {
SVGTree &_svg;
BasicDVIReader *_dvireader;
BoundingBox _bbox;
- int _pageCount;
- int _currentFontNum;
+ int _pageCount=0;
+ int _currentFontNum=-1;
mutable CharMap _usedChars;
FontSet _usedFonts;
- Color _bgcolor;
+ Color _bgcolor=Color::TRANSPARENT;
BoxMap _boxes;
+ bool _outputLocked=false;
};
diff --git a/dviware/dvisvgm/src/EmSpecialHandler.cpp b/dviware/dvisvgm/src/EmSpecialHandler.cpp
index 49fdb11aa1..c93a798fd7 100644
--- a/dviware/dvisvgm/src/EmSpecialHandler.cpp
+++ b/dviware/dvisvgm/src/EmSpecialHandler.cpp
@@ -31,10 +31,6 @@
using namespace std;
-EmSpecialHandler::EmSpecialHandler () : _linewidth(0.4*72/72.27) {
-}
-
-
/** Computes the "cut vector" that is used to compute the line shape.
* Because each line has a width > 0 the actual shape of the line is a tetragon.
* The 4 vertices can be influenced by the cut parameter c that specifies
@@ -79,6 +75,8 @@ static DPair cut_vector (char cuttype, const DPair &linedir, double linewidth) {
* @param[in] lw line width in PS point units
* @param[in] actions object providing the actions that can be performed by the SpecialHandler */
static void create_line (const DPair &p1, const DPair &p2, char c1, char c2, double lw, SpecialActions &actions) {
+ if (actions.outputLocked())
+ return;
unique_ptr<XMLElement> node;
DPair dir = p2-p1;
if (dir.x() == 0 || dir.y() == 0 || (c1 == 'p' && c2 == 'p')) {
diff --git a/dviware/dvisvgm/src/EmSpecialHandler.hpp b/dviware/dvisvgm/src/EmSpecialHandler.hpp
index 546702c97c..242ef248ef 100644
--- a/dviware/dvisvgm/src/EmSpecialHandler.hpp
+++ b/dviware/dvisvgm/src/EmSpecialHandler.hpp
@@ -38,7 +38,6 @@ class EmSpecialHandler : public SpecialHandler {
};
public:
- EmSpecialHandler ();
const char* name () const override {return "em";}
const char* info () const override {return "line drawing statements of the emTeX special set";}
std::vector<const char*> prefixes() const override;
@@ -54,9 +53,9 @@ class EmSpecialHandler : public SpecialHandler {
private:
std::unordered_map<int, DPair> _points; ///< points defined by special em:point
- std::vector<Line> _lines; ///< list of lines with undefined end points
- double _linewidth; ///< global line width
- DPair _pos; ///< current position of "graphic cursor"
+ std::vector<Line> _lines; ///< list of lines with undefined end points
+ double _linewidth=0.4*72/72.27; ///< global line width
+ DPair _pos; ///< current position of "graphic cursor"
};
#endif
diff --git a/dviware/dvisvgm/src/FileFinder.cpp b/dviware/dvisvgm/src/FileFinder.cpp
index 87d22d73c9..dfd60412cb 100644
--- a/dviware/dvisvgm/src/FileFinder.cpp
+++ b/dviware/dvisvgm/src/FileFinder.cpp
@@ -174,6 +174,10 @@ const char* FileFinder::findFile (const std::string &fname, const char *ftype) c
{"pro", kpse_tex_ps_header_format},
{"sfd", kpse_sfd_format},
{"eps", kpse_pict_format},
+ {"png", kpse_pict_format},
+ {"jpg", kpse_pict_format},
+ {"jpeg", kpse_pict_format},
+ {"svg", kpse_pict_format},
{"pdf", kpse_tex_format},
};
auto it = types.find(ext);
diff --git a/dviware/dvisvgm/src/FilePath.cpp b/dviware/dvisvgm/src/FilePath.cpp
index 95fdab8c25..c859d30469 100644
--- a/dviware/dvisvgm/src/FilePath.cpp
+++ b/dviware/dvisvgm/src/FilePath.cpp
@@ -277,6 +277,11 @@ string FilePath::relative (string reldir, bool with_filename) const {
}
+string FilePath::relative (const FilePath &filepath, bool with_filename) const {
+ return relative(filepath.absolute(false), with_filename);
+}
+
+
/** Return the absolute or relative path whichever is shorter.
* @param[in] reldir absolute path to a directory
* @param[in] with_filename if false, the filename is omitted */
@@ -289,4 +294,18 @@ string FilePath::shorterAbsoluteOrRelative (string reldir, bool with_filename) c
bool FilePath::exists () const {
return empty() ? false : FileSystem::exists(absolute());
-} \ No newline at end of file
+}
+
+
+/** Checks if a given path is absolute or relative.
+ * @param[in] path path string to check
+ * @return true if path is absolute */
+bool FilePath::isAbsolute (string path) {
+ path = util::trim(path);
+#ifdef _WIN32
+ path = FileSystem::adaptPathSeperators(path);
+ if (path.length() >= 2 && path[1] == ':' && isalpha(path[0]))
+ path.erase(0, 2); // remove drive letter and colon
+#endif
+ return !path.empty() && path[0] == '/';
+}
diff --git a/dviware/dvisvgm/src/FilePath.hpp b/dviware/dvisvgm/src/FilePath.hpp
index abcea4a9dc..32a9be6814 100644
--- a/dviware/dvisvgm/src/FilePath.hpp
+++ b/dviware/dvisvgm/src/FilePath.hpp
@@ -48,6 +48,7 @@ class FilePath {
void set (const std::string &path, bool isfile, const std::string &current_dir);
std::string absolute (bool with_filename=true) const;
std::string relative (std::string reldir="", bool with_filename=true) const;
+ std::string relative (const FilePath &filepath, bool with_filename=true) const;
std::string shorterAbsoluteOrRelative (std::string reldir="", bool with_filename=true) const;
std::string basename () const;
std::string suffix () const;
@@ -58,6 +59,7 @@ class FilePath {
const std::string& filename () const {return _fname;}
void filename (const std::string &fname) {_fname = fname;}
bool exists () const;
+ static bool isAbsolute (std::string path);
protected:
void init (std::string path, bool isfile, std::string current_dir);
diff --git a/dviware/dvisvgm/src/PSInterpreter.cpp b/dviware/dvisvgm/src/PSInterpreter.cpp
index ba368f17ee..aa9cab2107 100644
--- a/dviware/dvisvgm/src/PSInterpreter.cpp
+++ b/dviware/dvisvgm/src/PSInterpreter.cpp
@@ -286,6 +286,7 @@ void PSInterpreter::callActions (InputReader &in) {
{"setlinewidth", { 1, &PSActions::setlinewidth}},
{"setmatrix", { 6, &PSActions::setmatrix}},
{"setmiterlimit", { 1, &PSActions::setmiterlimit}},
+ {"setnulldevice", { 1, &PSActions::setnulldevice}},
{"setopacityalpha",{ 1, &PSActions::setopacityalpha}},
{"setshapealpha", { 1, &PSActions::setshapealpha}},
{"setpagedevice", { 0, &PSActions::setpagedevice}},
diff --git a/dviware/dvisvgm/src/PSInterpreter.hpp b/dviware/dvisvgm/src/PSInterpreter.hpp
index 7d35261a59..8e75e3c5f1 100644
--- a/dviware/dvisvgm/src/PSInterpreter.hpp
+++ b/dviware/dvisvgm/src/PSInterpreter.hpp
@@ -71,6 +71,7 @@ struct PSActions {
virtual void setlinewidth (std::vector<double> &p) =0;
virtual void setmatrix (std::vector<double> &p) =0;
virtual void setmiterlimit (std::vector<double> &p) =0;
+ virtual void setnulldevice (std::vector<double> &p) =0;
virtual void setopacityalpha (std::vector<double> &p) =0;
virtual void setshapealpha (std::vector<double> &p) =0;
virtual void setpagedevice (std::vector<double> &p) =0;
diff --git a/dviware/dvisvgm/src/PsSpecialHandler.cpp b/dviware/dvisvgm/src/PsSpecialHandler.cpp
index 871a34b4f2..405da48434 100644
--- a/dviware/dvisvgm/src/PsSpecialHandler.cpp
+++ b/dviware/dvisvgm/src/PsSpecialHandler.cpp
@@ -213,7 +213,8 @@ bool PsSpecialHandler::process (const string &prefix, istream &is, SpecialAction
else if (prefix == "psfile=" || prefix == "PSfile=" || prefix == "pdffile=") {
if (_actions) {
StreamInputReader in(is);
- const string fname = in.getQuotedString(in.peek() == '"' ? "\"" : nullptr);
+ string fname = in.getQuotedString(in.peek() == '"' ? "\"" : nullptr);
+ fname = FileSystem::adaptPathSeperators(fname);
FileType fileType = FileType::EPS;
if (prefix == "pdffile")
fileType = FileType::PDF;
@@ -295,15 +296,6 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
if (fname == "/dev/null")
return;
- string filepath;
- if (const char *path = FileFinder::instance().lookup(fname, false))
- filepath = FileSystem::adaptPathSeperators(path);
- if ((filepath.empty() || !FileSystem::exists(filepath)) && FileSystem::exists(fname))
- filepath = fname;
- if (filepath.empty()) {
- Message::wstream(true) << "file '" << fname << "' not found\n";
- return;
- }
map<string,string>::const_iterator it;
// bounding box of EPS figure in PS point units (lower left and upper right corner)
@@ -362,7 +354,7 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
_actions->setY(0);
moveToDVIPos();
- auto imgNode = createImageNode(filetype, filepath, pageno, BoundingBox(llx, lly, urx, ury), clipToBbox);
+ auto imgNode = createImageNode(filetype, fname, pageno, BoundingBox(llx, lly, urx, ury), clipToBbox);
if (imgNode) { // has anything been drawn?
Matrix matrix(1);
if (filetype == FileType::EPS || filetype == FileType::PDF)
@@ -391,23 +383,34 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
/** Creates an XML element containing the image data depending on the file type.
* @param[in] type file type of the image
- * @param[in] pathstr absolute path to image file
+ * @param[in] fname file name/path of image file
* @param[in] pageno number of page to process (PDF only)
* @param[in] bbox bounding box of the image
* @param[in] clip if true, the image is clipped to its bounding box
* @return pointer to the element or nullptr if there's no image data */
-unique_ptr<XMLElement> PsSpecialHandler::createImageNode (FileType type, const string &pathstr, int pageno, BoundingBox bbox, bool clip) {
+unique_ptr<XMLElement> PsSpecialHandler::createImageNode (FileType type, const string &fname, int pageno, BoundingBox bbox, bool clip) {
unique_ptr<XMLElement> node;
- if (type == FileType::BITMAP || type == FileType::SVG) {
+ string pathstr;
+ if (const char *path = FileFinder::instance().lookup(fname, false))
+ pathstr = FileSystem::adaptPathSeperators(path);
+ if ((pathstr.empty() || !FileSystem::exists(pathstr)) && FileSystem::exists(fname))
+ pathstr = fname;
+ if (pathstr.empty())
+ Message::wstream(true) << "file '" << fname << "' not found\n";
+ else if (type == FileType::BITMAP || type == FileType::SVG) {
node = util::make_unique<XMLElement>("image");
node->addAttribute("x", 0);
node->addAttribute("y", 0);
node->addAttribute("width", bbox.width());
node->addAttribute("height", bbox.height());
- // add path to image file relative to SVG file being generated
- FilePath imgPath(pathstr);
- string svgPathStr = _actions->getSVGFilePath(pageno).absolute(false);
- node->addAttribute("xlink:href", imgPath.relative(svgPathStr));
+
+ // Only reference the image with an absolute path if either an absolute path was given by the user
+ // or a given plain filename is not present in the current working directory but was found through
+ // the FileFinder, i.e. it's usually located somewhere in the texmf tree.
+ string href = pathstr;
+ if (!FilePath::isAbsolute(fname) && (fname.find('/') != string::npos || FilePath(fname).exists()))
+ href = FilePath(pathstr).relative(FilePath(_actions->getSVGFilePath(pageno)));
+ node->addAttribute("xlink:href", href);
}
else { // PostScript or PDF
// clip image to its bounding box if flag 'clip' is given
@@ -1228,6 +1231,18 @@ void PsSpecialHandler::executed () {
_actions->progress("ps");
}
+
+/** This method is called by PSInterpreter if the status of the output devices has changed.
+ * @param[in] p 1 if output device is the nulldevice, 1 otherwise */
+void PsSpecialHandler::setnulldevice (vector<double> &p) {
+ if (_actions) {
+ if (p[0] != 0)
+ _actions->lockOutput(); // prevent further SVG output
+ else
+ _actions->unlockOutput(); // enable SVG output again
+ }
+}
+
////////////////////////////////////////////
void PsSpecialHandler::ClippingStack::pushEmptyPath () {
diff --git a/dviware/dvisvgm/src/PsSpecialHandler.hpp b/dviware/dvisvgm/src/PsSpecialHandler.hpp
index 27da9c8471..c7a8a97815 100644
--- a/dviware/dvisvgm/src/PsSpecialHandler.hpp
+++ b/dviware/dvisvgm/src/PsSpecialHandler.hpp
@@ -104,7 +104,7 @@ class PsSpecialHandler : public SpecialHandler, protected PSActions {
void executeAndSync (std::istream &is, bool updatePos);
void processHeaderFile (const char *fname);
void imgfile (FileType type, const std::string &fname, const std::map<std::string,std::string> &attr);
- std::unique_ptr<XMLElement> createImageNode (FileType type, const std::string &pathstr, int pageno, BoundingBox bbox, bool clip);
+ std::unique_ptr<XMLElement> createImageNode (FileType type, const std::string &fname, int pageno, BoundingBox bbox, bool clip);
void dviEndPage (unsigned pageno, SpecialActions &actions) override;
void clip (Path path, bool evenodd);
void processSequentialPatchMesh (int shadingTypeID, ColorSpace cspace, VectorIterator<double> &it);
@@ -146,6 +146,7 @@ class PsSpecialHandler : public SpecialHandler, protected PSActions {
void setlinewidth (std::vector<double> &p) override {_linewidth = scale(p[0] ? p[0] : 0.5);}
void setmatrix (std::vector<double> &p) override;
void setmiterlimit (std::vector<double> &p) override {_miterlimit = p[0];}
+ void setnulldevice (std::vector<double> &p) override;
void setopacityalpha (std::vector<double> &p) override {_opacityalpha = p[0];}
void setshapealpha (std::vector<double> &p) override {_shapealpha = p[0];}
void setpagedevice (std::vector<double> &p) override;
diff --git a/dviware/dvisvgm/src/SpecialActions.hpp b/dviware/dvisvgm/src/SpecialActions.hpp
index 3a510e9ea3..7e53303792 100644
--- a/dviware/dvisvgm/src/SpecialActions.hpp
+++ b/dviware/dvisvgm/src/SpecialActions.hpp
@@ -57,6 +57,9 @@ class SpecialActions {
virtual std::string getBBoxFormatString () const =0;
virtual void progress (const char *id) {}
virtual int getDVIStackDepth () const {return 0;}
+ virtual void lockOutput () {}
+ virtual void unlockOutput () {}
+ virtual bool outputLocked () const {return false;}
static double PROGRESSBAR_DELAY; ///< progress bar doesn't appear before this time has elapsed (in sec)
};
diff --git a/dviware/dvisvgm/src/TpicSpecialHandler.cpp b/dviware/dvisvgm/src/TpicSpecialHandler.cpp
index 7bfb7937eb..b97fa439af 100644
--- a/dviware/dvisvgm/src/TpicSpecialHandler.cpp
+++ b/dviware/dvisvgm/src/TpicSpecialHandler.cpp
@@ -101,7 +101,7 @@ static unique_ptr<XMLElement> create_ellipse_element (double cx, double cy, doub
* @param[in] ddist dash/dot distance of line in PS point units (0:solid line, >0:dashed line, <0:dotted line)
* @param[in] actions object providing the actions that can be performed by the SpecialHandler */
void TpicSpecialHandler::drawLines (double ddist, SpecialActions &actions) {
- if (!_points.empty() && (_penwidth > 0 || _grayLevel >= 0)) {
+ if (!_points.empty() && (_penwidth > 0 || _grayLevel >= 0) && !actions.outputLocked()) {
unique_ptr<XMLElement> elem;
if (_points.size() == 1) {
const DPair &p = _points.back();
@@ -147,7 +147,7 @@ void TpicSpecialHandler::drawLines (double ddist, SpecialActions &actions) {
* @param[in] ddist length of dashes and gaps
* @param[in] actions object providing the actions that can be performed by the SpecialHandler */
void TpicSpecialHandler::drawSplines (double ddist, SpecialActions &actions) {
- if (!_points.empty() && _penwidth > 0) {
+ if (!_points.empty() && _penwidth > 0 && !actions.outputLocked()) {
const size_t numPoints = _points.size();
if (numPoints < 3) {
_grayLevel = -1;
@@ -197,7 +197,7 @@ void TpicSpecialHandler::drawSplines (double ddist, SpecialActions &actions) {
* @param[in] angle2 ending angle (clockwise) relative to x-axis
* @param[in] actions object providing the actions that can be performed by the SpecialHandler */
void TpicSpecialHandler::drawArc (double cx, double cy, double rx, double ry, double angle1, double angle2, SpecialActions &actions) {
- if (_penwidth > 0 || _grayLevel >= 0) {
+ if ((_penwidth > 0 || _grayLevel >= 0) && !actions.outputLocked()) {
cx += actions.getX();
cy += actions.getY();
unique_ptr<XMLElement> elem;
diff --git a/dviware/dvisvgm/src/XMLNode.hpp b/dviware/dvisvgm/src/XMLNode.hpp
index a8def772d8..b4e5ed6f2b 100644
--- a/dviware/dvisvgm/src/XMLNode.hpp
+++ b/dviware/dvisvgm/src/XMLNode.hpp
@@ -144,14 +144,15 @@ class XMLElement : public XMLNode {
XMLNode* lastChild () const {return _lastChild;}
std::ostream& write (std::ostream &os) const override;
bool empty () const {return !_firstChild;}
-// const Children& children () const {return _children;}
Attributes& attributes () {return _attributes;}
+ const Attributes& attributes () const {return _attributes;}
XMLNodeIterator begin () {return XMLNodeIterator(_firstChild.get());}
XMLNodeIterator end () {return XMLNodeIterator(nullptr);}
ConstXMLNodeIterator begin () const {return ConstXMLNodeIterator(_firstChild.get());}
ConstXMLNodeIterator end () const {return ConstXMLNodeIterator(nullptr);}
const std::string& name () const {return _name;}
const XMLElement* toElement () const override {return this;}
+ const Attribute* getAttribute (const std::string &name) const;
static std::unique_ptr<XMLNode> remove (XMLNode *child);
static XMLElement* wrap (XMLNode *first, XMLNode *last, const std::string &name);
@@ -159,7 +160,6 @@ class XMLElement : public XMLNode {
protected:
Attribute* getAttribute (const std::string &name);
- const Attribute* getAttribute (const std::string &name) const;
XMLNode* insertFirst (std::unique_ptr<XMLNode> child);
XMLNode* insertLast (std::unique_ptr<XMLNode> child);
diff --git a/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp b/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp
index 8cb9de5bab..c882e8b4c8 100644
--- a/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp
+++ b/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp
@@ -27,35 +27,32 @@
/** Moves common attributes of adjacent elements to enclosing groups. */
class AttributeExtractor : public OptimizerModule {
- friend class GroupCollapser;
- using Attribute = XMLElement::Attribute;
+ using Attribute = XMLElement::Attribute;
- /** Represents a range of adjacent nodes where all elements have a common attribute. */
- struct AttributeRun {
- public:
- AttributeRun (const Attribute &attr, XMLElement *first);
- XMLNode* first () {return _first;}
- XMLNode* last () {return _last;}
-// XMLNodeIterator begin () {return XMLNodeIterator(_first);}
-// XMLNodeIterator end () {return XMLNodeIterator(_last);}
- int length () const {return _length;}
+ /** Represents a range of adjacent nodes where all elements have a common attribute. */
+ struct AttributeRun {
+ public:
+ AttributeRun (const Attribute &attr, XMLElement *first);
+ XMLNode* first () {return _first;}
+ XMLNode* last () {return _last;}
+ int length () const {return _length;}
- private:
- int _length; ///< run length excluding non-element nodes
- XMLNode *_first, *_last; ///< first and last node in run
- };
+ private:
+ int _length; ///< run length excluding non-element nodes
+ XMLNode *_first, *_last; ///< first and last node in run
+ };
public:
void execute (XMLElement*, XMLElement *context) override {execute(context, true);};
const char* info () const override;
+ static bool groupable (const XMLElement &elem);
+ static bool inheritable (const Attribute &attrib);
+ static bool extractable (const Attribute &attr, XMLElement &element);
protected:
void execute (XMLElement *context, bool recurse);
XMLNode* extractAttribute (XMLElement *elem);
bool extracted (const Attribute &attr) const;
- static bool groupable (const XMLElement &elem);
- static bool inheritable (const Attribute &attrib);
- static bool extractable (const Attribute &attr, XMLElement &element);
private:
std::set<std::string> _extractedAttributes;
diff --git a/dviware/dvisvgm/src/optimizer/GroupCollapser.hpp b/dviware/dvisvgm/src/optimizer/GroupCollapser.hpp
index 8f8a22d4ba..9b5eb01829 100644
--- a/dviware/dvisvgm/src/optimizer/GroupCollapser.hpp
+++ b/dviware/dvisvgm/src/optimizer/GroupCollapser.hpp
@@ -30,7 +30,7 @@ class GroupCollapser : public OptimizerModule {
const char* info () const override;
protected:
- bool moveAttributes (XMLElement &source, XMLElement &dest);
+ static bool moveAttributes (XMLElement &source, XMLElement &dest);
static bool collapsible (const XMLElement &elem);
static bool unwrappable (const XMLElement &source, const XMLElement &dest);
};
diff --git a/dviware/dvisvgm/src/optimizer/Makefile.am b/dviware/dvisvgm/src/optimizer/Makefile.am
index fec405662a..1039627843 100644
--- a/dviware/dvisvgm/src/optimizer/Makefile.am
+++ b/dviware/dvisvgm/src/optimizer/Makefile.am
@@ -7,6 +7,7 @@ liboptimizer_la_SOURCES = \
OptimizerModule.hpp \
RedundantElementRemover.hpp RedundantElementRemover.cpp \
SVGOptimizer.hpp SVGOptimizer.cpp \
+ TextSimplifier.hpp TextSimplifier.cpp \
TransformSimplifier.hpp TransformSimplifier.cpp \
WSNodeRemover.hpp WSNodeRemover.cpp
diff --git a/dviware/dvisvgm/src/optimizer/Makefile.in b/dviware/dvisvgm/src/optimizer/Makefile.in
index 45344c1933..d31d32c12a 100644
--- a/dviware/dvisvgm/src/optimizer/Makefile.in
+++ b/dviware/dvisvgm/src/optimizer/Makefile.in
@@ -107,7 +107,7 @@ CONFIG_CLEAN_VPATH_FILES =
LTLIBRARIES = $(noinst_LTLIBRARIES)
liboptimizer_la_LIBADD =
am_liboptimizer_la_OBJECTS = AttributeExtractor.lo GroupCollapser.lo \
- RedundantElementRemover.lo SVGOptimizer.lo \
+ RedundantElementRemover.lo SVGOptimizer.lo TextSimplifier.lo \
TransformSimplifier.lo WSNodeRemover.lo
liboptimizer_la_OBJECTS = $(am_liboptimizer_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
@@ -132,7 +132,7 @@ am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/AttributeExtractor.Plo \
./$(DEPDIR)/GroupCollapser.Plo \
./$(DEPDIR)/RedundantElementRemover.Plo \
- ./$(DEPDIR)/SVGOptimizer.Plo \
+ ./$(DEPDIR)/SVGOptimizer.Plo ./$(DEPDIR)/TextSimplifier.Plo \
./$(DEPDIR)/TransformSimplifier.Plo \
./$(DEPDIR)/WSNodeRemover.Plo
am__mv = mv -f
@@ -366,6 +366,7 @@ liboptimizer_la_SOURCES = \
OptimizerModule.hpp \
RedundantElementRemover.hpp RedundantElementRemover.cpp \
SVGOptimizer.hpp SVGOptimizer.cpp \
+ TextSimplifier.hpp TextSimplifier.cpp \
TransformSimplifier.hpp TransformSimplifier.cpp \
WSNodeRemover.hpp WSNodeRemover.cpp
@@ -428,6 +429,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/GroupCollapser.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RedundantElementRemover.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SVGOptimizer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TextSimplifier.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TransformSimplifier.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WSNodeRemover.Plo@am__quote@ # am--include-marker
@@ -596,6 +598,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/GroupCollapser.Plo
-rm -f ./$(DEPDIR)/RedundantElementRemover.Plo
-rm -f ./$(DEPDIR)/SVGOptimizer.Plo
+ -rm -f ./$(DEPDIR)/TextSimplifier.Plo
-rm -f ./$(DEPDIR)/TransformSimplifier.Plo
-rm -f ./$(DEPDIR)/WSNodeRemover.Plo
-rm -f Makefile
@@ -647,6 +650,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/GroupCollapser.Plo
-rm -f ./$(DEPDIR)/RedundantElementRemover.Plo
-rm -f ./$(DEPDIR)/SVGOptimizer.Plo
+ -rm -f ./$(DEPDIR)/TextSimplifier.Plo
-rm -f ./$(DEPDIR)/TransformSimplifier.Plo
-rm -f ./$(DEPDIR)/WSNodeRemover.Plo
-rm -f Makefile
diff --git a/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp b/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp
index 31cfeff6ff..0320f8b565 100644
--- a/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp
+++ b/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp
@@ -27,6 +27,7 @@
#include "AttributeExtractor.hpp"
#include "GroupCollapser.hpp"
#include "RedundantElementRemover.hpp"
+#include "TextSimplifier.hpp"
#include "TransformSimplifier.hpp"
#include "WSNodeRemover.hpp"
@@ -37,6 +38,7 @@ string SVGOptimizer::MODULE_SEQUENCE;
SVGOptimizer::SVGOptimizer (SVGTree *svg) : _svg(svg) {
// optimizer modules available to the user; must be listed in default order
// _moduleEntries.emplace_back(ModuleEntry("remove-ws", util::make_unique<WSNodeRemover>()));
+ _moduleEntries.emplace_back(ModuleEntry("simplify-text", util::make_unique<TextSimplifier>()));
_moduleEntries.emplace_back(ModuleEntry("group-attributes", util::make_unique<AttributeExtractor>()));
_moduleEntries.emplace_back(ModuleEntry("collapse-groups", util::make_unique<GroupCollapser>()));
_moduleEntries.emplace_back(ModuleEntry("simplify-transform", util::make_unique<TransformSimplifier>()));
diff --git a/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp b/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp
new file mode 100644
index 0000000000..23e0e386df
--- /dev/null
+++ b/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp
@@ -0,0 +1,118 @@
+/*************************************************************************
+** TextSimplifier.cpp **
+** **
+** This file is part of dvisvgm -- a fast DVI to SVG converter **
+** Copyright (C) 2005-2019 Martin Gieseking <martin.gieseking@uos.de> **
+** **
+** This program is free software; you can redistribute it and/or **
+** modify it under the terms of the GNU General Public License as **
+** published by the Free Software Foundation; either version 3 of **
+** the License, or (at your option) any later version. **
+** **
+** This program is distributed in the hope that it will be useful, but **
+** WITHOUT ANY WARRANTY; without even the implied warranty of **
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
+** GNU General Public License for more details. **
+** **
+** You should have received a copy of the GNU General Public License **
+** along with this program; if not, see <http://www.gnu.org/licenses/>. **
+*************************************************************************/
+
+#include <vector>
+#include "AttributeExtractor.hpp"
+#include "TextSimplifier.hpp"
+#include "../XMLNode.hpp"
+
+using namespace std;
+
+
+const char* TextSimplifier::info () const {
+ return "merge data of tspans into enclosing text elements";
+}
+
+
+/** Returns all common inheritable attributes of multiple elements. */
+static XMLElement::Attributes common_inheritable_attributes (const vector<XMLElement*> &elements) {
+ bool intersected=false;
+ XMLElement::Attributes commonAttribs;
+ for (const XMLElement *elem : elements) {
+ if (commonAttribs.empty()) {
+ if (intersected)
+ break;
+ for (const auto attrib : elem->attributes()) {
+ if (AttributeExtractor::inheritable(attrib))
+ commonAttribs.push_back(attrib);
+ }
+ }
+ else {
+ for (auto it = commonAttribs.begin(); it != commonAttribs.end();) {
+ auto *attrib = elem->getAttribute(it->name);
+ if (!attrib || attrib->value != it->value)
+ it = commonAttribs.erase(it);
+ else
+ ++it;
+ }
+ }
+ intersected = true;
+ }
+ return commonAttribs;
+}
+
+
+/** Returns all tspan elements of a text element if the latter doesn't contain
+ * any non-whitespace text nodes. Otherwise, the returned vector is empty.
+ * @param[in] textElement text element to check
+ * @return the tspan children of the text element */
+static vector<XMLElement*> get_tspans (XMLElement *textElement) {
+ vector<XMLElement*> tspans;
+ bool failed=false;
+ for (XMLNode *child : *textElement) {
+ if (child->toWSNode() || child->toComment())
+ continue;
+ if (child->toText())
+ failed = true;
+ else if (XMLElement *childElement = child->toElement()) {
+ if (childElement->name() != "tspan")
+ failed = true;
+ else
+ tspans.push_back(childElement);
+ }
+ if (failed) {
+ tspans.clear();
+ break;
+ }
+ }
+ return tspans;
+}
+
+
+void TextSimplifier::execute (XMLElement *context) {
+ if (!context)
+ return;
+ if (context->name() == "text") {
+ vector<XMLElement*> tspans = get_tspans(context);
+ vector<XMLElement::Attribute> attribs = common_inheritable_attributes(tspans);
+ if (!tspans.empty() && !attribs.empty()) {
+ // move all common tspan attributes to the parent text element
+ for (const XMLElement::Attribute &attr : attribs)
+ context->addAttribute(attr.name, attr.value);
+ // remove all common attributes from the tspan elements
+ for (XMLElement *tspan : tspans) {
+ for (const XMLElement::Attribute &attr : attribs)
+ tspan->removeAttribute(attr.name);
+ // unwrap the tspan if there are no remaining attributes
+ if (tspan->attributes().empty())
+ XMLElement::unwrap(tspan);
+ }
+ }
+ }
+ else {
+ XMLNode *node = context->firstChild();
+ while (node) {
+ XMLNode *next = node->next(); // keep safe pointer to next node
+ if (XMLElement *elem = node->toElement())
+ execute(elem);
+ node = next;
+ }
+ }
+}
diff --git a/dviware/dvisvgm/src/optimizer/TextSimplifier.hpp b/dviware/dvisvgm/src/optimizer/TextSimplifier.hpp
new file mode 100644
index 0000000000..86f44e5051
--- /dev/null
+++ b/dviware/dvisvgm/src/optimizer/TextSimplifier.hpp
@@ -0,0 +1,30 @@
+/*************************************************************************
+** TextSimplifier.hpp **
+** **
+** This file is part of dvisvgm -- a fast DVI to SVG converter **
+** Copyright (C) 2005-2019 Martin Gieseking <martin.gieseking@uos.de> **
+** **
+** This program is free software; you can redistribute it and/or **
+** modify it under the terms of the GNU General Public License as **
+** published by the Free Software Foundation; either version 3 of **
+** the License, or (at your option) any later version. **
+** **
+** This program is distributed in the hope that it will be useful, but **
+** WITHOUT ANY WARRANTY; without even the implied warranty of **
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
+** GNU General Public License for more details. **
+** **
+** You should have received a copy of the GNU General Public License **
+** along with this program; if not, see <http://www.gnu.org/licenses/>. **
+*************************************************************************/
+
+#pragma once
+
+#include "OptimizerModule.hpp"
+
+class TextSimplifier : public OptimizerModule {
+ public:
+ void execute (XMLElement *defs, XMLElement *context) override {execute(context);}
+ static void execute (XMLElement *context);
+ const char *info () const override;
+};
diff --git a/dviware/dvisvgm/src/psdefs.cpp b/dviware/dvisvgm/src/psdefs.cpp
index 39c938dac9..d38e2354e0 100644
--- a/dviware/dvisvgm/src/psdefs.cpp
+++ b/dviware/dvisvgm/src/psdefs.cpp
@@ -22,97 +22,103 @@
const char *PSInterpreter::PSDEFS =
"<</Install{matrix setmatrix}/HWResolution[72 72]/PageSize[10000 10000]/Imaging"
-"BBox null>>setpagedevice/@dodraw true store/@patcnt 0 store/@SD systemdict def"
-"/@UD userdict def true setglobal @SD/:save @SD/save get put @SD/:restore @SD/r"
-"estore get put @SD/:gsave @SD/gsave get put @SD/:grestore @SD/grestore get put"
-" @SD/:grestoreall @SD/grestoreall get put @SD/:newpath @SD/newpath get put @SD"
-"/:stroke @SD/stroke get put @SD/:fill @SD/fill get put @SD/:eofill @SD/eofill "
-"get put @SD/:clip @SD/clip get put @SD/:eoclip @SD/eoclip get put @SD/:charpat"
-"h @SD/charpath get put @SD/:show @SD/show get put @SD/:stringwidth @SD/stringw"
-"idth get put @SD/.setopacityalpha known not{@SD/.setopacityalpha{pop}put}if @S"
-"D/.setshapealpha known not{@SD/.setshapealpha{pop}put}if @SD/.setblendmode kno"
-"wn not{@SD/.setblendmode{pop}put}if @SD/prseq{-1 1{-1 roll =only( )print}for(\\"
-"n)print}put @SD/prcmd{( )exch(\\ndvi.)3{print}repeat prseq}put @SD/cvxall{{cvx"
-"}forall}put @SD/defpr{[exch[/copy @SD]cvxall 5 -1 roll dup 6 1 roll[/get/exec]"
-"cvxall 6 -1 roll dup 7 1 roll 4 -1 roll dup 5 1 roll dup length string cvs/prc"
-"md cvx]cvx def}put @SD/querypos{{currentpoint}stopped{$error/newerror false pu"
-"t}{2(querypos)prcmd}ifelse}put @SD/applyscalevals{1 0 dtransform exch dup mul "
-"exch dup mul add sqrt 0 1 dtransform exch dup mul exch dup mul add sqrt 1 0 dt"
-"ransform dup mul exch dup dup mul 3 -1 roll add dup 0 eq{pop}{sqrt div}ifelse "
-"3(applyscalevals)prcmd}put @SD/prpath{{2(moveto)prcmd}{2(lineto)prcmd}{6(curve"
-"to)prcmd}{0(closepath)prcmd}pathforall}put @SD/charpath{/@dodraw false store :"
-"charpath/@dodraw true store}put @SD/stringwidth{/@dodraw false store :stringwi"
-"dth/@dodraw true store}put @SD/show{@dodraw{dup :gsave currentpoint 2{50 mul e"
-"xch}repeat :newpath moveto 50 50/scale sysexec true charpath fill :grestore/@d"
-"odraw false store :show/@dodraw true store}{:show}ifelse}put @SD/varxyshow{exc"
-"h dup type/arraytype eq{<</arr 3 -1 roll/prc 5 -1 roll/chr 1 string/idx 0>>beg"
-"in{chr 0 3 -1 roll put :gsave chr show :grestore currentpoint prc moveto/idx i"
-"dx 1 add store}forall end}{pop show}ifelse}put @SD/xyshow{{exch arr idx 2 mul "
-"get add exch arr idx 2 mul 1 add get add}varxyshow}put @SD/xshow{{exch arr idx"
-" get add exch}varxyshow}put @SD/yshow{{arr idx get add}varxyshow}put @SD/awidt"
-"hshow{{1 string dup 0 5 index put :gsave show :grestore pop 0 rmoveto 3 index "
-"eq{4 index 4 index rmoveto}if 1 index 1 index rmoveto}exch cshow 5{pop}repeat}"
-"put @SD/widthshow{0 0 3 -1 roll pstack awidthshow}put @SD/ashow{0 0 0 6 3 roll"
-" awidthshow}put @SD/newpath{:newpath 1 1(newpath)prcmd}put @SD/stroke{@dodraw{"
-"prcolor 0 1(newpath)prcmd prpath 0(stroke)prcmd :newpath}{:stroke}ifelse}put @"
-"SD/fill{@dodraw{prcolor 0 1(newpath)prcmd prpath 0(fill)prcmd :newpath}{:fill}"
-"ifelse}put @SD/eofill{@dodraw{prcolor 0 1(newpath)prcmd prpath 0(eofill)prcmd "
-":newpath}{:eofill}ifelse}put @SD/clip{:clip 0 1(newpath)prcmd prpath 0(clip)pr"
-"cmd}put @SD/eoclip{:eoclip 0 1(newpath)prcmd prpath 0(eoclip)prcmd}put @SD/shf"
-"ill{begin currentdict/ShadingType known currentdict/ColorSpace known and curre"
-"ntdict/DataSource known and currentdict/Function known not and ShadingType 4 g"
-"e and DataSource type/arraytype eq and{<</DeviceGray 1/DeviceRGB 3/DeviceCMYK "
-"4/bgknown currentdict/Background known/bbknown currentdict/BBox known>>begin c"
-"urrentdict ColorSpace known{ShadingType ColorSpace load bgknown{1 Background a"
-"load pop}{0}ifelse bbknown{1 BBox aload pop}{0}ifelse ShadingType 5 eq{Vertice"
-"sPerRow}if DataSource aload length 4 add bgknown{ColorSpace load add}if bbknow"
-"n{4 add}if ShadingType 5 eq{1 add}if(shfill)prcmd}if end}if end}put/@rect{4 -2"
-" roll moveto exch dup 0 rlineto exch 0 exch rlineto neg 0 rlineto closepath}bi"
-"nd def/@rectcc{4 -2 roll moveto 2 copy 0 lt exch 0 lt xor{dup 0 exch rlineto e"
-"xch 0 rlineto neg 0 exch rlineto}{exch dup 0 rlineto exch 0 exch rlineto neg 0"
-" rlineto}ifelse closepath}bind def @SD/rectclip{:newpath dup type/arraytype eq"
-"{aload length 4 idiv{@rectcc}repeat}{@rectcc}ifelse clip :newpath}put @SD/rect"
-"fill{gsave :newpath dup type/arraytype eq{aload length 4 idiv{@rectcc}repeat}{"
-"@rectcc}ifelse fill grestore}put @SD/rectstroke{gsave :newpath dup type/arrayt"
-"ype eq{aload length 4 idiv{@rect}repeat}{@rect}ifelse stroke grestore}put fals"
-"e setglobal @SD readonly pop/initclip 0 defpr/clippath 0 defpr/sysexec{@SD exc"
-"h get exec}def/adddot{dup length 1 add string dup 0 46 put dup 3 -1 roll 1 exc"
-"h putinterval}def/setlinewidth{dup/setlinewidth sysexec 1(setlinewidth)prcmd}d"
-"ef/setlinecap 1 defpr/setlinejoin 1 defpr/setmiterlimit 1 defpr/setdash{mark 3"
-" 1 roll 2 copy/setdash sysexec exch aload length 1 add -1 roll counttomark(set"
-"dash)prcmd pop}def/@setpagedevice{pop<<>>/setpagedevice sysexec matrix setmatr"
-"ix newpath 0(setpagedevice)prcmd}def/prcolor{currentrgbcolor 3(setrgbcolor)prc"
-"md}def/printgstate{@dodraw{matrix currentmatrix aload pop 6(setmatrix)prcmd ap"
-"plyscalevals currentlinewidth 1(setlinewidth)prcmd currentlinecap 1(setlinecap"
-")prcmd currentlinejoin 1(setlinejoin)prcmd currentmiterlimit 1(setmiterlimit)p"
-"rcmd currentrgbcolor 3(setrgbcolor)prcmd currentdash mark 3 1 roll exch aload "
-"length 1 add -1 roll counttomark(setdash)prcmd pop}if}def/setgstate{/setgstate"
-" sysexec printgstate}def/save{@UD begin/@saveID vmstatus pop pop def end :save"
-" @saveID 1(save)prcmd}def/restore{:restore printgstate @UD/@saveID known{@UD b"
-"egin @saveID end}{0}ifelse 1(restore)prcmd}def/gsave 0 defpr/grestore{:grestor"
-"e printgstate 0(grestore)prcmd}def/grestoreall{:grestoreall setstate 0(grestor"
-"eall)prcmd}def/rotate{dup type/arraytype ne @dodraw and{dup 1(rotate)prcmd}if/"
-"rotate sysexec applyscalevals}def/scale{dup type/arraytype ne @dodraw and{2 co"
-"py 2(scale)prcmd}if/scale sysexec applyscalevals}def/translate{dup type/arrayt"
-"ype ne @dodraw and{2 copy 2(translate)prcmd}if/translate sysexec}def/setmatrix"
-"{dup/setmatrix sysexec @dodraw{aload pop 6(setmatrix)prcmd applyscalevals}{pop"
-"}ifelse}def/initmatrix{matrix setmatrix}def/concat{matrix currentmatrix matrix"
-" concatmatrix setmatrix}def/makepattern{gsave<</mx 3 -1 roll>>begin dup/XUID[1"
-"000000 @patcnt]put mx/makepattern sysexec dup dup begin PatternType @patcnt BB"
-"ox aload pop XStep YStep PaintType mx aload pop 15(makepattern)prcmd :newpath "
-"matrix setmatrix PaintProc 0 1(makepattern)prcmd end/@patcnt @patcnt 1 add sto"
-"re end grestore}def/setpattern{begin PatternType 1 eq{PaintType 1 eq{XUID aloa"
-"d pop exch pop 1}{:gsave[currentcolorspace aload length -1 roll pop]setcolorsp"
-"ace/setcolor sysexec XUID aload pop exch pop currentrgbcolor :grestore 4}ifels"
-"e(setpattern)prcmd}{/setpattern sysexec}ifelse end}def/setcolor{dup type/dictt"
-"ype eq{setpattern}{/setcolor sysexec/currentrgbcolor sysexec setrgbcolor}ifels"
-"e}def/setgray 1 defpr/setcmykcolor 4 defpr/sethsbcolor 3 defpr/setrgbcolor 3 d"
-"efpr/.setopacityalpha{dup/.setopacityalpha sysexec 1(setopacityalpha)prcmd}def"
-"/.setshapealpha{dup/.setshapealpha sysexec 1(setshapealpha)prcmd}def/.setblend"
-"mode{dup/.setblendmode sysexec<</Normal 0/Compatible 0/Multiply 1/Screen 2/Ove"
-"rlay 3/SoftLight 4/HardLight 5/ColorDodge 6/ColorBurn 7/Darken 8/Lighten 9/Dif"
-"ference 10/Exclusion 11/Hue 12/Saturation 13/Color 14/Luminosity 15/Compatible"
-"Overprint 16>>exch get 1(setblendmode)prcmd}def/@pdfpagecount{(r)file runpdfbe"
-"gin pdfpagecount runpdfend}def/@pdfpagebox{(r)file runpdfbegin dup dup 1 lt ex"
-"ch pdfpagecount gt or{pop}{pdfgetpage/MediaBox pget pop aload pop}ifelse runpd"
-"fend}def DELAYBIND{.bindnow}if ";
+"BBox null>>setpagedevice/@dodraw true store/@nulldev false store/@patcnt 0 sto"
+"re/@SD systemdict def/@UD userdict def true setglobal @SD/:save @SD/save get p"
+"ut @SD/:restore @SD/restore get put @SD/:gsave @SD/gsave get put @SD/:grestore"
+" @SD/grestore get put @SD/:grestoreall @SD/grestoreall get put @SD/:newpath @S"
+"D/newpath get put @SD/:stroke @SD/stroke get put @SD/:fill @SD/fill get put @S"
+"D/:eofill @SD/eofill get put @SD/:clip @SD/clip get put @SD/:eoclip @SD/eoclip"
+" get put @SD/:charpath @SD/charpath get put @SD/:show @SD/show get put @SD/:st"
+"ringwidth @SD/stringwidth get put @SD/:nulldevice @SD/nulldevice get put @SD/."
+"setopacityalpha known not{@SD/.setopacityalpha{pop}put}if @SD/.setshapealpha k"
+"nown not{@SD/.setshapealpha{pop}put}if @SD/.setblendmode known not{@SD/.setble"
+"ndmode{pop}put}if @SD/prseq{-1 1{-1 roll =only( )print}for(\\n)print}put @SD/p"
+"rcmd{( )exch(\\ndvi.)3{print}repeat prseq}put @SD/cvxall{{cvx}forall}put @SD/d"
+"efpr{[exch[/copy @SD]cvxall 5 -1 roll dup 6 1 roll[/get/exec]cvxall 6 -1 roll "
+"dup 7 1 roll 4 -1 roll dup 5 1 roll dup length string cvs/prcmd cvx]cvx def}pu"
+"t @SD/querypos{{currentpoint}stopped{$error/newerror false put}{2(querypos)prc"
+"md}ifelse}put @SD/applyscalevals{1 0 dtransform exch dup mul exch dup mul add "
+"sqrt 0 1 dtransform exch dup mul exch dup mul add sqrt 1 0 dtransform dup mul "
+"exch dup dup mul 3 -1 roll add dup 0 eq{pop}{sqrt div}ifelse 3(applyscalevals)"
+"prcmd}put @SD/prpath{{2(moveto)prcmd}{2(lineto)prcmd}{6(curveto)prcmd}{0(close"
+"path)prcmd}pathforall}put @SD/nulldevice{/@nulldev true store :nulldevice 1 1("
+"setnulldevice)prcmd}put @SD/charpath{/@dodraw false store :charpath/@dodraw tr"
+"ue store}put @SD/stringwidth{/@dodraw false store :stringwidth/@dodraw true st"
+"ore}put @SD/show{@dodraw @nulldev not and{dup :gsave currentpoint 2{50 mul exc"
+"h}repeat :newpath moveto 50 50/scale sysexec true charpath fill :grestore/@dod"
+"raw false store :show/@dodraw true store}{:show}ifelse}put @SD/varxyshow{exch "
+"dup type/arraytype eq{<</arr 3 -1 roll/prc 5 -1 roll/chr 1 string/idx 0>>begin"
+"{chr 0 3 -1 roll put :gsave chr show :grestore currentpoint prc moveto/idx idx"
+" 1 add store}forall end}{pop show}ifelse}put @SD/xyshow{{exch arr idx 2 mul ge"
+"t add exch arr idx 2 mul 1 add get add}varxyshow}put @SD/xshow{{exch arr idx g"
+"et add exch}varxyshow}put @SD/yshow{{arr idx get add}varxyshow}put @SD/awidths"
+"how{{1 string dup 0 5 index put :gsave show :grestore pop 0 rmoveto 3 index eq"
+"{4 index 4 index rmoveto}if 1 index 1 index rmoveto}exch cshow 5{pop}repeat}pu"
+"t @SD/widthshow{0 0 3 -1 roll pstack awidthshow}put @SD/ashow{0 0 0 6 3 roll a"
+"widthshow}put @SD/newpath{:newpath 1 1(newpath)prcmd}put @SD/stroke{@dodraw @n"
+"ulldev not and{prcolor 0 1(newpath)prcmd prpath 0(stroke)prcmd :newpath}{:stro"
+"ke}ifelse}put @SD/fill{@dodraw @nulldev not and{prcolor 0 1(newpath)prcmd prpa"
+"th 0(fill)prcmd :newpath}{:fill}ifelse}put @SD/eofill{@dodraw @nulldev not and"
+"{prcolor 0 1(newpath)prcmd prpath 0(eofill)prcmd :newpath}{:eofill}ifelse}put "
+"@SD/clip{:clip @nulldev not{0 1(newpath)prcmd prpath 0(clip)prcmd}if}put @SD/e"
+"oclip{:eoclip @nulldev not{0 1(newpath)prcmd prpath 0(eoclip)prcmd}}put @SD/sh"
+"fill{begin currentdict/ShadingType known currentdict/ColorSpace known and curr"
+"entdict/DataSource known and currentdict/Function known not and ShadingType 4 "
+"ge and DataSource type/arraytype eq and{<</DeviceGray 1/DeviceRGB 3/DeviceCMYK"
+" 4/bgknown currentdict/Background known/bbknown currentdict/BBox known>>begin "
+"currentdict ColorSpace known{ShadingType ColorSpace load bgknown{1 Background "
+"aload pop}{0}ifelse bbknown{1 BBox aload pop}{0}ifelse ShadingType 5 eq{Vertic"
+"esPerRow}if DataSource aload length 4 add bgknown{ColorSpace load add}if bbkno"
+"wn{4 add}if ShadingType 5 eq{1 add}if(shfill)prcmd}if end}if end}put/@rect{4 -"
+"2 roll moveto exch dup 0 rlineto exch 0 exch rlineto neg 0 rlineto closepath}b"
+"ind def/@rectcc{4 -2 roll moveto 2 copy 0 lt exch 0 lt xor{dup 0 exch rlineto "
+"exch 0 rlineto neg 0 exch rlineto}{exch dup 0 rlineto exch 0 exch rlineto neg "
+"0 rlineto}ifelse closepath}bind def @SD/rectclip{:newpath dup type/arraytype e"
+"q{aload length 4 idiv{@rectcc}repeat}{@rectcc}ifelse clip :newpath}put @SD/rec"
+"tfill{gsave :newpath dup type/arraytype eq{aload length 4 idiv{@rectcc}repeat}"
+"{@rectcc}ifelse fill grestore}put @SD/rectstroke{gsave :newpath dup type/array"
+"type eq{aload length 4 idiv{@rect}repeat}{@rect}ifelse stroke grestore}put fal"
+"se setglobal @SD readonly pop/initclip 0 defpr/clippath 0 defpr/sysexec{@SD ex"
+"ch get exec}def/adddot{dup length 1 add string dup 0 46 put dup 3 -1 roll 1 ex"
+"ch putinterval}def/setlinewidth{dup/setlinewidth sysexec 1(setlinewidth)prcmd}"
+"def/setlinecap 1 defpr/setlinejoin 1 defpr/setmiterlimit 1 defpr/setdash{mark "
+"3 1 roll 2 copy/setdash sysexec exch aload length 1 add -1 roll counttomark(se"
+"tdash)prcmd pop}def/@setpagedevice{pop<<>>/setpagedevice sysexec matrix setmat"
+"rix newpath 0(setpagedevice)prcmd}def/@checknulldev{@nulldev{currentpagedevice"
+" maxlength 0 ne{/@nulldev false store 0 1(setnulldevice)prcmd}if}if}def/prcolo"
+"r{currentrgbcolor 3(setrgbcolor)prcmd}def/printgstate{@dodraw @nulldev not and"
+"{matrix currentmatrix aload pop 6(setmatrix)prcmd applyscalevals currentlinewi"
+"dth 1(setlinewidth)prcmd currentlinecap 1(setlinecap)prcmd currentlinejoin 1(s"
+"etlinejoin)prcmd currentmiterlimit 1(setmiterlimit)prcmd currentrgbcolor 3(set"
+"rgbcolor)prcmd currentdash mark 3 1 roll exch aload length 1 add -1 roll count"
+"tomark(setdash)prcmd pop}if}def/setgstate{/setgstate sysexec printgstate}def/s"
+"ave{@UD begin/@saveID vmstatus pop pop def end :save @saveID 1(save)prcmd}def/"
+"restore{:restore @checknulldev printgstate @UD/@saveID known{@UD begin @saveID"
+" end}{0}ifelse 1(restore)prcmd}def/gsave 0 defpr/grestore{:grestore @checknull"
+"dev printgstate 0(grestore)prcmd}def/grestoreall{:grestoreall @checknulldev se"
+"tstate 0(grestoreall)prcmd}def/rotate{dup type/arraytype ne @dodraw and{dup 1("
+"rotate)prcmd}if/rotate sysexec applyscalevals}def/scale{dup type/arraytype ne "
+"@dodraw and{2 copy 2(scale)prcmd}if/scale sysexec applyscalevals}def/translate"
+"{dup type/arraytype ne @dodraw and{2 copy 2(translate)prcmd}if/translate sysex"
+"ec}def/setmatrix{dup/setmatrix sysexec @dodraw{aload pop 6(setmatrix)prcmd app"
+"lyscalevals}{pop}ifelse}def/initmatrix{matrix setmatrix}def/concat{matrix curr"
+"entmatrix matrix concatmatrix setmatrix}def/makepattern{gsave<</mx 3 -1 roll>>"
+"begin dup/XUID[1000000 @patcnt]put mx/makepattern sysexec dup dup begin Patter"
+"nType @patcnt BBox aload pop XStep YStep PaintType mx aload pop 15(makepattern"
+")prcmd :newpath matrix setmatrix PaintProc 0 1(makepattern)prcmd end/@patcnt @"
+"patcnt 1 add store end grestore}def/setpattern{begin PatternType 1 eq{PaintTyp"
+"e 1 eq{XUID aload pop exch pop 1}{:gsave[currentcolorspace aload length -1 rol"
+"l pop]setcolorspace/setcolor sysexec XUID aload pop exch pop currentrgbcolor :"
+"grestore 4}ifelse(setpattern)prcmd}{/setpattern sysexec}ifelse end}def/setcolo"
+"r{dup type/dicttype eq{setpattern}{/setcolor sysexec/currentrgbcolor sysexec s"
+"etrgbcolor}ifelse}def/setgray 1 defpr/setcmykcolor 4 defpr/sethsbcolor 3 defpr"
+"/setrgbcolor 3 defpr/.setopacityalpha{dup/.setopacityalpha sysexec 1(setopacit"
+"yalpha)prcmd}def/.setshapealpha{dup/.setshapealpha sysexec 1(setshapealpha)prc"
+"md}def/.setblendmode{dup/.setblendmode sysexec<</Normal 0/Compatible 0/Multipl"
+"y 1/Screen 2/Overlay 3/SoftLight 4/HardLight 5/ColorDodge 6/ColorBurn 7/Darken"
+" 8/Lighten 9/Difference 10/Exclusion 11/Hue 12/Saturation 13/Color 14/Luminosi"
+"ty 15/CompatibleOverprint 16>>exch get 1(setblendmode)prcmd}def/@pdfpagecount{"
+"(r)file runpdfbegin pdfpagecount runpdfend}def/@pdfpagebox{(r)file runpdfbegin"
+" dup dup 1 lt exch pdfpagecount gt or{pop}{pdfgetpage/MediaBox pget pop aload "
+"pop}ifelse runpdfend}def DELAYBIND{.bindnow}if ";
+
diff --git a/dviware/dvisvgm/tests/CMapReaderTest.cpp b/dviware/dvisvgm/tests/CMapReaderTest.cpp
index 06836c4abc..7e64d6d4e5 100644
--- a/dviware/dvisvgm/tests/CMapReaderTest.cpp
+++ b/dviware/dvisvgm/tests/CMapReaderTest.cpp
@@ -73,6 +73,11 @@ const char *CMapReaderTest::cmapsrc =
"<5833> <5834> <8c86>\n"
"<5837> <5838> <8c9b>\n"
"endbfrange\n"
+"3 begincidchar\n"
+"<1000> 50\n"
+"<1005> 60\n"
+"<1008> 70\n"
+"endcidchar\n"
"2 begincidrange\n"
"<1234> <1240> 100\n"
"<1300> <1302> 200\n"
@@ -109,7 +114,11 @@ TEST_F(CMapReaderTest, cid) {
const SegmentedCMap *seg_cmap = dynamic_cast<const SegmentedCMap*>(cmap.get());
ASSERT_NE(seg_cmap, nullptr);
ASSERT_EQ(seg_cmap->numBFRanges(), 9u);
- ASSERT_EQ(seg_cmap->numCIDRanges(), 2u);
+ ASSERT_EQ(seg_cmap->numCIDRanges(), 5u);
+ ASSERT_EQ(seg_cmap->cid(0x1000), 50u);
+ ASSERT_EQ(seg_cmap->cid(0x1005), 60u);
+ ASSERT_EQ(seg_cmap->cid(0x1008), 70u);
+
ASSERT_EQ(seg_cmap->cid(0x1233), 0u);
ASSERT_EQ(seg_cmap->cid(0x1234), 100u);
ASSERT_EQ(seg_cmap->cid(0x1240), 112u);
diff --git a/dviware/dvisvgm/tests/PSInterpreterTest.cpp b/dviware/dvisvgm/tests/PSInterpreterTest.cpp
index 4203830ebf..629f8d753b 100644
--- a/dviware/dvisvgm/tests/PSInterpreterTest.cpp
+++ b/dviware/dvisvgm/tests/PSInterpreterTest.cpp
@@ -61,6 +61,7 @@ class PSTestActions : public PSActions {
void setlinewidth (vector<double> &p) override {print("setlinewidth", p);}
void setmatrix (vector<double> &p) override {print("setmatrix", p);}
void setmiterlimit (vector<double> &p) override {print("setmiterlimit", p);}
+ void setnulldevice (vector<double> &p) override {print("setnulldevice", p);}
void setopacityalpha (vector<double> &p) override {print("setopacityalpha", p);}
void setshapealpha (vector<double> &p) override {print("setshapealpha", p);}
void setrgbcolor (vector<double> &p) override {print("setrgbcolor", p);}