From e874d39878309e456c7ffe8d5fc6a91be571b50c Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sun, 14 Aug 2022 03:00:41 +0000 Subject: CTAN sync 202208140300 --- dviware/dvisvgm/src/CLCommandLine.cpp | 6 +- dviware/dvisvgm/src/CMap.cpp | 2 +- dviware/dvisvgm/src/DVIReader.cpp | 2 +- dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp | 174 ++---------- dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp | 27 +- dviware/dvisvgm/src/FileFinder.cpp | 25 +- dviware/dvisvgm/src/FilePath.cpp | 10 +- dviware/dvisvgm/src/Font.cpp | 2 +- dviware/dvisvgm/src/FontManager.cpp | 11 +- dviware/dvisvgm/src/FontMap.cpp | 4 +- dviware/dvisvgm/src/FontWriter.cpp | 10 +- dviware/dvisvgm/src/GraphicsPathParser.hpp | 299 +++++++++++++++++++++ dviware/dvisvgm/src/HashFunction.hpp | 2 +- dviware/dvisvgm/src/HyperlinkManager.cpp | 2 +- dviware/dvisvgm/src/HyperlinkManager.hpp | 4 +- dviware/dvisvgm/src/Makefile.am | 2 + dviware/dvisvgm/src/Makefile.in | 54 ++-- dviware/dvisvgm/src/MapLine.cpp | 6 +- dviware/dvisvgm/src/MetafontWrapper.cpp | 2 +- dviware/dvisvgm/src/MiKTeXCom.cpp | 5 +- dviware/dvisvgm/src/Opacity.hpp | 6 +- dviware/dvisvgm/src/PDFParser.cpp | 6 +- dviware/dvisvgm/src/PageSize.cpp | 2 +- dviware/dvisvgm/src/PapersizeSpecialHandler.cpp | 2 +- dviware/dvisvgm/src/PsSpecialHandler.cpp | 2 +- dviware/dvisvgm/src/SVGElement.hpp | 2 +- dviware/dvisvgm/src/SVGOutput.cpp | 4 +- dviware/dvisvgm/src/SVGTree.cpp | 2 +- dviware/dvisvgm/src/TFM.cpp | 4 +- dviware/dvisvgm/src/Unicode.cpp | 2 +- dviware/dvisvgm/src/XMLNode.cpp | 38 ++- dviware/dvisvgm/src/XMLNode.hpp | 3 +- dviware/dvisvgm/src/XMLParser.cpp | 182 +++++++++++++ dviware/dvisvgm/src/XMLParser.hpp | 58 ++++ dviware/dvisvgm/src/XMLString.cpp | 2 +- dviware/dvisvgm/src/dvisvgm.cpp | 9 +- .../dvisvgm/src/optimizer/AttributeExtractor.cpp | 26 +- .../dvisvgm/src/optimizer/AttributeExtractor.hpp | 1 - dviware/dvisvgm/src/optimizer/GroupCollapser.cpp | 7 +- dviware/dvisvgm/src/optimizer/Makefile.am | 2 +- dviware/dvisvgm/src/optimizer/Makefile.in | 24 +- dviware/dvisvgm/src/optimizer/TextSimplifier.cpp | 2 +- dviware/dvisvgm/src/utility.cpp | 16 +- dviware/dvisvgm/src/utility.hpp | 8 +- 44 files changed, 733 insertions(+), 326 deletions(-) create mode 100644 dviware/dvisvgm/src/GraphicsPathParser.hpp create mode 100644 dviware/dvisvgm/src/XMLParser.cpp create mode 100644 dviware/dvisvgm/src/XMLParser.hpp (limited to 'dviware/dvisvgm/src') diff --git a/dviware/dvisvgm/src/CLCommandLine.cpp b/dviware/dvisvgm/src/CLCommandLine.cpp index 17ba2c2d1f..e8b538e783 100644 --- a/dviware/dvisvgm/src/CLCommandLine.cpp +++ b/dviware/dvisvgm/src/CLCommandLine.cpp @@ -154,7 +154,7 @@ void CommandLine::parseLongOption (istream &is) { /** Returns all options that match the given long name. */ vector CommandLine::lookupOption (const string &optname) const { vector matches; - int len = optname.length(); + auto len = optname.length(); for (OptSectPair optsect : options()) { if (optsect.first->longName() == optname) { // exact match? matches.clear(); @@ -187,7 +187,7 @@ void CommandLine::help (ostream &os, int mode) const { string usage = _usage; int count=0; while (!usage.empty()) { - size_t pos = usage.find('\n'); + auto pos = usage.find('\n'); os << (count++ == 0 ? "Usage: " : " ") << PROGRAM_NAME << ' ' << usage.substr(0, pos) << '\n'; if (pos != string::npos) usage = usage.substr(pos+1); @@ -201,7 +201,7 @@ void CommandLine::help (ostream &os, int mode) const { unordered_map> linecols; size_t col1width=0; for (const OptSectPair &ospair : options()) { - size_t pos; + string::size_type pos; string line = ospair.first->helpline(); if ((pos = line.find('\t')) != string::npos) { linecols.emplace(ospair.first, pair(line.substr(0, pos), line.substr(pos+1))); diff --git a/dviware/dvisvgm/src/CMap.cpp b/dviware/dvisvgm/src/CMap.cpp index 5979745e4c..cedc1a7c57 100644 --- a/dviware/dvisvgm/src/CMap.cpp +++ b/dviware/dvisvgm/src/CMap.cpp @@ -66,7 +66,7 @@ string SegmentedCMap::getROString() const { bool SegmentedCMap::mapsToUnicode () const { vector encstrings = {"UTF8", "UTF16", "UCS2", "UCS4", "UCS32"}; for (const string &encstr : encstrings) { - size_t pos = _filename.find(encstr); + auto pos = _filename.find(encstr); if (pos != string::npos && (pos == 0 || _filename[pos-1] == '-')) return true; } diff --git a/dviware/dvisvgm/src/DVIReader.cpp b/dviware/dvisvgm/src/DVIReader.cpp index 094dd6e499..b8c5bc449e 100644 --- a/dviware/dvisvgm/src/DVIReader.cpp +++ b/dviware/dvisvgm/src/DVIReader.cpp @@ -460,7 +460,7 @@ const Font* DVIReader::defineFont (uint32_t fontnum, const string &name, uint32_ Font *font = fm.getFont(fontnum); if (!font && !name.empty()) { // font not registered yet? if (name[0] == '[') { // LuaTeX native font reference? - size_t last = name.rfind(']'); + auto last = name.rfind(']'); if (last != string::npos) { string path = name.substr(1, last-1); FontStyle style; diff --git a/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp b/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp index 077e030776..1acef64f0e 100644 --- a/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp +++ b/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp @@ -153,19 +153,19 @@ bool DvisvgmSpecialHandler::process (const string &prefix, istream &is, SpecialA static void expand_constants (string &str, SpecialActions &actions) { bool repl_bbox = true; while (repl_bbox) { - size_t pos = str.find("{?bbox "); + const auto pos = str.find("{?bbox "); if (pos == string::npos) repl_bbox = false; else { - size_t endpos = pos+7; + auto endpos = pos+7; while (endpos < str.length() && isalnum(str[endpos])) ++endpos; - if (str[endpos] == '}') { - BoundingBox &box=actions.bbox(str.substr(pos+7, endpos-pos-7)); + if (str[endpos] != '}') + repl_bbox = false; + else { + BoundingBox &box = actions.bbox(str.substr(pos+7, endpos-pos-7)); str.replace(pos, endpos-pos+1, box.svgViewBoxString()); } - else - repl_bbox = false; } } struct Constant { @@ -181,7 +181,7 @@ static void expand_constants (string &str, SpecialActions &actions) { }}; for (const Constant &constant : constants) { const string pattern = string("{?")+constant.name+"}"; - size_t pos = str.find(pattern); + auto pos = str.find(pattern); while (pos != string::npos) { str.replace(pos, strlen(constant.name)+3, constant.val); pos = str.find(pattern, pos+constant.val.length()); // look for further matches @@ -194,9 +194,9 @@ static void expand_constants (string &str, SpecialActions &actions) { * and replaces the substring by the computed value. * @param[in,out] str string to scan for expressions */ static void evaluate_expressions (string &str, const SpecialActions &actions) { - size_t left = str.find("{?("); // start position of expression macro + auto left = str.find("{?("); // start position of expression macro while (left != string::npos) { - size_t right = str.find(")}", left+2); // end position of expression macro + auto right = str.find(")}", left+2); // end position of expression macro if (right == string::npos) break; Calculator calc; @@ -232,7 +232,7 @@ void DvisvgmSpecialHandler::processRaw (InputReader &ir, SpecialActions &actions if (!xml.empty()) { evaluate_expressions(xml, actions); expand_constants(xml, actions); - _pageParser.parse(xml, actions); + _pageParser.parse(xml, actions.svgTree()); } } } @@ -244,7 +244,7 @@ void DvisvgmSpecialHandler::processRawDef (InputReader &ir, SpecialActions &acti if (!xml.empty()) { evaluate_expressions(xml, actions); expand_constants(xml, actions); - _defsParser.parse(xml, actions); + _defsParser.parse(xml, actions.svgTree()); } } } @@ -276,9 +276,9 @@ void DvisvgmSpecialHandler::processRawPut (InputReader &ir, SpecialActions &acti if ((type == 'P' || type == 'D') && !def.empty()) { expand_constants(def, actions); if (type == 'P') - _pageParser.parse(def, actions); + _pageParser.parse(def, actions.svgTree()); else { // type == 'D' - _defsParser.parse(def, actions); + _defsParser.parse(def, actions.svgTree()); type = 'L'; // locked } } @@ -414,8 +414,8 @@ void DvisvgmSpecialHandler::dviPreprocessingFinished () { void DvisvgmSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) { - _defsParser.finish(actions); - _pageParser.finish(actions); + _defsParser.finish(actions.svgTree()); + _pageParser.finish(actions.svgTree()); actions.bbox().unlock(); for (auto &strvecpair : _macros) { StringVector &vec = strvecpair.second; @@ -432,147 +432,3 @@ vector DvisvgmSpecialHandler::prefixes() const { vector pfx {"dvisvgm:"}; return pfx; } - -//////////////////////////////////////////////////////////////////////////////// - -/** Parses a fragment of XML code, creates corresponding XML nodes and adds them - * to the SVG tree. The code may be split and processed by several calls of this - * function. Incomplete chunks that can't be processed yet are stored and picked - * up again together with the next incoming XML fragment. If a call of this function - * is supposed to finish the parsing of an XML subtree, parameter 'finish' must be set. - * @param[in] xml XML fragment to parse - * @param[in] actions object providing the SVG tree functions - * @param[in] finish if true, no more XML is expected and parsing is finished */ -void DvisvgmSpecialHandler::XMLParser::parse (const string &xml, SpecialActions &actions, bool finish) { - // collect/extract an XML fragment that only contains complete tags - // incomplete tags are held back - _xmlbuf += xml; - size_t left=0; - try { - while (left != string::npos) { - size_t right = _xmlbuf.find('<', left); - if (left < right && left < _xmlbuf.length()) // plain text found? - (actions.svgTree().*_append)(util::make_unique(_xmlbuf.substr(left, right-left))); - if (right != string::npos) { - left = right; - if (_xmlbuf.compare(left, 9, "", left+9); - if (right == string::npos) { - if (finish) throw SpecialException("expected ']]>' at end of CDATA block"); - break; - } - (actions.svgTree().*_append)(util::make_unique(_xmlbuf.substr(left+9, right-left-9))); - right += 2; - } - else if (_xmlbuf.compare(left, 4, "", left+4); - if (right == string::npos) { - if (finish) throw SpecialException("expected '-->' at end of comment"); - break; - } - (actions.svgTree().*_append)(util::make_unique(_xmlbuf.substr(left+4, right-left-4))); - right += 2; - } - else if (_xmlbuf.compare(left, 2, "", left+2); - if (right == string::npos) { - if (finish) throw SpecialException("expected '?>' at end of processing instruction"); - break; - } - (actions.svgTree().*_append)(util::make_unique(_xmlbuf.substr(left, right-left+2))); - right++; - } - else if (_xmlbuf.compare(left, 2, "', left+2); - if (right == string::npos) { - if (finish) throw SpecialException("missing '>' at end of closing XML tag"); - break; - } - closeElement(_xmlbuf.substr(left+2, right-left-2), actions); - } - else { - right = _xmlbuf.find('>', left+1); - if (right == string::npos) { - if (finish) throw SpecialException("missing '>' or '/>' at end of opening XML tag"); - break; - } - openElement(_xmlbuf.substr(left+1, right-left-1), actions); - } - } - left = right; - if (right != string::npos) - left++; - } - } - catch (const SpecialException &e) { - _error = true; - throw; - } - if (left == string::npos) - _xmlbuf.clear(); - else - _xmlbuf.erase(0, left); -} - - -/** Processes an opening element tag. - * @param[in] tag tag without leading and trailing angle brackets */ -void DvisvgmSpecialHandler::XMLParser::openElement (const string &tag, SpecialActions &actions) { - StringInputBuffer ib(tag); - BufferInputReader ir(ib); - string name = ir.getString("/ \t\n\r"); - ir.skipSpace(); - auto elemNode = util::make_unique(name); - map attribs; - if (ir.parseAttributes(attribs, true, "\"'")) { - for (const auto &attrpair : attribs) - elemNode->addAttribute(attrpair.first, attrpair.second); - } - ir.skipSpace(); - if (ir.peek() == '/') // end of empty element tag - (actions.svgTree().*_append)(std::move(elemNode)); - else if (ir.peek() < 0) { // end of opening tag - _nameStack.push_back(name); - (actions.svgTree().*_pushContext)(std::move(elemNode)); - } - else - throw SpecialException("'>' or '/>' expected at end of opening tag <"+name); -} - - -/** Processes a closing element tag. - * @param[in] tag tag without leading and trailing angle brackets */ -void DvisvgmSpecialHandler::XMLParser::closeElement (const string &tag, SpecialActions &actions) { - StringInputBuffer ib(tag); - BufferInputReader ir(ib); - string name = ir.getString(" \t\n\r"); - ir.skipSpace(); - if (ir.peek() >= 0) - throw SpecialException("'>' expected at end of closing tag "); - if (_nameStack.back() != name) - throw SpecialException("expected but found "); - (actions.svgTree().*_popContext)(); - _nameStack.pop_back(); -} - - -/** Processes any remaining XML fragments, checks for missing closing tags, - * and resets the parser state. */ -void DvisvgmSpecialHandler::XMLParser::finish (SpecialActions &actions) { - if (!_xmlbuf.empty()) { - if (!_error) - parse("", actions, true); - _xmlbuf.clear(); - } - string tags; - while (!_nameStack.empty()) { - tags += ", "; - _nameStack.pop_back(); - } - if (!tags.empty() && !_error) { - tags.resize(tags.length()-2); - throw SpecialException("missing closing tag(s): "+tags); - } -} diff --git a/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp b/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp index f5ae43f411..7d1ad20cfe 100644 --- a/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp +++ b/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp @@ -26,6 +26,7 @@ #include #include #include "SpecialHandler.hpp" +#include "XMLParser.hpp" class InputReader; class SpecialActions; @@ -43,32 +44,6 @@ class XMLNode; #endif class DvisvgmSpecialHandler : public SpecialHandler { - class XMLParser { - using AppendFunc = void (SVGTree::*)(std::unique_ptr); - using PushFunc = void (SVGTree::*)(std::unique_ptr); - using PopFunc = void (SVGTree::*)(); - using NameStack = std::vector; - - public: - XMLParser (AppendFunc append, PushFunc push, PopFunc pop) - : _append(append), _pushContext(push), _popContext(pop) {} - - void parse (const std::string &xml, SpecialActions &actions, bool finish=false); - void finish (SpecialActions &actions); - - protected: - void openElement (const std::string &tag, SpecialActions &actions); - void closeElement (const std::string &tag, SpecialActions &actions); - - private: - AppendFunc _append; - PushFunc _pushContext; - PopFunc _popContext; - std::string _xmlbuf; - NameStack _nameStack; ///< names of nested elements still missing a closing tag - bool _error=false; - }; - using StringVector = std::vector; using MacroMap = std::unordered_map; diff --git a/dviware/dvisvgm/src/FileFinder.cpp b/dviware/dvisvgm/src/FileFinder.cpp index e7efe2786f..c95ab1023f 100644 --- a/dviware/dvisvgm/src/FileFinder.cpp +++ b/dviware/dvisvgm/src/FileFinder.cpp @@ -21,7 +21,6 @@ #include #ifdef MIKTEX #include "MiKTeXCom.hpp" - #include "utility.hpp" #else #ifdef KPSE_CXX_UNSAFE extern "C" { @@ -43,6 +42,7 @@ #include "Message.hpp" #include "MessageException.hpp" #include "Process.hpp" +#include "utility.hpp" std::string FileFinder::_argv0; std::string FileFinder::_progname; @@ -120,12 +120,12 @@ const char* FileFinder::findFile (const std::string &fname, const char *ftype) c if (ftype) ext = ftype; else { - size_t pos = fname.rfind('.'); + auto pos = fname.rfind('.'); if (pos == std::string::npos) return nullptr; // no extension and no file type => no search ext = fname.substr(pos+1); } - + ext = util::tolower(ext); #ifdef _WIN32 if (ext == "dll" || ext == "exe") return lookupExecutable(fname); @@ -140,7 +140,18 @@ const char* FileFinder::findFile (const std::string &fname, const char *ftype) c return _pathbuf.empty() ? nullptr : _pathbuf.c_str(); } try { - return _miktex->findFile(fname.c_str()); + if (!ftype) // no file type given? + return _miktex->findFile(fname.c_str()); // lookup given filename + // handle file type "ttf" similar to kpathsea and look for .ttf, .ttc, and .dfont + std::vector suffixes{ext}; + if (ext == "ttf") { + suffixes.emplace_back("ttc"); + suffixes.emplace_back("dfont"); + } + for (const auto &suffix : suffixes) { + if (const char *path = _miktex->findFile((fname+"."+suffix).c_str())) + return path; + } } catch (const MessageException &e) { return nullptr; @@ -181,8 +192,8 @@ const char* FileFinder::findFile (const std::string &fname, const char *ftype) c std::free(path); return _pathbuf.c_str(); } - return nullptr; #endif // !MIKTEX + return nullptr; } @@ -191,7 +202,7 @@ const char* FileFinder::findFile (const std::string &fname, const char *ftype) c * @param[in] fname name of file to look up * @return file path on success, 0 otherwise */ const char* FileFinder::findMappedFile (std::string fname) const { - size_t pos = fname.rfind('.'); + auto pos = fname.rfind('.'); if (pos == std::string::npos) return nullptr; const std::string ext = fname.substr(pos+1); // file extension @@ -214,7 +225,7 @@ const char* FileFinder::findMappedFile (std::string fname) const { * @param[in] fname name of file to build * @return file path on success, 0 otherwise */ const char* FileFinder::mktex (const std::string &fname) const { - size_t pos = fname.rfind('.'); + auto pos = fname.rfind('.'); if (!_enableMktex || pos == std::string::npos) return nullptr; diff --git a/dviware/dvisvgm/src/FilePath.cpp b/dviware/dvisvgm/src/FilePath.cpp index 998204b684..3c89c2c3ea 100644 --- a/dviware/dvisvgm/src/FilePath.cpp +++ b/dviware/dvisvgm/src/FilePath.cpp @@ -29,7 +29,7 @@ using namespace std; /** Removes redundant slashes from a given path. */ static string& single_slashes (string &str) { - size_t pos=0; + string::size_type pos=0; while ((pos = str.find("//", pos)) != string::npos) str.replace(pos, 2, "/"); return str; @@ -124,7 +124,7 @@ void FilePath::init (string path, bool isfile, string current_dir) { path = FileSystem::ensureForwardSlashes(path); #endif if (isfile) { - size_t pos = path.rfind('/'); + auto pos = path.rfind('/'); _fname = path.substr((pos == string::npos) ? 0 : pos+1); // remove filename from path if (pos == 0 && _fname.length() > 1) // file in root directory? @@ -163,12 +163,12 @@ void FilePath::add (const string &dir) { * location of a directory (and not of a file) an empty string * is returned. */ string FilePath::suffix () const { - size_t start = 0; + string::size_type start = 0; // ignore leading dots while (start < _fname.length() && _fname[start] == '.') start++; string sub = _fname.substr(start); - size_t pos = sub.rfind('.'); + auto pos = sub.rfind('.'); if (pos != string::npos && pos < sub.length()-1) return sub.substr(pos+1); return ""; @@ -194,7 +194,7 @@ void FilePath::suffix (const string &newSuffix) { * Example: FilePath("/a/b/c.def", true) == "c" */ string FilePath::basename () const { if (!_fname.empty()) { - size_t len = suffix().length(); + auto len = suffix().length(); if (len > 0) len++; // strip dot too return _fname.substr(0, _fname.length()-len); diff --git a/dviware/dvisvgm/src/Font.cpp b/dviware/dvisvgm/src/Font.cpp index 9627c421e2..2fa4ce5be9 100644 --- a/dviware/dvisvgm/src/Font.cpp +++ b/dviware/dvisvgm/src/Font.cpp @@ -54,7 +54,7 @@ const FontEncoding* Font::encoding () const { const FontMap::Entry* Font::fontMapEntry () const { string fontname = name(); - size_t pos = fontname.rfind('.'); + auto pos = fontname.rfind('.'); if (pos != string::npos) fontname = fontname.substr(0, pos); // strip extension return FontMap::instance().lookup(fontname); diff --git a/dviware/dvisvgm/src/FontManager.cpp b/dviware/dvisvgm/src/FontManager.cpp index 2262346c60..e85a9dc528 100644 --- a/dviware/dvisvgm/src/FontManager.cpp +++ b/dviware/dvisvgm/src/FontManager.cpp @@ -145,7 +145,7 @@ const VirtualFont* FontManager::getVF () const { static unique_ptr create_font (const string &filename, const string &fontname, int fontindex, uint32_t checksum, double dsize, double ssize) { string ext; if (const char *dot = strrchr(filename.c_str(), '.')) - ext = dot+1; + ext = util::tolower(dot+1); if (!ext.empty() && FileFinder::instance().lookup(filename)) { if (ext == "pfb") return PhysicalFont::create(fontname, checksum, dsize, ssize, PhysicalFont::Type::PFB); @@ -271,8 +271,13 @@ int FontManager::registerFont (uint32_t fontnum, string filename, int fontIndex, newfont = font->clone(ptsize, style, color); } else { - if (!FileSystem::exists(path)) - path = FileFinder::instance().lookup(filename, false); + if (!FileSystem::exists(path)) { + const char *fontFormats[] = {nullptr, "otf", "ttf"}; + for (const char *format : fontFormats) { + if ((path = FileFinder::instance().lookup(filename, format, false)) != nullptr) + break; + } + } if (path) { newfont.reset(new NativeFontImpl(path, fontIndex, ptsize, style, color)); newfont->findAndAssignBaseFontMap(); diff --git a/dviware/dvisvgm/src/FontMap.cpp b/dviware/dvisvgm/src/FontMap.cpp index e5c0caef5d..c9e2f8912c 100644 --- a/dviware/dvisvgm/src/FontMap.cpp +++ b/dviware/dvisvgm/src/FontMap.cpp @@ -125,13 +125,13 @@ bool FontMap::apply (const MapLine& mapline, char modechar) { * @return true if at least one of the given map files was found */ bool FontMap::read (const string &fname_seq) { bool found = false; - size_t left=0; + string::size_type left=0; while (left < fname_seq.length()) { const char modechar = fname_seq[left]; if (strchr("+-=", modechar)) left++; string fname; - size_t right = fname_seq.find(',', left); + auto right = fname_seq.find(',', left); if (right != string::npos) fname = fname_seq.substr(left, right-left); else { diff --git a/dviware/dvisvgm/src/FontWriter.cpp b/dviware/dvisvgm/src/FontWriter.cpp index b05f63c3df..0f7946f567 100644 --- a/dviware/dvisvgm/src/FontWriter.cpp +++ b/dviware/dvisvgm/src/FontWriter.cpp @@ -128,7 +128,7 @@ struct SFDActions : Glyph::IterationActions { } ostream &_os; - Glyph::Point _startPoint, _currentPoint; + Glyph::Point _startPoint, _currentPoint; }; @@ -174,8 +174,12 @@ static void writeSFD (const string &sfdname, const PhysicalFont &font, const set "SplineSet\n"; Glyph glyph; if (font.getGlyph(c, glyph, cb)) { - SFDActions actions(sfd); - glyph.iterate(actions, false); + if (glyph.empty()) + sfd << "0 0 m 0\n"; + else { + SFDActions actions(sfd); + glyph.iterate(actions, false); + } } sfd << "EndSplineSet\n" diff --git a/dviware/dvisvgm/src/GraphicsPathParser.hpp b/dviware/dvisvgm/src/GraphicsPathParser.hpp new file mode 100644 index 0000000000..0106f21a52 --- /dev/null +++ b/dviware/dvisvgm/src/GraphicsPathParser.hpp @@ -0,0 +1,299 @@ +/************************************************************************* +** GraphicsPathParser.hpp ** +** ** +** This file is part of dvisvgm -- a fast DVI to SVG converter ** +** Copyright (C) 2005-2022 Martin Gieseking ** +** ** +** 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 . ** +*************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include "GraphicsPath.hpp" +#include "MessageException.hpp" + + +struct GraphicsPathParserException : public MessageException { + explicit GraphicsPathParserException (const std::string &msg) : MessageException(msg) {} +}; + + +template +class GraphicsPathParser { + public: + GraphicsPath parse (std::istream &is); + + GraphicsPath parse (const std::string &str) { + std::istringstream iss(str); + return parse(iss); + } + + protected: + template + NumType parseNumberOfType (std::istream &is) const { + is >> std::ws; + NumType number; + is >> number; + if (is.fail()) + error("number expected", is); + is >> std::ws; + return number; + } + + T parseNumber (std::istream &is) const { + return parseNumberOfType(is); + } + + Pair parsePair (std::istream &is) { + T x = parseNumber(is); + skipCommaAndWhitespace(is); + T y = parseNumber(is); + return Pair(x, y); + } + + void skipCommaAndWhitespace (std::istream &is) { + is >> std::ws; + if (is.peek() == ',') { + is.get(); + is >> std::ws; + } + } + + void parseMoveTo (std::istream &is, bool relative); + void parseClosePath (); + void parseLineTo (std::istream &is, bool relative); + void parseHorizontalLineTo (std::istream &is, bool relative); + void parseVerticalLineTo (std::istream &is, bool relative); + void parseCubicTo (std::istream &is, bool relative); + void parseShortCubicTo (std::istream &is, bool relative); + void parseQuadraticTo (std::istream &is, bool relative); + void parseShortQuadraticTo (std::istream &is, bool relative); + void parseArcTo (std::istream &is, bool relative); + + void error (const std::string &msg, std::istream &is) const { + std::string postext; + if (_startpos >= 0) { // valid start position? + if (is) + postext = " at position "+ std::to_string(is.tellg()-_startpos); + else + postext = " (premature end of data)"; + } + throw GraphicsPathParserException(msg + postext); + } + + private: + std::istream::pos_type _startpos=0; ///< stream position where the parsing started + GraphicsPath *_path=nullptr; ///< path being parsed + Pair _startPoint; ///< start point of current subpath + Pair _currentPoint; ///< current point reached by last path command + Pair _prevCtrlPoint; ///< last control point of preceding curve command +}; + + +/** Creates a GraphicsPath object from a SVG path data string read from a given input stream. + * @param[in] is stream to read from + * @return GraphicsPath object created from the SVG path description + * @throw GraphicsPathParserException if the path data contains syntax error */ +template +GraphicsPath GraphicsPathParser::parse (std::istream &is) { + GraphicsPath path; + _path = &path; + _startpos = is.tellg(); + _currentPoint = _startPoint = _prevCtrlPoint = Pair(0, 0); + int cmd=0; + while (!is.eof()) { + is >> std::ws; + if (is.peek() < 0) + break; + if (isalpha(is.peek())) + cmd = is.get(); + else { + // further set of parameters appended to preceding command (command letter omitted) + skipCommaAndWhitespace(is); + // subsequent coordinate pairs of a "moveto" command lead to implicit "lineto" commands + // https://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands + if (cmd == 'M') + cmd = 'L'; + else if (cmd == 'm') + cmd = 'l'; + } + int lower_cmd = std::tolower(cmd); + bool relative = (cmd == lower_cmd); + switch (lower_cmd) { + case 'm': parseMoveTo(is, relative); break; + case 'z': parseClosePath(); break; + case 'l': parseLineTo(is, relative); break; + case 'h': parseHorizontalLineTo(is, relative); break; + case 'v': parseVerticalLineTo(is, relative); break; + case 'c': parseCubicTo(is, relative); break; + case 's': parseShortCubicTo(is, relative); break; + case 'q': parseQuadraticTo(is, relative); break; + case 't': parseShortQuadraticTo(is, relative); break; + case 'a': parseArcTo(is, relative); break; + case 0 : error("missing command at beginning of SVG path", is); + default : error("invalid SVG path command '"+std::string(1, char(cmd))+"'", is); + } + if (strchr("csqt", lower_cmd) == nullptr) // not a Bézier curve command? + _prevCtrlPoint = _currentPoint; // => no control point, use current point + } + _path = nullptr; + return path; +} + + +/** Parses a single parameter pair of a "moveto" command. */ +template +void GraphicsPathParser::parseMoveTo (std::istream &is, bool relative) { + Pair p = parsePair(is); + if (!relative || _path->empty()) + _currentPoint = p; + else + _currentPoint += p; + _path->moveto(_currentPoint); + _startPoint = _currentPoint; +} + + +/** Handles a "closepath" command. */ +template +void GraphicsPathParser::parseClosePath () { + _path->closepath(); + _currentPoint = _startPoint; +} + + +/** Parses a single parameter pair of a "lineto" command. */ +template +void GraphicsPathParser::parseLineTo (std::istream &is, bool relative) { + Pair p = parsePair(is); + if (relative) + _currentPoint += p; + else + _currentPoint = p; + _path->lineto(_currentPoint); +} + + +/** Parses a single parameter of a horizontal "lineto" command. */ +template +void GraphicsPathParser::parseHorizontalLineTo (std::istream &is, bool relative) { + T x = parseNumber(is); + if (relative) + _currentPoint += Pair(x, 0); + else + _currentPoint = Pair(x, _currentPoint.y()); + _path->lineto(_currentPoint); +} + + +/** Parses a single parameter of a vertical "lineto" command. */ +template +void GraphicsPathParser::parseVerticalLineTo (std::istream &is, bool relative) { + T y = parseNumber(is); + if (relative) + _currentPoint += Pair(0, y); + else + _currentPoint = Pair(_currentPoint.x(), y); + _path->lineto(_currentPoint); +} + + +/** Parses a single parameter set a "cubicto" (cubic Bézier curve) command. */ +template +void GraphicsPathParser::parseCubicTo (std::istream &is, bool relative) { + Pair p1 = parsePair(is); + Pair p2 = parsePair(is); + Pair pe = parsePair(is); + if (!relative) + _currentPoint = pe; + else { + p1 += _currentPoint; + p2 += _currentPoint; + _currentPoint += pe; + } + _path->cubicto(p1, p2, _currentPoint); + _prevCtrlPoint = p2; +} + + +/** Parses a single parameter set a shorthand "cubicto" (cubic Bézier curve) command. */ +template +void GraphicsPathParser::parseShortCubicTo (std::istream &is, bool relative) { + Pair p1 = _prevCtrlPoint + (_currentPoint-_prevCtrlPoint)*T(2); + Pair p2 = parsePair(is); + Pair pe = parsePair(is); + if (!relative) + _currentPoint = pe; + else { + p2 += _currentPoint; + _currentPoint += pe; + } + _path->cubicto(p1, p2, _currentPoint); + _prevCtrlPoint = p2; +} + + +/** Parses a single parameter set a "quadto" (quadratic Bézier curve) command. */ +template +void GraphicsPathParser::parseQuadraticTo (std::istream &is, bool relative) { + Pair p1 = parsePair(is); + Pair pe = parsePair(is); + if (!relative) + _currentPoint = pe; + else { + p1 += _currentPoint; + _currentPoint += pe; + } + _path->quadto(p1, _currentPoint); + _prevCtrlPoint = p1; +} + + +/** Parses a single parameter set a shorthand "quadto" (quadratic Bézier curve) command. */ +template +void GraphicsPathParser::parseShortQuadraticTo (std::istream &is, bool relative) { + Pair p1 = _prevCtrlPoint + (_currentPoint-_prevCtrlPoint)*T(2); + Pair pe = parsePair(is); + if (relative) + _currentPoint += pe; + else + _currentPoint = pe; + _path->quadto(p1, _currentPoint); + _prevCtrlPoint = p1; +} + + +/** Parses a single parameter set an "arcto" command. */ +template +void GraphicsPathParser::parseArcTo (std::istream &is, bool relative) { + Pair r = parsePair(is); + double xrot = parseNumberOfType(is); + int largeArgFlag = parseNumberOfType(is); + if (largeArgFlag != 0 && largeArgFlag != 1) + error("large-arc-flag must be 0 or 1", is); + int sweepFlag = parseNumberOfType(is); + if (sweepFlag != 0 && sweepFlag != 1) + error("sweep-flag must be 0 or 1", is); + T x = parseNumber(is); + T y = parseNumber(is); + Pair p(x, y); + if (relative) + p += _currentPoint; + _currentPoint = p; + _path->arcto(r.x(), r.y(), xrot, bool(largeArgFlag), bool(sweepFlag), _currentPoint); +} diff --git a/dviware/dvisvgm/src/HashFunction.hpp b/dviware/dvisvgm/src/HashFunction.hpp index 15f7725543..ecd938bcbb 100644 --- a/dviware/dvisvgm/src/HashFunction.hpp +++ b/dviware/dvisvgm/src/HashFunction.hpp @@ -28,7 +28,7 @@ /** Common base class for all hash functions. */ class HashFunction { - public: + public: virtual ~HashFunction () =default; virtual int digestSize () const =0; virtual void reset () =0; diff --git a/dviware/dvisvgm/src/HyperlinkManager.cpp b/dviware/dvisvgm/src/HyperlinkManager.cpp index 2371234499..65ee534a32 100644 --- a/dviware/dvisvgm/src/HyperlinkManager.cpp +++ b/dviware/dvisvgm/src/HyperlinkManager.cpp @@ -229,7 +229,7 @@ void HyperlinkManager::createViews (unsigned pageno, SpecialActions &actions) { bool HyperlinkManager::setLinkMarker (const string &marker) { string type; // "none", "box", "line", or a background color specifier string color; // optional line color specifier - size_t seppos = marker.find(':'); + auto seppos = marker.find(':'); if (seppos == string::npos) type = marker; else { diff --git a/dviware/dvisvgm/src/HyperlinkManager.hpp b/dviware/dvisvgm/src/HyperlinkManager.hpp index 3e7ba17373..99151db0e5 100644 --- a/dviware/dvisvgm/src/HyperlinkManager.hpp +++ b/dviware/dvisvgm/src/HyperlinkManager.hpp @@ -42,7 +42,7 @@ class HyperlinkManager { enum class ColorSource {DEFAULT, LINKMARKER, STATIC}; using NamedAnchors = std::unordered_map; - public: + public: HyperlinkManager (const HyperlinkManager&) =delete; HyperlinkManager (HyperlinkManager&&) =delete; void addHrefAnchor (const std::string &uri); @@ -68,7 +68,7 @@ class HyperlinkManager { static Color LINK_LINECOLOR; ///< line color if linkmark type is LM_LINE or LM_BOX static ColorSource COLORSOURCE; ///< if true, LINK_LINECOLOR is applied - private: + private: AnchorType _anchorType=AnchorType::NONE; ///< type of active anchor int _depthThreshold=0; ///< break anchor box if the DVI stack depth underruns this threshold double _linewidth=-1; ///< line width of link marker (-1 => compute individual value per link) diff --git a/dviware/dvisvgm/src/Makefile.am b/dviware/dvisvgm/src/Makefile.am index df03cea805..bd2cfd3a43 100644 --- a/dviware/dvisvgm/src/Makefile.am +++ b/dviware/dvisvgm/src/Makefile.am @@ -87,6 +87,7 @@ libdvisvgm_la_SOURCES = \ Glyph.hpp \ GlyphTracerMessages.hpp \ GraphicsPath.hpp \ + GraphicsPathParser.hpp \ HashFunction.hpp HashFunction.cpp \ HtmlSpecialHandler.hpp HtmlSpecialHandler.cpp \ HyperlinkManager.hpp HyperlinkManager.cpp \ @@ -157,6 +158,7 @@ libdvisvgm_la_SOURCES = \ windows.hpp \ XMLDocument.hpp XMLDocument.cpp \ XMLNode.hpp XMLNode.cpp \ + XMLParser.hpp XMLParser.cpp \ XMLString.hpp XMLString.cpp \ XXHashFunction.hpp \ ZLibOutputStream.hpp diff --git a/dviware/dvisvgm/src/Makefile.in b/dviware/dvisvgm/src/Makefile.in index 0ef19f52c2..65df7c6162 100644 --- a/dviware/dvisvgm/src/Makefile.in +++ b/dviware/dvisvgm/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.2 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -114,12 +114,7 @@ bin_PROGRAMS = dvisvgm$(EXEEXT) @ENABLE_WOFF_TRUE@am__append_13 = $(TTFAUTOHINT_LIBS) subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_code_coverage.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -154,8 +149,9 @@ am__libdvisvgm_la_SOURCES_DIST = AGLTable.hpp BasicDVIReader.hpp \ FontStyle.hpp FontWriter.hpp FontWriter.cpp GFGlyphTracer.hpp \ GFGlyphTracer.cpp GFReader.hpp GFReader.cpp GFTracer.hpp \ GFTracer.cpp Ghostscript.hpp Ghostscript.cpp Glyph.hpp \ - GlyphTracerMessages.hpp GraphicsPath.hpp HashFunction.hpp \ - HashFunction.cpp HtmlSpecialHandler.hpp HtmlSpecialHandler.cpp \ + GlyphTracerMessages.hpp GraphicsPath.hpp \ + GraphicsPathParser.hpp HashFunction.hpp HashFunction.cpp \ + HtmlSpecialHandler.hpp HtmlSpecialHandler.cpp \ HyperlinkManager.hpp HyperlinkManager.cpp ImageToSVG.hpp \ ImageToSVG.cpp InputBuffer.hpp InputBuffer.cpp InputReader.hpp \ InputReader.cpp JFM.hpp JFM.cpp Length.hpp Length.cpp \ @@ -193,8 +189,9 @@ am__libdvisvgm_la_SOURCES_DIST = AGLTable.hpp BasicDVIReader.hpp \ Unicode.cpp utility.hpp utility.cpp VectorIterator.hpp \ VectorStream.hpp VFActions.hpp VFReader.hpp VFReader.cpp \ windows.hpp XMLDocument.hpp XMLDocument.cpp XMLNode.hpp \ - XMLNode.cpp XMLString.hpp XMLString.cpp XXHashFunction.hpp \ - ZLibOutputStream.hpp ffwrapper.c ffwrapper.h + XMLNode.cpp XMLParser.hpp XMLParser.cpp XMLString.hpp \ + XMLString.cpp XXHashFunction.hpp ZLibOutputStream.hpp \ + ffwrapper.c ffwrapper.h @ENABLE_WOFF_TRUE@am__objects_1 = ffwrapper.lo am_libdvisvgm_la_OBJECTS = BasicDVIReader.lo Bezier.lo \ BgColorSpecialHandler.lo Bitmap.lo BoundingBox.lo \ @@ -222,7 +219,8 @@ am_libdvisvgm_la_OBJECTS = BasicDVIReader.lo Bezier.lo \ TensorProductPatch.lo Terminal.lo TFM.lo ToUnicodeMap.lo \ TpicSpecialHandler.lo TriangularPatch.lo TrueTypeFont.lo \ TTFAutohint.lo Unicode.lo utility.lo VFReader.lo \ - XMLDocument.lo XMLNode.lo XMLString.lo $(am__objects_1) + XMLDocument.lo XMLNode.lo XMLParser.lo XMLString.lo \ + $(am__objects_1) libdvisvgm_la_OBJECTS = $(am_libdvisvgm_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -303,9 +301,9 @@ am__depfiles_remade = ./$(DEPDIR)/BasicDVIReader.Plo \ ./$(DEPDIR)/TriangularPatch.Plo ./$(DEPDIR)/TrueTypeFont.Plo \ ./$(DEPDIR)/Unicode.Plo ./$(DEPDIR)/VFReader.Plo \ ./$(DEPDIR)/XMLDocument.Plo ./$(DEPDIR)/XMLNode.Plo \ - ./$(DEPDIR)/XMLString.Plo ./$(DEPDIR)/dvisvgm.Po \ - ./$(DEPDIR)/ffwrapper.Plo ./$(DEPDIR)/psdefs.Plo \ - ./$(DEPDIR)/utility.Plo + ./$(DEPDIR)/XMLParser.Plo ./$(DEPDIR)/XMLString.Plo \ + ./$(DEPDIR)/dvisvgm.Po ./$(DEPDIR)/ffwrapper.Plo \ + ./$(DEPDIR)/psdefs.Plo ./$(DEPDIR)/utility.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -383,8 +381,6 @@ am__define_uniq_tagged_files = \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/../libs/defs.am $(srcdir)/Makefile.in \ $(srcdir)/version.hpp.in $(top_srcdir)/depcomp @@ -438,6 +434,8 @@ CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ @@ -453,8 +451,10 @@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ +ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +FILECMD = @FILECMD@ FREETYPE_CFLAGS = @FREETYPE_CFLAGS@ FREETYPE_LIBS = @FREETYPE_LIBS@ GCOV = @GCOV@ @@ -611,8 +611,9 @@ libdvisvgm_la_SOURCES = AGLTable.hpp BasicDVIReader.hpp \ FontStyle.hpp FontWriter.hpp FontWriter.cpp GFGlyphTracer.hpp \ GFGlyphTracer.cpp GFReader.hpp GFReader.cpp GFTracer.hpp \ GFTracer.cpp Ghostscript.hpp Ghostscript.cpp Glyph.hpp \ - GlyphTracerMessages.hpp GraphicsPath.hpp HashFunction.hpp \ - HashFunction.cpp HtmlSpecialHandler.hpp HtmlSpecialHandler.cpp \ + GlyphTracerMessages.hpp GraphicsPath.hpp \ + GraphicsPathParser.hpp HashFunction.hpp HashFunction.cpp \ + HtmlSpecialHandler.hpp HtmlSpecialHandler.cpp \ HyperlinkManager.hpp HyperlinkManager.cpp ImageToSVG.hpp \ ImageToSVG.cpp InputBuffer.hpp InputBuffer.cpp InputReader.hpp \ InputReader.cpp JFM.hpp JFM.cpp Length.hpp Length.cpp \ @@ -650,8 +651,9 @@ libdvisvgm_la_SOURCES = AGLTable.hpp BasicDVIReader.hpp \ Unicode.cpp utility.hpp utility.cpp VectorIterator.hpp \ VectorStream.hpp VFActions.hpp VFReader.hpp VFReader.cpp \ windows.hpp XMLDocument.hpp XMLDocument.cpp XMLNode.hpp \ - XMLNode.cpp XMLString.hpp XMLString.cpp XXHashFunction.hpp \ - ZLibOutputStream.hpp $(am__append_8) + XMLNode.cpp XMLParser.hpp XMLParser.cpp XMLString.hpp \ + XMLString.cpp XXHashFunction.hpp ZLibOutputStream.hpp \ + $(am__append_8) libdvisvgm_la_LIBADD = optimizer/liboptimizer.la EXTRA_DIST = options.xml options.dtd iapi.h ierrors.h MiKTeXCom.hpp MiKTeXCom.cpp AM_CFLAGS = -Wall $(ZLIB_CFLAGS) $(CODE_COVERAGE_CFLAGS) \ @@ -676,9 +678,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/../libs/defs.am $(am__co exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign src/Makefile + $(AUTOMAKE) --gnu src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -866,6 +868,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/VFReader.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLDocument.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLNode.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLParser.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLString.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dvisvgm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ffwrapper.Plo@am__quote@ # am--include-marker @@ -1030,7 +1033,6 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -1227,6 +1229,7 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/VFReader.Plo -rm -f ./$(DEPDIR)/XMLDocument.Plo -rm -f ./$(DEPDIR)/XMLNode.Plo + -rm -f ./$(DEPDIR)/XMLParser.Plo -rm -f ./$(DEPDIR)/XMLString.Plo -rm -f ./$(DEPDIR)/dvisvgm.Po -rm -f ./$(DEPDIR)/ffwrapper.Plo @@ -1370,6 +1373,7 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/VFReader.Plo -rm -f ./$(DEPDIR)/XMLDocument.Plo -rm -f ./$(DEPDIR)/XMLNode.Plo + -rm -f ./$(DEPDIR)/XMLParser.Plo -rm -f ./$(DEPDIR)/XMLString.Plo -rm -f ./$(DEPDIR)/dvisvgm.Po -rm -f ./$(DEPDIR)/ffwrapper.Plo diff --git a/dviware/dvisvgm/src/MapLine.cpp b/dviware/dvisvgm/src/MapLine.cpp index 531550788d..0c432cab23 100644 --- a/dviware/dvisvgm/src/MapLine.cpp +++ b/dviware/dvisvgm/src/MapLine.cpp @@ -37,7 +37,7 @@ MapLine::MapLine (istream &is) : MapLine() { MapLine::MapLine (string str) : MapLine() { - size_t pos = str.rfind('\n'); + auto pos = str.rfind('\n'); if (pos != string::npos) str = str.substr(0, pos); parse(str.c_str()); @@ -75,9 +75,9 @@ bool MapLine::isDVIPSFormat (const char *line) const { * @param[out] sfdname name of subfont definition * @return true on success */ static bool split_fontname (string &fontname, string &sfdname) { - size_t pos1; // index of first '@' + string::size_type pos1; // index of first '@' if ((pos1 = fontname.find('@')) != string::npos && pos1 > 0) { - size_t pos2; // index of second '@' + string::size_type pos2; // index of second '@' if ((pos2 = fontname.find('@', pos1+1)) != string::npos && pos2 > pos1+1) { sfdname = fontname.substr(pos1+1, pos2-pos1-1); fontname = fontname.substr(0, pos1) + fontname.substr(pos2+1); diff --git a/dviware/dvisvgm/src/MetafontWrapper.cpp b/dviware/dvisvgm/src/MetafontWrapper.cpp index bf1bbf1ba4..03c3e2b5ef 100644 --- a/dviware/dvisvgm/src/MetafontWrapper.cpp +++ b/dviware/dvisvgm/src/MetafontWrapper.cpp @@ -118,7 +118,7 @@ int MetafontWrapper::getResolution (const string &mfMessage) const { string line = buf; if (line.substr(0, 18) == "Output written on ") { line = line.substr(18); - size_t pos = line.find(' '); + auto pos = line.find(' '); line = line.substr(0, pos); pos = line.rfind('.'); if (pos != string::npos && line.substr(line.length()-2) == "gf") { diff --git a/dviware/dvisvgm/src/MiKTeXCom.cpp b/dviware/dvisvgm/src/MiKTeXCom.cpp index 7da0342f9f..6f9d6f6e13 100644 --- a/dviware/dvisvgm/src/MiKTeXCom.cpp +++ b/dviware/dvisvgm/src/MiKTeXCom.cpp @@ -66,8 +66,7 @@ string MiKTeXCom::getVersion () { MiKTeXSetupInfo info; _session->GetMiKTeXSetupInfo(&info); #endif - _bstr_t version = info.version; - return string(version); + return to_string(info.series/100)+"."+ to_string(info.series%100); } @@ -105,6 +104,6 @@ const char* MiKTeXCom::findFile (const char *fname) { return nullptr; } catch (_com_error &e) { - throw MessageException((const char*)e.Description()); + throw MessageException(static_cast(e.Description())); } } diff --git a/dviware/dvisvgm/src/Opacity.hpp b/dviware/dvisvgm/src/Opacity.hpp index 3d211b82bc..84880e7b66 100644 --- a/dviware/dvisvgm/src/Opacity.hpp +++ b/dviware/dvisvgm/src/Opacity.hpp @@ -48,11 +48,11 @@ class Opacity { BM_HUE, BM_SATURATION, BM_COLOR, BM_LUMINOSITY }; - public: + public: Opacity () =default; Opacity (OpacityAlpha fillalpha, OpacityAlpha strokealpha, BlendMode bm) : _fillalpha(fillalpha), _strokealpha(strokealpha), _blendMode(bm) {} Opacity (OpacityAlpha fillalpha, OpacityAlpha strokealpha) : Opacity(fillalpha, strokealpha, BM_NORMAL) {} - explicit Opacity (BlendMode bm) : _blendMode(bm) {} + explicit Opacity (BlendMode bm) : _blendMode(bm) {} OpacityAlpha& fillalpha () {return _fillalpha;} OpacityAlpha& strokealpha () {return _strokealpha;} const OpacityAlpha& fillalpha () const {return _fillalpha;} @@ -66,7 +66,7 @@ class Opacity { bool operator == (const Opacity &opacity) const; bool operator != (const Opacity &opacity) const; - private: + private: OpacityAlpha _fillalpha; OpacityAlpha _strokealpha; BlendMode _blendMode=BM_NORMAL; diff --git a/dviware/dvisvgm/src/PDFParser.cpp b/dviware/dvisvgm/src/PDFParser.cpp index 04b9fe661c..5d00a5da58 100644 --- a/dviware/dvisvgm/src/PDFParser.cpp +++ b/dviware/dvisvgm/src/PDFParser.cpp @@ -207,7 +207,7 @@ static bool parse_number (const string &str, NumberVariant &nv) { if (str.empty()) return false; try { - size_t dotpos = str.find('.'); + auto dotpos = str.find('.'); if (dotpos == string::npos) { // not a real number? size_t count; nv = NumberVariant(stoi(str, &count, 10)); // then try to convert str to int @@ -218,7 +218,7 @@ static bool parse_number (const string &str, NumberVariant &nv) { // which is not allowed in PDF real number constants if (!postdot.empty() && isdigit(postdot[0])) { size_t count; - stoi(postdot, &count, 10); + static_cast(stoi(postdot, &count, 10)); if (count != postdot.length()) return false; } @@ -310,7 +310,7 @@ static PDFObjectRef parse_object_ref (vector &objects) { /** Replaces all occurences of "#XX" (XX are two hex digits) with the corresponding character. */ static string& subst_numeric_chars (string &str) { - for (size_t pos=str.find('#'); pos != string::npos; pos=str.find('#', pos+1)) { + for (auto pos=str.find('#'); pos != string::npos; pos=str.find('#', pos+1)) { if (pos > str.length()-3) throw PDFException("sign character # must be followed by two hexadecimal digits"); if (isxdigit(str[pos+1]) && isxdigit(str[pos+2])) { diff --git a/dviware/dvisvgm/src/PageSize.cpp b/dviware/dvisvgm/src/PageSize.cpp index 2fcdd917fb..a1d29bfcb6 100644 --- a/dviware/dvisvgm/src/PageSize.cpp +++ b/dviware/dvisvgm/src/PageSize.cpp @@ -96,7 +96,7 @@ void PageSize::resize (string name) { name = util::tolower(name); // extract optional suffix - size_t pos = name.rfind('-'); + auto pos = name.rfind('-'); bool landscape = false; if (pos != string::npos) { string suffix = name.substr(pos); diff --git a/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp b/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp index 42a9fcbc1a..557bea8679 100644 --- a/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp +++ b/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp @@ -28,7 +28,7 @@ void PapersizeSpecialHandler::preprocess (const string&, std::istream &is, Speci string params; is >> params; Length w, h; - const size_t splitpos = params.find(','); + const auto splitpos = params.find(','); try { if (splitpos == string::npos) { w.set(params); diff --git a/dviware/dvisvgm/src/PsSpecialHandler.cpp b/dviware/dvisvgm/src/PsSpecialHandler.cpp index 45328fbd18..7ece9b16ba 100644 --- a/dviware/dvisvgm/src/PsSpecialHandler.cpp +++ b/dviware/dvisvgm/src/PsSpecialHandler.cpp @@ -190,7 +190,7 @@ void PsSpecialHandler::preprocess (const string &prefix, istream &is, SpecialAct static string filename_suffix (const string &fname) { string ret; - size_t pos = fname.rfind('.'); + auto pos = fname.rfind('.'); if (pos != string::npos) ret = util::tolower(fname.substr(pos+1)); return ret; diff --git a/dviware/dvisvgm/src/SVGElement.hpp b/dviware/dvisvgm/src/SVGElement.hpp index c326b79f87..d1103a8551 100644 --- a/dviware/dvisvgm/src/SVGElement.hpp +++ b/dviware/dvisvgm/src/SVGElement.hpp @@ -33,7 +33,7 @@ class SVGElement : public XMLElement { enum LineCap {LC_BUTT, LC_ROUND, LC_SQUARE}; enum LineJoin {LJ_BEVEL, LJ_MITER, LJ_ROUND}; - public: + public: explicit SVGElement (std::string name) : XMLElement(std::move(name)) {} explicit SVGElement (const XMLElement &node) : XMLElement(node) {} explicit SVGElement (XMLElement &&node) noexcept : XMLElement(std::move(node)) {} diff --git a/dviware/dvisvgm/src/SVGOutput.cpp b/dviware/dvisvgm/src/SVGOutput.cpp index 6ba41f1c7d..013dd8f8f9 100644 --- a/dviware/dvisvgm/src/SVGOutput.cpp +++ b/dviware/dvisvgm/src/SVGOutput.cpp @@ -108,7 +108,7 @@ FilePath SVGOutput::filepath (int page, int numPages, const HashTriple &hashes) string SVGOutput::expandFormatString (string str, int page, int numPages, const HashTriple &hashes) const { string result; while (!str.empty()) { - size_t pos = str.find('%'); + auto pos = str.find('%'); if (pos == string::npos) { result += str; str.clear(); @@ -150,7 +150,7 @@ string SVGOutput::expandFormatString (string str, int page, int numPages, const result += oss.str(); break; case '(': { - size_t endpos = str.find(')', pos); + auto endpos = str.find(')', pos); if (endpos == string::npos) throw MessageException("missing ')' in filename pattern"); else if (endpos-pos-1 > 1) { diff --git a/dviware/dvisvgm/src/SVGTree.cpp b/dviware/dvisvgm/src/SVGTree.cpp index f4cd9a85ef..1fed40b443 100644 --- a/dviware/dvisvgm/src/SVGTree.cpp +++ b/dviware/dvisvgm/src/SVGTree.cpp @@ -92,7 +92,7 @@ void SVGTree::setFont (int num, const Font &font) { bool SVGTree::setFontFormat (string formatstr) { - size_t pos = formatstr.find(','); + auto pos = formatstr.find(','); string opt; if (pos != string::npos) { opt = formatstr.substr(pos+1); diff --git a/dviware/dvisvgm/src/TFM.cpp b/dviware/dvisvgm/src/TFM.cpp index 37da112757..a5b88f180d 100644 --- a/dviware/dvisvgm/src/TFM.cpp +++ b/dviware/dvisvgm/src/TFM.cpp @@ -43,8 +43,8 @@ static void read_words (StreamReader &reader, vector &v, unsigned n) { TFM::TFM (istream &is) : _checksum(0), _firstChar(0), _lastChar(0), _designSize(0), _ascent(0), _descent(0) { - if (!is) - return; + if (!is) + return; is.seekg(0); StreamReader reader(is); uint16_t lf = uint16_t(reader.readUnsigned(2)); // length of entire file in 4 byte words diff --git a/dviware/dvisvgm/src/Unicode.cpp b/dviware/dvisvgm/src/Unicode.cpp index af35bdf9bd..2293be0399 100644 --- a/dviware/dvisvgm/src/Unicode.cpp +++ b/dviware/dvisvgm/src/Unicode.cpp @@ -189,7 +189,7 @@ static const char* get_suffix (const string &name) { "small", "swash", "superior", "inferior", "numerator", "denominator", "oldstyle", "display", "text", "big", "bigg", "Big", "Bigg", 0 }; - size_t pos = name.rfind('.'); + auto pos = name.rfind('.'); if (pos != string::npos) { string suffix = name.substr(pos+1); for (const char **p=suffixes; *p; p++) diff --git a/dviware/dvisvgm/src/XMLNode.cpp b/dviware/dvisvgm/src/XMLNode.cpp index 743da58b93..0f2602f8f0 100644 --- a/dviware/dvisvgm/src/XMLNode.cpp +++ b/dviware/dvisvgm/src/XMLNode.cpp @@ -103,6 +103,19 @@ void XMLElement::clear () { } +/** Returns true if element has no child nodes or, alternatively, only whitespace children. + * @param[in] ignoreWhitespace if true and if there are only whitespace children, the functions returns true */ +bool XMLElement::empty (bool ignoreWhitespace) const { + if (!_firstChild || !ignoreWhitespace) + return _firstChild == nullptr; + for (const XMLNode *node : *this) { + if (!node->toWSNode()) + return false; + } + return true; +} + + void XMLElement::addAttribute (const string &name, const string &value) { if (Attribute *attr = getAttribute(name)) attr->value = value; @@ -363,7 +376,7 @@ ostream& XMLElement::write (ostream &os) const { os << attrib.name << "='" << attrib.value << '\''; else { os << attrib.name.substr(1) << "='"; - size_t pos = attrib.value.find("base64,"); + auto pos = attrib.value.find("base64,"); if (pos == string::npos) os << attrib.value; else { @@ -434,6 +447,29 @@ const XMLElement::Attribute* XMLElement::getAttribute (const string &name) const } +/** Checks whether an SVG attribute A of an element E implicitly propagates its properties + * to all child elements of E that don't specify A. For now we only consider a subset of + * the inheritable properties. + * @return true if the attribute is inheritable */ +bool XMLElement::Attribute::inheritable () const { + // subset of inheritable properties listed on https://www.w3.org/TR/SVG11/propidx.html + // 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 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(std::begin(names), std::end(names), name, [](const string &name1, const string &name2) { + return name1 < name2; + }); +} + + ////////////////////// void XMLText::append (unique_ptr node) { diff --git a/dviware/dvisvgm/src/XMLNode.hpp b/dviware/dvisvgm/src/XMLNode.hpp index f1880352f2..fc34f05db8 100644 --- a/dviware/dvisvgm/src/XMLNode.hpp +++ b/dviware/dvisvgm/src/XMLNode.hpp @@ -118,6 +118,7 @@ class XMLElement : public XMLNode { public: struct Attribute { Attribute (std::string nam, std::string val) : name(std::move(nam)), value(std::move(val)) {} + bool inheritable () const; std::string name; std::string value; }; @@ -146,7 +147,7 @@ class XMLElement : public XMLNode { XMLNode* firstChild () const {return _firstChild.get();} XMLNode* lastChild () const {return _lastChild;} std::ostream& write (std::ostream &os) const override; - bool empty () const {return !_firstChild;} + bool empty (bool ignoreWhitespace=false) const; Attributes& attributes () {return _attributes;} const Attributes& attributes () const {return _attributes;} XMLNodeIterator begin () {return XMLNodeIterator(_firstChild.get());} diff --git a/dviware/dvisvgm/src/XMLParser.cpp b/dviware/dvisvgm/src/XMLParser.cpp new file mode 100644 index 0000000000..166a49e0cf --- /dev/null +++ b/dviware/dvisvgm/src/XMLParser.cpp @@ -0,0 +1,182 @@ +/************************************************************************* +** XMLParser.cpp ** +** ** +** This file is part of dvisvgm -- a fast DVI to SVG converter ** +** Copyright (C) 2005-2022 Martin Gieseking ** +** ** +** 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 . ** +*************************************************************************/ + +#include "InputReader.hpp" +#include "GraphicsPathParser.hpp" +#include "XMLParser.hpp" + +using namespace std; + +/** Parses a fragment of XML code, creates corresponding XML nodes and adds them + * to an SVG tree. The code may be split and processed by several calls of this + * function. Incomplete chunks that can't be processed yet are stored and picked + * up again together with the next incoming XML fragment. If a call of this function + * is supposed to finish the parsing of an XML subtree, parameter 'finish' must be set. + * @param[in] xml XML fragment to parse + * @param[in] svgTree the parsed nodes are added to this SVG tree + * @param[in] finish if true, no more XML is expected and parsing is finished */ +void XMLParser::parse (const string &xml, SVGTree &svgTree, bool finish) { + // collect/extract an XML fragment that only contains complete tags + // incomplete tags are held back + _xmlbuf += xml; + string::size_type left=0; + try { + while (left != string::npos) { + auto right = _xmlbuf.find('<', left); + if (left < right && left < _xmlbuf.length()) // plain text found? + (svgTree.*_append)(util::make_unique(_xmlbuf.substr(left, right - left))); + if (right != string::npos) { + left = right; + if (_xmlbuf.compare(left, 9, "", left+9); + if (right == string::npos) { + if (finish) throw XMLParserException("expected ']]>' at end of CDATA block"); + break; + } + (svgTree.*_append)(util::make_unique(_xmlbuf.substr(left + 9, right - left - 9))); + right += 2; + } + else if (_xmlbuf.compare(left, 4, "", left+4); + if (right == string::npos) { + if (finish) throw XMLParserException("expected '-->' at end of comment"); + break; + } + (svgTree.*_append)(util::make_unique(_xmlbuf.substr(left + 4, right - left - 4))); + right += 2; + } + else if (_xmlbuf.compare(left, 2, "", left+2); + if (right == string::npos) { + if (finish) throw XMLParserException("expected '?>' at end of processing instruction"); + break; + } + (svgTree.*_append)(util::make_unique(_xmlbuf.substr(left, right - left + 2))); + right++; + } + else if (_xmlbuf.compare(left, 2, "', left+2); + if (right == string::npos) { + if (finish) throw XMLParserException("missing '>' at end of closing XML tag"); + break; + } + closeElement(_xmlbuf.substr(left+2, right-left-2), svgTree); + } + else { + right = _xmlbuf.find('>', left+1); + if (right == string::npos) { + if (finish) throw XMLParserException("missing '>' or '/>' at end of opening XML tag"); + break; + } + openElement(_xmlbuf.substr(left+1, right-left-1), svgTree); + } + } + left = right; + if (right != string::npos) + left++; + } + } + catch (const XMLParserException &e) { + _error = true; + throw; + } + if (left == string::npos) + _xmlbuf.clear(); + else + _xmlbuf.erase(0, left); +} + + +/** Processes an opening element tag. + * @param[in] tag tag without leading and trailing angle brackets */ +void XMLParser::openElement (const string &tag, SVGTree &svgTree) { + StringInputBuffer ib(tag); + BufferInputReader ir(ib); + string name = ir.getString("/ \t\n\r"); + bool isPathElement = (name == "path" || name == "svg:path"); + ir.skipSpace(); + auto elemNode = util::make_unique(name); + map attribs; + if (ir.parseAttributes(attribs, true, "\"'")) { + for (const auto &attrpair : attribs) { + if (!isPathElement || attrpair.first != "d") + elemNode->addAttribute(attrpair.first, attrpair.second); + else { + try { + // parse and reformat path definition + auto path = GraphicsPathParser().parse(attrpair.second); + ostringstream oss; + path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS); + elemNode->addAttribute("d", oss.str()); + } + catch (const GraphicsPathParserException &e) { + throw XMLParserException(string("error in path data: ")+e.what()); + } + } + } + } + ir.skipSpace(); + if (ir.peek() == '/') // end of empty element tag + (svgTree.*_append)(std::move(elemNode)); + else if (ir.peek() < 0) { // end of opening tag + _nameStack.push_back(name); + (svgTree.*_pushContext)(std::move(elemNode)); + } + else + throw XMLParserException("'>' or '/>' expected at end of opening tag <"+name); +} + + +/** Processes a closing element tag. + * @param[in] tag tag without leading and trailing angle brackets */ +void XMLParser::closeElement (const string &tag, SVGTree &svgTree) { + StringInputBuffer ib(tag); + BufferInputReader ir(ib); + string name = ir.getString(" \t\n\r"); + ir.skipSpace(); + if (ir.peek() >= 0) + throw XMLParserException("'>' expected at end of closing tag "); + if (_nameStack.back() != name) + throw XMLParserException("expected but found "); + (svgTree.*_popContext)(); + _nameStack.pop_back(); +} + + +/** Processes any remaining XML fragments, checks for missing closing tags, + * and resets the parser state. */ +void XMLParser::finish (SVGTree &svgTree) { + if (!_xmlbuf.empty()) { + if (!_error) + parse("", svgTree, true); + _xmlbuf.clear(); + } + string tags; + while (!_nameStack.empty()) { + tags += ", "; + _nameStack.pop_back(); + } + if (!tags.empty() && !_error) { + tags.resize(tags.length()-2); + throw XMLParserException("missing closing tag(s): "+tags); + } +} diff --git a/dviware/dvisvgm/src/XMLParser.hpp b/dviware/dvisvgm/src/XMLParser.hpp new file mode 100644 index 0000000000..51f6a62544 --- /dev/null +++ b/dviware/dvisvgm/src/XMLParser.hpp @@ -0,0 +1,58 @@ +/************************************************************************* +** XMLParser.hpp ** +** ** +** This file is part of dvisvgm -- a fast DVI to SVG converter ** +** Copyright (C) 2005-2022 Martin Gieseking ** +** ** +** 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 . ** +*************************************************************************/ + +#ifndef XMLPARSER_HPP +#define XMLPARSER_HPP + +#include +#include "MessageException.hpp" +#include "SVGTree.hpp" + +struct XMLParserException : MessageException { + explicit XMLParserException (const std::string &msg) : MessageException(msg) {} +}; + +class XMLParser { + using AppendFunc = void (SVGTree::*)(std::unique_ptr); + using PushFunc = void (SVGTree::*)(std::unique_ptr); + using PopFunc = void (SVGTree::*)(); + using NameStack = std::vector; + + public: + XMLParser (AppendFunc append, PushFunc push, PopFunc pop) + : _append(append), _pushContext(push), _popContext(pop) {} + + void parse (const std::string &xml, SVGTree &svgTree, bool finish=false); + void finish (SVGTree &svgTree); + + protected: + void openElement (const std::string &tag, SVGTree &svgTree); + void closeElement (const std::string &tag, SVGTree &svgTree); + + private: + AppendFunc _append; + PushFunc _pushContext; + PopFunc _popContext; + std::string _xmlbuf; + NameStack _nameStack; ///< names of nested elements still missing a closing tag + bool _error=false; +}; + +#endif diff --git a/dviware/dvisvgm/src/XMLString.cpp b/dviware/dvisvgm/src/XMLString.cpp index 6ce89b3d86..ae489ea16c 100644 --- a/dviware/dvisvgm/src/XMLString.cpp +++ b/dviware/dvisvgm/src/XMLString.cpp @@ -91,7 +91,7 @@ XMLString::XMLString (double x) { if (std::abs(x) < 1e-6) x = 0; assign(util::to_string(x)); - size_t pos = find("0."); + auto pos = find("0."); if (pos != string::npos && (pos == 0 || at(pos-1) == '-')) erase(pos, 1); } diff --git a/dviware/dvisvgm/src/dvisvgm.cpp b/dviware/dvisvgm/src/dvisvgm.cpp index 089064a1b9..488e9e2b47 100644 --- a/dviware/dvisvgm/src/dvisvgm.cpp +++ b/dviware/dvisvgm/src/dvisvgm.cpp @@ -48,6 +48,7 @@ #include "optimizer/SVGOptimizer.hpp" #include "SVGOutput.hpp" #include "System.hpp" +#include "XMLParser.hpp" #include "XXHashFunction.hpp" #include "utility.hpp" #include "version.hpp" @@ -65,7 +66,7 @@ using namespace std; static string remove_path (string fname) { fname = FileSystem::ensureForwardSlashes(fname); - size_t slashpos = fname.rfind('/'); + auto slashpos = fname.rfind('/'); if (slashpos == string::npos) return fname; return fname.substr(slashpos+1); @@ -74,7 +75,7 @@ static string remove_path (string fname) { static string ensure_suffix (string fname, const string &suffix) { if (!fname.empty()) { - size_t dotpos = remove_path(fname).rfind('.'); + auto dotpos = remove_path(fname).rfind('.'); if (dotpos == string::npos) fname += "." + suffix; } @@ -494,6 +495,10 @@ int main (int argc, char *argv[]) { Message::estream() << "\nPostScript error: " << e.what() << '\n'; return -2; } + catch (XMLParserException &e) { + Message::estream() << "\nXML error: " << e.what() << '\n'; + return -5; + } catch (SignalException &e) { Message::wstream().clearline(); Message::wstream(true) << "execution interrupted by user\n"; diff --git a/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp b/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp index d36314aae4..1d1c6579b8 100644 --- a/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp +++ b/dviware/dvisvgm/src/optimizer/AttributeExtractor.cpp @@ -82,7 +82,7 @@ void AttributeExtractor::execute (XMLElement *context, bool recurse) { * @return the new group element if attributes could be grouped, 'elem' otherwise */ XMLNode* AttributeExtractor::extractAttribute (XMLElement *elem) { for (const auto ¤tAttribute : elem->attributes()) { - if (!inheritable(currentAttribute) || extracted(currentAttribute)) + if (!currentAttribute.inheritable() || extracted(currentAttribute)) continue; AttributeRun run(currentAttribute, elem); if (run.length() >= MIN_RUN_LENGTH) { @@ -126,30 +126,6 @@ bool AttributeExtractor::groupable (const XMLElement &elem) { } -/** Checks whether an SVG attribute A of an element E implicitly propagates its properties - * to all child elements of E that don't specify A. For now we only consider a subset of - * the inheritable properties. - * @param[in] attrib name of attribute to check - * @return true if the attribute is inheritable */ -bool AttributeExtractor::inheritable (const Attribute &attrib) { - // subset of inheritable properties listed on https://www.w3.org/TR/SVG11/propidx.html - // 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 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(begin(names), end(names), attrib.name, [](const string &name1, const string &name2) { - return name1 < name2; - }); -} - - /** Checks whether an attribute is allowed to be removed from a given element. */ bool AttributeExtractor::extractable (const Attribute &attrib, XMLElement &element) { if (element.hasAttribute("id")) diff --git a/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp b/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp index 19cd4f0fed..4b53b66551 100644 --- a/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp +++ b/dviware/dvisvgm/src/optimizer/AttributeExtractor.hpp @@ -46,7 +46,6 @@ class AttributeExtractor : public OptimizerModule { void execute (XMLElement*, XMLElement *context) override {execute(context, true);}; const char* info () const override; static bool groupable (const XMLElement &elem); - static bool inheritable (const Attribute &attrib); static bool extractable (const Attribute &attr, XMLElement &element); protected: diff --git a/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp b/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp index 03fc3ac185..e86313e74b 100644 --- a/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp +++ b/dviware/dvisvgm/src/optimizer/GroupCollapser.cpp @@ -22,7 +22,6 @@ #include #include #include -#include "AttributeExtractor.hpp" #include "GroupCollapser.hpp" #include "TransformSimplifier.hpp" #include "../XMLNode.hpp" @@ -83,8 +82,8 @@ void GroupCollapser::execute (XMLElement *context, int depth) { XMLNode *next=child->next(); if (XMLElement *childElement = child->toElement()) { execute(childElement, depth+1); - // check for groups without attributes and remove them - if (childElement->name() == "g" && childElement->attributes().empty()) { + // remove empty groups and groups without attributes + if (childElement->name() == "g" && (childElement->attributes().empty() || (!childElement->hasAttribute("id") && childElement->empty(true)))) { remove_ws_nodes(childElement); if (XMLNode *firstUnwrappedNode = XMLElement::unwrap(childElement)) next = firstUnwrappedNode; @@ -127,7 +126,7 @@ bool GroupCollapser::moveAttributes (XMLElement &source, XMLElement &dest) { dest.addAttribute("transform", transform); movedAttributes.emplace_back("transform"); } - else if (AttributeExtractor::inheritable(attr)) { + else if (attr.inheritable()) { dest.addAttribute(attr.name, attr.value); movedAttributes.emplace_back(attr.name); } diff --git a/dviware/dvisvgm/src/optimizer/Makefile.am b/dviware/dvisvgm/src/optimizer/Makefile.am index af23fd22a7..cdd802b2ab 100644 --- a/dviware/dvisvgm/src/optimizer/Makefile.am +++ b/dviware/dvisvgm/src/optimizer/Makefile.am @@ -10,7 +10,7 @@ liboptimizer_la_SOURCES = \ SVGOptimizer.hpp SVGOptimizer.cpp \ TextSimplifier.hpp TextSimplifier.cpp \ TransformSimplifier.hpp TransformSimplifier.cpp \ - WSNodeRemover.hpp WSNodeRemover.cpp + WSNodeRemover.hpp WSNodeRemover.cpp include ../../libs/defs.am diff --git a/dviware/dvisvgm/src/optimizer/Makefile.in b/dviware/dvisvgm/src/optimizer/Makefile.in index 3d7cf25df6..a100cb9d65 100644 --- a/dviware/dvisvgm/src/optimizer/Makefile.in +++ b/dviware/dvisvgm/src/optimizer/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.2 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -95,12 +95,7 @@ target_triplet = @target@ @HAVE_WOFF2_FALSE@am__append_4 = ../libs/woff2/libwoff2.a subdir = src/optimizer ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \ - $(top_srcdir)/m4/ax_code_coverage.m4 \ - $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ - $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ - $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ - $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -202,8 +197,6 @@ am__define_uniq_tagged_files = \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags am__DIST_COMMON = $(srcdir)/../../libs/defs.am $(srcdir)/Makefile.in \ $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -231,6 +224,8 @@ CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@ CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ CXX = @CXX@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ @@ -246,8 +241,10 @@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ +ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +FILECMD = @FILECMD@ FREETYPE_CFLAGS = @FREETYPE_CFLAGS@ FREETYPE_LIBS = @FREETYPE_LIBS@ GCOV = @GCOV@ @@ -377,7 +374,7 @@ liboptimizer_la_SOURCES = \ SVGOptimizer.hpp SVGOptimizer.cpp \ TextSimplifier.hpp TextSimplifier.cpp \ TransformSimplifier.hpp TransformSimplifier.cpp \ - WSNodeRemover.hpp WSNodeRemover.cpp + WSNodeRemover.hpp WSNodeRemover.cpp @HAVE_POTRACE_FALSE@POTRACE_CFLAGS = -I$(dvisvgm_srcdir)/libs/potrace @HAVE_POTRACE_FALSE@POTRACE_LIBS = ../libs/potrace/libpotrace.a @@ -397,9 +394,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/../../libs/defs.am $(am_ exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/optimizer/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/optimizer/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign src/optimizer/Makefile + $(AUTOMAKE) --gnu src/optimizer/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ @@ -535,7 +532,6 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags - distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am diff --git a/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp b/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp index b07db7701b..855d88c101 100644 --- a/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp +++ b/dviware/dvisvgm/src/optimizer/TextSimplifier.cpp @@ -40,7 +40,7 @@ static XMLElement::Attributes common_inheritable_attributes (const vectorattributes()) { - if (AttributeExtractor::inheritable(attrib)) + if (attrib.inheritable()) commonAttribs.push_back(attrib); } } diff --git a/dviware/dvisvgm/src/utility.cpp b/dviware/dvisvgm/src/utility.cpp index 56f2c17ae7..4877690ef4 100644 --- a/dviware/dvisvgm/src/utility.cpp +++ b/dviware/dvisvgm/src/utility.cpp @@ -84,10 +84,10 @@ double math::normalize_0_2pi (double rad) { * @param[in] ws characters treated as whitespace * @return the trimmed string */ string util::trim (const std::string &str, const char *ws) { - size_t first = str.find_first_not_of(ws); + auto first = str.find_first_not_of(ws); if (first == string::npos) return ""; - size_t last = str.find_last_not_of(ws); + auto last = str.find_last_not_of(ws); return str.substr(first, last-first+1); } @@ -99,9 +99,9 @@ string util::trim (const std::string &str, const char *ws) { * @return the normalized string */ string util::normalize_space (string str, const char *ws) { str = trim(str); - size_t first = str.find_first_of(ws); + auto first = str.find_first_of(ws); while (first != string::npos) { - size_t last = str.find_first_not_of(ws, first); + auto last = str.find_first_not_of(ws, first); str.replace(first, last-first, " "); first = str.find_first_of(ws, first+1); } @@ -116,7 +116,7 @@ string util::normalize_space (string str, const char *ws) { * @return the resulting string */ string util::replace (string str, const string &find, const string &repl) { if (!find.empty() && !repl.empty()) { - size_t first = str.find(find); + auto first = str.find(find); while (first != string::npos) { str.replace(first, find.length(), repl); first = str.find(find, first+repl.length()); @@ -136,9 +136,9 @@ vector util::split (const string &str, const string &sep) { if (str.empty() || sep.empty()) parts.push_back(str); else { - size_t left=0; + string::size_type left=0; while (left <= str.length()) { - size_t right = str.find(sep, left); + auto right = str.find(sep, left); if (right == string::npos) { parts.push_back(str.substr(left)); left = string::npos; @@ -164,7 +164,7 @@ string util::tolower (const string &str) { 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'); + auto pos = str.find_last_not_of('0'); if (pos != string::npos) // trailing zeros str.erase(pos+1, string::npos); if (str.back() == '.') // trailing dot? diff --git a/dviware/dvisvgm/src/utility.hpp b/dviware/dvisvgm/src/utility.hpp index 9ab0d60542..b8a36daceb 100644 --- a/dviware/dvisvgm/src/utility.hpp +++ b/dviware/dvisvgm/src/utility.hpp @@ -137,17 +137,17 @@ inline void base64_copy (std::istream &is, std::ostream &os, int wrap=0) { * @param[in] args arguments forwarded to an constructor of T */ template std::unique_ptr make_unique (Args&&... args) { - return std::unique_ptr(new T(std::forward(args)...)); + return std::unique_ptr(new T(std::forward(args)...)); } template std::unique_ptr static_unique_ptr_cast (std::unique_ptr &&old){ - return std::unique_ptr{static_cast(old.release())}; + return std::unique_ptr{static_cast(old.release())}; } template -struct set_const_of { +struct set_const_of { template struct by { using type = typename std::conditional< @@ -155,7 +155,7 @@ struct set_const_of { typename std::add_const::type, typename std::remove_const::type >::type; - }; + }; }; } // namespace util -- cgit v1.2.3