summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm/src
diff options
context:
space:
mode:
Diffstat (limited to 'dviware/dvisvgm/src')
-rw-r--r--dviware/dvisvgm/src/BasicDVIReader.cpp12
-rw-r--r--dviware/dvisvgm/src/Bezier.cpp13
-rw-r--r--dviware/dvisvgm/src/Bezier.hpp8
-rw-r--r--dviware/dvisvgm/src/Bitmap.cpp18
-rw-r--r--dviware/dvisvgm/src/Bitmap.hpp14
-rw-r--r--dviware/dvisvgm/src/CMapManager.cpp4
-rw-r--r--dviware/dvisvgm/src/CMapReader.cpp23
-rw-r--r--dviware/dvisvgm/src/CMapReader.hpp1
-rw-r--r--dviware/dvisvgm/src/CRC32.cpp112
-rw-r--r--dviware/dvisvgm/src/CRC32.hpp47
-rw-r--r--dviware/dvisvgm/src/Color.cpp10
-rw-r--r--dviware/dvisvgm/src/DVIReader.cpp14
-rw-r--r--dviware/dvisvgm/src/DVIToSVG.cpp28
-rw-r--r--dviware/dvisvgm/src/DVIToSVG.hpp3
-rw-r--r--dviware/dvisvgm/src/DVIToSVGActions.cpp12
-rw-r--r--dviware/dvisvgm/src/DVIToSVGActions.hpp2
-rw-r--r--dviware/dvisvgm/src/EllipticalArc.cpp260
-rw-r--r--dviware/dvisvgm/src/EllipticalArc.hpp59
-rw-r--r--dviware/dvisvgm/src/FilePath.cpp39
-rw-r--r--dviware/dvisvgm/src/FilePath.hpp7
-rw-r--r--dviware/dvisvgm/src/Font.cpp10
-rw-r--r--dviware/dvisvgm/src/Font.hpp2
-rw-r--r--dviware/dvisvgm/src/FontCache.cpp128
-rw-r--r--dviware/dvisvgm/src/FontCache.hpp12
-rw-r--r--dviware/dvisvgm/src/FontEngine.cpp10
-rw-r--r--dviware/dvisvgm/src/FontWriter.cpp55
-rw-r--r--dviware/dvisvgm/src/GFReader.cpp7
-rw-r--r--dviware/dvisvgm/src/GraphicsPath.hpp786
-rw-r--r--dviware/dvisvgm/src/HashFunction.cpp20
-rw-r--r--dviware/dvisvgm/src/HashFunction.hpp2
-rw-r--r--dviware/dvisvgm/src/HyperlinkManager.cpp2
-rw-r--r--dviware/dvisvgm/src/ImageToSVG.cpp9
-rw-r--r--dviware/dvisvgm/src/ImageToSVG.hpp2
-rw-r--r--dviware/dvisvgm/src/JFM.cpp8
-rw-r--r--dviware/dvisvgm/src/Length.cpp12
-rw-r--r--dviware/dvisvgm/src/MD5HashFunction.hpp2
-rw-r--r--dviware/dvisvgm/src/Makefile.am2
-rw-r--r--dviware/dvisvgm/src/Makefile.in63
-rw-r--r--dviware/dvisvgm/src/MapLine.cpp4
-rw-r--r--dviware/dvisvgm/src/Matrix.cpp13
-rw-r--r--dviware/dvisvgm/src/MetafontWrapper.cpp5
-rw-r--r--dviware/dvisvgm/src/MiKTeXCom.hpp4
-rw-r--r--dviware/dvisvgm/src/PDFParser.cpp6
-rw-r--r--dviware/dvisvgm/src/PDFToSVG.hpp1
-rw-r--r--dviware/dvisvgm/src/PSInterpreter.cpp9
-rw-r--r--dviware/dvisvgm/src/Pair.hpp26
-rw-r--r--dviware/dvisvgm/src/PathClipper.cpp6
-rw-r--r--dviware/dvisvgm/src/PsSpecialHandler.cpp143
-rw-r--r--dviware/dvisvgm/src/PsSpecialHandler.hpp3
-rw-r--r--dviware/dvisvgm/src/SVGOutput.cpp61
-rw-r--r--dviware/dvisvgm/src/SVGOutput.hpp4
-rw-r--r--dviware/dvisvgm/src/ShadingPatch.hpp4
-rw-r--r--dviware/dvisvgm/src/SpecialActions.hpp5
-rw-r--r--dviware/dvisvgm/src/StreamReader.cpp60
-rw-r--r--dviware/dvisvgm/src/StreamReader.hpp16
-rw-r--r--dviware/dvisvgm/src/StreamWriter.cpp27
-rw-r--r--dviware/dvisvgm/src/StreamWriter.hpp8
-rw-r--r--dviware/dvisvgm/src/TensorProductPatch.cpp18
-rw-r--r--dviware/dvisvgm/src/TensorProductPatch.hpp4
-rw-r--r--dviware/dvisvgm/src/TpicSpecialHandler.cpp31
-rw-r--r--dviware/dvisvgm/src/TriangularPatch.cpp12
-rw-r--r--dviware/dvisvgm/src/TriangularPatch.hpp4
-rw-r--r--dviware/dvisvgm/src/TrueTypeFont.cpp6
-rw-r--r--dviware/dvisvgm/src/VFReader.cpp10
-rw-r--r--dviware/dvisvgm/src/XMLString.cpp14
-rw-r--r--dviware/dvisvgm/src/XXHashFunction.hpp40
-rw-r--r--dviware/dvisvgm/src/dvisvgm.cpp27
-rw-r--r--dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp20
-rw-r--r--dviware/dvisvgm/src/optimizer/GroupCollapser.cpp16
-rw-r--r--dviware/dvisvgm/src/optimizer/Makefile.am1
-rw-r--r--dviware/dvisvgm/src/optimizer/Makefile.in1
-rw-r--r--dviware/dvisvgm/src/optimizer/TransformSimplifier.cpp49
-rw-r--r--dviware/dvisvgm/src/optimizer/TransformSimplifier.hpp1
-rw-r--r--dviware/dvisvgm/src/psdefs.cpp43
-rw-r--r--dviware/dvisvgm/src/utility.cpp65
-rw-r--r--dviware/dvisvgm/src/utility.hpp35
76 files changed, 1663 insertions, 957 deletions
diff --git a/dviware/dvisvgm/src/BasicDVIReader.cpp b/dviware/dvisvgm/src/BasicDVIReader.cpp
index 350e130166..272c88c3ad 100644
--- a/dviware/dvisvgm/src/BasicDVIReader.cpp
+++ b/dviware/dvisvgm/src/BasicDVIReader.cpp
@@ -19,7 +19,6 @@
*************************************************************************/
#include <algorithm>
-#include <sstream>
#include "BasicDVIReader.hpp"
using namespace std;
@@ -100,11 +99,8 @@ int BasicDVIReader::evalCommand (CommandHandler &handler, int &param) {
handler = &BasicDVIReader::cmdDir;
num_param_bytes = 1;
}
- else if (opcode > OP_POSTPOST) {
- ostringstream oss;
- oss << "undefined DVI command (opcode " << opcode << ')';
- throw DVIException(oss.str());
- }
+ else if (opcode > OP_POSTPOST)
+ throw DVIException("undefined DVI command (opcode " + to_string(opcode) + ")");
else {
const int offset = opcode < OP_FNTNUM0 ? OP_SET1 : (OP_FNTNUM63+1)-(OP_FNTNUM0-OP_SET1);
handler = commands[opcode-offset].handler;
@@ -194,9 +190,7 @@ void BasicDVIReader::setDVIVersion (DVIVersion version) {
case DVI_XDV7:
break;
default:
- ostringstream oss;
- oss << "DVI version " << _dviVersion << " not supported";
- throw DVIException(oss.str());
+ throw DVIException("DVI version " + to_string(_dviVersion) + " not supported");
}
}
diff --git a/dviware/dvisvgm/src/Bezier.cpp b/dviware/dvisvgm/src/Bezier.cpp
index 70656d14f9..d2077bcfeb 100644
--- a/dviware/dvisvgm/src/Bezier.cpp
+++ b/dviware/dvisvgm/src/Bezier.cpp
@@ -21,6 +21,7 @@
#include <algorithm>
#include <utility>
#include "Bezier.hpp"
+#include "Matrix.hpp"
using namespace std;
@@ -230,8 +231,8 @@ static bool solve_quadratic_equation (double a, double b, double c, double &x1,
/** Returns a tight bounding box parallel to the x- and y-axis. */
-void Bezier::getBBox (BoundingBox &bbox) const {
- bbox.invalidate();
+BoundingBox Bezier::getBBox () const {
+ BoundingBox bbox;
// coefficients of the derivative
DPair pa = _points[3] - _points[2]*3.0 + _points[1]*3.0 - _points[0];
DPair pb = (_points[2]-_points[1]*2.0+_points[0])*2.0;
@@ -253,4 +254,12 @@ void Bezier::getBBox (BoundingBox &bbox) const {
}
bbox.embed(_points[0]);
bbox.embed(_points[3]);
+ return bbox;
+}
+
+
+Bezier& Bezier::transform (const Matrix &matrix) {
+ for (int i=0; i < 4; i++)
+ _points[i] = matrix*_points[i];
+ return *this;
}
diff --git a/dviware/dvisvgm/src/Bezier.hpp b/dviware/dvisvgm/src/Bezier.hpp
index f4f93dcf2f..525ef23435 100644
--- a/dviware/dvisvgm/src/Bezier.hpp
+++ b/dviware/dvisvgm/src/Bezier.hpp
@@ -25,8 +25,9 @@
#include "BoundingBox.hpp"
#include "Pair.hpp"
-class Bezier
-{
+class Matrix;
+
+class Bezier {
public:
Bezier ();
Bezier (const DPair &p0, const DPair &p1, const DPair &p2);
@@ -37,10 +38,11 @@ class Bezier
DPair valueAt (double t) const;
DPair blossomValue (double u, double v, double w) const;
void subdivide (double t, Bezier *bezier1, Bezier *bezier2) const;
+ Bezier& transform (const Matrix &matrix);
int approximate (double delta, std::vector<DPair> &p, std::vector<double> *t=nullptr) const;
const DPair& point (int i) const {return _points[i];}
int reduceDegree (double delta, std::vector<DPair> &p) const;
- void getBBox (BoundingBox &bbox) const;
+ BoundingBox getBBox () const;
protected:
int approximate (double delta, double t0, double t1, std::vector<DPair> &p, std::vector<double> *t) const;
diff --git a/dviware/dvisvgm/src/Bitmap.cpp b/dviware/dvisvgm/src/Bitmap.cpp
index e39ad79c47..3fa554eed4 100644
--- a/dviware/dvisvgm/src/Bitmap.cpp
+++ b/dviware/dvisvgm/src/Bitmap.cpp
@@ -25,10 +25,6 @@
using namespace std;
-Bitmap::Bitmap () : _rows(0), _cols(0), _xshift(0), _yshift(0), _bpr(0), _bytes(0)
-{
-}
-
/** Constructs a Bitmap */
Bitmap::Bitmap (int minx, int maxx, int miny , int maxy) {
@@ -48,8 +44,7 @@ void Bitmap::resize (int minx, int maxx, int miny , int maxy) {
_yshift = miny;
_bpr = _cols/8 + (_cols % 8 ? 1 : 0); // bytes per row
_bytes.resize(_rows*_bpr);
- for (uint8_t &byte : _bytes)
- byte = 0;
+ std::fill(_bytes.begin(), _bytes.end(), 0);
}
@@ -69,7 +64,7 @@ void Bitmap::setBits (int row, int col, int n) {
int m = min(n, b+1); // number of bits to be set in current byte
int bitseq = (1 << m)-1; // sequence of n set bits (bits 0..n-1 are set)
bitseq <<= b-m+1; // move bit sequence so that bit b is the leftmost set bit
- *byte |= uint8_t(bitseq); // apply bit sequence to current byte
+ *byte |= uint8_t(bitseq); // apply bit sequence to current byte
byte++;
n -= m;
col += m;
@@ -90,10 +85,8 @@ void Bitmap::forAllPixels (Callback &data) const {
}
-class BBoxCallback : public Bitmap::Callback
-{
+class BBoxCallback : public Bitmap::Callback {
public:
- BBoxCallback () : _changed(false), _minx(numeric_limits<int>::max()), _miny(_minx), _maxx(0), _maxy(0) {}
int minx () const {return _minx;}
int miny () const {return _miny;}
int maxx () const {return _maxx;}
@@ -116,8 +109,9 @@ class BBoxCallback : public Bitmap::Callback
}
private:
- bool _changed;
- int _minx, _miny, _maxx, _maxy;
+ bool _changed = false;
+ int _minx = numeric_limits<int>::max(), _miny=_minx;
+ int _maxx = 0, _maxy = 0;
};
diff --git a/dviware/dvisvgm/src/Bitmap.hpp b/dviware/dvisvgm/src/Bitmap.hpp
index fe9f31f053..026edae9a2 100644
--- a/dviware/dvisvgm/src/Bitmap.hpp
+++ b/dviware/dvisvgm/src/Bitmap.hpp
@@ -25,8 +25,7 @@
#include <vector>
-class Bitmap
-{
+class Bitmap {
public:
struct Callback {
virtual ~Callback() =default;
@@ -36,7 +35,7 @@ class Bitmap
};
public:
- Bitmap ();
+ Bitmap () =default;
Bitmap (int minx, int maxx, int miny , int maxy);
void resize (int minx, int maxx, int miny , int maxy);
void setBits(int row, int col, int n);
@@ -60,9 +59,9 @@ class Bitmap
// std::ostream& write (std::ostream &os) const;
private:
- int _rows, _cols; ///< number of rows, columns
- int _xshift, _yshift; ///< horizontal/vertical shift
- int _bpr; ///< number of bytes per row
+ int _rows=0, _cols=0; ///< number of rows, columns
+ int _xshift=0, _yshift=0; ///< horizontal/vertical shift
+ int _bpr=0; ///< number of bytes per row
std::vector<uint8_t> _bytes;
};
@@ -81,7 +80,7 @@ int Bitmap::copy (std::vector<T> &target, bool vflip) const {
int targetrow = vflip ? _rows-r-1 : r;
for (int b=0; b < _bpr; b++) {
T &t = target[targetrow*tpr + b/s];
- T chunk = (T)_bytes[r*_bpr+b] << (8*(s-1-b%s));
+ T chunk = static_cast<T>(_bytes[r*_bpr+b]) << (8*(s-1-b%s));
if (b % s == 0)
t = chunk;
else
@@ -107,5 +106,4 @@ void Bitmap::write (std::ostream &os, const std::vector<T> &v) const {
}
}*/
-
#endif
diff --git a/dviware/dvisvgm/src/CMapManager.cpp b/dviware/dvisvgm/src/CMapManager.cpp
index 24b5b69f24..779c6ee3a8 100644
--- a/dviware/dvisvgm/src/CMapManager.cpp
+++ b/dviware/dvisvgm/src/CMapManager.cpp
@@ -44,9 +44,7 @@ CMap* CMapManager::lookup (const string &name) {
if (_includedCMaps.find(name) != _includedCMaps.end()) {
_level = 0;
- ostringstream oss;
- oss << "circular reference of CMap " << name;
- throw CMapReaderException(oss.str());
+ throw CMapReaderException("circular reference of CMap " + name);
}
unique_ptr<CMap> cmap_ptr;
diff --git a/dviware/dvisvgm/src/CMapReader.cpp b/dviware/dvisvgm/src/CMapReader.cpp
index 08450cf17f..f873bded0a 100644
--- a/dviware/dvisvgm/src/CMapReader.cpp
+++ b/dviware/dvisvgm/src/CMapReader.cpp
@@ -89,9 +89,10 @@ void CMapReader::executeOperator (const string &opname, InputReader &ir) {
const char *name;
void (CMapReader::*handler)(InputReader&);
};
- constexpr array<Operator, 6> operators {{
+ constexpr array<Operator, 7> operators {{
{"beginbfchar", &CMapReader::op_beginbfchar},
{"beginbfrange", &CMapReader::op_beginbfrange},
+ {"begincidchar", &CMapReader::op_begincidchar},
{"begincidrange", &CMapReader::op_begincidrange},
{"def", &CMapReader::op_def},
{"endcmap", &CMapReader::op_endcmap},
@@ -154,6 +155,22 @@ static uint32_t parse_hexentry (InputReader &ir) {
}
+void CMapReader::op_begincidchar (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 cid;
+ ir.skipSpace();
+ if (!ir.parseUInt(cid))
+ throw CMapReaderException("invalid char entry (decimal value expected)");
+ _cmap->addCIDRange(first, first, cid);
+ ir.skipSpace();
+ }
+ }
+}
+
void CMapReader::op_begincidrange (InputReader &ir) {
if (!_tokens.empty() && _tokens.back().type() == Token::Type::NUMBER) {
ir.skipSpace();
@@ -249,9 +266,7 @@ void CMapReader::Token::scan (InputReader &ir) {
else if (isdigit(ir.peek())) { // number?
double val;
if (ir.parseDouble(val)) {
- ostringstream oss;
- oss << val;
- _value = oss.str();
+ _value = util::to_string(val);
_type = Type::NUMBER;
}
}
diff --git a/dviware/dvisvgm/src/CMapReader.hpp b/dviware/dvisvgm/src/CMapReader.hpp
index 820882c04d..b39ca9874c 100644
--- a/dviware/dvisvgm/src/CMapReader.hpp
+++ b/dviware/dvisvgm/src/CMapReader.hpp
@@ -59,6 +59,7 @@ class CMapReader {
void executeOperator (const std::string &op, InputReader &ir);
void op_beginbfchar (InputReader &ir);
void op_beginbfrange (InputReader &ir);
+ void op_begincidchar (InputReader &ir);
void op_begincidrange (InputReader &ir);
void op_def (InputReader &ir);
void op_endcmap (InputReader &ir);
diff --git a/dviware/dvisvgm/src/CRC32.cpp b/dviware/dvisvgm/src/CRC32.cpp
deleted file mode 100644
index 583b019324..0000000000
--- a/dviware/dvisvgm/src/CRC32.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*************************************************************************
-** CRC32.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 <cstring>
-#include "CRC32.hpp"
-
-using namespace std;
-
-
-CRC32::CRC32 () : _crc32(0xFFFFFFFF)
-{
- const uint32_t poly = 0xEDB88320;
- for (int i = 0; i < 256; i++) {
- uint32_t crc=i;
- for (int j=8; j > 0; j--) {
- if (crc & 1)
- crc = (crc >> 1) ^ poly;
- else
- crc >>= 1;
- }
- _tab[i] = crc;
- }
-}
-
-
-/** Resets CRC32 sum to 0. */
-void CRC32::reset () {
- _crc32 = 0xFFFFFFFF;
-}
-
-
-/** Appends string bytes to the previous data and computes the resulting checksum.
- * @param[in] data string to update the checksum with */
-void CRC32::update (const char *data) {
- update((const uint8_t*)data, strlen(data));
-}
-
-
-/** Appends a single value to the previous data and computes the resulting checksum.
- * @param[in] n value to update the checksum with
- * @param[in] bytes number of bytes to consider (0-4) */
-void CRC32::update (uint32_t n, int bytes) {
- for (int i=bytes-1; i >= 0; --i) {
- uint8_t byte = uint8_t((n >> (8*i)) & 0xff);
- update(&byte, 1);
- }
-}
-
-
-/** Appends a sequence of bytes to the previous data and computes the resulting checksum.
- * @param[in] bytes pointer to array of bytes
- * @param[in] len number of bytes in array */
-void CRC32::update (const uint8_t *bytes, size_t len) {
- for (size_t i=0; i < len; ++i)
- _crc32 = ((_crc32 >> 8) & 0x00FFFFFF) ^ _tab[(_crc32 ^ *bytes++) & 0xFF];
-}
-
-
-void CRC32::update (istream &is) {
- char buf [4096];
- while (is) {
- is.read(buf, 4096);
- update((uint8_t*)buf, is.gcount());
- }
-}
-
-
-/** Returns the checksum computed from values added with the update functions. */
-uint32_t CRC32::get () const {
- return _crc32 ^ 0xFFFFFFFF;
-}
-
-
-/** Computes the CRC32 checksum of a sequence of bytes.
- * @param[in] bytes pointer to array of bytes
- * @param[in] len number of bytes in array
- * @return CRC32 checksum */
-uint32_t CRC32::compute (const uint8_t *bytes, size_t len) {
- CRC32 crc32;
- crc32.update(bytes, len);
- return crc32.get();
-}
-
-
-/** Computes the CRC32 checksum of a string. */
-uint32_t CRC32::compute (const char *str) {
- return compute((const uint8_t*)str, strlen(str));
-}
-
-
-uint32_t CRC32::compute (istream &is) {
- CRC32 crc32;
- crc32.update(is);
- return crc32.get();
-}
diff --git a/dviware/dvisvgm/src/CRC32.hpp b/dviware/dvisvgm/src/CRC32.hpp
deleted file mode 100644
index 880f8b737e..0000000000
--- a/dviware/dvisvgm/src/CRC32.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*************************************************************************
-** CRC32.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/>. **
-*************************************************************************/
-
-#ifndef CRC32_HPP
-#define CRC32_HPP
-
-#include <cstdlib>
-#include <istream>
-
-class CRC32 {
- public:
- CRC32 ();
- CRC32 (const CRC32 &crc32) =delete;
- void update (const uint8_t *bytes, size_t len);
- void update (uint32_t n, int bytes=4);
- void update (const char *str);
- void update (std::istream &is);
- uint32_t get () const;
- void reset ();
- static uint32_t compute (const uint8_t *bytes, size_t len);
- static uint32_t compute (const char *str);
- static uint32_t compute (std::istream &is);
-
- private:
- uint32_t _crc32;
- uint32_t _tab[256];
-};
-
-
-#endif
diff --git a/dviware/dvisvgm/src/Color.cpp b/dviware/dvisvgm/src/Color.cpp
index 949fcba56c..de7e9074ab 100644
--- a/dviware/dvisvgm/src/Color.cpp
+++ b/dviware/dvisvgm/src/Color.cpp
@@ -40,7 +40,7 @@ const Color Color::TRANSPARENT(uint32_t(0xff000000));
static inline uint8_t double_to_byte (double v) {
v = max(0.0, min(1.0, v));
- return uint8_t(floor(255*v+0.5));
+ return uint8_t(round(255.0*v));
}
@@ -632,14 +632,14 @@ double Color::deltaE (const Color &c) const {
c.getLab(l2, a2, b2);
const double deltaL = l2-l1;
const double lBar = (l1+l2)/2;
- const double c1 = sqrt(a1*a1 + b1*b1);
- const double c2 = sqrt(a2*a2 + b2*b2);
+ const double c1 = hypot(a1, b1);
+ const double c2 = hypot(a2, b2);
const double cBar = (c1+c2)/2.0;
const double g = (1.0-sqrt(pow(cBar, 7.0)/(pow(cBar, 7.0)+6103515625.0)))/2.0;
const double aa1 = a1*(1.0+g);
const double aa2 = a2*(1.0+g);
- const double cc1 = sqrt(aa1*aa1 + b1*b1);
- const double cc2 = sqrt(aa2*aa2 + b2*b2);
+ const double cc1 = hypot(aa1, b1);
+ const double cc2 = hypot(aa2, b2);
const double ccBar = (cc1+cc2)/2.0;
const double deltaCC = cc2-cc1;
double hh1 = atan2(b1, aa1);
diff --git a/dviware/dvisvgm/src/DVIReader.cpp b/dviware/dvisvgm/src/DVIReader.cpp
index 38862839a0..b0bbabee94 100644
--- a/dviware/dvisvgm/src/DVIReader.cpp
+++ b/dviware/dvisvgm/src/DVIReader.cpp
@@ -457,11 +457,8 @@ void DVIReader::cmdDir (int) {
uint8_t wmode = readUnsigned(1);
if (wmode == 4) // yoko mode (4) equals default LR mode (0)
wmode = 0;
- if (wmode == 2 || wmode > 3) {
- ostringstream oss;
- oss << "invalid writing mode value " << wmode << " (0, 1, 3, or 4 expected)";
- throw DVIException(oss.str());
- }
+ if (wmode == 2 || wmode > 3)
+ throw DVIException("invalid writing mode value " + std::to_string(wmode) + " (0, 1, 3, or 4 expected)");
_dviState.d = (WritingMode)wmode;
dviDir(_dviState.d);
}
@@ -485,11 +482,8 @@ void DVIReader::setFont (int fontnum, SetFontMode mode) {
_currFontNum = fontnum;
dviFontNum(uint32_t(fontnum), mode, font);
}
- else {
- ostringstream oss;
- oss << "undefined font number " << fontnum;
- throw DVIException(oss.str());
- }
+ else
+ throw DVIException("undefined font number " + std::to_string(fontnum));
}
diff --git a/dviware/dvisvgm/src/DVIToSVG.cpp b/dviware/dvisvgm/src/DVIToSVG.cpp
index 4911dfe321..4801bd80a0 100644
--- a/dviware/dvisvgm/src/DVIToSVG.cpp
+++ b/dviware/dvisvgm/src/DVIToSVG.cpp
@@ -111,11 +111,11 @@ void DVIToSVG::convert (unsigned first, unsigned last, HashFunction *hashFunc) {
combinedHash = hashFunc->digestString();
}
const SVGOutput::HashTriple hashTriple(dviHash, shortenedOptHash, combinedHash);
- string fname = _out.filename(i, numberOfPages(), hashTriple);
- if (!dviHash.empty() && !PAGE_HASH_SETTINGS.isSet(HashSettings::P_REPLACE) && FileSystem::exists(fname)) {
+ FilePath path = _out.filepath(i, numberOfPages(), hashTriple);
+ if (!dviHash.empty() && !PAGE_HASH_SETTINGS.isSet(HashSettings::P_REPLACE) && path.exists()) {
Message::mstream(false, Message::MC_PAGE_NUMBER) << "skipping page " << i;
Message::mstream().indent(1);
- Message::mstream(false, Message::MC_PAGE_WRITTEN) << "\nfile " << fname << " exists\n";
+ Message::mstream(false, Message::MC_PAGE_WRITTEN) << "\nfile " << path.shorterAbsoluteOrRelative() << " exists\n";
Message::mstream().indent(0);
}
else {
@@ -123,6 +123,7 @@ void DVIToSVG::convert (unsigned first, unsigned last, HashFunction *hashFunc) {
SVGOptimizer(_svg).execute();
embedFonts(_svg.rootNode());
bool success = _svg.write(_out.getPageStream(currentPageNumber(), numberOfPages(), hashTriple));
+ string fname = path.shorterAbsoluteOrRelative();
if (fname.empty())
fname = "<stdout>";
if (success)
@@ -332,12 +333,16 @@ Matrix DVIToSVG::getPageTransformation () const {
static void collect_chars (unordered_map<const Font*, set<int>> &fontmap) {
+ unordered_map<const Font*, set<int>> insertedChars;
for (const auto &entry : fontmap) {
- if (entry.first->uniqueFont() != entry.first) {
+ const Font *unique_font = entry.first->uniqueFont();
+ if (unique_font != entry.first) {
for (int c : entry.second)
- fontmap[entry.first->uniqueFont()].insert(c);
+ insertedChars[unique_font].insert(c);
}
}
+ for (const auto &entry : insertedChars)
+ fontmap[entry.first].insert(entry.second.begin(), entry.second.end());
}
@@ -421,8 +426,8 @@ void DVIToSVG::setProcessSpecials (const char *ignorelist, bool pswarning) {
}
-string DVIToSVG::getSVGFilename (unsigned pageno) const {
- return _out.filename(pageno, numberOfPages());
+FilePath DVIToSVG::getSVGFilePath (unsigned pageno) const {
+ return _out.filepath(pageno, numberOfPages());
}
@@ -568,6 +573,15 @@ void DVIToSVG::HashSettings::setParameters (const string &paramstr) {
_params.insert(it->second);
else if (_algo.empty() && HashFunction::isSupportedAlgorithm(name))
_algo = name;
+ else if (!name.empty()) {
+ string msg = "invalid hash parameter '"+name+"' (supported algorithms: ";
+ for (string str : HashFunction::supportedAlgorithms())
+ msg += str + ", ";
+ msg.pop_back();
+ msg.pop_back();
+ msg += ')';
+ throw MessageException(msg);
+ }
}
// set default hash algorithm if none is given
if (_algo.empty())
diff --git a/dviware/dvisvgm/src/DVIToSVG.hpp b/dviware/dvisvgm/src/DVIToSVG.hpp
index 9463f78736..40dbdc884f 100644
--- a/dviware/dvisvgm/src/DVIToSVG.hpp
+++ b/dviware/dvisvgm/src/DVIToSVG.hpp
@@ -25,6 +25,7 @@
#include <string>
#include <utility>
#include "DVIReader.hpp"
+#include "FilePath.hpp"
#include "SVGTree.hpp"
struct DVIActions;
@@ -62,7 +63,7 @@ class DVIToSVG : public DVIReader {
void finishLine () override {_prevYPos = std::numeric_limits<double>::min();}
void listHashes (const std::string &rangestr, std::ostream &os);
- std::string getSVGFilename (unsigned pageno) const;
+ FilePath getSVGFilePath (unsigned pageno) const;
std::string getUserBBoxString () const {return _bboxFormatString;}
static void setProcessSpecials (const char *ignorelist=nullptr, bool pswarning=false);
diff --git a/dviware/dvisvgm/src/DVIToSVGActions.cpp b/dviware/dvisvgm/src/DVIToSVGActions.cpp
index 6448fc2d42..3d53d1bd4d 100644
--- a/dviware/dvisvgm/src/DVIToSVGActions.cpp
+++ b/dviware/dvisvgm/src/DVIToSVGActions.cpp
@@ -69,17 +69,19 @@ void DVIToSVGActions::moveToY (double y, bool forceSVGMove) {
}
-string DVIToSVGActions::getSVGFilename (unsigned pageno) const {
+FilePath DVIToSVGActions::getSVGFilePath (unsigned pageno) const {
+ FilePath path;
if (auto dvi2svg = dynamic_cast<DVIToSVG*>(_dvireader))
- return dvi2svg->getSVGFilename(pageno);
- return "";
+ path = dvi2svg->getSVGFilePath(pageno);
+ return path;
}
string DVIToSVGActions::getBBoxFormatString () const {
+ string boxstr;
if (auto dvi2svg = dynamic_cast<DVIToSVG*>(_dvireader))
- return dvi2svg->getUserBBoxString();
- return "";
+ boxstr = dvi2svg->getUserBBoxString();
+ return boxstr;
}
diff --git a/dviware/dvisvgm/src/DVIToSVGActions.hpp b/dviware/dvisvgm/src/DVIToSVGActions.hpp
index e1292a5efb..88fa6028ee 100644
--- a/dviware/dvisvgm/src/DVIToSVGActions.hpp
+++ b/dviware/dvisvgm/src/DVIToSVGActions.hpp
@@ -73,7 +73,7 @@ class DVIToSVGActions : public DVIActions, public SpecialActions {
BoundingBox& bbox (const std::string &name, bool reset=false) override;
void embed (const BoundingBox &bbox) override;
void embed (const DPair &p, double r=0) override;
- std::string getSVGFilename (unsigned pageno) const override;
+ FilePath getSVGFilePath (unsigned pageno) const override;
std::string getBBoxFormatString () const override;
CharMap& getUsedChars () const {return _usedChars;}
const FontSet& getUsedFonts () const {return _usedFonts;}
diff --git a/dviware/dvisvgm/src/EllipticalArc.cpp b/dviware/dvisvgm/src/EllipticalArc.cpp
new file mode 100644
index 0000000000..b820af0052
--- /dev/null
+++ b/dviware/dvisvgm/src/EllipticalArc.cpp
@@ -0,0 +1,260 @@
+/*************************************************************************
+** EllipticalArc.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 <cmath>
+#include "EllipticalArc.hpp"
+#include "utility.hpp"
+
+using namespace std;
+
+
+/** Constructs an elliptical arc from end point parameterization.
+ * @param[in] start start point of arc
+ * @param[in] rx length of semi-major axis
+ * @param[in] ry length of semi-minor axis
+ * @param[in] angle rotation of ellipse around its center (in radians)
+ * @param[in] laf if true, choose the larger arc between start and end point
+ * @param[in] sweep if true, arc is drawn in the direction of increasing angles
+ * @param[in] end end point of the arc */
+EllipticalArc::EllipticalArc (const DPair &start, double rx, double ry, double angle, bool laf, bool sweep, const DPair &end)
+ : _rx(abs(rx)), _ry(abs(ry)), _rotationAngle(math::normalize_angle(angle, math::PI)),
+ _largeArc(laf), _sweepPositive(sweep), _startPoint(start), _endPoint(end)
+{
+ if (!isStraightLine()) {
+ // fix out-of-range radii according to section F.6.6.3 in
+ // https://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii
+ double c = cos(_rotationAngle);
+ double s = sin(_rotationAngle);
+ DPair p = (_startPoint-_endPoint)/2.0;
+ p = DPair(c*p.x()+s*p.y(), c*p.y()-s*p.x());
+ double lambda = (p.x()*p.x())/(_rx*_rx) + (p.y()*p.y())/(_ry*_ry);
+ if (lambda > 1) {
+ lambda = sqrt(lambda);
+ _rx *= lambda;
+ _ry *= lambda;
+ }
+ }
+}
+
+
+/** Constructs an elliptical arc from center parameterization
+ * @param[in] center absolute coordinates of the center of the ellipse
+ * @param[in] rx length of semi-major axis
+ * @param[in] ry length of semi-minor axis
+ * @param[in] rot rotation of ellipse around its center (in radians)
+ * @param[in] startAngle angle between major axis and vector from center to start point
+ * @param[in] deltaAngle angle between the vectors from center to start and end point, respectively */
+EllipticalArc::EllipticalArc (const DPair &center, double rx, double ry, double rot, double startAngle, double deltaAngle)
+ : _rx(rx), _ry(ry), _rotationAngle(math::normalize_angle(rot, math::TWO_PI)),
+ _largeArc(abs(deltaAngle) > math::PI), _sweepPositive(deltaAngle > 0)
+{
+ // https://www.w3.org/TR/SVG/implnote.html#ArcConversionCenterToEndpoint
+ double c = cos(_rotationAngle);
+ double s = sin(_rotationAngle);
+ double c1 = cos(startAngle);
+ double s1 = sin(startAngle);
+ double c2 = cos(startAngle+deltaAngle);
+ double s2 = sin(startAngle+deltaAngle);
+ _startPoint = DPair(c*rx*c1*c - s*ry*s1, s*rx*c1 + c*ry*s1) + center;
+ _endPoint = DPair(c*rx*c2*c - s*ry*s2, s*rx*c2 + c*ry*s2) + center;
+}
+
+
+/** Returns the angle between (1, 0) and a given vector.
+ * The angle is normalized to the range [0, 2pi). */
+static inline double angle (const DPair &p) {
+ return math::normalize_0_2pi(atan2(p.y(), p.x()));
+}
+
+
+/** Computes the center parameterization of the arc. */
+EllipticalArc::CenterParams EllipticalArc::getCenterParams () const {
+ EllipticalArc::CenterParams params;
+ if (isStraightLine()) {
+ params.center = (_endPoint-_startPoint)/2.0;
+ params.startAngle = params.deltaAngle = 0;
+ }
+ else {
+ // https://www.w3.org/TR/SVG/implnote.html#ArcConversionEndpointToCenter
+ double c = cos(_rotationAngle);
+ double s = sin(_rotationAngle);
+ DPair p = (_startPoint-_endPoint)/2.0;
+ p = DPair(c*p.x()+s*p.y(), c*p.y()-s*p.x());
+ double rx2 = _rx*_rx, ry2 = _ry*_ry;
+ double px2 = p.x()*p.x(), py2 = p.y()*p.y();
+ double radicand = rx2*ry2 - rx2*py2 - ry2*px2;
+ if (radicand < 0) // should not happen if out-of-range radii were fixed correctly
+ radicand = 0;
+ else
+ radicand /= rx2*py2 + ry2*px2;
+ double root = sqrt(radicand) * (_largeArc == _sweepPositive ? -1 : 1);
+ DPair cp(root*p.y()*_rx/_ry, -root*p.x()*_ry/_rx);
+ DPair mid = (_startPoint+_endPoint)/2.0;
+ params.center = DPair(c*cp.x() - s*cp.y() + mid.x(), s*cp.x() + c*cp.y() + mid.y());
+ DPair q1((p.x() - cp.x())/_rx, (p.y() - cp.y())/_ry);
+ DPair q2(-(p.x() + cp.x())/_rx, -(p.y() + cp.y())/_ry);
+ params.startAngle = angle(q1);
+ params.deltaAngle = angle(q2) - params.startAngle;
+ if (_sweepPositive && params.deltaAngle < 0)
+ params.deltaAngle += math::TWO_PI;
+ else if (!_sweepPositive && params.deltaAngle > 0)
+ params.deltaAngle -= math::TWO_PI;
+ }
+ return params;
+}
+
+
+/** Applies the affine transformation described by a given matrix to the arc. */
+void EllipticalArc::transform (const Matrix &matrix) {
+ double c = cos(_rotationAngle);
+ double s = sin(_rotationAngle);
+ Matrix ellipse({_rx*c, -_ry*s, 0, _rx*s, _ry*c}); // E := rotate(xrot)*scale(rx, ry)
+ ellipse.lmultiply(matrix); // E':= M*E
+ // Compute the singular value decomposition of the transformed ellipse shape:
+ // E' = rotate(phi)*scale(sx, sy)*rotate(theta)
+ // The initial, right-hand rotation can be ignored because it rotates the unit circle
+ // around the origin, i.e. rotate(theta) maps the circle to itself.
+ // The signs of sx and sy don't matter either. They just flip the yet unrotated
+ // ellipse on the x- and/or y-axis. Thus, |sx| and |sy| are the new radii,
+ // and phi the new rotation angle.
+ auto vec = math::svd({{ellipse.get(0,0), ellipse.get(0,1)}, {ellipse.get(1,0), ellipse.get(1,1)}});
+ if (std::abs(vec[1]-vec[2]) < 1e-7) { // circle?
+ _rx = _ry = vec[1]; // always >= 0
+ _rotationAngle = 0;
+ }
+ else {
+ _rx = vec[1]; // always >= 0
+ _ry = abs(vec[2]); // ensure >= 0
+ _rotationAngle = math::normalize_angle(vec[0], math::HALF_PI);
+ }
+ // change drawing direction (clockwise vs. counter-clockwise) if 'matrix'
+ // flipped the ellipse horizontally or vertically but not both
+ if ((matrix.get(0, 0) < 0) != (matrix.get(1, 1) < 0))
+ _sweepPositive = !_sweepPositive;
+ _startPoint = matrix * _startPoint;
+ _endPoint = matrix * _endPoint;
+}
+
+
+/** Approximates an arc of the unit circle by a single cubic Bézier curve.
+ * @param[in] phi start angle of the arc in radians
+ * @param[in] delta length of the arc */
+static Bezier approx_unit_arc (double phi, double delta) {
+ double c = 0.551915024494; // see http://spencermortensen.com/articles/bezier-circle
+ if (abs(delta + math::HALF_PI) < 1e-7)
+ c = -c;
+ else
+ c = 4.0/3*tan(delta/4);
+ DPair p1(cos(phi), sin(phi));
+ DPair p4(cos(phi+delta), sin(phi+delta));
+ DPair p2(p1.x()-c*p1.y(), p1.y()+c*p1.x());
+ DPair p3(p4.x()+c*p4.y(), p4.y()-c*p4.x());
+ return Bezier(p1, p2, p3, p4);
+}
+
+
+/** Approximates the arc by a sequence of cubic Bézier curves. */
+vector<Bezier> EllipticalArc::approximate () const {
+ vector<Bezier> beziers;
+ if (_startPoint != _endPoint) {
+ if (isStraightLine()) {
+ DPair dir = (_endPoint - _startPoint);
+ dir /= dir.length()/3.0;
+ beziers.emplace_back(Bezier(_startPoint, _startPoint+dir, _endPoint-dir, _endPoint));
+ }
+ else {
+ CenterParams cparams = getCenterParams();
+ int numCurves = ceil(cparams.deltaAngle/math::HALF_PI);
+ double remainder = abs(fmod(cparams.deltaAngle, math::HALF_PI));
+ if (remainder < 1e-7)
+ numCurves--;
+ else if (math::HALF_PI-remainder < 1e-7)
+ numCurves++;
+ if (numCurves > 0) {
+ double c = cos(_rotationAngle);
+ double s = sin(_rotationAngle);
+ Matrix ellipse = {_rx*c, -_ry*s, cparams.center.x(), _rx*s, _ry*c, cparams.center.y()};
+ double angle = cparams.startAngle;
+ double diff = cparams.deltaAngle/numCurves;
+ while (numCurves-- > 0) {
+ beziers.emplace_back(approx_unit_arc(angle, diff).transform(ellipse));
+ angle += diff;
+ }
+ }
+ }
+ }
+ return beziers;
+}
+
+
+static inline bool is_angle_between (double t, double angle1, double angle2) {
+ if (angle1 < angle2)
+ return angle1 < t && t < angle2;
+ return angle2 > t || t > angle1;
+}
+
+
+/** Returns the tight bounding box of the arc. */
+BoundingBox EllipticalArc::getBBox () const {
+ BoundingBox bbox;
+ bbox.embed(_startPoint);
+ bbox.embed(_endPoint);
+ if (!isStraightLine()) {
+ // compute extremes of ellipse centered at the origin
+ double c = cos(_rotationAngle);
+ double s = sin(_rotationAngle);
+ double tx1 = math::normalize_0_2pi(-atan2(_ry*s, _rx*c)); // position of vertical tangent, d/dt E(tx1)=(0, y)
+ double tx2 = math::normalize_0_2pi(math::PI+tx1); // position of second vertical tangent
+ double ct = cos(tx1);
+ double st = sin(tx1);
+ DPair pv1(_rx*c*ct - _ry*s*st, _rx*s*ct + _ry*c*st); // E(tx1), 1st point on ellipse with vertical tangent
+ DPair pv2 = -pv1; // E(tx2), 2nd point on ellipse with vertical tangent
+
+ double ty1 = math::normalize_0_2pi(atan2(_ry*c, _rx*s)); // position of horizontal tangent, d/dt E(ty1)=(x, 0)
+ double ty2 = math::normalize_0_2pi(math::PI+ty1); // position of second horizontal tangent
+ ct = cos(ty1);
+ st = sin(ty1);
+ DPair ph1(_rx*c*ct - _ry*s*st, _rx*s*ct + _ry*c*st); // E(ty1), 1st point on ellipse with horizontal tangent
+ DPair ph2 = -ph1; // E(ty2), 2nd point on ellipse with horizontal tangent
+
+ // translate extreme points to actual coordinates
+ CenterParams cparams = getCenterParams();
+ pv1 += cparams.center;
+ pv2 += cparams.center;
+ ph1 += cparams.center;
+ ph2 += cparams.center;
+
+ double angle1 = cparams.startAngle;
+ double angle2 = math::normalize_0_2pi(angle1+cparams.deltaAngle);
+ if (!_sweepPositive)
+ swap(angle1, angle2);
+
+ // only consider extreme points located on the arc
+ if (is_angle_between(tx1, angle1, angle2))
+ bbox.embed(pv1);
+ if (is_angle_between(tx2, angle1, angle2))
+ bbox.embed(pv2);
+ if (is_angle_between(ty1, angle1, angle2))
+ bbox.embed(ph1);
+ if (is_angle_between(ty2, angle1, angle2))
+ bbox.embed(ph2);
+ }
+ return bbox;
+}
diff --git a/dviware/dvisvgm/src/EllipticalArc.hpp b/dviware/dvisvgm/src/EllipticalArc.hpp
new file mode 100644
index 0000000000..5d20558196
--- /dev/null
+++ b/dviware/dvisvgm/src/EllipticalArc.hpp
@@ -0,0 +1,59 @@
+/*************************************************************************
+** EllipticalArc.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 "Bezier.hpp"
+#include "BoundingBox.hpp"
+#include "Matrix.hpp"
+#include "Pair.hpp"
+
+class EllipticalArc {
+ public:
+ struct CenterParams {
+ DPair center; ///< absolute coordinates of the center point
+ double startAngle; ///< angle of start point relative to semi-major axis (in radians)
+ double deltaAngle; ///< difference between angles of the start and end point (>0 if sweep flag is set)
+ };
+
+ public:
+ EllipticalArc (const DPair &start, double rx, double ry, double angle, bool laf, bool sweep, const DPair &end);
+ EllipticalArc (const DPair &center, double rx, double ry, double rot, double startAngle, double deltaAngle);
+ CenterParams getCenterParams () const;
+ double rx () const {return _rx;}
+ double ry () const {return _ry;}
+ bool largeArc () const {return _largeArc;}
+ bool sweepPositive () const {return _sweepPositive;}
+ double rotationAngle () const {return _rotationAngle;}
+ DPair startPoint () const {return _startPoint;}
+ DPair endPoint () const {return _endPoint;}
+ bool isStraightLine () const {return _rx < 1e-7 || _ry < 1e-7;}
+ BoundingBox getBBox () const;
+ void transform (const Matrix &matrix);
+ std::vector<Bezier> approximate () const;
+
+ private:
+ double _rx, _ry; ///< length of semi-major and semi-minor axes
+ double _rotationAngle; ///< rotation angle around center (in radians)
+ bool _largeArc; ///< if true, the longer arc from start to end point is chosen, else the shorter one
+ bool _sweepPositive; ///< if true, arc is drawn in direction of positive angles, else the opposite direction
+ DPair _startPoint, _endPoint; ///< absolute coordinates of start and end point
+};
+
diff --git a/dviware/dvisvgm/src/FilePath.cpp b/dviware/dvisvgm/src/FilePath.cpp
index 98449c101b..95fdab8c25 100644
--- a/dviware/dvisvgm/src/FilePath.cpp
+++ b/dviware/dvisvgm/src/FilePath.cpp
@@ -87,10 +87,28 @@ FilePath::FilePath (const string &path, bool isfile, const string &current_dir)
}
-/** Assigns a new path. Relative paths are relative to the current working directory.
+/** Assigns a new path of a file or directory that already exists.
+ * Relative paths are relative to the current working directory.
* @param[in] path absolute or relative path to a file or directory */
-void FilePath::set(const string &path) {
- init(path, !FileSystem::isDirectory(path), FileSystem::getcwd());
+void FilePath::set (const string &path) {
+ set(path, !FileSystem::isDirectory(path));
+}
+
+
+/** Assigns a new path. Relative paths are relative to the current working directory.
+ * @param[in] path absolute or relative path to a file or directory
+ * @param[in] isfile true if 'path' references a file, false if a directory is referenced */
+void FilePath::set (const string &path, bool isfile) {
+ init(path, isfile, FileSystem::getcwd());
+}
+
+
+/** Assigns a new path. Relative paths are relative to the current working directory.
+ * @param[in] path absolute or relative path to a file or directory
+ * @param[in] isfile true if 'path' references a file, false if a directory is referenced
+ * @param[in] current_dir if 'path' is a relative path expression it will be related to 'current_dir' */
+void FilePath::set (const string &path, bool isfile, const string &current_dir) {
+ init(path, isfile, current_dir);
}
@@ -257,3 +275,18 @@ string FilePath::relative (string reldir, bool with_filename) const {
path = ".";
return single_slashes(path);
}
+
+
+/** 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 */
+string FilePath::shorterAbsoluteOrRelative (string reldir, bool with_filename) const {
+ string abs = absolute(with_filename);
+ string rel = relative(reldir, with_filename);
+ return abs.length() < rel.length() ? abs : rel;
+}
+
+
+bool FilePath::exists () const {
+ return empty() ? false : FileSystem::exists(absolute());
+} \ No newline at end of file
diff --git a/dviware/dvisvgm/src/FilePath.hpp b/dviware/dvisvgm/src/FilePath.hpp
index d152c19f20..abcea4a9dc 100644
--- a/dviware/dvisvgm/src/FilePath.hpp
+++ b/dviware/dvisvgm/src/FilePath.hpp
@@ -39,12 +39,16 @@ class FilePath {
};
public:
+ FilePath () =default;
explicit FilePath (const std::string &path) {set(path);}
FilePath (const std::string &path, bool isfile) : FilePath(path, isfile, "") {}
FilePath (const std::string &path, bool isfile, const std::string &current_dir);
void set (const std::string &path);
+ void set (const std::string &path, bool isfile);
+ 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 shorterAbsoluteOrRelative (std::string reldir="", bool with_filename=true) const;
std::string basename () const;
std::string suffix () const;
void suffix (const std::string &s);
@@ -53,6 +57,7 @@ class FilePath {
bool empty () const {return _dirs.empty() && _fname.empty();}
const std::string& filename () const {return _fname;}
void filename (const std::string &fname) {_fname = fname;}
+ bool exists () const;
protected:
void init (std::string path, bool isfile, std::string current_dir);
@@ -62,7 +67,7 @@ class FilePath {
std::vector<Directory> _dirs;
std::string _fname;
#ifdef _WIN32
- char _drive;
+ char _drive=0;
#endif
};
diff --git a/dviware/dvisvgm/src/Font.cpp b/dviware/dvisvgm/src/Font.cpp
index f5bdff6674..97520c5953 100644
--- a/dviware/dvisvgm/src/Font.cpp
+++ b/dviware/dvisvgm/src/Font.cpp
@@ -159,7 +159,7 @@ bool TFMFont::verifyChecksums () const {
// static class variables
bool PhysicalFont::EXACT_BBOX = false;
bool PhysicalFont::KEEP_TEMP_FILES = false;
-const char *PhysicalFont::CACHE_PATH = nullptr;
+string PhysicalFont::CACHE_PATH;
double PhysicalFont::METAFONT_MAG = 4;
FontCache PhysicalFont::_cache;
@@ -324,7 +324,7 @@ std::string PhysicalFont::styleName () const {
bool PhysicalFont::getGlyph (int c, GraphicsPath<int32_t> &glyph, GFGlyphTracer::Callback *callback) const {
if (type() == Type::MF) {
const Glyph *cached_glyph=nullptr;
- if (CACHE_PATH) {
+ if (!CACHE_PATH.empty()) {
_cache.write(CACHE_PATH);
_cache.read(name(), CACHE_PATH);
cached_glyph = _cache.getGlyph(c);
@@ -342,7 +342,7 @@ bool PhysicalFont::getGlyph (int c, GraphicsPath<int32_t> &glyph, GFGlyphTracer:
tracer.setGlyph(glyph);
tracer.executeChar(c);
glyph.closeOpenSubPaths();
- if (CACHE_PATH)
+ if (!CACHE_PATH.empty())
_cache.setGlyph(c, glyph);
return true;
}
@@ -392,7 +392,7 @@ bool PhysicalFont::createGF (string &gfname) const {
* @return number of glyphs traced */
int PhysicalFont::traceAllGlyphs (bool includeCached, GFGlyphTracer::Callback *cb) const {
int count = 0;
- if (type() == Type::MF && CACHE_PATH) {
+ if (type() == Type::MF && !CACHE_PATH.empty()) {
if (const FontMetrics *metrics = getMetrics()) {
int fchar = metrics->firstChar();
int lchar = metrics->lastChar();
@@ -479,7 +479,7 @@ PhysicalFontImpl::PhysicalFontImpl (const string &name, int fontindex, uint32_t
PhysicalFontImpl::~PhysicalFontImpl () {
- if (CACHE_PATH)
+ if (!CACHE_PATH.empty())
_cache.write(CACHE_PATH);
if (!KEEP_TEMP_FILES)
tidy();
diff --git a/dviware/dvisvgm/src/Font.hpp b/dviware/dvisvgm/src/Font.hpp
index bbc0d62b8a..3273a0c14b 100644
--- a/dviware/dvisvgm/src/Font.hpp
+++ b/dviware/dvisvgm/src/Font.hpp
@@ -142,7 +142,7 @@ class PhysicalFont : public virtual Font {
public:
static bool EXACT_BBOX;
static bool KEEP_TEMP_FILES;
- static const char *CACHE_PATH; ///< path to cache directory (0 if caching is disabled)
+ static std::string CACHE_PATH; ///< path to cache directory ("" if caching is disabled)
static double METAFONT_MAG; ///< magnification factor for Metafont calls
protected:
diff --git a/dviware/dvisvgm/src/FontCache.cpp b/dviware/dvisvgm/src/FontCache.cpp
index 5f61fc341c..34478542df 100644
--- a/dviware/dvisvgm/src/FontCache.cpp
+++ b/dviware/dvisvgm/src/FontCache.cpp
@@ -23,13 +23,12 @@
#include <fstream>
#include <iomanip>
#include <sstream>
-#include "CRC32.hpp"
#include "FileSystem.hpp"
#include "FontCache.hpp"
-#include "Glyph.hpp"
#include "Pair.hpp"
#include "StreamReader.hpp"
#include "StreamWriter.hpp"
+#include "XXHashFunction.hpp"
using namespace std;
@@ -78,10 +77,9 @@ bool FontCache::write (const string &fontname, const string &dir) const {
return true;
if (!fontname.empty()) {
- string dirstr = dir.empty() ? FileSystem::getcwd() : dir;
- ostringstream oss;
- oss << dirstr << '/' << fontname << ".fgd";
- ofstream ofs(oss.str(), ios::binary);
+ string pathstr = dir.empty() ? FileSystem::getcwd() : dir;
+ pathstr += "/" + fontname + ".fgd";
+ ofstream ofs(pathstr, ios::binary);
return write(fontname, ofs);
}
return false;
@@ -94,7 +92,7 @@ bool FontCache::write (const string &dir) const {
/** Returns the minimal number of bytes needed to store the given value. */
-static int max_int_size (int32_t value) {
+static int max_number_of_bytes (int32_t value) {
int32_t limit = 0x7f;
for (int i=1; i <= 4; i++) {
if ((value < 0 && -value <= limit+1) || (value >= 0 && value <= limit))
@@ -104,19 +102,49 @@ static int max_int_size (int32_t value) {
return 4;
}
+static int max_int_size () {
+ return 0;
+}
-/** Returns the minimal number of bytes needed to store the biggest
- * pair component of the given vector. */
-static int max_int_size (const Pair<int32_t> *pairs, size_t n) {
- int ret=0;
- for (size_t i=0; i < n; i++) {
- ret = max(ret, max_int_size(pairs[i].x()));
- ret = max(ret, max_int_size(pairs[i].y()));
- }
- return ret;
+template <typename ...Args>
+static int max_int_size (const Glyph::Point &p1, const Args& ...args) {
+ int max1 = max(max_number_of_bytes(p1.x()), max_number_of_bytes(p1.y()));
+ return max(max1, max_int_size(args...));
}
+struct WriteActions : Glyph::IterationActions {
+ WriteActions (StreamWriter &sw, HashFunction &hashfunc) : _sw(sw), _hashfunc(hashfunc) {}
+
+ using Point = Glyph::Point;
+ void moveto (const Point &p) override {write('M', p);}
+ void lineto (const Point &p) override {write('L', p);}
+ void quadto (const Point &p1, const Point &p2) override {write('Q', p1, p2);}
+ void cubicto (const Point &p1, const Point &p2, const Point &p3) override {write('C', p1, p2, p3); }
+ void closepath () override {write('Z');}
+
+ template <typename ...Args>
+ void write (char cmd, Args ...args) {
+ int bytesPerValue = max_int_size(args...);
+ int cmdchar = (bytesPerValue << 5) | (cmd - 'A');
+ _sw.writeUnsigned(cmdchar, 1, _hashfunc);
+ writeParams(bytesPerValue, args...);
+ }
+
+ static void writeParams (int bytesPerValue) {}
+
+ template <typename ...Args>
+ void writeParams (int bytesPerValue, const Point &p, const Args& ...args) {
+ _sw.writeSigned(p.x(), bytesPerValue, _hashfunc);
+ _sw.writeSigned(p.y(), bytesPerValue, _hashfunc);
+ writeParams(bytesPerValue, args...);
+ }
+
+ StreamWriter &_sw;
+ HashFunction &_hashfunc;
+};
+
+
/** Writes the current cache data to a stream (only if anything changed after
* the last call of read()).
* @param[in] fontname name of current font
@@ -129,36 +157,22 @@ bool FontCache::write (const string &fontname, ostream &os) const {
return false;
StreamWriter sw(os);
- CRC32 crc32;
-
- struct WriteActions : Glyph::Actions {
- WriteActions (StreamWriter &sw, CRC32 &crc32) : _sw(sw), _crc32(crc32) {}
-
- void draw (char cmd, const Glyph::Point *points, int n) override {
- int bytes = max_int_size(points, n);
- int cmdchar = (bytes << 5) | (cmd - 'A');
- _sw.writeUnsigned(cmdchar, 1, _crc32);
- for (int i=0; i < n; i++) {
- _sw.writeSigned(points[i].x(), bytes, _crc32);
- _sw.writeSigned(points[i].y(), bytes, _crc32);
- }
- }
- StreamWriter &_sw;
- CRC32 &_crc32;
- } actions(sw, crc32);
-
- sw.writeUnsigned(FORMAT_VERSION, 1, crc32);
- sw.writeUnsigned(0, 4); // space for checksum
- sw.writeString(fontname, crc32, true);
- sw.writeUnsigned(_glyphs.size(), 4, crc32);
+ XXH32HashFunction hashfunc;
+
+ sw.writeUnsigned(FORMAT_VERSION, 1, hashfunc);
+ sw.writeBytes(hashfunc.digestValue()); // space for checksum
+ sw.writeString(fontname, hashfunc, true);
+ sw.writeUnsigned(_glyphs.size(), 4, hashfunc);
+ WriteActions actions(sw, hashfunc);
for (const auto &charglyphpair : _glyphs) {
const Glyph &glyph = charglyphpair.second;
- sw.writeUnsigned(charglyphpair.first, 4, crc32);
- sw.writeUnsigned(glyph.size(), 2, crc32);
+ sw.writeUnsigned(charglyphpair.first, 4, hashfunc);
+ sw.writeUnsigned(glyph.size(), 2, hashfunc);
glyph.iterate(actions, false);
}
os.seekp(1);
- sw.writeUnsigned(crc32.get(), 4); // insert CRC32 checksum
+ auto digest = hashfunc.digestValue();
+ sw.writeBytes(digest); // insert checksum
os.seekp(0, ios::end);
return true;
}
@@ -195,17 +209,17 @@ bool FontCache::read (const string &fontname, istream &is) {
return false;
StreamReader sr(is);
- CRC32 crc32;
- if (sr.readUnsigned(1, crc32) != FORMAT_VERSION)
+ XXH32HashFunction hashfunc;
+ if (sr.readUnsigned(1, hashfunc) != FORMAT_VERSION)
return false;
- uint32_t crc32_cmp = sr.readUnsigned(4);
- crc32.update(is);
- if (crc32.get() != crc32_cmp)
+ auto hashcmp = sr.readBytes(hashfunc.digestSize());
+ hashfunc.update(is);
+ if (hashfunc.digestValue() != hashcmp)
return false;
is.clear();
- is.seekg(5); // continue reading after checksum
+ is.seekg(hashfunc.digestSize()+1); // continue reading after checksum
string fname = sr.readString();
if (fname != fontname)
@@ -237,7 +251,7 @@ bool FontCache::read (const string &fontname, istream &is) {
case 'Q': {
Pair32 p1 = read_pair(bytes, sr);
Pair32 p2 = read_pair(bytes, sr);
- glyph.conicto(p1, p2);
+ glyph.quadto(p1, p2);
break;
}
case 'Z':
@@ -289,17 +303,17 @@ bool FontCache::fontinfo (std::istream &is, FontInfo &info) {
is.seekg(0);
try {
StreamReader sr(is);
- CRC32 crc32;
- if ((info.version = sr.readUnsigned(1, crc32)) != FORMAT_VERSION)
+ XXH32HashFunction hashfunc;
+ if ((info.version = sr.readUnsigned(1, hashfunc)) != FORMAT_VERSION)
return false;
- info.checksum = sr.readUnsigned(4);
- crc32.update(is);
- if (crc32.get() != info.checksum)
+ info.checksum = sr.readBytes(hashfunc.digestSize());
+ hashfunc.update(is);
+ if (hashfunc.digestValue() != info.checksum)
return false;
is.clear();
- is.seekg(5); // continue reading after checksum
+ is.seekg(hashfunc.digestSize()+1); // continue reading after checksum
info.name = sr.readString();
info.numchars = sr.readUnsigned(4);
@@ -361,8 +375,10 @@ void FontCache::fontinfo (const string &dirname, ostream &os, bool purge) {
<< setw(5) << right << strinfopair.second->numchars << " glyph" << (strinfopair.second->numchars == 1 ? ' ':'s')
<< setw(10) << right << strinfopair.second->numcmds << " cmd" << (strinfopair.second->numcmds == 1 ? ' ':'s')
<< setw(12) << right << strinfopair.second->numbytes << " byte" << (strinfopair.second->numbytes == 1 ? ' ':'s')
- << setw(6) << "crc:" << setw(8) << hex << right << setfill('0') << strinfopair.second->checksum
- << endl;
+ << " hash:" << hex;
+ for (int byte : strinfopair.second->checksum)
+ os << setw(2) << setfill('0') << byte;
+ os << '\n';
}
}
if (purge) {
diff --git a/dviware/dvisvgm/src/FontCache.hpp b/dviware/dvisvgm/src/FontCache.hpp
index d242e54ad3..0068b6c78b 100644
--- a/dviware/dvisvgm/src/FontCache.hpp
+++ b/dviware/dvisvgm/src/FontCache.hpp
@@ -30,12 +30,12 @@
class FontCache {
public:
struct FontInfo {
- std::string name; // fontname
- uint16_t version; // file format version
- uint32_t checksum; // CRC32 checksum of file data
- uint32_t numchars; // number of characters
- uint32_t numbytes; // number of bytes
- uint32_t numcmds; // number of path commands
+ std::string name; // fontname
+ uint16_t version; // file format version
+ std::vector<uint8_t> checksum; // checksum of file data
+ uint32_t numchars; // number of characters
+ uint32_t numbytes; // number of bytes
+ uint32_t numcmds; // number of path commands
};
public:
diff --git a/dviware/dvisvgm/src/FontEngine.cpp b/dviware/dvisvgm/src/FontEngine.cpp
index ef02f37335..61efd68916 100644
--- a/dviware/dvisvgm/src/FontEngine.cpp
+++ b/dviware/dvisvgm/src/FontEngine.cpp
@@ -351,9 +351,9 @@ static int lineto (FTVectorPtr to, void *user) {
}
-static int conicto (FTVectorPtr control, FTVectorPtr to, void *user) {
+static int quadto (FTVectorPtr control, FTVectorPtr to, void *user) {
auto glyph = static_cast<Glyph*>(user);
- glyph->conicto(control->x, control->y, to->x, to->y);
+ glyph->quadto(control->x, control->y, to->x, to->y);
return 0;
}
@@ -366,7 +366,7 @@ static int cubicto (FTVectorPtr control1, FTVectorPtr control2, FTVectorPtr to,
/** Traces the outline of a glyph by calling the corresponding "drawing" functions.
- * Each glyph is composed of straight lines, quadratic (conic) or cubic B�zier curves.
+ * Each glyph is composed of straight lines, quadratic or cubic Bézier curves.
* This function creates a Glyph object representing these graphics segments.
* @param[in] face FreeType object representing the font to scan
* @param[in] font corresponding Font object providing additional data
@@ -392,7 +392,7 @@ static bool trace_outline (FT_Face face, const Font *font, int index, Glyph &gly
if (style->bold != 0)
FT_Outline_Embolden(&outline, style->bold/font->scaledSize()*face->units_per_EM);
}
- const FT_Outline_Funcs funcs = {moveto, lineto, conicto, cubicto, 0, 0};
+ const FT_Outline_Funcs funcs = {moveto, lineto, quadto, cubicto, 0, 0};
FT_Outline_Decompose(&outline, &funcs, &glyph);
return true;
}
@@ -402,7 +402,7 @@ static bool trace_outline (FT_Face face, const Font *font, int index, Glyph &gly
/** Traces the outline of a glyph by calling the corresponding "drawing" functions.
- * Each glyph is composed of straight lines, quadratic (conic) or cubic B�zier curves.
+ * Each glyph is composed of straight lines, quadratic or cubic Bézier curves.
* This function creates a Glyph object representing these graphics segments.
* @param[in] c the glyph of this character will be traced
* @param[out] glyph resulting Glyph object containing the graphics segments
diff --git a/dviware/dvisvgm/src/FontWriter.cpp b/dviware/dvisvgm/src/FontWriter.cpp
index 863b142c5c..d93402baf5 100644
--- a/dviware/dvisvgm/src/FontWriter.cpp
+++ b/dviware/dvisvgm/src/FontWriter.cpp
@@ -95,33 +95,38 @@ FontWriter::FontWriter (const PhysicalFont &font) : _font(font) {
}
-struct SFDActions : Glyph::Actions {
+struct SFDActions : Glyph::IterationActions {
explicit SFDActions (ostream &os) : _os(os) {}
- void draw (char cmd, const Glyph::Point *points, int n) override {
- if (cmd == 'Q') {
- // convert quadratic Bézier curve to cubic one
- DPair p0(_currentPoint.x(), _currentPoint.y());
- DPair p1(points[0].x(), points[0].y());
- DPair p2(points[1].x(), points[1].y());
- Bezier bezier(p0, p1, p2);
- for (int i=1; i < 4; i++)
- _os << lround(bezier.point(i).x()) << ' ' << lround(bezier.point(i).y()) << ' ';
- _os << 'c';
- }
- else {
- for (int i=0; i < n; i++)
- _os << points[i].x() << ' ' << points[i].y() << ' ';
- switch (cmd) {
- case 'M': _os << 'm'; _startPoint = points[0]; break;
- case 'L': _os << 'l'; break;
- case 'C': _os << 'c'; break;
- case 'Z': _os << _startPoint.x() << ' ' << _startPoint.y() << " l"; _currentPoint = _startPoint; break;
- }
- }
- if (n > 0)
- _currentPoint = points[n-1];
- _os << " 0\n";
+
+ using Point = Glyph::Point;
+ void moveto (const Point &p) override {write('m', p);}
+ void lineto (const Point &p) override {write('l', p);}
+ void cubicto (const Point &p1, const Point &p2, const Point &p3) override {write('c', p1, p2, p3); }
+ void closepath () override {write('m', startPoint());}
+
+ void quadto (const Point &p1, const Point &p2) override {
+ // convert quadratic Bézier curve to cubic one
+ DPair pt0(currentPoint().x(), currentPoint().y());
+ DPair pt1(p1.x(), p1.y());
+ DPair pt2(p2.x(), p2.y());
+ Bezier b(pt0, pt1, pt2);
+ write('c', round(b.point(0)), round(b.point(1)), round(b.point(2)), round(b.point(3)));
}
+
+ template <typename ...Args>
+ void write (char cmd, const Args& ...args) {
+ writeParams(args...);
+ _os << cmd << " 0\n";
+ }
+
+ static void writeParams () {}
+
+ template <typename Pt, typename ...Args>
+ void writeParams (const Pt &p, const Args& ...args) const {
+ _os << p.x() << ' ' << p.y() << ' ';
+ writeParams(args...);
+ }
+
ostream &_os;
Glyph::Point _startPoint, _currentPoint;
};
diff --git a/dviware/dvisvgm/src/GFReader.cpp b/dviware/dvisvgm/src/GFReader.cpp
index 3cba8e5069..32ba354443 100644
--- a/dviware/dvisvgm/src/GFReader.cpp
+++ b/dviware/dvisvgm/src/GFReader.cpp
@@ -106,11 +106,8 @@ int GFReader::executeCommand () {
cmdPaint0(opcode);
else if (opcode >= 74 && opcode <= 238)
cmdNewRow(opcode-74);
- else if (opcode >= 250) {
- ostringstream oss;
- oss << "undefined GF command (opcode " << opcode << ")";
- throw GFException(oss.str());
- }
+ else if (opcode >= 250)
+ throw GFException("undefined GF command (opcode " + std::to_string(opcode) + ")");
else {
int offset = opcode <= 73 ? 64 : 239-(73-64+1);
const GFCommand &cmd = commands[opcode-offset];
diff --git a/dviware/dvisvgm/src/GraphicsPath.hpp b/dviware/dvisvgm/src/GraphicsPath.hpp
index bf453d11d4..78b29d4630 100644
--- a/dviware/dvisvgm/src/GraphicsPath.hpp
+++ b/dviware/dvisvgm/src/GraphicsPath.hpp
@@ -18,17 +18,148 @@
** along with this program; if not, see <http://www.gnu.org/licenses/>. **
*************************************************************************/
-#ifndef GRAPHICSPATH_HPP
-#define GRAPHICSPATH_HPP
+#pragma once
+#include <array>
#include <cctype>
#include <deque>
#include <ostream>
+#include <type_traits>
+#include <mpark/variant.hpp>
#include "BoundingBox.hpp"
+#include "EllipticalArc.hpp"
#include "Matrix.hpp"
#include "Pair.hpp"
+#include "utility.hpp"
#include "XMLString.hpp"
+template <typename T>
+class GraphicsPath;
+
+namespace gp {
+
+/// Base class for all path data commands, like moveto, lineto, curveto, etc.
+struct CommandBase {};
+
+/** Base class for all path data commands with NUM_POINTS point parameters
+ * @tparam NUM_POINTS number of parameter pairs representing points, e.g. 1 for moveto and lineto */
+template <typename T, int NUM_POINTS>
+class Command : public CommandBase {
+ friend class GraphicsPath<T>;
+ public:
+ int numPoints () const {return NUM_POINTS;}
+ Pair<T>& point (int n) {return points[n];}
+ const Pair<T>& point (int n) const {return points[n];}
+
+ /** Transforms the command by a given transformation matrix.
+ * @params[in] matrix describes the affine transformation to apply
+ * @params[in] currentPoint the untransformed end point of the preceding command */
+ void transform (const Matrix &matrix, const Pair<T> &currentPoint) {
+ for (Pair<T> &p : points)
+ p = matrix * p;
+ }
+
+ /** Returns true if all points are identical to those of another command. */
+ bool pointsEqual (const Command &cmd) const {
+ for (int i=0; i < NUM_POINTS; i++)
+ if (points[i] != cmd.points[i])
+ return false;
+ return true;
+ }
+
+ protected:
+ explicit Command () =default;
+ explicit Command (std::array<Pair<T>, NUM_POINTS> &&pts) : points(std::move(pts)) {}
+
+ protected:
+ std::array<Pair<T>, NUM_POINTS> points;
+};
+
+template <typename T>
+struct MoveTo : public Command<T, 1> {
+ explicit MoveTo (const Pair<T> &p) : Command<T, 1>({p}) {}
+};
+
+template <typename T>
+struct LineTo : public Command<T, 1> {
+ explicit LineTo (const Pair<T> &p) : Command<T, 1>({p}) {}
+};
+
+template <typename T>
+struct CubicTo : public Command<T, 3> {
+ explicit CubicTo (const Pair<T> &p1, const Pair<T> &p2, const Pair<T> &p3) : Command<T, 3>({p1, p2, p3}) {}
+};
+
+template <typename T>
+struct QuadTo : public Command<T, 2> {
+ explicit QuadTo (const Pair<T> &p1, const Pair<T> &p2) : Command<T, 2>({p1, p2}) {}
+};
+
+template <typename T>
+struct ClosePath : public Command<T, 0> {
+ ClosePath () : Command<T, 0>() {}
+};
+
+template <typename T>
+struct ArcTo : Command<T, 1> {
+ ArcTo (T rxx, T ryy, double xrot, bool laf, bool sf, const Pair<T> &pp)
+ : Command<T, 1>({pp}), rx(rxx < 0 ? -rxx : rxx), ry(ryy < 0 ? -ryy : ryy),
+ xrotation(xrot), largeArcFlag(laf), sweepFlag(sf) {}
+
+ bool operator == (const ArcTo &arc) const {
+ return rx == arc.rx
+ && ry == arc.ry
+ && xrotation == arc.xrotation
+ && largeArcFlag == arc.largeArcFlag
+ && sweepFlag == arc.sweepFlag
+ && this->points[0] == arc.points[0];
+ }
+
+ void transform (const Matrix &matrix, const Pair<T> &currentPoint);
+
+ bool operator != (const ArcTo &arc) const {return !(*this == arc);}
+
+ T rx, ry; ///< length of the semi-major and semi-minor axes
+ double xrotation; ///< rotation of the semi-major axis in degrees
+ bool largeArcFlag; ///< if true, the longer arc from start to end point is chosen, else the shorter one
+ bool sweepFlag; ///< if true, arc is drawn in direction of positive angles, else the opposite direction
+};
+
+/** Applies an affine transformation described by a given matrix to the arc segment.
+ * @params[in] matrix describes the affine transformation to apply
+ * @params[in] currentPoint the untransformed end point of the preceding command */
+template <typename T>
+void ArcTo<T>::transform (const Matrix &matrix, const Pair<T> &currentPoint) {
+ EllipticalArc arc(currentPoint, rx, ry, math::deg2rad(xrotation), largeArcFlag, sweepFlag, this->points[0]);
+ arc.transform(matrix);
+ rx = arc.rx();
+ ry = arc.ry();
+ xrotation = math::rad2deg(arc.rotationAngle());
+ largeArcFlag = arc.largeArc();
+ sweepFlag = arc.sweepPositive();
+ this->points[0] = Pair<T>(arc.endPoint());
+}
+
+/** Returns true if two path command objects are identical (same command and same parameters). */
+template <typename Cmd1, typename Cmd2>
+inline typename std::enable_if<std::is_base_of<CommandBase, Cmd1>::value, bool>::type
+operator == (const Cmd1 &cmd1, const Cmd2 &cmd2) {
+ if (std::is_convertible<Cmd1, Cmd2>::value && std::is_convertible<Cmd2, Cmd1>::value)
+ return cmd1.pointsEqual(cmd2);
+ return false;
+}
+
+/** Returns true if two path command objects differ (different commands or different parameters). */
+template <typename Cmd1, typename Cmd2>
+inline typename std::enable_if<std::is_base_of<CommandBase, Cmd1>::value, bool>::type
+operator != (const Cmd1 &cmd1, const Cmd2 &cmd2) {
+ if (std::is_convertible<Cmd1, Cmd2>::value && std::is_convertible<Cmd2, Cmd1>::value)
+ return !cmd1.pointsEqual(cmd2);
+ return true;
+}
+
+} // namespace gp
+
template <typename T>
class GraphicsPath {
@@ -37,74 +168,268 @@ class GraphicsPath {
enum class WindingRule {EVEN_ODD, NON_ZERO};
using Point = Pair<T>;
- struct Command {
- enum class Type {MOVETO, LINETO, CONICTO, CUBICTO, CLOSEPATH};
+ protected:
- explicit Command (Type t) : type(t) {}
+ static XMLString to_param_str (double v, double s, double d, bool leadingSpace) {
+ XMLString str(v*s + d);
+ if (leadingSpace && (str[0] != '-'))
+ str.insert(0, " ");
+ return str;
+ }
- Command (Type t, const Point &p) : type(t) {
- params[0] = p;
- }
+ static XMLString to_param_str (double val, double prev, double s, double d, bool leadingSpace) {
+ XMLString str((val-prev)*s + d);
+ if (leadingSpace && (str[0] != '-'))
+ str.insert(0, " ");
+ return str;
+ }
- Command (Type t, const Point &p1, const Point &p2) : type(t) {
- params[0] = p1;
- params[1] = p2;
- }
+ static std::string to_param_str (const Point &p, double sx, double sy, double dx, double dy, bool leadingSpace) {
+ return to_param_str(p.x(), sx, dx, leadingSpace) + to_param_str(p.y(), sy, dy, true);
+ }
- Command (Type t, const Point &p1, const Point &p2, const Point &p3) : type(t) {
- params[0] = p1;
- params[1] = p2;
- params[2] = p3;
- }
+ static std::string to_param_str (const Point &p, const Point &prev, double sx, double sy, double dx, double dy, bool leadingSpace) {
+ return to_param_str(p.x()-prev.x(), sx, dx, leadingSpace) + to_param_str(p.y()-prev.y(), sy, dy, true);
+ }
+
+ using MoveTo = gp::MoveTo<T>;
+ using LineTo = gp::LineTo<T>;
+ using CubicTo = gp::CubicTo<T>;
+ using QuadTo = gp::QuadTo<T>;
+ using ArcTo = gp::ArcTo<T>;
+ using ClosePath = gp::ClosePath<T>;
+
+ /// Variant representing a single path command
+ using CommandVariant = mpark::variant<MoveTo, LineTo, CubicTo, QuadTo, ArcTo, ClosePath>;
- int numParams () const {
- switch (type) {
- case Type::CLOSEPATH : return 0;
- case Type::MOVETO :
- case Type::LINETO : return 1;
- case Type::CONICTO : return 2;
- case Type::CUBICTO : return 3;
+ class IterationVisitor;
+
+ public:
+ /** Base class providing several template methods being called when executing
+ * GraphicsPath::iterate(). */
+ class IterationActions {
+ friend class IterationVisitor;
+ public:
+ virtual ~IterationActions () =default;
+ virtual void moveto (const Point &p) {}
+ virtual void lineto (const Point &p) {}
+ virtual void hlineto (const T &x) {}
+ virtual void vlineto (const T &y) {}
+ virtual void quadto (const Point &p) {}
+ virtual void quadto (const Point &p1, const Point &p2) {}
+ virtual void cubicto (const Point &p1, const Point &p2) {}
+ virtual void cubicto (const Point &p1, const Point &p2, const Point &p3) {}
+ virtual void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) {}
+ virtual void closepath () {}
+ virtual bool quit () {return false;}
+ virtual void finished () {}
+ const Point& startPoint () const {return _startPoint;}
+ const Point& currentPoint () const {return _currentPoint;}
+
+ private:
+ Point _startPoint; ///< first point of the current sub-path
+ Point _currentPoint; ///< point reached by preceding path command, or (0,0) otherwise
+ };
+
+ protected:
+ class ModificationActions : public IterationActions {
+ friend class GraphicsPath;
+ public:
+ explicit ModificationActions (GraphicsPath &path) : _path(path) {}
+
+ protected:
+ GraphicsPath& path () {return _path;}
+ int commandPos () const {return _commandPos;}
+
+ private:
+ GraphicsPath &_path;
+ int _commandPos=0; ///< number of command in path being processed
+ };
+
+ class WriteActions : public IterationActions {
+ public:
+ WriteActions (std::ostream &os, bool rel, double sx, double sy, double dx, double dy)
+ : _os(os), _relative(rel), _sx(sx), _sy(sy), _dx(dx), _dy(dy) {}
+
+ void moveto (const Point &p) override {write('M', {p});}
+ void lineto (const Point &p) override {write('L', {p});}
+ void hlineto (const T &x) override {write('H', x, this->currentPoint().x(), _sx, _dx);}
+ void vlineto (const T &y) override {write('V', y, this->currentPoint().y(), _sy, _dy);}
+ void quadto (const Point &p) override {write('T', {p});}
+ void quadto (const Point &p1, const Point &p2) override {write('Q', {p1, p2});}
+ void cubicto (const Point &p1, const Point &p2) override {write('S', {p1, p2});}
+ void cubicto (const Point &p1, const Point &p2, const Point &p3) override {write('C', {p1, p2, p3});}
+ void closepath () override {_os << (_relative ? 'z' : 'Z');}
+
+ void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) override {
+ Point diff = p-this->currentPoint();
+ if (std::abs(diff.x()) < 1e-7 && std::abs(diff.y()) < 1e-7)
+ return;
+ if (std::abs(rx) < 1e-7 && std::abs(ry) < 1e-7)
+ lineto(p);
+ else {
+ if (std::abs(std::abs(_sx) - std::abs(_sy)) < 1e-7) { // symmetric scaling?
+ angle *= math::sgn(_sx) * math::sgn(_sy);
+ rx *= std::abs(_sx);
+ ry *= std::abs(_sx);
+ }
+ else { // asymmetric scaling => compute new shape parameters
+ EllipticalArc arc(this->currentPoint(), double(rx), double(ry), math::deg2rad(angle), largeArcFlag, sweepFlag, p);
+ arc.transform(ScalingMatrix(_sx, _sy));
+ angle = math::rad2deg(arc.rotationAngle());
+ rx = arc.rx();
+ ry = arc.ry();
+ }
+ _os << (_relative ? 'a' : 'A')
+ << to_param_str(rx, 1.0, 0, false)
+ << to_param_str(ry, 1.0, 0, true)
+ << to_param_str(angle, 1.0, 0, true)
+ << ' ' << (largeArcFlag ? 1 : 0)
+ << ' ' << (sweepFlag ? 1 : 0);
+ if (_relative)
+ _os << to_param_str(p, this->currentPoint(), _sx, _sy, _dx, _dy, true);
+ else
+ _os << to_param_str(p, _sx, _sy, _dx, _dy, true);
+ }
}
- return 0;
- }
- void transform (const Matrix &matrix) {
- for (int i=0; i < numParams(); i++)
- params[i] = matrix * params[i];
- }
+ protected:
+ void write (char cmdchar, std::initializer_list<Point> points) const {
+ int count=0;
+ if (_relative) {
+ _os << char(tolower(cmdchar));
+ for (const Point &p : points)
+ _os << to_param_str(p, this->currentPoint(), _sx, _sy, _dx, _dy, count++ > 0);
+ }
+ else {
+ _os << cmdchar;
+ for (const Point &p : points)
+ _os << to_param_str(p, _sx, _sy, _dx, _dy, count++ > 0);
+ }
+ }
- bool operator == (const Command &cmd) const {
- bool ret = (type == cmd.type);
- for (int i=0; ret && i < numParams(); i++)
- ret &= (params[i] == cmd.params[i]);
- return ret;
- }
+ void write (char cmdchar, T val, T relval, double s, double d) const {
+ if (_relative)
+ _os << char(tolower(cmdchar)) << to_param_str(val, relval, s, d, false);
+ else
+ _os << cmdchar << to_param_str(val, s, d, false);
+ }
- bool operator != (const Command &cmd) const {
- bool ret = (type != cmd.type);
- for (int i=0; !ret && i < numParams(); i++)
- ret |= (params[i] != cmd.params[i]);
- return ret;
- }
+ private:
+ std::ostream &_os; ///< write output to this stream
+ bool _relative; ///< if true, use relative coordinates in path commands
+ double _sx, _sy; ///< horizontal and vertical scaling factors
+ double _dx, _dy; ///< horizontal and vertical translation values
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /** Calls the corresponding template method of an Action object for the current path command.
+ * If parameter 'useShortCmds' is true, the visitor operators check whether a command
+ * can be shortened due to special cases, e.g. horizontal or vertical lines, smooth
+ * curve connections etc. Otherwise, the full command templates are triggered. */
+ class IterationVisitor {
+ public:
+ IterationVisitor (IterationActions &actions, bool useShortCmds, double eps=1e-7)
+ : _actions(actions), _shortCommandsActive(useShortCmds), _eps(eps) {}
+
+ void setPrevCommand (const CommandVariant &prevCommand) {
+ _prevCommand = &prevCommand;
+ }
+
+ void operator () (const MoveTo &cmd) {
+ _actions.moveto(cmd.points[0]);
+ _actions._startPoint = _actions._currentPoint = cmd.points[0];
+ }
+
+ void operator () (const LineTo &cmd) {
+ Point diff = abs(_actions._currentPoint-cmd.points[0]);
+ if (diff.x() >= _eps || diff.y() >= _eps) {
+ if (!_shortCommandsActive)
+ _actions.lineto(cmd.points[0]);
+ else {
+ if (diff.x() < _eps)
+ _actions.vlineto(cmd.points[0].y());
+ else if (diff.y() < _eps)
+ _actions.hlineto(cmd.points[0].x());
+ else
+ _actions.lineto(cmd.points[0]);
+ }
+ }
+ _actions._currentPoint = cmd.points[0];
+ }
+
+ void operator () (const CubicTo &cmd) {
+ bool smooth=false;
+ if (_shortCommandsActive) {
+ if (auto *prevCubic = mpark::get_if<CubicTo>(_prevCommand)) {
+ Point diff = abs(cmd.points[0] - prevCubic->points[2]*T(2) + prevCubic->points[1]);
+ if ((smooth = (diff.x() < _eps && diff.y() < _eps)))
+ _actions.cubicto(cmd.points[1], cmd.points[2]);
+ }
+ }
+ if (!smooth)
+ _actions.cubicto(cmd.points[0], cmd.points[1], cmd.points[2]);
+ _actions._currentPoint = cmd.points[2];
+ }
+
+ void operator () (const QuadTo &cmd) {
+ bool smooth=false;
+ if (_shortCommandsActive) {
+ if (auto *prevQuad = mpark::get_if<QuadTo>(_prevCommand)) {
+ Point diff = abs(cmd.points[0] - prevQuad->points[1] * T(2) + prevQuad->points[0]);
+ if ((smooth = (diff.x() < _eps && diff.y() < _eps))) // is reflection?
+ _actions.quadto(cmd.points[1]);
+ }
+ }
+ if (!smooth)
+ _actions.quadto(cmd.points[0], cmd.points[1]);
+ _actions._currentPoint = cmd.points[1];
+ }
- Type type;
- Point params[3];
+ void operator () (const ClosePath &cmd) {
+ _actions.closepath();
+ _actions._currentPoint = _actions._startPoint;
+ }
+
+ void operator () (const ArcTo &cmd) {
+ _actions.arcto(cmd.rx, cmd.ry, cmd.xrotation, cmd.largeArcFlag, cmd.sweepFlag, cmd.points[0]);
+ _actions._currentPoint = cmd.points[0];
+ }
+
+ private:
+ IterationActions &_actions;
+ bool _shortCommandsActive=false;
+ double _eps=1e-7;
+ const CommandVariant *_prevCommand=nullptr;
};
- struct Actions {
- virtual ~Actions () =default;
- virtual void moveto (const Point &p) {}
- virtual void lineto (const Point &p) {}
- virtual void hlineto (const T &y) {}
- virtual void vlineto (const T &x) {}
- virtual void conicto (const Point &p) {}
- virtual void conicto (const Point &p1, const Point &p2) {}
- virtual void cubicto (const Point &p1, const Point &p2) {}
- virtual void cubicto (const Point &p1, const Point &p2, const Point &p3) {}
- virtual void closepath () {}
- virtual void draw (char cmd, const Point *points, int n) {}
- virtual bool quit () {return false;}
- virtual void finished () {}
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /** Transforms all Point parameters of a path command. */
+ class TransformVisior {
+ public:
+ explicit TransformVisior (const Matrix &m) : matrix(m) {}
+
+ template <typename Cmd> void operator () (Cmd &cmd) {
+ Point cp = cmd.point(cmd.numPoints()-1);
+ cmd.transform(matrix, _currentPoint);
+ _currentPoint = cp;
+ }
+
+ void operator () (MoveTo &cmd) {
+ Point cp = cmd.point(0);
+ cmd.transform(matrix, _currentPoint);
+ _startPoint = _currentPoint = cp;
+ }
+
+ void operator () (ClosePath &cmd) {
+ _currentPoint = _startPoint;
+ }
+
+ private:
+ const Matrix &matrix;
+ Point _startPoint, _currentPoint; ///< untransformed start end current point
};
public:
@@ -127,6 +452,9 @@ class GraphicsPath {
return _commands.size();
}
+ const Point& startPoint () const {return _startPoint;}
+ const Point& finalPoint () const {return _finalPoint;}
+
/// Insert another path at the beginning of this one.
void prepend (const GraphicsPath &path) {
_commands.insert(_commands.begin(), path._commands.begin(), path._commands.end());
@@ -138,10 +466,11 @@ class GraphicsPath {
void moveto (const Point &p) {
// avoid sequences of several MOVETOs; always use latest
- if (_commands.empty() || _commands.back().type != Command::Type::MOVETO)
- _commands.emplace_back(Command(Command::Type::MOVETO, p));
+ if (_commands.empty() || !mpark::get_if<MoveTo>(&_commands.back()))
+ _commands.emplace_back(MoveTo{p});
else
- _commands.back().params[0] = p;
+ mpark::get<MoveTo>(_commands.back()).points[0] = p;
+ _startPoint = _finalPoint = p;
}
void lineto (const T &x, const T &y) {
@@ -149,57 +478,94 @@ class GraphicsPath {
}
void lineto (const Point &p) {
- _commands.emplace_back(Command(Command::Type::LINETO, p));
+ _commands.emplace_back(LineTo{p});
+ _finalPoint = p;
}
- void conicto (const T &x1, const T &y1, const T &x2, const T &y2) {
- conicto(Point(x1, y1), Point(x2, y2));
+ void quadto (const T &x1, const T &y1, const T &x2, const T &y2) {
+ quadto(Point(x1, y1), Point(x2, y2));
}
- void conicto (const Point &p1, const Point &p2) {
- _commands.emplace_back(Command(Command::Type::CONICTO, p1, p2));
+ /** Creates a quadratic Bézier segment. */
+ void quadto (const Point &p1, const Point &p2) {
+ _commands.emplace_back(QuadTo{p1, p2});
+ _finalPoint = p2;
+ }
+
+ /** Creates a quadratic Bézier segment smoothly extending a preceding one, i.e. the gradients
+ * of the two curves are identical at the connection point. The control point of the second
+ * curve is computed as the reflection of the preceding curve's control point at the connection
+ * point. */
+ void quadto (const Point &p2) {
+ Point p1;
+ if (!_commands.empty()) {
+ if (auto qto = mpark::get_if<QuadTo>(&_commands.back()))
+ p1 = _finalPoint*T(2) - qto->point(0); // reflect previous control point at current point
+ else // previous command isn't a quadto?
+ p1 = _finalPoint; // => use current point as control point
+ }
+ quadto(p1, p2);
}
void cubicto (const T &x1, const T &y1, const T &x2, const T &y2, const T &x3, const T &y3) {
cubicto(Point(x1, y1), Point(x2, y2), Point(x3, y3));
}
+ /** Creates a cubic Bézier segment. */
void cubicto (const Point &p1, const Point &p2, const Point &p3) {
- _commands.emplace_back(Command(Command::Type::CUBICTO, p1, p2, p3));
+ _commands.emplace_back(CubicTo{p1, p2, p3});
+ _finalPoint = p3;
}
- void closepath () {
- _commands.emplace_back(Command(Command::Type::CLOSEPATH));
+ /** Creates a cubic Bézier segment smoothly extending a preceding one, i.e. the gradients
+ * of the two curves are identical at the connection point. The first control point of
+ * the second curve is computed as the reflection of the preceding curve's second control
+ * point at the connection point. */
+ void cubicto (const Point &p2, const Point &p3) {
+ Point p1;
+ if (!_commands.empty()) {
+ if (auto cto = mpark::get_if<CubicTo>(&_commands.back()))
+ p1 = _finalPoint*T(2) - cto->point(1); // reflect previous control point at current point
+ else // previous command isn't a cubicto?
+ p1 = _finalPoint; // => use current point as control point
+ }
+ cubicto(p1, p2, p3);
}
- const std::vector<Command>& commands () const {
- return _commands;
+ void closepath () {
+ if (!_commands.empty() && !mpark::get_if<ClosePath>(&_commands.back())) {
+ _commands.emplace_back(ClosePath{});
+ _finalPoint = _startPoint;
+ }
}
+ void arcto (double rx, double ry, double angle, bool laf, bool sweep, const Point &p) {
+ _commands.emplace_back(ArcTo{rx, ry, angle, laf, sweep, p});
+ _finalPoint = p;
+ }
/** Detects all open subpaths and closes them by adding a closePath command.
* Most font formats only support closed outline paths so there are no explicit closePath statements
* in the glyph's outline description. All open paths are automatically closed by the renderer.
* This method detects all open paths and adds the missing closePath statement. */
void closeOpenSubPaths () {
- Command *prevCommand=0;
+ CommandVariant *prevCmd = nullptr;
for (auto it=_commands.begin(); it != _commands.end(); ++it) {
- if (it->type == Command::Type::MOVETO && prevCommand && prevCommand->type != Command::Type::CLOSEPATH) {
- prevCommand = &(*it);
- it = _commands.insert(it, Command(Command::Type::CLOSEPATH))+1;
+ if (mpark::get_if<MoveTo>(&*it) && prevCmd && !mpark::get_if<ClosePath>(prevCmd)) {
+ prevCmd = &*it;
+ it = _commands.insert(it, ClosePath{})+1;
}
else
- prevCommand = &(*it);
+ prevCmd = &*it;
}
- if (!_commands.empty() && _commands.back().type != Command::Type::CLOSEPATH)
+ if (!_commands.empty() && !mpark::get_if<ClosePath>(&_commands.back()))
closepath();
}
-
- /** Removes redundant path commands commands. Currently, it only removes movetos. */
+ /** Removes redundant path commands commands. Currently, only removes movetos. */
void removeRedundantCommands () {
// remove trailing moveto commands
- while (!_commands.empty() && _commands.back().type == Command::Type::MOVETO)
+ while (!_commands.empty() && mpark::get_if<MoveTo>(&_commands.back()))
_commands.pop_back();
// resolve intermediate sequences of moveto commands
auto it=_commands.begin();
@@ -207,7 +573,7 @@ class GraphicsPath {
return;
auto prev = it++;
while (it != _commands.end()) {
- if (prev->type != Command::Type::MOVETO || it->type != Command::Type::MOVETO)
+ if (!mpark::get_if<MoveTo>(&*prev) || !mpark::get_if<MoveTo>(&*it))
prev = it++;
else {
prev = _commands.erase(prev); // remove leading MOVETO and advance 'prev' to 'it'
@@ -216,7 +582,6 @@ class GraphicsPath {
}
}
-
/** Writes the path data as SVG path drawing command to a given output stream.
* @param[in] os output stream used to write the SVG commands to
* @param[in] relative if true, create relative rather than absolute coordinate values
@@ -225,102 +590,38 @@ class GraphicsPath {
* @param[in] dx horizontal translation in PS point units
* @param[in] dy vertical translation in PS point units */
void writeSVG (std::ostream &os, bool relative, double sx=1.0, double sy=1.0, double dx=0.0, double dy=0.0) const {
- struct WriteActions : Actions {
- WriteActions (std::ostream &os, bool relative, double sx, double sy, double dx, double dy)
- : _os(os), _relative(relative), _sx(sx), _sy(sy), _dx(dx), _dy(dy) {}
-
- void draw (char cmd, const Point *points, int n) override {
- if (_relative)
- cmd = tolower(cmd);
- _os << cmd;
- switch (cmd) {
- case 'h': _os << XMLString(_sx*(points->x()-_currentPoint.x())+_dx); break;
- case 'v': _os << XMLString(_sy*(points->y()-_currentPoint.y())+_dy); break;
- case 'z': _currentPoint = _startPoint; break;
- case 'H': _os << XMLString(_sx*points->x()+_dx); break;
- case 'V': _os << XMLString(_sy*points->y()+_dy); break;
- default : {
- for (int i=0; i < n; i++) {
- Point p = points[i];
- if (_relative)
- p -= _currentPoint;
- double x = _sx*p.x() + _dx;
- XMLString xstr(x);
- if (i > 0 && (xstr[0] != '-')) // space required to separate numbers?
- _os << ' ';
- _os << xstr;
- double y = _sy*p.y() + _dy;
- XMLString ystr(y);
- if (ystr[0] != '-') // space required to separate numbers?
- _os << ' ';
- _os << ystr;
- }
- }
- }
- if (cmd == 'm')
- _startPoint = points[0];
- if (islower(cmd) && n > 0)
- _currentPoint = points[n-1];
- }
- std::ostream &_os;
- bool _relative;
- double _sx, _sy, _dx, _dy;
- Point _startPoint, _currentPoint;
- } actions(os, relative, sx, sy, dx, dy);
+ WriteActions actions(os, relative, sx, sy, dx, dy);
iterate(actions, true);
}
-#if 0
- void writePS (std::ostream &os, double sx=1.0, double sy=1.0, double dx=0.0, double dy=0.0) const {
- struct WriteActions : Actions {
- WriteActions (std::ostream &os, double sx, double sy, double dx, double dy)
- : _os(os), _sx(sx), _sy(sy), _dx(dx), _dy(dy) {}
- void draw (char cmd, const Point *points, int n) {
- for (int i=0; i < n; i++)
- _os << _sx*points[i].x()+_dx << ' ' << _sy*points[i].y()+_dy << ' ';
- switch (cmd) {
- case 'M': _os << "moveto"; break;
- case 'L': _os << "lineto"; break;
- case 'C': _os << "curveto"; break;
- case 'Z': _os << "closepath"; break;
- default: ;
- }
- _os << '\n';
- }
- std::ostream &_os;
- bool _relative;
- double _sx, _sy, _dx, _dy;
- } actions(os, sx, sy, dx, dy);
- iterate(actions, false);
- }
-#endif
-
-
/** Computes the bounding box of the current path.
* @param[out] bbox the computed bounding box */
void computeBBox (BoundingBox &bbox) const {
- struct BBoxActions : Actions {
+ struct BBoxActions : IterationActions {
explicit BBoxActions (BoundingBox &bb) : bbox(bb) {}
void moveto (const Point &p) override {bbox.embed(p);}
void lineto (const Point &p) override {bbox.embed(p);}
- void conicto (const Point &p1, const Point &p2) override {bbox.embed(p1); bbox.embed(p2);}
+ void quadto (const Point &p1, const Point &p2) override {bbox.embed(p1); bbox.embed(p2);}
void cubicto (const Point &p1, const Point &p2, const Point &p3) override {bbox.embed(p1); bbox.embed(p2); bbox.embed(p3);}
+ void arcto (T rx, T ry, double angle, bool laf, bool sweep, const Point &p) override {
+ bbox.embed(EllipticalArc(this->currentPoint(), double(rx), double(ry), angle, laf, sweep, p).getBBox());
+ }
BoundingBox &bbox;
} actions(bbox);
iterate(actions, false);
}
-
/** Checks whether the current path describes a dot/point only (with no extent).
* @param[out] p coordinates of the point if path describes a dot
* @return true if path is a dot/point */
bool isDot (Point &p) const {
- struct DotActions : Actions {
+ struct DotActions : IterationActions {
DotActions () : differs(false) {}
void moveto (const Point &p) override {point = p;}
void lineto (const Point &p) override {differs = (p != point);}
- void conicto (const Point &p1, const Point &p2) override {differs = (point != p1 || point != p2);}
+ void quadto (const Point &p1, const Point &p2) override { differs = (point != p1 || point != p2);}
void cubicto (const Point &p1, const Point &p2, const Point &p3) override {differs = (point != p1 || point != p2 || point != p3);}
+ void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) override { differs = (point != p);}
bool quit () override {return differs;}
Point point;
bool differs;
@@ -330,138 +631,105 @@ class GraphicsPath {
return !actions.differs;
}
+ /** Replaces all elliptic arcs with cubic Bézier curves. */
+ void approximateArcs () {
+ struct ArcActions : ModificationActions {
+ explicit ArcActions (GraphicsPath &path) : ModificationActions(path) {}
+ void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) override {
+ EllipticalArc arc(this->currentPoint(), rx, ry, angle, largeArcFlag, sweepFlag, p);
+ std::vector<CommandVariant> cmds;
+ for (const Bezier &bezier : arc.approximate())
+ cmds.emplace_back(CubicTo{bezier.point(1), bezier.point(2), bezier.point(3)});
+ this->path().replace(this->commandPos(), cmds);
+ }
+ } actions(*this);
+ iterate(actions);
+ }
/** Transforms the path according to a given Matrix.
* @param[in] matrix Matrix describing the affine transformation */
void transform (const Matrix &matrix) {
- for (Command &command : _commands)
- command.transform(matrix);
+ TransformVisior visior(matrix);
+ for (CommandVariant &command : _commands)
+ mpark::visit(visior, command);
}
-
+ /** Returns true if this path equals another one, i.e. it consists the same sequence
+ * of commands and coordinates. */
bool operator == (const GraphicsPath &path) const {
if (size() != path.size())
return false;
auto it = _commands.begin();
- for (const Command &cmd : path._commands) {
+ for (const auto &cmd : path._commands) {
if (*it++ != cmd)
return false;
}
return true;
}
-
+ /** Returns true if this path differs from another one (command-wise). */
bool operator != (const GraphicsPath &path) const {
if (size() != path.size())
return true;
auto it = _commands.begin();
- for (const Command &cmd : path._commands) {
+ for (const auto &cmd : path._commands) {
if (*it++ != cmd)
return true;
}
return false;
}
-
- void iterate (Actions &actions, bool optimize) const;
-
- private:
- std::deque<Command> _commands;
- WindingRule _windingRule;
-};
-
-
-/** Iterates over all commands defining this path and calls the corresponding template methods.
- * In the case of successive bezier curve sequences, control points or tangent slopes are often
- * identical so that the path description contains redundant information. SVG provides shorthand
- * curve commands that require less parameters. If 'optimize' is true, this method detects such
- * command sequences.
- * @param[in] actions template methods called by each iteration step
- * @param[in] optimize if true, shorthand drawing commands (hlineto, vlineto,...) are considered */
-template <typename T>
-void GraphicsPath<T>::iterate (Actions &actions, bool optimize) const {
- auto prev = _commands.end(); // pointer to preceding command
- Point fp; // first point of current path
- Point cp; // current point
- Point pstore[2];
- const double eps = XMLString::DECIMAL_PLACES > 0 ? pow(10, -XMLString::DECIMAL_PLACES) : 1e-7;
- for (auto it=_commands.begin(); it != _commands.end() && !actions.quit(); ++it) {
- const Point *params = it->params;
- switch (it->type) {
- case Command::Type::MOVETO:
- actions.moveto(params[0]);
- actions.draw('M', params, 1);
- fp = params[0];
- break;
- case Command::Type::LINETO: {
- Point diff = abs(cp - params[0]);
- if (diff.x() < eps && diff.y() < eps)
+ /** Iterates over all commands defining this path and calls the corresponding template methods.
+ * In the case of successive bezier curve sequences, control points or tangent slopes are often
+ * identical so that the path description contains redundant information. SVG provides shorthand
+ * curve commands that require less parameters. If 'optimize' is true, this method detects such
+ * command sequences.
+ * @param[in] actions template methods called by each iteration step
+ * @param[in] optimize if true, shorthand drawing commands (hlineto, vlineto,...) are considered */
+ void iterate (IterationActions &actions, bool optimize) const {
+ double eps = XMLString::DECIMAL_PLACES > 0 ? pow(10, -XMLString::DECIMAL_PLACES) : 1e-7;
+ IterationVisitor visitor(actions, optimize, eps);
+ for (const CommandVariant &cmd : _commands) {
+ if (actions.quit())
break;
- if (optimize) {
- if (diff.x() < eps) {
- actions.vlineto(params[0].y());
- actions.draw('V', params, 1);
- }
- else if (diff.y() < eps) {
- actions.hlineto(params[0].x());
- actions.draw('H', params, 1);
- }
- else {
- actions.lineto(params[0]);
- actions.draw('L', params, 1);
- }
- }
- else {
- actions.lineto(params[0]);
- actions.draw('L', params, 1);
- }
- break;
+ mpark::visit(visitor, cmd);
+ visitor.setPrevCommand(cmd);
}
- case Command::Type::CONICTO: {
- // check if first control point is the reflection of the preceding second control point?
- Point diff = abs(params[0]-pstore[1]*T(2)+pstore[0]);
- bool isReflection = diff.x() < eps && diff.y() < eps;
- if (optimize && prev != _commands.end() && prev->type == Command::Type::CONICTO && isReflection) {
- actions.conicto(params[1]);
- actions.draw('T', params+1, 1);
- }
- else {
- actions.conicto(params[0], params[1]);
- actions.draw('Q', params, 2);
- }
- pstore[0] = params[0]; // store control point and
- pstore[1] = params[1]; // curve endpoint
- break;
+ actions.finished();
+ }
+
+ protected:
+ /** Replaces a command by a sequence of other ones.
+ * @param[in] pos position of command to replace (0-based)
+ * @param[in] cmds commands to insert */
+ void replace (int pos, const std::vector<CommandVariant> &cmds) {
+ auto it = _commands.end();
+ if (!_commands.empty()) {
+ it = _commands.begin()+pos;
+ it = _commands.erase(it);
}
- case Command::Type::CUBICTO: {
- // check if first control point is the reflection of the preceding second control point?
- Point diff = abs(params[0]-pstore[1]*T(2)+pstore[0]);
- bool isReflection = diff.x() < eps && diff.y() < eps;
- // is first control point reflection of preceding second control point?
- if (optimize && prev != _commands.end() && prev->type == Command::Type::CUBICTO && isReflection) {
- actions.cubicto(params[1], params[2]);
- actions.draw('S', params+1, 2);
- }
- else {
- actions.cubicto(params[0], params[1], params[2]);
- actions.draw('C', params, 3);
- }
- pstore[0] = params[1]; // store second control point and
- pstore[1] = params[2]; // curve endpoint
- break;
+ _commands.insert(it, cmds.begin(), cmds.end());
+ }
+
+ /** Iterates over all commands of the path and calls the corresponding template methods.
+ * In contrast to the public iterate() method, this one allows to modify the command sequence.
+ * @param[in] actions template methods called by each iteration step */
+ void iterate (ModificationActions &actions) {
+ IterationVisitor visitor(actions, false);
+ // no iterators here since they may be invalidated during path modifications
+ for (size_t i=0; i < _commands.size(); i++) {
+ if (actions.quit())
+ break;
+ actions._commandPos = i;
+ mpark::visit(visitor, _commands[i]);
+ visitor.setPrevCommand(_commands[i]);
}
- case Command::Type::CLOSEPATH:
- actions.closepath();
- actions.draw('Z', params, 0);
- cp = fp;
- }
- // update current point
- const int np = it->numParams();
- if (np > 0)
- cp = it->params[np-1];
- prev = it;
- }
- actions.finished();
-}
+ actions.finished();
+ }
-#endif
+ private:
+ std::deque<CommandVariant> _commands; ///< sequence of path commands
+ WindingRule _windingRule;
+ Point _startPoint; ///< start point of final sub-path
+ Point _finalPoint; ///< final point reached by last command in path
+};
diff --git a/dviware/dvisvgm/src/HashFunction.cpp b/dviware/dvisvgm/src/HashFunction.cpp
index 7479fca604..517cc8f3cc 100644
--- a/dviware/dvisvgm/src/HashFunction.cpp
+++ b/dviware/dvisvgm/src/HashFunction.cpp
@@ -31,7 +31,12 @@ using namespace std;
/** Returns a vector containing the names of the currently supported hash algorithms. */
vector<string> HashFunction::supportedAlgorithms () {
- return vector<string> {"md5", "xxh32", "xxh64"};
+ return vector<string> {
+ "md5", "xxh32", "xxh64",
+#ifdef ENABLE_XXH128
+ "xxh128"
+#endif
+ };
}
@@ -52,6 +57,10 @@ unique_ptr<HashFunction> HashFunction::create (const string &name) {
return util::make_unique<XXH32HashFunction>();
if (lowerName == "xxh64")
return util::make_unique<XXH64HashFunction>();
+#ifdef ENABLE_XXH128
+ if (lowerName == "xxh128")
+ return util::make_unique<XXH128HashFunction>();
+#endif
return nullptr;
}
@@ -74,6 +83,15 @@ std::unique_ptr<HashFunction> HashFunction::create (const string &name, const ve
}
+void HashFunction::update (istream &is) {
+ char buf[4096];
+ while (is) {
+ is.read(buf, 4096);
+ update(buf, is.gcount());
+ }
+}
+
+
/** Returns the current digest as hexadecimal value. */
string HashFunction::digestString () const {
ostringstream oss;
diff --git a/dviware/dvisvgm/src/HashFunction.hpp b/dviware/dvisvgm/src/HashFunction.hpp
index 1b1259393f..bba61918ed 100644
--- a/dviware/dvisvgm/src/HashFunction.hpp
+++ b/dviware/dvisvgm/src/HashFunction.hpp
@@ -21,6 +21,7 @@
#ifndef HASHFUNCTION_HPP
#define HASHFUNCTION_HPP
+#include <istream>
#include <memory>
#include <string>
#include <vector>
@@ -35,6 +36,7 @@ class HashFunction {
virtual void update (const std::string &data) =0;
virtual void update (const std::vector<uint8_t> &data) =0;
virtual std::vector<uint8_t> digestValue () const =0;
+ void update (std::istream &is);
std::string digestString () const;
static std::vector<std::string> supportedAlgorithms ();
static bool isSupportedAlgorithm (const std::string &algo);
diff --git a/dviware/dvisvgm/src/HyperlinkManager.cpp b/dviware/dvisvgm/src/HyperlinkManager.cpp
index ad5cabd93c..de412afcdc 100644
--- a/dviware/dvisvgm/src/HyperlinkManager.cpp
+++ b/dviware/dvisvgm/src/HyperlinkManager.cpp
@@ -96,7 +96,7 @@ void HyperlinkManager::createLink (string uri, SpecialActions &actions) {
uri = "#loc"+XMLString(id);
if (actions.getCurrentPageNumber() != it->second.pageno) {
ostringstream oss;
- oss << actions.getSVGFilename(it->second.pageno) << uri;
+ oss << actions.getSVGFilePath(it->second.pageno).relative() << uri;
uri = oss.str();
}
}
diff --git a/dviware/dvisvgm/src/ImageToSVG.cpp b/dviware/dvisvgm/src/ImageToSVG.cpp
index 92e5ece1dd..26edb3c753 100644
--- a/dviware/dvisvgm/src/ImageToSVG.cpp
+++ b/dviware/dvisvgm/src/ImageToSVG.cpp
@@ -85,7 +85,7 @@ void ImageToSVG::convert (int pageno) {
_svg.setBBox(_bbox);
_svg.appendToDoc(util::make_unique<XMLComment>(" This file was generated by dvisvgm " + string(PROGRAM_VERSION) + " "));
bool success = _svg.write(_out.getPageStream(pageno, totalPageCount()));
- string svgfname = _out.filename(pageno, totalPageCount());
+ string svgfname = _out.filepath(pageno, totalPageCount()).shorterAbsoluteOrRelative();
if (svgfname.empty())
svgfname = "<stdout>";
if (!success)
@@ -145,10 +145,11 @@ void ImageToSVG::convert (const std::string &rangestr, pair<int,int> *pageinfo)
}
-string ImageToSVG::getSVGFilename (unsigned pageno) const {
+FilePath ImageToSVG::getSVGFilePath (unsigned pageno) const {
+ FilePath path;
if (pageno == 1)
- return _out.filename(1, 1);
- return "";
+ path = _out.filepath(1, 1);
+ return path;
}
diff --git a/dviware/dvisvgm/src/ImageToSVG.hpp b/dviware/dvisvgm/src/ImageToSVG.hpp
index 88564fd736..a4e4de223f 100644
--- a/dviware/dvisvgm/src/ImageToSVG.hpp
+++ b/dviware/dvisvgm/src/ImageToSVG.hpp
@@ -68,7 +68,7 @@ class ImageToSVG : protected SpecialActions {
unsigned getCurrentPageNumber() const override {return 0;}
BoundingBox& bbox () override {return _bbox;}
BoundingBox& bbox (const std::string &name, bool reset=false) override {return _bbox;}
- std::string getSVGFilename (unsigned pageno) const override;
+ FilePath getSVGFilePath (unsigned pageno) const override;
std::string getBBoxFormatString () const override {return "";}
private:
diff --git a/dviware/dvisvgm/src/JFM.cpp b/dviware/dvisvgm/src/JFM.cpp
index a1835f6732..a79fdf0c03 100644
--- a/dviware/dvisvgm/src/JFM.cpp
+++ b/dviware/dvisvgm/src/JFM.cpp
@@ -32,11 +32,9 @@ JFM::JFM (istream &is) {
is.seekg(0);
StreamReader reader(is);
uint16_t id = uint16_t(reader.readUnsigned(2)); // JFM ID (9 or 11)
- if (id != 9 && id != 11) {
- ostringstream oss;
- oss << "invalid JFM identifier " << id << " (9 or 11 expected)";
- throw FontMetricException(oss.str());
- }
+ if (id != 9 && id != 11)
+ throw FontMetricException("invalid JFM identifier " + std::to_string(id) + " (9 or 11 expected)");
+
_vertical = (id == 9);
uint16_t nt = uint16_t(reader.readUnsigned(2)); // length of character type table
uint16_t lf = uint16_t(reader.readUnsigned(2)); // length of entire file in 4 byte words
diff --git a/dviware/dvisvgm/src/Length.cpp b/dviware/dvisvgm/src/Length.cpp
index ba7dd2a225..cbee103d49 100644
--- a/dviware/dvisvgm/src/Length.cpp
+++ b/dviware/dvisvgm/src/Length.cpp
@@ -60,9 +60,7 @@ void Length::set (double val, Unit unit) {
case Unit::SP: _pt = val/pt2sp; break;
default:
// this isn't supposed to happen
- ostringstream oss;
- oss << "invalid length unit: (" << static_cast<int>(unit) << ")";
- throw UnitException(oss.str());
+ throw UnitException("invalid length unit: (" + std::to_string(static_cast<int>(unit)) + ")");
}
}
@@ -80,9 +78,7 @@ double Length::get (Unit unit) const {
case Unit::SP: return sp();
}
// this isn't supposed to happen
- ostringstream oss;
- oss << "invalid length unit: (" << static_cast<int>(unit) << ")";
- throw UnitException(oss.str());
+ throw UnitException("invalid length unit: (" + std::to_string(static_cast<int>(unit)) + ")");
}
@@ -111,7 +107,7 @@ Length::Unit Length::stringToUnit (const std::string &unitstr) {
case unit_id('s','p'): return Unit::SP;
}
}
- throw UnitException(string("invalid length unit: ")+unitstr);
+ throw UnitException(string("invalid length unit: ") + unitstr);
}
@@ -136,7 +132,7 @@ void Length::set (double val, string unitstr) {
if (unitstr.empty())
unitstr = "pt";
else if (unitstr.length() != 2)
- throw UnitException(string("invalid length unit: ")+unitstr);
+ throw UnitException(string("invalid length unit: ") + unitstr);
set(val, stringToUnit(unitstr));
}
diff --git a/dviware/dvisvgm/src/MD5HashFunction.hpp b/dviware/dvisvgm/src/MD5HashFunction.hpp
index 4ff18f3f3e..e233673d36 100644
--- a/dviware/dvisvgm/src/MD5HashFunction.hpp
+++ b/dviware/dvisvgm/src/MD5HashFunction.hpp
@@ -32,7 +32,7 @@
class MD5HashFunction : public HashFunction {
public:
- explicit MD5HashFunction () {MD5_Init(&_context);}
+ MD5HashFunction () {MD5_Init(&_context);}
MD5HashFunction (const char *data, size_t length) : MD5HashFunction() {update(data, length);}
explicit MD5HashFunction (const std::string &data) : MD5HashFunction() {update(data);}
explicit MD5HashFunction (const std::vector<uint8_t> &data) : MD5HashFunction() {update(data);}
diff --git a/dviware/dvisvgm/src/Makefile.am b/dviware/dvisvgm/src/Makefile.am
index 6bdbea32e6..fcab2ba5cc 100644
--- a/dviware/dvisvgm/src/Makefile.am
+++ b/dviware/dvisvgm/src/Makefile.am
@@ -55,7 +55,6 @@ libdvisvgm_la_SOURCES = \
Color.hpp Color.cpp \
ColorSpecialHandler.hpp ColorSpecialHandler.cpp \
CommandLine.hpp \
- CRC32.hpp CRC32.cpp \
Directory.hpp Directory.cpp \
DVIActions.hpp \
DLLoader.hpp DLLoader.cpp \
@@ -63,6 +62,7 @@ libdvisvgm_la_SOURCES = \
DvisvgmSpecialHandler.hpp DvisvgmSpecialHandler.cpp \
DVIToSVG.hpp DVIToSVG.cpp \
DVIToSVGActions.hpp DVIToSVGActions.cpp \
+ EllipticalArc.hpp EllipticalArc.cpp \
EmSpecialHandler.hpp EmSpecialHandler.cpp \
EncFile.hpp EncFile.cpp \
EPSFile.hpp EPSFile.cpp \
diff --git a/dviware/dvisvgm/src/Makefile.in b/dviware/dvisvgm/src/Makefile.in
index 6369ce9995..7c6cf1ae15 100644
--- a/dviware/dvisvgm/src/Makefile.in
+++ b/dviware/dvisvgm/src/Makefile.in
@@ -139,12 +139,12 @@ am__libdvisvgm_la_SOURCES_DIST = AGLTable.hpp BasicDVIReader.hpp \
CLCommandLine.hpp CLCommandLine.cpp CMap.hpp CMap.cpp \
CMapManager.hpp CMapManager.cpp CMapReader.hpp CMapReader.cpp \
CLOption.hpp Color.hpp Color.cpp ColorSpecialHandler.hpp \
- ColorSpecialHandler.cpp CommandLine.hpp CRC32.hpp CRC32.cpp \
- Directory.hpp Directory.cpp DVIActions.hpp DLLoader.hpp \
- DLLoader.cpp DVIReader.hpp DVIReader.cpp \
- DvisvgmSpecialHandler.hpp DvisvgmSpecialHandler.cpp \
- DVIToSVG.hpp DVIToSVG.cpp DVIToSVGActions.hpp \
- DVIToSVGActions.cpp EmSpecialHandler.hpp EmSpecialHandler.cpp \
+ ColorSpecialHandler.cpp CommandLine.hpp Directory.hpp \
+ Directory.cpp DVIActions.hpp DLLoader.hpp DLLoader.cpp \
+ DVIReader.hpp DVIReader.cpp DvisvgmSpecialHandler.hpp \
+ DvisvgmSpecialHandler.cpp DVIToSVG.hpp DVIToSVG.cpp \
+ DVIToSVGActions.hpp DVIToSVGActions.cpp EllipticalArc.hpp \
+ EllipticalArc.cpp EmSpecialHandler.hpp EmSpecialHandler.cpp \
EncFile.hpp EncFile.cpp EPSFile.hpp EPSFile.cpp EPSToSVG.hpp \
FileFinder.hpp FileFinder.cpp FilePath.hpp FilePath.cpp \
FileSystem.hpp FileSystem.cpp FixWord.hpp Font.hpp Font.cpp \
@@ -199,8 +199,8 @@ am_libdvisvgm_la_OBJECTS = BasicDVIReader.lo Bezier.lo \
BgColorSpecialHandler.lo Bitmap.lo BoundingBox.lo \
Calculator.lo CharMapID.lo CLCommandLine.lo CMap.lo \
CMapManager.lo CMapReader.lo Color.lo ColorSpecialHandler.lo \
- CRC32.lo Directory.lo DLLoader.lo DVIReader.lo \
- DvisvgmSpecialHandler.lo DVIToSVG.lo DVIToSVGActions.lo \
+ Directory.lo DLLoader.lo DVIReader.lo DvisvgmSpecialHandler.lo \
+ DVIToSVG.lo DVIToSVGActions.lo EllipticalArc.lo \
EmSpecialHandler.lo EncFile.lo EPSFile.lo FileFinder.lo \
FilePath.lo FileSystem.lo Font.lo FontCache.lo FontEncoding.lo \
FontEngine.lo FontManager.lo FontMap.lo FontMetrics.lo \
@@ -255,21 +255,22 @@ am__depfiles_remade = ./$(DEPDIR)/BasicDVIReader.Plo \
./$(DEPDIR)/Bitmap.Plo ./$(DEPDIR)/BoundingBox.Plo \
./$(DEPDIR)/CLCommandLine.Plo ./$(DEPDIR)/CMap.Plo \
./$(DEPDIR)/CMapManager.Plo ./$(DEPDIR)/CMapReader.Plo \
- ./$(DEPDIR)/CRC32.Plo ./$(DEPDIR)/Calculator.Plo \
- ./$(DEPDIR)/CharMapID.Plo ./$(DEPDIR)/Color.Plo \
- ./$(DEPDIR)/ColorSpecialHandler.Plo ./$(DEPDIR)/DLLoader.Plo \
- ./$(DEPDIR)/DVIReader.Plo ./$(DEPDIR)/DVIToSVG.Plo \
- ./$(DEPDIR)/DVIToSVGActions.Plo ./$(DEPDIR)/Directory.Plo \
+ ./$(DEPDIR)/Calculator.Plo ./$(DEPDIR)/CharMapID.Plo \
+ ./$(DEPDIR)/Color.Plo ./$(DEPDIR)/ColorSpecialHandler.Plo \
+ ./$(DEPDIR)/DLLoader.Plo ./$(DEPDIR)/DVIReader.Plo \
+ ./$(DEPDIR)/DVIToSVG.Plo ./$(DEPDIR)/DVIToSVGActions.Plo \
+ ./$(DEPDIR)/Directory.Plo \
./$(DEPDIR)/DvisvgmSpecialHandler.Plo ./$(DEPDIR)/EPSFile.Plo \
- ./$(DEPDIR)/EmSpecialHandler.Plo ./$(DEPDIR)/EncFile.Plo \
- ./$(DEPDIR)/FileFinder.Plo ./$(DEPDIR)/FilePath.Plo \
- ./$(DEPDIR)/FileSystem.Plo ./$(DEPDIR)/Font.Plo \
- ./$(DEPDIR)/FontCache.Plo ./$(DEPDIR)/FontEncoding.Plo \
- ./$(DEPDIR)/FontEngine.Plo ./$(DEPDIR)/FontManager.Plo \
- ./$(DEPDIR)/FontMap.Plo ./$(DEPDIR)/FontMetrics.Plo \
- ./$(DEPDIR)/FontWriter.Plo ./$(DEPDIR)/GFGlyphTracer.Plo \
- ./$(DEPDIR)/GFReader.Plo ./$(DEPDIR)/GFTracer.Plo \
- ./$(DEPDIR)/Ghostscript.Plo ./$(DEPDIR)/HashFunction.Plo \
+ ./$(DEPDIR)/EllipticalArc.Plo ./$(DEPDIR)/EmSpecialHandler.Plo \
+ ./$(DEPDIR)/EncFile.Plo ./$(DEPDIR)/FileFinder.Plo \
+ ./$(DEPDIR)/FilePath.Plo ./$(DEPDIR)/FileSystem.Plo \
+ ./$(DEPDIR)/Font.Plo ./$(DEPDIR)/FontCache.Plo \
+ ./$(DEPDIR)/FontEncoding.Plo ./$(DEPDIR)/FontEngine.Plo \
+ ./$(DEPDIR)/FontManager.Plo ./$(DEPDIR)/FontMap.Plo \
+ ./$(DEPDIR)/FontMetrics.Plo ./$(DEPDIR)/FontWriter.Plo \
+ ./$(DEPDIR)/GFGlyphTracer.Plo ./$(DEPDIR)/GFReader.Plo \
+ ./$(DEPDIR)/GFTracer.Plo ./$(DEPDIR)/Ghostscript.Plo \
+ ./$(DEPDIR)/HashFunction.Plo \
./$(DEPDIR)/HtmlSpecialHandler.Plo \
./$(DEPDIR)/HyperlinkManager.Plo ./$(DEPDIR)/ImageToSVG.Plo \
./$(DEPDIR)/InputBuffer.Plo ./$(DEPDIR)/InputReader.Plo \
@@ -593,12 +594,12 @@ libdvisvgm_la_SOURCES = AGLTable.hpp BasicDVIReader.hpp \
CLCommandLine.hpp CLCommandLine.cpp CMap.hpp CMap.cpp \
CMapManager.hpp CMapManager.cpp CMapReader.hpp CMapReader.cpp \
CLOption.hpp Color.hpp Color.cpp ColorSpecialHandler.hpp \
- ColorSpecialHandler.cpp CommandLine.hpp CRC32.hpp CRC32.cpp \
- Directory.hpp Directory.cpp DVIActions.hpp DLLoader.hpp \
- DLLoader.cpp DVIReader.hpp DVIReader.cpp \
- DvisvgmSpecialHandler.hpp DvisvgmSpecialHandler.cpp \
- DVIToSVG.hpp DVIToSVG.cpp DVIToSVGActions.hpp \
- DVIToSVGActions.cpp EmSpecialHandler.hpp EmSpecialHandler.cpp \
+ ColorSpecialHandler.cpp CommandLine.hpp Directory.hpp \
+ Directory.cpp DVIActions.hpp DLLoader.hpp DLLoader.cpp \
+ DVIReader.hpp DVIReader.cpp DvisvgmSpecialHandler.hpp \
+ DvisvgmSpecialHandler.cpp DVIToSVG.hpp DVIToSVG.cpp \
+ DVIToSVGActions.hpp DVIToSVGActions.cpp EllipticalArc.hpp \
+ EllipticalArc.cpp EmSpecialHandler.hpp EmSpecialHandler.cpp \
EncFile.hpp EncFile.cpp EPSFile.hpp EPSFile.cpp EPSToSVG.hpp \
FileFinder.hpp FileFinder.cpp FilePath.hpp FilePath.cpp \
FileSystem.hpp FileSystem.cpp FixWord.hpp Font.hpp Font.cpp \
@@ -778,7 +779,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CMap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CMapManager.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CMapReader.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CRC32.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Calculator.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CharMapID.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Color.Plo@am__quote@ # am--include-marker
@@ -790,6 +790,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Directory.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DvisvgmSpecialHandler.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EPSFile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EllipticalArc.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EmSpecialHandler.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/EncFile.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FileFinder.Plo@am__quote@ # am--include-marker
@@ -1137,7 +1138,6 @@ distclean: distclean-recursive
-rm -f ./$(DEPDIR)/CMap.Plo
-rm -f ./$(DEPDIR)/CMapManager.Plo
-rm -f ./$(DEPDIR)/CMapReader.Plo
- -rm -f ./$(DEPDIR)/CRC32.Plo
-rm -f ./$(DEPDIR)/Calculator.Plo
-rm -f ./$(DEPDIR)/CharMapID.Plo
-rm -f ./$(DEPDIR)/Color.Plo
@@ -1149,6 +1149,7 @@ distclean: distclean-recursive
-rm -f ./$(DEPDIR)/Directory.Plo
-rm -f ./$(DEPDIR)/DvisvgmSpecialHandler.Plo
-rm -f ./$(DEPDIR)/EPSFile.Plo
+ -rm -f ./$(DEPDIR)/EllipticalArc.Plo
-rm -f ./$(DEPDIR)/EmSpecialHandler.Plo
-rm -f ./$(DEPDIR)/EncFile.Plo
-rm -f ./$(DEPDIR)/FileFinder.Plo
@@ -1278,7 +1279,6 @@ maintainer-clean: maintainer-clean-recursive
-rm -f ./$(DEPDIR)/CMap.Plo
-rm -f ./$(DEPDIR)/CMapManager.Plo
-rm -f ./$(DEPDIR)/CMapReader.Plo
- -rm -f ./$(DEPDIR)/CRC32.Plo
-rm -f ./$(DEPDIR)/Calculator.Plo
-rm -f ./$(DEPDIR)/CharMapID.Plo
-rm -f ./$(DEPDIR)/Color.Plo
@@ -1290,6 +1290,7 @@ maintainer-clean: maintainer-clean-recursive
-rm -f ./$(DEPDIR)/Directory.Plo
-rm -f ./$(DEPDIR)/DvisvgmSpecialHandler.Plo
-rm -f ./$(DEPDIR)/EPSFile.Plo
+ -rm -f ./$(DEPDIR)/EllipticalArc.Plo
-rm -f ./$(DEPDIR)/EmSpecialHandler.Plo
-rm -f ./$(DEPDIR)/EncFile.Plo
-rm -f ./$(DEPDIR)/FileFinder.Plo
diff --git a/dviware/dvisvgm/src/MapLine.cpp b/dviware/dvisvgm/src/MapLine.cpp
index 483fec4b71..57aa116f02 100644
--- a/dviware/dvisvgm/src/MapLine.cpp
+++ b/dviware/dvisvgm/src/MapLine.cpp
@@ -221,9 +221,7 @@ void MapLine::parseDVIPDFMLine (InputReader &ir) {
throw_number_expected('w', true);
break;
default:
- ostringstream oss;
- oss << "invalid option: -" << option;
- throw MapLineException(oss.str());
+ throw MapLineException("invalid option: -" + string(1, option));
}
ir.skipSpace();
}
diff --git a/dviware/dvisvgm/src/Matrix.cpp b/dviware/dvisvgm/src/Matrix.cpp
index 7b425596e9..f94e84809c 100644
--- a/dviware/dvisvgm/src/Matrix.cpp
+++ b/dviware/dvisvgm/src/Matrix.cpp
@@ -396,11 +396,8 @@ Matrix Matrix::parse (istream &is, Calculator &calc) {
if (c != 'X' && c != 'Y')
throw ParserException("transformation command 'K' must be followed by 'X' or 'Y'");
double a = getArgument(is, calc, 0, false, false);
- if (std::abs(cos(deg2rad(a))) < numeric_limits<double>::epsilon()) {
- ostringstream oss;
- oss << "illegal skewing angle: " << a << " degrees";
- throw ParserException(oss.str());
- }
+ if (std::abs(cos(deg2rad(a))) < numeric_limits<double>::epsilon())
+ throw ParserException("illegal skewing angle: " + util::to_string(a) + " degrees");
if (c == 'X')
ret.xskewByAngle(a);
else
@@ -419,9 +416,7 @@ Matrix Matrix::parse (istream &is, Calculator &calc) {
break;
}
default:
- ostringstream oss;
- oss << "transformation command expected (found '" << char(cmd) << "' instead)";
- throw ParserException(oss.str());
+ throw ParserException("transformation command expected (found '" + string(1, cmd) + "' instead)");
}
}
return ret;
@@ -606,4 +601,4 @@ XSkewingMatrix::XSkewingMatrix (double deg) {
YSkewingMatrix::YSkewingMatrix (double deg) {
double xyratio = tan(deg2rad(deg));
lmultiply(Matrix({1, 0, 0, xyratio}));
-} \ No newline at end of file
+}
diff --git a/dviware/dvisvgm/src/MetafontWrapper.cpp b/dviware/dvisvgm/src/MetafontWrapper.cpp
index dfb7c9b465..5807d7254e 100644
--- a/dviware/dvisvgm/src/MetafontWrapper.cpp
+++ b/dviware/dvisvgm/src/MetafontWrapper.cpp
@@ -18,7 +18,6 @@
** along with this program; if not, see <http://www.gnu.org/licenses/>. **
*************************************************************************/
-#include <cstdlib>
#include <cctype>
#include <fstream>
#include <sstream>
@@ -38,7 +37,7 @@ MetafontWrapper::MetafontWrapper (string fname, string dir)
// ensure that folder paths ends with slash
if (_dir.empty())
_dir = "./";
- else if (_dir != "/" && dir[dir.length()-1] != '/')
+ else if (_dir != "/" && _dir.back() != '/')
_dir += '/';
}
@@ -88,7 +87,7 @@ bool MetafontWrapper::call (const string &mode, double mag) {
iss.getline(buf, sizeof(buf));
string line = buf;
if (line.substr(0, 3) == ">> ") {
- resolution = atoi(line.substr(3).c_str());
+ resolution = stoi(line.substr(3));
break;
}
}
diff --git a/dviware/dvisvgm/src/MiKTeXCom.hpp b/dviware/dvisvgm/src/MiKTeXCom.hpp
index c87d0e3d86..5bf1981167 100644
--- a/dviware/dvisvgm/src/MiKTeXCom.hpp
+++ b/dviware/dvisvgm/src/MiKTeXCom.hpp
@@ -25,10 +25,10 @@
#include "windows.hpp"
#ifdef _MSC_VER
-#import <MiKTeX209-session.tlb>
+#import <miktex-session.tlb>
using namespace MiKTeXSession2_9;
#else
-#include "miktex209-session.h"
+#include "miktex-session.h"
#endif
class MiKTeXCom {
diff --git a/dviware/dvisvgm/src/PDFParser.cpp b/dviware/dvisvgm/src/PDFParser.cpp
index 12637a6368..1ecd40cba8 100644
--- a/dviware/dvisvgm/src/PDFParser.cpp
+++ b/dviware/dvisvgm/src/PDFParser.cpp
@@ -168,10 +168,8 @@ static string parse_literal_string (InputReader &ir) {
static char get_hex_digit (InputReader &ir) {
int c = ir.get();
if (isxdigit(c))
- return static_cast<char>(c);
- ostringstream oss;
- oss << "invalid hexadecimal digit '" << static_cast<char>(c) << "'";
- throw PDFException(oss.str());
+ return char(c);
+ throw PDFException("invalid hexadecimal digit '" + string(1, char(c)) + "'");
}
diff --git a/dviware/dvisvgm/src/PDFToSVG.hpp b/dviware/dvisvgm/src/PDFToSVG.hpp
index f999de88e8..d1871d0bb1 100644
--- a/dviware/dvisvgm/src/PDFToSVG.hpp
+++ b/dviware/dvisvgm/src/PDFToSVG.hpp
@@ -60,4 +60,3 @@ class PDFToSVG : public ImageToSVG {
};
#endif
-
diff --git a/dviware/dvisvgm/src/PSInterpreter.cpp b/dviware/dvisvgm/src/PSInterpreter.cpp
index e9d33d7265..ba368f17ee 100644
--- a/dviware/dvisvgm/src/PSInterpreter.cpp
+++ b/dviware/dvisvgm/src/PSInterpreter.cpp
@@ -51,8 +51,15 @@ void PSInterpreter::init () {
"-dWRITESYSTEMDICT", // leave systemdict writable as some operators must be replaced
"-dNOPROMPT"
};
- if (int gsrev = _gs.revision())
+ if (int gsrev = _gs.revision()) {
gsargs.emplace_back(gsrev == 922 ? "-dREALLYDELAYBIND" : "-dDELAYBIND");
+ // As of GS 9.50, -dSAFER is active by default which leads to warnings
+ // in conjunction with -dDELAYBIND and -dWRITESYSTEMDICT.
+ // Thus, -dDELAYSAFER (or -dNOSAFER) must be added.
+ // https://www.ghostscript.com/doc/9.50/Use.htm#Safer
+ if (gsrev >= 950)
+ gsargs.emplace_back("-dDELAYSAFER");
+ }
_gs.init(gsargs.size(), gsargs.data(), this);
_gs.set_stdio(input, output, error);
_initialized = true;
diff --git a/dviware/dvisvgm/src/Pair.hpp b/dviware/dvisvgm/src/Pair.hpp
index 5804a2b467..5dd7c68a93 100644
--- a/dviware/dvisvgm/src/Pair.hpp
+++ b/dviware/dvisvgm/src/Pair.hpp
@@ -26,17 +26,21 @@
#include "macros.hpp"
template <typename T>
-class Pair
-{
+class Pair {
public:
explicit Pair (T x=0, T y=0) : _x(x), _y(y) {}
+ template <typename U> Pair (const Pair<U> &p) : _x(U(p.x())), _y(U(p.y())) {}
+ Pair (const Pair &p) =default;
+ Pair (Pair &&p) =default;
+ Pair& operator = (const Pair &p) =default;
+ Pair& operator = (Pair &&p) =default;
Pair operator += (const Pair &p) {_x += p._x; _y += p._y; return *this;}
Pair operator -= (const Pair &p) {_x -= p._x; _y -= p._y; return *this;}
Pair operator *= (T c) {_x *= c; _y *= c; return *this;}
Pair operator /= (T c) {_x /= c; _y /= c; return *this;}
Pair operator - () const {return Pair(-_x, -_y);}
Pair ortho () const {return Pair(-_y, _x);}
- double length () const {return std::sqrt(_x*_x + _y*_y);}
+ double length () const {return std::hypot(_x, _y);}
bool operator == (const Pair &p) const {return _x == p._x && _y == p._y;}
bool operator != (const Pair &p) const {return _x != p._x || _y != p._y;}
T x () const {return _x;}
@@ -54,6 +58,18 @@ inline Pair<T> abs (const Pair<T> &p) {
return Pair<T>(std::abs(p.x()), std::abs(p.y()));
}
+/** Returns the dot product of two 2D vectors. */
+template <typename T>
+inline T dot (const Pair<T> &p1, const Pair<T> &p2) {
+ return p1.x()*p1.y() + p1.y()*p2.y();
+}
+
+/** Returns the determinant of two 2D vectors. */
+template <typename T>
+inline T det (const Pair<T> &p1, const Pair<T> &p2) {
+ return p1.x()*p2.y() - p1.y()*p2.x();
+}
+
struct Pair32 : public Pair<int32_t> {
explicit Pair32 (int32_t x=0, int32_t y=0) : Pair<int32_t>(x, y) {}
explicit Pair32 (double x, double y) : Pair<int32_t>(lround(x), lround(y)) {}
@@ -62,6 +78,10 @@ struct Pair32 : public Pair<int32_t> {
typedef Pair<double> DPair;
+inline DPair round (const DPair &p) {
+ return DPair(std::lround(p.x()), std::lround(p.y()));
+}
+
template <typename T>
IMPLEMENT_ARITHMETIC_OPERATOR(Pair<T>, +)
diff --git a/dviware/dvisvgm/src/PathClipper.cpp b/dviware/dvisvgm/src/PathClipper.cpp
index 1663e7ae36..f646289d25 100644
--- a/dviware/dvisvgm/src/PathClipper.cpp
+++ b/dviware/dvisvgm/src/PathClipper.cpp
@@ -49,7 +49,7 @@ inline DPair to_DPair (const IntPoint &p) {
/** In order to flatten a curved path, all path segements are processed sequentially.
* Depending on the type of the segment, one of the methods provided by this class
* is called. */
-class FlattenActions : public CurvedPath::Actions {
+class FlattenActions : public CurvedPath::IterationActions {
public:
FlattenActions (vector<Bezier> &curves, Polygons &polygons, int &numLines)
: _polygons(polygons), _curves(curves), _numLines(numLines) {}
@@ -73,7 +73,7 @@ class FlattenActions : public CurvedPath::Actions {
_currentPoint = p;
}
- void conicto (const CurvedPath::Point &p1, const CurvedPath::Point &p2) override {
+ void quadto (const CurvedPath::Point &p1, const CurvedPath::Point &p2) override {
Bezier bezier(_currentPoint, p1, p2);
addCurvePoints(bezier);
}
@@ -206,7 +206,7 @@ static double division_ratio (const IntPoint &p1, const IntPoint &p2, const IntP
/** Returns the label of point q that lies on the line between points p1 and p2. */
inline ZLabel division_label (const IntPoint &p1, const IntPoint &p2, const IntPoint &q) {
- double t1, t2;
+ double t1=0, t2=0;
double s=0;
int32_t id = segment_id(p1, p2, t1, t2);
if (id > 0)
diff --git a/dviware/dvisvgm/src/PsSpecialHandler.cpp b/dviware/dvisvgm/src/PsSpecialHandler.cpp
index 6d5c906a8c..871a34b4f2 100644
--- a/dviware/dvisvgm/src/PsSpecialHandler.cpp
+++ b/dviware/dvisvgm/src/PsSpecialHandler.cpp
@@ -25,6 +25,7 @@
#include <sstream>
#include "EPSFile.hpp"
#include "FileFinder.hpp"
+#include "FilePath.hpp"
#include "FileSystem.hpp"
#include "Message.hpp"
#include "PathClipper.hpp"
@@ -183,6 +184,15 @@ void PsSpecialHandler::preprocess (const string &prefix, istream &is, SpecialAct
}
+static string filename_suffix (const string &fname) {
+ string ret;
+ size_t pos = fname.rfind('.');
+ if (pos != string::npos)
+ ret = util::tolower(fname.substr(pos+1));
+ return ret;
+}
+
+
bool PsSpecialHandler::process (const string &prefix, istream &is, SpecialActions &actions) {
// process PS headers only once (in prescan)
if (prefix == "!" || prefix == "header=")
@@ -204,9 +214,22 @@ bool PsSpecialHandler::process (const string &prefix, istream &is, SpecialAction
if (_actions) {
StreamInputReader in(is);
const string fname = in.getQuotedString(in.peek() == '"' ? "\"" : nullptr);
+ FileType fileType = FileType::EPS;
+ if (prefix == "pdffile")
+ fileType = FileType::PDF;
+ else {
+ // accept selected non-PS files in psfile special
+ string ext = filename_suffix(fname);
+ if (ext == "pdf")
+ fileType = FileType::PDF;
+ else if (ext == "svg")
+ fileType = FileType::SVG;
+ else if (ext == "jpg" || ext == "jpeg" || ext == "png")
+ fileType = FileType::BITMAP;
+ }
map<string,string> attr;
in.parseAttributes(attr, false);
- imgfile(prefix == "pdffile=" ? FileType::PDF : FileType::EPS, fname, attr);
+ imgfile(fileType, fname, attr);
}
}
else if (prefix == "ps::") {
@@ -290,7 +313,9 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
double ury = (it = attr.find("ury")) != attr.end() ? stod(it->second) : 0;
int pageno = (it = attr.find("page")) != attr.end() ? stoi(it->second, nullptr, 10) : 1;
- if (filetype == FileType::PDF && llx == 0 && lly == 0 && urx == 0 && ury == 0) {
+ if (filetype == FileType::BITMAP || filetype == FileType::SVG)
+ swap(lly, ury);
+ else if (filetype == FileType::PDF && llx == 0 && lly == 0 && urx == 0 && ury == 0) {
BoundingBox pagebox = _psi.pdfPageBox(fname, pageno);
if (pagebox.valid()) {
llx = pagebox.minX();
@@ -337,26 +362,12 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
_actions->setY(0);
moveToDVIPos();
- // clip image to its bounding box if flag 'clip' is given
- string rectclip;
- if (clipToBbox)
- rectclip = to_string(llx)+" "+to_string(lly)+" "+to_string(urx-llx)+" "+to_string(ury-lly)+" rectclip";
-
- auto groupNode = util::make_unique<XMLElement>("g"); // put SVG nodes created from the EPS/PDF file in this group
- _xmlnode = groupNode.get();
- _psi.execute(
- "\n@beginspecial @setspecial" // enter special environment
- "/setpagedevice{@setpagedevice}def " // activate processing of operator "setpagedevice"
- "matrix setmatrix" // don't apply outer PS transformations
- "/FirstPage "+to_string(pageno)+" def" // set number of fisrt page to convert (PDF only)
- "/LastPage "+to_string(pageno)+" def " // set number of last page to convert (PDF only)
- +rectclip+ // clip to bounding box (if requexted by attribute 'clip')
- "("+ filepath + ")run " // execute file content
- "@endspecial " // leave special environment
- );
- if (!groupNode->empty()) { // has anything been drawn?
+ auto imgNode = createImageNode(filetype, filepath, pageno, BoundingBox(llx, lly, urx, ury), clipToBbox);
+ if (imgNode) { // has anything been drawn?
Matrix matrix(1);
- matrix.scale(sx, -sy).rotate(-angle).scale(hscale/100, vscale/100); // apply transformation attributes
+ if (filetype == FileType::EPS || filetype == FileType::PDF)
+ sy = -sy; // adapt orientation of y-coordinates
+ matrix.scale(sx, sy).rotate(-angle).scale(hscale/100, vscale/100); // apply transformation attributes
matrix.translate(x+hoffset, y-voffset); // move image to current DVI position
matrix.lmultiply(_actions->getMatrix());
@@ -365,14 +376,12 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
bbox.transform(matrix);
_actions->embed(bbox);
- // insert group containing SVG nodes created from image
+ // insert element containing the image data
matrix.rmultiply(TranslationMatrix(-llx, -lly)); // move lower left corner of image to origin
if (!matrix.isIdentity())
- groupNode->addAttribute("transform", matrix.toSVG());
- _actions->svgTree().appendToPage(std::move(groupNode));
+ imgNode->addAttribute("transform", matrix.toSVG());
+ _actions->svgTree().appendToPage(std::move(imgNode));
}
- _xmlnode = nullptr; // append following elements to page group again
-
// restore DVI position
_actions->setX(x);
_actions->setY(y);
@@ -380,6 +389,52 @@ 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] 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> node;
+ 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));
+ }
+ else { // PostScript or PDF
+ // clip image to its bounding box if flag 'clip' is given
+ string rectclip;
+ if (clip)
+ rectclip = to_string(bbox.minX())+" "+to_string(bbox.minY())+" "+to_string(bbox.width())+" "+to_string(bbox.height())+" rectclip";
+
+ node = util::make_unique<XMLElement>("g"); // put SVG nodes created from the EPS/PDF file in this group
+ _xmlnode = node.get();
+ _psi.execute(
+ "\n@beginspecial @setspecial" // enter special environment
+ "/setpagedevice{@setpagedevice}def " // activate processing of operator "setpagedevice"
+ "matrix setmatrix" // don't apply outer PS transformations
+ "/FirstPage "+to_string(pageno)+" def" // set number of first page to convert (PDF only)
+ "/LastPage "+to_string(pageno)+" def " // set number of last page to convert (PDF only)
+ +rectclip+ // clip to bounding box (if requexted by attribute 'clip')
+ "(" + pathstr + ")run " // execute file content
+ "@endspecial " // leave special environment
+ );
+ if (node->empty())
+ node.reset(nullptr);
+ _xmlnode = nullptr; // append following elements to page group again
+ }
+ return node;
+}
+
+
/** Apply transformation to width, height, and depth set by preview package.
* @param[in] matrix transformation matrix to apply
* @param[out] w width
@@ -394,15 +449,17 @@ static bool transform_box_extents (const Matrix &matrix, double &w, double &h, d
return false; // => non-horizontal baseline, can't compute meaningful extents
if (ex.y() == 0) // horizontal scaling or skewing?
- w *= fabs(ex.x());
+ w *= abs(ex.x());
if (ey.x()==0 || ex.y()==0) { // vertical scaling?
- if (ey.y() < 0) swap(h, d);
- if (double sy = fabs(ey.y())/ey.length()) {
- h *= fabs(ey.y()/sy);
- d *= fabs(ey.y()/sy);
- }
- else
+ if (ey.y() < 0)
+ swap(h, d);
+ double sy = abs(ey.y())/ey.length();
+ if (sy < 1e-8)
h = d = 0;
+ else {
+ h *= abs(ey.y()/sy);
+ d *= abs(ey.y()/sy);
+ }
}
return true;
}
@@ -600,13 +657,11 @@ void PsSpecialHandler::stroke (vector<double> &p) {
if (_blendmode > 0 && _blendmode < 16)
path->addAttribute("style", "mix-blend-mode:"+css_blendmode_name(_blendmode));
if (!_dashpattern.empty()) {
- ostringstream oss;
- for (size_t i=0; i < _dashpattern.size(); i++) {
- if (i > 0)
- oss << ',';
- oss << XMLString(_dashpattern[i]);
- }
- path->addAttribute("stroke-dasharray", oss.str());
+ string patternStr;
+ for (double dashValue : _dashpattern)
+ patternStr += XMLString(dashValue)+",";
+ patternStr.pop_back();
+ path->addAttribute("stroke-dasharray", patternStr);
if (_dashoffset != 0)
path->addAttribute("stroke-dashoffset", _dashoffset);
}
@@ -690,7 +745,7 @@ static void create_matrix (vector<double> &v, int startindex, Matrix &matrix) {
// using corresponding values of the identity matrix.
if (v.size()-startindex < 6) {
v.resize(6+startindex);
- for (int i=v.size()-startindex; i < 6; ++i)
+ for (size_t i=v.size()-startindex; i < 6; ++i)
v[i+startindex] = (i%3 ? 0 : 1);
}
// PS matrix [a b c d e f] equals ((a,b,0),(c,d,0),(e,f,1)).
@@ -1007,16 +1062,14 @@ void PsSpecialHandler::processSequentialPatchMesh (int shadingTypeID, ColorSpace
#if 0
if (bgcolorGiven) {
// fill whole patch area with given background color
- GraphicsPath<double> outline;
- patch->getBoundaryPath(outline);
+ GraphicsPath<double> outline = patch->getBoundaryPath();
callback.patchSegment(outline, bgcolor);
}
#endif
patch->approximate(SHADING_SEGMENT_SIZE, SHADING_SEGMENT_OVERLAP, SHADING_SIMPLIFY_DELTA, callback);
if (!_xmlnode) {
// update bounding box
- BoundingBox bbox;
- patch->getBBox(bbox);
+ BoundingBox bbox = patch->getBBox();
bbox.transform(_actions->getMatrix());
_actions->embed(bbox);
}
diff --git a/dviware/dvisvgm/src/PsSpecialHandler.hpp b/dviware/dvisvgm/src/PsSpecialHandler.hpp
index 4c06e6a1b6..27da9c8471 100644
--- a/dviware/dvisvgm/src/PsSpecialHandler.hpp
+++ b/dviware/dvisvgm/src/PsSpecialHandler.hpp
@@ -77,7 +77,7 @@ class PsSpecialHandler : public SpecialHandler, protected PSActions {
};
enum PsSection {PS_NONE, PS_HEADERS, PS_BODY};
- enum class FileType {EPS, PDF};
+ enum class FileType {EPS, PDF, SVG, BITMAP};
public:
PsSpecialHandler ();
@@ -104,6 +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);
void dviEndPage (unsigned pageno, SpecialActions &actions) override;
void clip (Path path, bool evenodd);
void processSequentialPatchMesh (int shadingTypeID, ColorSpace cspace, VectorIterator<double> &it);
diff --git a/dviware/dvisvgm/src/SVGOutput.cpp b/dviware/dvisvgm/src/SVGOutput.cpp
index 469e5f5af2..7c4108c876 100644
--- a/dviware/dvisvgm/src/SVGOutput.cpp
+++ b/dviware/dvisvgm/src/SVGOutput.cpp
@@ -46,8 +46,8 @@ SVGOutput::SVGOutput (const string &base, string pattern, int zipLevel)
* @param[in] hash hash value of the current page
* @return output stream for the given page */
ostream& SVGOutput::getPageStream (int page, int numPages, const HashTriple &hashes) const {
- string fname = filename(page, numPages, hashes);
- if (fname.empty()) {
+ FilePath path = filepath(page, numPages, hashes);
+ if (path.empty()) {
if (_zipLevel == 0) {
_osptr.reset();
return cout;
@@ -63,52 +63,35 @@ ostream& SVGOutput::getPageStream (int page, int numPages, const HashTriple &has
_page = page;
if (_zipLevel > 0)
- _osptr = util::make_unique<ZLibOutputFileStream>(fname, ZLIB_GZIP, _zipLevel);
+ _osptr = util::make_unique<ZLibOutputFileStream>(path.absolute(), ZLIB_GZIP, _zipLevel);
else
- _osptr = util::make_unique<ofstream>(fname);
+ _osptr = util::make_unique<ofstream>(path.absolute());
if (!_osptr)
- throw MessageException("can't open file "+fname+" for writing");
+ throw MessageException("can't open file "+path.shorterAbsoluteOrRelative()+" for writing");
return *_osptr;
}
-/** Returns the name of the SVG file containing the given page.
+/** Returns the path of the SVG file containing the given page number.
* @param[in] page number of current page
* @param[in] numPages total number of pages
* @param[in] hash hash value of current page */
-string SVGOutput::filename (int page, int numPages, const HashTriple &hashes) const {
- if (_stdout)
- return "";
-
- string expanded_pattern = util::trim(expandFormatString(_pattern, page, numPages, hashes));
- // set and expand default pattern if necessary
- if (expanded_pattern.empty()) {
- string pattern = hashes.empty() ? (numPages > 1 ? "%f-%p" : "%f") : "%f-%hd";
- expanded_pattern = expandFormatString(pattern, page, numPages, hashes);
+FilePath SVGOutput::filepath (int page, int numPages, const HashTriple &hashes) const {
+ FilePath outpath;
+ if (!_stdout) {
+ string expanded_pattern = util::trim(expandFormatString(_pattern, page, numPages, hashes));
+ // set and expand default pattern if necessary
+ if (expanded_pattern.empty()) {
+ string pattern = hashes.empty() ? (numPages > 1 ? "%f-%p" : "%f") : "%f-%hd";
+ expanded_pattern = expandFormatString(pattern, page, numPages, hashes);
+ }
+ // append suffix if necessary
+ outpath.set(expanded_pattern, true);
+ if (outpath.suffix().empty())
+ outpath.suffix(_zipLevel > 0 ? "svgz" : "svg");
}
- // append suffix if necessary
- FilePath outpath(expanded_pattern, true);
- if (outpath.suffix().empty())
- outpath.suffix(_zipLevel > 0 ? "svgz" : "svg");
- string abspath = outpath.absolute();
- string relpath = outpath.relative();
- return abspath.length() < relpath.length() ? abspath : relpath;
-}
-
-
-#if 0
-string SVGOutput::outpath (int page, int numPages) const {
- string path = filename(page, numPages);
- if (path.empty())
- return "";
- size_t pos = path.rfind('/');
- if (pos == string::npos)
- return ".";
- if (pos == 0)
- return "/";
- return path.substr(0, pos);
+ return outpath;
}
-#endif
/** Replaces expressions in a given string by the corresponding values and returns the result.
@@ -174,9 +157,7 @@ string SVGOutput::expandFormatString (string str, int page, int numPages, const
result += oss.str();
}
catch (CalculatorException &e) {
- oss.str("");
- oss << "error in filename pattern (" << e.what() << ")";
- throw MessageException(oss.str());
+ throw MessageException("error in filename pattern (" + string(e.what()) + ")");
}
pos = endpos;
}
diff --git a/dviware/dvisvgm/src/SVGOutput.hpp b/dviware/dvisvgm/src/SVGOutput.hpp
index 38193dbdc8..3c382f0b7f 100644
--- a/dviware/dvisvgm/src/SVGOutput.hpp
+++ b/dviware/dvisvgm/src/SVGOutput.hpp
@@ -46,7 +46,7 @@ struct SVGOutputBase {
virtual ~SVGOutputBase () =default;
virtual std::ostream& getPageStream (int page, int numPages, const HashTriple &hashes=HashTriple()) const =0;
- virtual std::string filename (int page, int numPages, const HashTriple &hashes=HashTriple()) const =0;
+ virtual FilePath filepath (int page, int numPages, const HashTriple &hashes= HashTriple()) const =0;
virtual bool ignoresHashes () const {return true;}
};
@@ -58,7 +58,7 @@ class SVGOutput : public SVGOutputBase {
SVGOutput (const std::string &base, const std::string &pattern) : SVGOutput(base, pattern, 0) {}
SVGOutput (const std::string &base, std::string pattern, int zipLevel);
std::ostream& getPageStream (int page, int numPages, const HashTriple &hash=HashTriple()) const override;
- std::string filename (int page, int numPages, const HashTriple &hash=HashTriple()) const override;
+ FilePath filepath (int page, int numPages, const HashTriple &hash=HashTriple()) const override;
bool ignoresHashes () const override;
protected:
diff --git a/dviware/dvisvgm/src/ShadingPatch.hpp b/dviware/dvisvgm/src/ShadingPatch.hpp
index a39edc5100..4346301e7b 100644
--- a/dviware/dvisvgm/src/ShadingPatch.hpp
+++ b/dviware/dvisvgm/src/ShadingPatch.hpp
@@ -42,8 +42,8 @@ class ShadingPatch {
virtual ~ShadingPatch () =default;
virtual int psShadingType () const =0;
virtual void approximate (int gridsize, bool overlap, double delta, Callback &callback) const =0;
- virtual void getBBox (BoundingBox &bbox) const =0;
- virtual void getBoundaryPath (GraphicsPath<double> &path) const =0;
+ virtual BoundingBox getBBox () const =0;
+ virtual GraphicsPath<double> getBoundaryPath () const =0;
virtual void setPoints (const PointVec &points, int edgeflag, ShadingPatch *patch) =0;
virtual void setColors (const ColorVec &colors, int edgeflag, ShadingPatch *patch) =0;
virtual int numPoints (int edgeflag) const =0;
diff --git a/dviware/dvisvgm/src/SpecialActions.hpp b/dviware/dvisvgm/src/SpecialActions.hpp
index 0f7a43074d..3a510e9ea3 100644
--- a/dviware/dvisvgm/src/SpecialActions.hpp
+++ b/dviware/dvisvgm/src/SpecialActions.hpp
@@ -25,6 +25,7 @@
#include <memory>
#include "BoundingBox.hpp"
#include "Color.hpp"
+#include "FilePath.hpp"
#include "Matrix.hpp"
#include "SVGTree.hpp"
@@ -52,7 +53,7 @@ class SpecialActions {
virtual void embed (const BoundingBox &bbox) =0;
virtual void embed (const DPair &p, double r=0) =0;
virtual unsigned getCurrentPageNumber () const =0;
- virtual std::string getSVGFilename (unsigned pageno) const =0;
+ virtual FilePath getSVGFilePath (unsigned pageno) const =0;
virtual std::string getBBoxFormatString () const =0;
virtual void progress (const char *id) {}
virtual int getDVIStackDepth () const {return 0;}
@@ -80,7 +81,7 @@ class EmptySpecialActions : public SpecialActions {
void embed (const BoundingBox &bbox) override {}
void embed (const DPair &p, double r=0) override {}
unsigned getCurrentPageNumber() const override {return 0;}
- std::string getSVGFilename (unsigned pageno) const override {return "";}
+ FilePath getSVGFilePath (unsigned pageno) const override {return FilePath();}
std::string getBBoxFormatString () const override {return "";}
private:
diff --git a/dviware/dvisvgm/src/StreamReader.cpp b/dviware/dvisvgm/src/StreamReader.cpp
index f06dcd42dd..ccfb176abe 100644
--- a/dviware/dvisvgm/src/StreamReader.cpp
+++ b/dviware/dvisvgm/src/StreamReader.cpp
@@ -20,8 +20,9 @@
#include <algorithm>
#include <vector>
-#include "CRC32.hpp"
+#include "HashFunction.hpp"
#include "StreamReader.hpp"
+#include "utility.hpp"
using namespace std;
@@ -46,13 +47,13 @@ uint32_t StreamReader::readUnsigned (int bytes) {
}
-/** Reads an unsigned integer from assigned input stream and updates the CRC32 checksum.
- * @param[in] bytes number of bytes to read (max. 4)
- * @param[in,out] crc32 checksum to be updated
+/** Reads an unsigned integer from assigned input stream and updates the hash value.
+ * @param[in] n number of bytes to read (max. 4)
+ * @param[in,out] hashfunc hash to update
* @return read integer */
-uint32_t StreamReader::readUnsigned (int bytes, CRC32 &crc32) {
- uint32_t ret = readUnsigned(bytes);
- crc32.update(ret, bytes);
+uint32_t StreamReader::readUnsigned (int n, HashFunction &hashfunc) {
+ uint32_t ret = readUnsigned(n);
+ hashfunc.update(util::bytes(ret, n));
return ret;
}
@@ -70,13 +71,13 @@ int32_t StreamReader::readSigned (int bytes) {
}
-/** Reads an signed integer from assigned input stream and updates the CRC32 checksum.
+/** Reads an signed integer from assigned input stream and updates the hash value.
* @param[in] bytes number of bytes to read (max. 4)
- * @param[in,out] crc32 checksum to be updated
+ * @param[in,out] hashfunc hash to update
* @return read integer */
-int32_t StreamReader::readSigned (int bytes, CRC32 &crc32) {
- int32_t ret = readSigned(bytes);
- crc32.update(uint32_t(ret), bytes);
+int32_t StreamReader::readSigned (int n, HashFunction &hashfunc) {
+ int32_t ret = readSigned(n);
+ hashfunc.update(util::bytes(ret, n));
return ret;
}
@@ -93,15 +94,15 @@ string StreamReader::readString () {
}
-/** Reads a string terminated by a 0-byte and updates the CRC32 checksum.
- * @param[in,out] crc32 checksum to be updated
+/** Reads a string terminated by a 0-byte and updates the hash value.
+ * @param[in,out] hashfunc hash to update
* @param[in] finalZero consider final 0-byte in checksum
* @return the string read */
-string StreamReader::readString (CRC32 &crc32, bool finalZero) {
+string StreamReader::readString (HashFunction &hashfunc, bool finalZero) {
string ret = readString();
- crc32.update(reinterpret_cast<const uint8_t*>(ret.data()), ret.length());
+ hashfunc.update(ret.data(), ret.length());
if (finalZero)
- crc32.update(0, 1);
+ hashfunc.update(0, 1);
return ret;
}
@@ -119,36 +120,37 @@ string StreamReader::readString (int length) {
}
-/** Reads a string of a given length and updates the CRC32 checksum.
+/** Reads a string of a given length and updates the hash value.
* @param[in] length number of characters to read
- * @param[in,out] crc32 checksum to be updated
+ * @param[in,out] hashfunc hash to update
* @return the string read */
-string StreamReader::readString (int length, CRC32 &crc32) {
+string StreamReader::readString (int length, HashFunction &hashfunc) {
string ret = readString(length);
- crc32.update(reinterpret_cast<const uint8_t*>(ret.data()), length);
+ hashfunc.update(ret.data(), length);
return ret;
}
-vector<uint8_t>& StreamReader::readBytes (int n, vector<uint8_t> &bytes) {
+vector<uint8_t> StreamReader::readBytes (int n) {
+ vector<uint8_t> bytes(n);
if (n > 0)
- _is->read((char*)&bytes[0], n);
+ _is->read(reinterpret_cast<char*>(bytes.data()), n);
return bytes;
}
-vector<uint8_t>& StreamReader::readBytes (int n, vector<uint8_t> &bytes, CRC32 &crc32) {
- readBytes(n, bytes);
- crc32.update(&bytes[0], bytes.size());
+vector<uint8_t> StreamReader::readBytes (int n, HashFunction &hashfunc) {
+ vector<uint8_t> bytes = readBytes(n);
+ hashfunc.update(bytes);
return bytes;
}
-int StreamReader::readByte (CRC32 &crc32) {
+int StreamReader::readByte (HashFunction &hashfunc) {
int ret = readByte();
if (ret >= 0) {
- const uint8_t c = uint8_t(ret & 0xff);
- crc32.update(&c, 1);
+ char c = ret & 0xff;
+ hashfunc.update(&c, 1);
}
return ret;
}
diff --git a/dviware/dvisvgm/src/StreamReader.hpp b/dviware/dvisvgm/src/StreamReader.hpp
index 2b95f653ca..05c4e1e701 100644
--- a/dviware/dvisvgm/src/StreamReader.hpp
+++ b/dviware/dvisvgm/src/StreamReader.hpp
@@ -26,7 +26,7 @@
#include <vector>
#include "MessageException.hpp"
-class CRC32;
+class HashFunction;
class StreamReader {
public:
@@ -37,17 +37,17 @@ class StreamReader {
void clearStream () {_is->clear();}
std::istream& replaceStream (std::istream &s);
uint32_t readUnsigned (int n);
- uint32_t readUnsigned (int n, CRC32 &crc32);
+ uint32_t readUnsigned (int n, HashFunction &hashfunc);
int32_t readSigned (int n);
- int32_t readSigned (int n, CRC32 &crc32);
+ int32_t readSigned (int n, HashFunction &hashfunc);
std::string readString ();
- std::string readString (CRC32 &crc32, bool finalZero=false);
+ std::string readString (HashFunction &hashfunc, bool finalZero=false);
std::string readString (int length);
- std::string readString (int length, CRC32 &crc32);
- std::vector<uint8_t>& readBytes (int n, std::vector<uint8_t> &bytes);
- std::vector<uint8_t>& readBytes (int n, std::vector<uint8_t> &bytes, CRC32 &crc32);
+ std::string readString (int length, HashFunction &hashfunc);
+ std::vector<uint8_t> readBytes (int n);
+ std::vector<uint8_t> readBytes (int n, HashFunction &hash);
int readByte () {return _is->get();}
- int readByte (CRC32 &crc32);
+ int readByte (HashFunction &hashfunc);
void seek (std::streampos pos, std::ios::seekdir dir) {_is->seekg(pos, dir);}
void seek (std::streampos pos) {_is->seekg(pos);}
std::streampos tell () const {return _is->tellg();}
diff --git a/dviware/dvisvgm/src/StreamWriter.cpp b/dviware/dvisvgm/src/StreamWriter.cpp
index c5630f56ba..6216468695 100644
--- a/dviware/dvisvgm/src/StreamWriter.cpp
+++ b/dviware/dvisvgm/src/StreamWriter.cpp
@@ -18,8 +18,9 @@
** along with this program; if not, see <http://www.gnu.org/licenses/>. **
*************************************************************************/
-#include "CRC32.hpp"
+#include "HashFunction.hpp"
#include "StreamWriter.hpp"
+#include "utility.hpp"
using namespace std;
@@ -37,7 +38,7 @@ void StreamWriter::writeUnsigned (uint32_t val, int n) {
* @param[in] val the value to write
* @param[in] n number of bytes to be considered */
void StreamWriter::writeSigned (int32_t val, int n) {
- writeUnsigned((uint32_t)val, n);
+ writeUnsigned(uint32_t(val), n);
}
@@ -71,27 +72,27 @@ void StreamWriter::writeString (const string &str, bool finalZero) {
/** Writes an unsigned integer to the output stream.
* @param[in] val the value to write
* @param[in] n number of bytes to be considered
- * @param[in,out] crc32 checksum to be updated */
-void StreamWriter::writeUnsigned (uint32_t val, int n, CRC32 &crc32) {
+ * @param[in,out] hashfunc hash to update */
+void StreamWriter::writeUnsigned (uint32_t val, int n, HashFunction &hashfunc) {
writeUnsigned(val, n);
- crc32.update(val, n);
+ hashfunc.update(util::bytes(val, n));
}
-/** Writes a signed integer to the output stream and updates the CRC32 checksum.
+/** Writes a signed integer to the output stream and updates the hash value.
* @param[in] val the value to write
* @param[in] n number of bytes to be considered
- * @param[in,out] crc32 checksum to be updated */
-void StreamWriter::writeSigned (int32_t val, int n, CRC32 &crc32) {
- writeUnsigned((uint32_t)val, n, crc32);
+ * @param[in,out] hashfunc hash to update */
+void StreamWriter::writeSigned (int32_t val, int n, HashFunction &hashfunc) {
+ writeUnsigned(uint32_t(val), n, hashfunc);
}
-/** Writes a string to the output stream and updates the CRC32 checksum.
+/** Writes a string to the output stream and updates the hash value.
* @param[in] str the string to write
- * @param[in,out] crc32 checksum to be updated
+ * @param[in,out] hashfunc hash to update
* @param[in] finalZero if true, a final 0-byte is appended */
-void StreamWriter::writeString (const std::string &str, CRC32 &crc32, bool finalZero) {
+void StreamWriter::writeString (const std::string &str, HashFunction &hashfunc, bool finalZero) {
writeString(str, finalZero);
- crc32.update((const uint8_t*)str.c_str(), str.length() + (finalZero ? 1 : 0));
+ hashfunc.update(str.data(), str.length() + (finalZero ? 1 : 0));
}
diff --git a/dviware/dvisvgm/src/StreamWriter.hpp b/dviware/dvisvgm/src/StreamWriter.hpp
index 49b3401589..441a9a7e7f 100644
--- a/dviware/dvisvgm/src/StreamWriter.hpp
+++ b/dviware/dvisvgm/src/StreamWriter.hpp
@@ -24,7 +24,7 @@
#include <ostream>
#include <vector>
-class CRC32;
+class HashFunction;
class StreamWriter {
public:
@@ -35,9 +35,9 @@ class StreamWriter {
void writeBytes (const uint8_t *buf, size_t bufsize);
void writeBytes (int byte, size_t count);
void writeString (const std::string &str, bool finalZero=false);
- void writeUnsigned (uint32_t val, int n, CRC32 &crc32);
- void writeSigned (int32_t val, int n, CRC32 &crc32);
- void writeString (const std::string &str, CRC32 &crc32, bool finalZero=false);
+ void writeUnsigned (uint32_t val, int n, HashFunction &hashfunc);
+ void writeSigned (int32_t val, int n, HashFunction &hashfunc);
+ void writeString (const std::string &str, HashFunction &hashfunc, bool finalZero=false);
private:
std::ostream &_os;
diff --git a/dviware/dvisvgm/src/TensorProductPatch.cpp b/dviware/dvisvgm/src/TensorProductPatch.cpp
index 13650d71ee..52acd63179 100644
--- a/dviware/dvisvgm/src/TensorProductPatch.cpp
+++ b/dviware/dvisvgm/src/TensorProductPatch.cpp
@@ -201,16 +201,18 @@ Color TensorProductPatch::averageColor (const Color &c1, const Color &c2, const
}
-void TensorProductPatch::getBoundaryPath (GraphicsPath<double> &path) const {
+GraphicsPath<double> TensorProductPatch::getBoundaryPath () const {
// Simple approach: Use the outer curves as boundary path. This doesn't always lead
// to correct results since, depending on the control points, P(u,v) might exceed
// the simple boundary.
+ GraphicsPath<double> path;
path.moveto(_points[0][0]);
path.cubicto(_points[0][1], _points[0][2], _points[0][3]);
path.cubicto(_points[1][3], _points[2][3], _points[3][3]);
path.cubicto(_points[3][2], _points[3][1], _points[3][0]);
path.cubicto(_points[2][0], _points[1][0], _points[0][0]);
path.closepath();
+ return path;
}
@@ -354,8 +356,7 @@ void TensorProductPatch::approximateRow (double v1, double inc, bool overlap, do
void TensorProductPatch::approximate (int gridsize, bool overlap, double delta, Callback &callback) const {
if (_colors[0] == _colors[1] && _colors[1] == _colors[2] && _colors[2] == _colors[3]) {
// simple case: monochromatic patch
- GraphicsPath<double> path;
- getBoundaryPath(path);
+ GraphicsPath<double> path = getBoundaryPath();
callback.patchSegment(path, _colors[0]);
}
else {
@@ -377,17 +378,16 @@ void TensorProductPatch::approximate (int gridsize, bool overlap, double delta,
}
-void TensorProductPatch::getBBox (BoundingBox &bbox) const {
+BoundingBox TensorProductPatch::getBBox () const {
+ BoundingBox bbox;
Bezier bezier;
- BoundingBox bezierBox;
for (int i=0; i <= 1; i++) {
horizontalCurve(i, bezier);
- bezier.getBBox(bezierBox);
- bbox.embed(bezierBox);
+ bbox.embed(bezier.getBBox());
verticalCurve(i, bezier);
- bezier.getBBox(bezierBox);
- bbox.embed(bezierBox);
+ bbox.embed(bezier.getBBox());
}
+ return bbox;
}
diff --git a/dviware/dvisvgm/src/TensorProductPatch.hpp b/dviware/dvisvgm/src/TensorProductPatch.hpp
index 444b93fd39..711feefc2d 100644
--- a/dviware/dvisvgm/src/TensorProductPatch.hpp
+++ b/dviware/dvisvgm/src/TensorProductPatch.hpp
@@ -51,12 +51,12 @@ class TensorProductPatch : public ShadingPatch {
Color averageColor () const override;
void horizontalCurve (double v, Bezier &bezier) const;
void verticalCurve (double u, Bezier &bezier) const;
- void getBoundaryPath (GraphicsPath<double> &path) const override;
+ GraphicsPath<double> getBoundaryPath () const override;
void subpatch (double u1, double u2, double v1, double v2, TensorProductPatch &patch) const;
DPair blossomValue (double u1, double u2, double u3, double v1, double v2, double v3) const;
DPair blossomValue (double u[3], double v[3]) const {return blossomValue(u[0], u[1], u[2], v[0], v[1], v[2]);}
void approximate (int gridsize, bool overlap, double delta, Callback &callback) const override;
- void getBBox (BoundingBox &bbox) const override;
+ BoundingBox getBBox () const override;
int numPoints (int edgeflag) const override {return edgeflag == 0 ? 16 : 12;}
int numColors (int edgeflag) const override {return edgeflag == 0 ? 4 : 2;}
diff --git a/dviware/dvisvgm/src/TpicSpecialHandler.cpp b/dviware/dvisvgm/src/TpicSpecialHandler.cpp
index ee539f6b02..7bfb7937eb 100644
--- a/dviware/dvisvgm/src/TpicSpecialHandler.cpp
+++ b/dviware/dvisvgm/src/TpicSpecialHandler.cpp
@@ -23,6 +23,7 @@
#include <sstream>
#include <string>
#include "Color.hpp"
+#include "EllipticalArc.hpp"
#include "InputBuffer.hpp"
#include "InputReader.hpp"
#include "GraphicsPath.hpp"
@@ -164,7 +165,7 @@ void TpicSpecialHandler::drawSplines (double ddist, SpecialActions &actions) {
const DPair p1 = p+_points[i];
const DPair p2 = p+_points[i+1];
mid = p1+(p2-p1)/2.0;
- path.conicto(p1, mid);
+ path.quadto(p1, mid);
actions.embed(mid);
actions.embed((p0+p1*6.0+p2)/8.0, _penwidth);
}
@@ -187,14 +188,6 @@ void TpicSpecialHandler::drawSplines (double ddist, SpecialActions &actions) {
}
-static double normalized_angle (double rad) {
- rad = fmod(rad, math::TWO_PI);
- if (rad < 0)
- rad += math::TWO_PI;
- return rad;
-}
-
-
/** Draws an elliptical arc.
* @param[in] cx x-coordinate of arc center relative to current DVI position
* @param[in] cy y-coordinate of arc center relative to current DVI position
@@ -205,8 +198,6 @@ static double normalized_angle (double rad) {
* @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) {
- angle1 = -angle1;
- angle2 = -angle2;
cx += actions.getX();
cy += actions.getY();
unique_ptr<XMLElement> elem;
@@ -214,21 +205,17 @@ void TpicSpecialHandler::drawArc (double cx, double cy, double rx, double ry, do
if (abs(angle2-angle1) >= math::TWO_PI) // closed ellipse?
elem = create_ellipse_element(cx, cy, rx, ry);
else {
- angle1 = normalized_angle(angle1);
- angle2 = normalized_angle(angle2);
- double delta = normalized_angle(angle2-angle1);
- int large_arg = (delta < math::PI) ? 1 : 0;
- ostringstream oss;
- oss << 'M' << XMLString(cx+rx*cos(angle1)) << ' ' << XMLString(cy+ry*sin(-angle1))
- << 'A' << XMLString(rx) << ' ' << XMLString(ry)
- << " 0 " // no rotation of x-axis
- << large_arg << " 1 " // always draw arc clockwise (sweep flag == 1)
- << XMLString(cx+rx*cos(angle2)) << ' ' << XMLString(cy-ry*sin(angle2));
+ EllipticalArc arc(DPair(cx, cy), rx, ry, 0, -angle1, math::normalize_0_2pi(angle2-angle1));
+ GraphicsPath<double> path;
+ path.moveto(arc.startPoint());
+ path.arcto(rx, ry, 0, arc.largeArc(), arc.sweepPositive(), arc.endPoint());
if (_grayLevel >= 0)
- oss << 'Z';
+ path.closepath();
else
closed = false;
elem = util::make_unique<XMLElement>("path");
+ ostringstream oss;
+ path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
elem->addAttribute("d", oss.str());
}
if (_penwidth > 0) {
diff --git a/dviware/dvisvgm/src/TriangularPatch.cpp b/dviware/dvisvgm/src/TriangularPatch.cpp
index 929fe1b885..01efe2e8f0 100644
--- a/dviware/dvisvgm/src/TriangularPatch.cpp
+++ b/dviware/dvisvgm/src/TriangularPatch.cpp
@@ -161,8 +161,7 @@ static inline double snap (double x) {
* @param[in] callback object notified */
void TriangularPatch::approximate (int gridsize, bool overlap, double delta, Callback &callback) const {
if (_colors[0] == _colors[1] && _colors[1] == _colors[2]) {
- GraphicsPath<double> path;
- getBoundaryPath(path);
+ GraphicsPath<double> path = getBoundaryPath();
callback.patchSegment(path, _colors[0]);
}
else {
@@ -197,18 +196,21 @@ void TriangularPatch::approximate (int gridsize, bool overlap, double delta, Cal
}
-void TriangularPatch::getBoundaryPath(GraphicsPath<double> &path) const {
+GraphicsPath<double> TriangularPatch::getBoundaryPath () const {
+ GraphicsPath<double> path;
path.clear();
path.moveto(_points[0]);
path.lineto(_points[1]);
path.lineto(_points[2]);
path.closepath();
+ return path;
}
-void TriangularPatch::getBBox (BoundingBox &bbox) const {
- bbox.invalidate();
+BoundingBox TriangularPatch::getBBox () const {
+ BoundingBox bbox;
bbox.embed(_points[0]);
bbox.embed(_points[1]);
bbox.embed(_points[2]);
+ return bbox;
}
diff --git a/dviware/dvisvgm/src/TriangularPatch.hpp b/dviware/dvisvgm/src/TriangularPatch.hpp
index 8c6405ef08..d329e43fa1 100644
--- a/dviware/dvisvgm/src/TriangularPatch.hpp
+++ b/dviware/dvisvgm/src/TriangularPatch.hpp
@@ -39,8 +39,8 @@ class TriangularPatch : public ShadingPatch {
void setColors (const ColorVec &colors, int edgeflag, ShadingPatch *patch) override;
void setColors (const Color &c1, const Color &c2, const Color &c3);
void approximate (int gridsize, bool overlap, double delta, Callback &listener) const override;
- void getBBox (BoundingBox &bbox) const override;
- void getBoundaryPath(GraphicsPath<double> &path) const override;
+ BoundingBox getBBox () const override;
+ GraphicsPath<double> getBoundaryPath () const override;
int numPoints (int edgeflag) const override {return edgeflag == 0 ? 3 : 1;}
int numColors (int edgeflag) const override {return edgeflag == 0 ? 3 : 1;}
diff --git a/dviware/dvisvgm/src/TrueTypeFont.cpp b/dviware/dvisvgm/src/TrueTypeFont.cpp
index 51056c36cc..408a00eede 100644
--- a/dviware/dvisvgm/src/TrueTypeFont.cpp
+++ b/dviware/dvisvgm/src/TrueTypeFont.cpp
@@ -127,7 +127,7 @@ void TrueTypeFont::writeWOFF (ostream &os) const {
void TrueTypeFont::writeWOFF (const string &fname) const {
- ofstream ofs(fname.c_str(), ios::binary);
+ ofstream ofs(fname, ios::binary);
writeWOFF(ofs);
}
@@ -151,7 +151,7 @@ bool TrueTypeFont::writeWOFF2 (ostream &os) const {
bool TrueTypeFont::writeWOFF2 (const string &fname) const {
- ofstream ofs(fname.c_str(), ios::binary);
+ ofstream ofs(fname, ios::binary);
return writeWOFF2(ofs);
}
@@ -172,4 +172,4 @@ uint32_t TrueTypeFont::TTFTableRecord::computeChecksum () const {
while (startptr < endptr)
sum += *startptr++;
return sum;
-} \ No newline at end of file
+}
diff --git a/dviware/dvisvgm/src/VFReader.cpp b/dviware/dvisvgm/src/VFReader.cpp
index a618f96ef7..cff67c919c 100644
--- a/dviware/dvisvgm/src/VFReader.cpp
+++ b/dviware/dvisvgm/src/VFReader.cpp
@@ -66,9 +66,7 @@ int VFReader::executeCommand (ApproveAction approve) {
case 248: cmdPost(); break; // postamble
default : { // invalid opcode
replaceActions(actions); // reenable actions
- ostringstream oss;
- oss << "undefined VF command (opcode " << opcode << ')';
- throw VFException(oss.str());
+ throw VFException("undefined VF command (opcode " + std::to_string(opcode) + ")");
}
}
}
@@ -139,8 +137,7 @@ void VFReader::cmdLongChar () {
else {
uint32_t cc = readUnsigned(4); // character code
readUnsigned(4); // equals character width from corresponding TFM file
- vector<uint8_t> dvi(pl); // DVI subroutine
- readBytes(pl, dvi);
+ auto dvi = readBytes(pl); // DVI subroutine
_actions->defineVFChar(cc, std::move(dvi)); // call template method for user actions
}
}
@@ -154,8 +151,7 @@ void VFReader::cmdShortChar (int pl) {
else {
uint32_t cc = readUnsigned(1); // character code
readUnsigned(3); // character width from corresponding TFM file
- vector<uint8_t> dvi(pl); // DVI subroutine
- readBytes(pl, dvi);
+ auto dvi = readBytes(pl); // DVI subroutine
_actions->defineVFChar(cc, std::move(dvi)); // call template method for user actions
}
}
diff --git a/dviware/dvisvgm/src/XMLString.cpp b/dviware/dvisvgm/src/XMLString.cpp
index b07e0e287b..fcafb8d1ee 100644
--- a/dviware/dvisvgm/src/XMLString.cpp
+++ b/dviware/dvisvgm/src/XMLString.cpp
@@ -21,6 +21,7 @@
#include <cmath>
#include <cstdlib>
#include "Unicode.hpp"
+#include "utility.hpp"
#include "XMLString.hpp"
using namespace std;
@@ -89,17 +90,8 @@ XMLString::XMLString (double x) {
}
if (std::abs(x) < 1e-6)
x = 0;
- assign(to_string(x));
- size_t pos = find('.');
- if (pos != string::npos) {
- pos = find_last_not_of('0');
- if (pos != string::npos) {
- erase(pos+1); // remove trailing zeros
- if (at(length()-1) == '.')
- pop_back(); // remove trailing dot
- }
- }
- pos = find("0.");
+ assign(util::to_string(x));
+ size_t pos = find("0.");
if (pos != string::npos && (pos == 0 || at(pos-1) == '-'))
erase(pos, 1);
}
diff --git a/dviware/dvisvgm/src/XXHashFunction.hpp b/dviware/dvisvgm/src/XXHashFunction.hpp
index c0ae14e20b..f81deddbd4 100644
--- a/dviware/dvisvgm/src/XXHashFunction.hpp
+++ b/dviware/dvisvgm/src/XXHashFunction.hpp
@@ -23,7 +23,11 @@
#include <xxhash.h>
#include "HashFunction.hpp"
+#include "utility.hpp"
+#if (XXH_VERSION_NUMBER >= 701) && defined(XXH3_SECRET_SIZE_MIN)
+#define ENABLE_XXH128
+#endif
template <int HASH_SIZE>
struct XXHInterface {
@@ -49,6 +53,17 @@ struct XXHInterface<8> {
static constexpr auto digest = &XXH64_digest;
};
+#ifdef ENABLE_XXH128
+template<>
+struct XXHInterface<16> {
+ using State = XXH3_state_t;
+ static constexpr auto createState = &XXH3_createState;
+ static constexpr auto freeState = &XXH3_freeState;
+ static constexpr auto reset = &XXH3_128bits_reset_withSeed;
+ static constexpr auto update = &XXH3_128bits_update;
+ static constexpr auto digest = &XXH3_128bits_digest;
+};
+#endif
/** Implements the HashFunction class for the xxHash algorithms. */
template <int HASH_BYTES>
@@ -66,14 +81,10 @@ class XXHashFunction : public HashFunction {
void update (const std::string &data) override {update(data.data(), data.length());}
void update (const std::vector<uint8_t> &data) override {update(reinterpret_cast<const char*>(data.data()), data.size());}
+ using HashFunction::update; // unhide update(istream &is) defined in base class
+
std::vector<uint8_t> digestValue () const override {
- std::vector<uint8_t> hash(HASH_BYTES);
- auto digest = Interface::digest(_state);
- for (int i=HASH_BYTES-1; i >= 0; i--) {
- hash[i] = digest & 0xff;
- digest >>= 8;
- }
- return hash;
+ return util::bytes(Interface::digest(_state), HASH_BYTES);
}
static unsigned version () {return XXH_versionNumber();}
@@ -85,4 +96,19 @@ class XXHashFunction : public HashFunction {
using XXH32HashFunction = XXHashFunction<4>;
using XXH64HashFunction = XXHashFunction<8>;
+#ifdef ENABLE_XXH128
+using XXH128HashFunction = XXHashFunction<16>;
+
+template<>
+inline std::vector<uint8_t> XXHashFunction<16>::digestValue () const {
+ std::vector<uint8_t> hash;
+ auto digest = Interface::digest(_state);
+ for (auto chunk : {digest.high64, digest.low64}) {
+ auto bytes = util::bytes(chunk);
+ hash.insert(hash.end(), bytes.begin(), bytes.end());
+ }
+ return hash;
+}
+#endif
+
#endif
diff --git a/dviware/dvisvgm/src/dvisvgm.cpp b/dviware/dvisvgm/src/dvisvgm.cpp
index a2c28f8a77..37584d5ddb 100644
--- a/dviware/dvisvgm/src/dvisvgm.cpp
+++ b/dviware/dvisvgm/src/dvisvgm.cpp
@@ -109,27 +109,36 @@ static void set_libgs (CommandLine &args) {
static bool set_cache_dir (const CommandLine &args) {
if (args.cacheOpt.given() && !args.cacheOpt.value().empty()) {
if (args.cacheOpt.value() == "none")
- PhysicalFont::CACHE_PATH = nullptr;
+ PhysicalFont::CACHE_PATH.clear();
else if (FileSystem::exists(args.cacheOpt.value()))
- PhysicalFont::CACHE_PATH = args.cacheOpt.value().c_str();
+ PhysicalFont::CACHE_PATH = args.cacheOpt.value();
else
Message::wstream(true) << "cache directory '" << args.cacheOpt.value() << "' does not exist (caching disabled)\n";
}
- else if (const char *userdir = FileSystem::userdir()) {
+ else {
+ string &cachepath = PhysicalFont::CACHE_PATH;
+ const char *cachehome = getenv("XDG_CACHE_HOME");
+ if (!cachehome || util::trim(cachehome).empty()) {
#ifdef _WIN32
- string cachedir = "\\.dvisvgm\\cache";
+ cachehome = "~\\.cache";
#else
- string cachedir = "/.dvisvgm/cache";
+ cachehome = "~/.cache";
#endif
- static string cachepath = userdir + cachedir;
+ }
+ cachepath = util::trim(cachehome) + FileSystem::PATHSEP + "dvisvgm";
+ if (cachepath[0] == '~' && cachepath[1] == FileSystem::PATHSEP) {
+ if (FileSystem::userdir())
+ cachepath.replace(0, 1, FileSystem::userdir());
+ else
+ cachepath.erase(0, 2); // strip leading "~/"
+ }
if (!FileSystem::exists(cachepath))
FileSystem::mkdir(cachepath);
- PhysicalFont::CACHE_PATH = cachepath.c_str();
}
if (args.cacheOpt.given() && args.cacheOpt.value().empty()) {
- cout << "cache directory: " << (PhysicalFont::CACHE_PATH ? PhysicalFont::CACHE_PATH : "(none)") << '\n';
+ cout << "cache directory: " << (PhysicalFont::CACHE_PATH.empty() ? "(none)" : PhysicalFont::CACHE_PATH) << '\n';
try {
- if (PhysicalFont::CACHE_PATH)
+ if (!PhysicalFont::CACHE_PATH.empty())
FontCache::fontinfo(PhysicalFont::CACHE_PATH, cout, true);
}
catch (StreamReaderException &e) {
diff --git a/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp b/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp
index ae2a0e36d7..e26018253d 100644
--- a/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp
+++ b/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp
@@ -113,14 +113,14 @@ XMLNode* AttributeExtractor::extractAttribute (XMLElement *elem) {
* @return true if the element is groupable */
bool AttributeExtractor::groupable (const XMLElement &elem) {
// https://www.w3.org/TR/SVG/struct.html#GElement
- static constexpr auto names = util::make_array(
+ static const char *names[] = {
"a", "altGlyphDef", "animate", "animateColor", "animateMotion", "animateTransform",
"circle", "clipPath", "color-profile", "cursor", "defs", "desc", "ellipse", "filter",
"font", "font-face", "foreignObject", "g", "image", "line", "linearGradient", "marker",
"mask", "path", "pattern", "polygon", "polyline", "radialGradient", "rect", "set",
"style", "switch", "symbol", "text", "title", "use", "view"
- );
- return binary_search(names.begin(), names.end(), elem.name(), [](const string &name1, const string &name2) {
+ };
+ return binary_search(begin(names), end(names), elem.name(), [](const string &name1, const string &name2) {
return name1 < name2;
});
}
@@ -136,15 +136,15 @@ bool AttributeExtractor::inheritable (const Attribute &attrib) {
// clip-path is not inheritable but can be moved to the parent element as long as
// no child gets an different clip-path attribute
// https://www.w3.org/TR/SVG11/styling.html#Inheritance
- static constexpr auto names = util::make_array(
+ static const char *names[] = {
"clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile",
"color-rendering", "direction", "fill", "fill-opacity", "fill-rule", "font", "font-family", "font-size",
"font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "glyph-orientation-horizontal",
"glyph-orientation-vertical", "letter-spacing", "paint-order", "stroke", "stroke-dasharray", "stroke-dashoffset",
"stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke-width", "transform",
"visibility", "word-spacing", "writing-mode"
- );
- return binary_search(names.begin(), names.end(), attrib.name, [](const string &name1, const string &name2) {
+ };
+ return binary_search(begin(names), end(names), attrib.name, [](const string &name1, const string &name2) {
return name1 < name2;
});
}
@@ -159,13 +159,13 @@ bool AttributeExtractor::extractable (const Attribute &attrib, XMLElement &eleme
// the 'fill' attribute of animation elements has different semantics than
// that of graphics elements => don't extract it from animation nodes
// https://www.w3.org/TR/SVG11/animate.html#TimingAttributes
- static constexpr auto names = util::make_array(
+ static const char *names[] = {
"animate", "animateColor", "animateMotion", "animateTransform", "set"
- );
- auto it = find_if(names.begin(), names.end(), [&](const string &name) {
+ };
+ auto it = find_if(begin(names), end(names), [&](const string &name) {
return element.name() == name;
});
- return it == names.end();
+ return it == end(names);
}
diff --git a/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp b/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp
index 5d580f5ba0..36bf3a8cd5 100644
--- a/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp
+++ b/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp
@@ -132,13 +132,13 @@ bool GroupCollapser::moveAttributes (XMLElement &source, XMLElement &dest) {
bool GroupCollapser::collapsible (const XMLElement &element) {
// the 'fill' attribute of animation elements has different semantics than
// that of graphics elements => don't collapse them
- static constexpr auto names = util::make_array(
+ static const char *names[] = {
"animate", "animateColor", "animateMotion", "animateTransform", "set"
- );
- auto it = find_if(names.begin(), names.end(), [&](const string &name) {
+ };
+ auto it = find_if(begin(names), end(names), [&](const string &name) {
return element.name() == name;
});
- return it == names.end();
+ return it == end(names);
}
@@ -155,11 +155,11 @@ bool GroupCollapser::unwrappable (const XMLElement &source, const XMLElement &de
}
}
// these attributes prevent a group from being unwrapped
- static constexpr auto attribs = util::make_array(
+ static const char *attribs[] = {
"class", "id", "filter", "mask", "style"
- );
- auto it = find_if(attribs.begin(), attribs.end(), [&](const string &name) {
+ };
+ auto it = find_if(begin(attribs), end(attribs), [&](const string &name) {
return source.hasAttribute(name) || dest.hasAttribute(name);
});
- return it == attribs.end();
+ return it == end(attribs);
}
diff --git a/dviware/dvisvgm/src/optimizer/Makefile.am b/dviware/dvisvgm/src/optimizer/Makefile.am
index 41c0266009..fec405662a 100644
--- a/dviware/dvisvgm/src/optimizer/Makefile.am
+++ b/dviware/dvisvgm/src/optimizer/Makefile.am
@@ -10,3 +10,4 @@ liboptimizer_la_SOURCES = \
TransformSimplifier.hpp TransformSimplifier.cpp \
WSNodeRemover.hpp WSNodeRemover.cpp
+AM_CXXFLAGS = -I$(dvisvgm_srcdir)/libs/variant/include
diff --git a/dviware/dvisvgm/src/optimizer/Makefile.in b/dviware/dvisvgm/src/optimizer/Makefile.in
index 64b288a723..45344c1933 100644
--- a/dviware/dvisvgm/src/optimizer/Makefile.in
+++ b/dviware/dvisvgm/src/optimizer/Makefile.in
@@ -369,6 +369,7 @@ liboptimizer_la_SOURCES = \
TransformSimplifier.hpp TransformSimplifier.cpp \
WSNodeRemover.hpp WSNodeRemover.cpp
+AM_CXXFLAGS = -I$(dvisvgm_srcdir)/libs/variant/include
all: all-am
.SUFFIXES:
diff --git a/dviware/dvisvgm/src/optimizer/TransformSimplifier.cpp b/dviware/dvisvgm/src/optimizer/TransformSimplifier.cpp
index 06d28926d4..b9d4c3db59 100644
--- a/dviware/dvisvgm/src/optimizer/TransformSimplifier.cpp
+++ b/dviware/dvisvgm/src/optimizer/TransformSimplifier.cpp
@@ -19,6 +19,7 @@
*************************************************************************/
#include <cmath>
+#include <cstdlib>
#include "TransformSimplifier.hpp"
#include "../Matrix.hpp"
#include "../utility.hpp"
@@ -38,14 +39,16 @@ void TransformSimplifier::execute (XMLElement *context) {
return;
if (const char *transform = context->getAttributeValue("transform")) {
Matrix matrix = Matrix::parseSVGTransform(transform);
- string decomp = decompose(matrix);
- if (decomp.length() > matrix.toSVG().length())
- context->addAttribute("transform", matrix.toSVG());
- else {
- if (decomp.empty())
- context->removeAttribute("transform");
- else
- context->addAttribute("transform", decomp);
+ if (!incorporateTransform(context, matrix)) {
+ string decomp = decompose(matrix);
+ if (decomp.length() > matrix.toSVG().length())
+ context->addAttribute("transform", matrix.toSVG());
+ else {
+ if (decomp.empty())
+ context->removeAttribute("transform");
+ else
+ context->addAttribute("transform", decomp);
+ }
}
}
// continue with child elements
@@ -56,6 +59,36 @@ void TransformSimplifier::execute (XMLElement *context) {
}
+/** Tries to incorporate the translation and scaling components of the 'transform' attribute
+ * of a given element into the positional and/or size attributes of that element. If successful,
+ * the 'transform' attribute is removed.
+ * Currently, only 'image' and 'rect' elements are considered.
+ * @param[in] elem element to check
+ * @param[in] matrix matrix representing the 'transform' attribute of the element
+ * @return true on success */
+bool TransformSimplifier::incorporateTransform (XMLElement *elem, const Matrix &matrix) {
+ if ((elem->name() == "image" || elem->name() == "rect") && matrix.get(0, 1) == 0 && matrix.get(1, 0) == 0) {
+ double tx = matrix.get(0, 2);
+ double ty = matrix.get(1, 2);
+ double sx = matrix.get(0, 0);
+ double sy = matrix.get(1, 1);
+ if (const char *xstr = elem->getAttributeValue("x"))
+ tx += sx*strtod(xstr, nullptr);
+ if (const char *ystr = elem->getAttributeValue("y"))
+ ty += sy*strtod(ystr, nullptr);
+ if (const char *wstr = elem->getAttributeValue("width"))
+ elem->addAttribute("width", sx*strtod(wstr, nullptr));
+ if (const char *hstr = elem->getAttributeValue("height"))
+ elem->addAttribute("height", sy*strtod(hstr, nullptr));
+ elem->addAttribute("x", tx); // update x attribute
+ elem->addAttribute("y", ty); // update x attribute
+ elem->removeAttribute("transform");
+ return true;
+ }
+ return false;
+}
+
+
static string translate_cmd (double dx, double dy) {
string ret;
XMLString dxstr(dx), dystr(dy);
diff --git a/dviware/dvisvgm/src/optimizer/TransformSimplifier.hpp b/dviware/dvisvgm/src/optimizer/TransformSimplifier.hpp
index 40ac21a465..c4b6d9ee16 100644
--- a/dviware/dvisvgm/src/optimizer/TransformSimplifier.hpp
+++ b/dviware/dvisvgm/src/optimizer/TransformSimplifier.hpp
@@ -32,5 +32,6 @@ class TransformSimplifier : public OptimizerModule {
const char* info () const override;
protected:
+ bool incorporateTransform (XMLElement *elem, const Matrix &matrix);
std::string decompose (const Matrix &matrix);
};
diff --git a/dviware/dvisvgm/src/psdefs.cpp b/dviware/dvisvgm/src/psdefs.cpp
index d8a07177b7..39c938dac9 100644
--- a/dviware/dvisvgm/src/psdefs.cpp
+++ b/dviware/dvisvgm/src/psdefs.cpp
@@ -95,25 +95,24 @@ const char *PSInterpreter::PSDEFS =
"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}if}d"
-"ef/initmatrix{matrix setmatrix}def/concat{matrix currentmatrix matrix concatma"
-"trix setmatrix}def/makepattern{gsave<</mx 3 -1 roll>>begin dup/XUID[1000000 @p"
-"atcnt]put mx/makepattern sysexec dup dup begin PatternType @patcnt BBox aload "
-"pop XStep YStep PaintType mx aload pop 15(makepattern)prcmd :newpath matrix se"
-"tmatrix PaintProc 0 1(makepattern)prcmd end/@patcnt @patcnt 1 add store end gr"
-"estore}def/setpattern{begin PatternType 1 eq{PaintType 1 eq{XUID aload pop exc"
-"h pop 1}{:gsave[currentcolorspace aload length -1 roll pop]setcolorspace/setco"
-"lor sysexec XUID aload pop exch pop currentrgbcolor :grestore 4}ifelse(setpatt"
-"ern)prcmd}{/setpattern sysexec}ifelse end}def/setcolor{dup type/dicttype eq{se"
-"tpattern}{/setcolor sysexec/currentrgbcolor sysexec setrgbcolor}ifelse}def/set"
-"gray 1 defpr/setcmykcolor 4 defpr/sethsbcolor 3 defpr/setrgbcolor 3 defpr/.set"
-"opacityalpha{dup/.setopacityalpha sysexec 1(setopacityalpha)prcmd}def/.setshap"
-"ealpha{dup/.setshapealpha sysexec 1(setshapealpha)prcmd}def/.setblendmode{dup/"
-".setblendmode sysexec<</Normal 0/Compatible 0/Multiply 1/Screen 2/Overlay 3/So"
-"ftLight 4/HardLight 5/ColorDodge 6/ColorBurn 7/Darken 8/Lighten 9/Difference 1"
-"0/Exclusion 11/Hue 12/Saturation 13/Color 14/Luminosity 15/CompatibleOverprint"
-" 16>>exch get 1(setblendmode)prcmd}def/@pdfpagecount{GS_PDF_ProcSet begin pdfd"
-"ict begin(r)file pdfopen begin pdfpagecount currentdict pdfclose end end end}d"
-"ef/@pdfpagebox{GS_PDF_ProcSet begin pdfdict begin(r)file pdfopen begin dup dup"
-" 1 lt exch pdfpagecount gt or{pop}{pdfgetpage/MediaBox pget pop aload pop}ifel"
-"se currentdict pdfclose end end end}def DELAYBIND{.bindnow}if ";
+"{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 ";
diff --git a/dviware/dvisvgm/src/utility.cpp b/dviware/dvisvgm/src/utility.cpp
index 99e8e194a0..f1ae59ec08 100644
--- a/dviware/dvisvgm/src/utility.cpp
+++ b/dviware/dvisvgm/src/utility.cpp
@@ -20,6 +20,7 @@
#include <algorithm>
#include <cctype>
+#include <cmath>
#include <fstream>
#include <functional>
#include <iterator>
@@ -27,6 +28,56 @@
using namespace std;
+/** Computes the singular value decomposition of a given 2x2 matrix M
+ * so that M = rotate(phi)*scale(sx,sy)*rotate(theta), where
+ * rotate(t):={{cos t, -sin t}, {sin t, cos t}} and scale(sx, sy):={{sx, 0}, {0, sy}}.
+ * The corresponding math can be found in Jim Blinn: "Consider the Lowly 2x2 Matrix"
+ * https://ieeexplore.ieee.org/document/486688
+ * Also published in "Jim Blinn's Corner: Notation, Notation, Notation", pp. 69--95.
+ * @param[in] m matrix {{m00, m01}, {m10, m11}} where the inner pairs denote the rows
+ * @return vector {phi, sx, sy, theta} */
+vector<double> math::svd (const double (&m)[2][2]) {
+ double phi=0, theta=0, sx=0, sy=0;
+ if (m[0][0] != 0 || m[0][1] != 0 || m[1][0] != 0 || m[1][1] != 0) {
+ double e = (m[0][0] + m[1][1])/2; // = cos(phi+theta)*(sx+sy)/2
+ double f = (m[0][0] - m[1][1])/2; // = cos(phi-theta)*(sx-sy)/2
+ double g = (m[1][0] + m[0][1])/2; // = sin(phi-theta)*(sx-sy)/2
+ double h = (m[1][0] - m[0][1])/2; // = sin(phi+theta)*(sx+sy)/2
+ double hyp1 = hypot(e, h); // = (sx+sy)/2
+ double hyp2 = hypot(f, g); // = (sx-sy)/2
+ sx = hyp1+hyp2;
+ sy = hyp1-hyp2;
+ if (hyp2 == 0) // uniformly scaled rotation?
+ theta = atan2(h, e);
+ else if (hyp1 == 0) // uniformly scaled reflection?
+ theta = -atan2(g, f);
+ else {
+ double a1 = atan2(g, f); // = phi-theta (g/f = tan(phi-theta))
+ double a2 = atan2(h, e); // = phi+theta (h/e = tan(phi+theta))
+ phi = (a2+a1)/2;
+ theta = (a2-a1)/2;
+ }
+ }
+ return vector<double>{phi, sx, sy, theta};
+}
+
+
+/** Normalizes an angle to the interval [-mod, mod). */
+double math::normalize_angle (double angle, double mod) {
+ angle = fmod(angle+mod, 2.0*mod);
+ if (angle < 0)
+ angle += 2.0*mod;
+ return angle-mod;
+}
+
+
+double math::normalize_0_2pi (double rad) {
+ rad = fmod(rad, TWO_PI);
+ if (rad < 0)
+ rad += TWO_PI;
+ return rad;
+}
+
/** Returns a given string with leading and trailing whitespace removed.
* @param[in] str the string to process
@@ -109,6 +160,20 @@ string util::tolower (const string &str) {
}
+/** Converts a double to a string and strips redundant trailing digits/dots. */
+string util::to_string (double val) {
+ string str = std::to_string(val);
+ if (str.find('.') != string::npos) { // double value and not an integer?
+ size_t pos = str.find_last_not_of('0');
+ if (pos != string::npos) // trailing zeros
+ str.erase(pos+1, string::npos);
+ if (str.back() == '.') // trailing dot?
+ str.pop_back();
+ }
+ return str;
+}
+
+
/** Returns the integer part of log10 of a given integer \f$n>0\f$.
* If \f$n<0\f$, the result is 0. */
int util::ilog10 (int n) {
diff --git a/dviware/dvisvgm/src/utility.hpp b/dviware/dvisvgm/src/utility.hpp
index ca0b0873dc..7127280284 100644
--- a/dviware/dvisvgm/src/utility.hpp
+++ b/dviware/dvisvgm/src/utility.hpp
@@ -36,6 +36,13 @@ constexpr const double SQRT2 = 1.414213562373095048801688724209698079;
inline double deg2rad (double deg) {return PI*deg/180.0;}
inline double rad2deg (double rad) {return 180.0*rad/PI;}
+double normalize_angle (double angle, double mod);
+double normalize_0_2pi (double rad);
+std::vector<double> svd (const double (&m)[2][2]);
+
+/** Signum function (returns x/abs(x) if x != 0, and 0 otherwise). */
+template <typename T>
+inline int sgn (T x) {return (x > T(0)) - (x < T(0));}
} // namespace math
@@ -52,12 +59,31 @@ std::string trim (const std::string &str, const char *ws=" \t\n\r\f");
std::string normalize_space (std::string str, const char *ws=" \t\n\r\f");
std::string tolower (const std::string &str);
std::string replace (std::string str, const std::string &find, const std::string &repl);
+std::string to_string (double val);
std::vector<std::string> split (const std::string &str, const std::string &sep);
int ilog10 (int n);
std::string read_file_contents (const std::string &fname);
void write_file_contents (const std::string &fname, std::string::iterator start, std::string::iterator end);
+
+/** Returns a sequence of bytes of a given integral value.
+ * @param[in] val get bytes of this value
+ * @param[in] n number of bytes to extract (from LSB to MSB), all if n == 0
+ * @return vector of bytes in big-endian order */
+template <typename T>
+std::vector<uint8_t> bytes (T val, int n=0) {
+ if (n <= 0)
+ n = sizeof(T);
+ std::vector<uint8_t> ret(n);
+ for (int i=0; i < n; i++) {
+ ret[n-i-1] = val & 0xff;
+ val >>= 8;
+ }
+ return ret;
+}
+
+
/** Encodes the bytes in the half-open range [first,last) to Base64 and writes
* the result to the range starting at 'dest'.
* @param[in] first initial position of the range to be encoded
@@ -103,15 +129,6 @@ std::unique_ptr<T> static_unique_ptr_cast (std::unique_ptr<U> &&old){
return std::unique_ptr<T>{static_cast<T*>(old.release())};
}
-
-template <typename... T>
-constexpr auto make_array (T&&... values) ->
- std::array<typename std::decay<typename std::common_type<T...>::type>::type, sizeof...(T)> {
- return std::array<typename std::decay<typename std::common_type<T...>::type>::type, sizeof...(T)>{
- std::forward<T>(values)...
- };
- }
-
} // namespace util
#endif