diff options
Diffstat (limited to 'dviware/dvisvgm/src')
71 files changed, 931 insertions, 280 deletions
diff --git a/dviware/dvisvgm/src/Bezier.cpp b/dviware/dvisvgm/src/Bezier.cpp index 2d90122531..568fe19578 100644 --- a/dviware/dvisvgm/src/Bezier.cpp +++ b/dviware/dvisvgm/src/Bezier.cpp @@ -270,7 +270,7 @@ vector<QuadBezier> CubicBezier::toQuadBeziers (double precision, vector<double> QuadBezier CubicBezier::midpointApproximation () const { DPair p0 = (_points[1]*3.0 - _points[0])/2.0; DPair p1 = (_points[2]*3.0 - _points[3])/2.0; - return QuadBezier(_points[0], (p0+p1)/2.0, _points[3]); + return {_points[0], (p0+p1)/2.0, _points[3]}; } @@ -305,7 +305,7 @@ void CubicBezier::toQuadBeziers (double t0, double t1, double precision, vector< double tmax3 = 18.0/sqrt(3.0)*precision/dist; // the cube of tmax if (tmax3 >= 1.0) { // curve is already quadratic, no subdivision necessary, return MPA - qbeziers.emplace_back(QuadBezier(_points[0], (q1+q2)/2.0, _points[3])); + qbeziers.emplace_back(_points[0], (q1+q2)/2.0, _points[3]); if (startParams) startParams->push_back(t0); } diff --git a/dviware/dvisvgm/src/BgColorSpecialHandler.cpp b/dviware/dvisvgm/src/BgColorSpecialHandler.cpp index 86e5fb72d7..84cb55eb10 100644 --- a/dviware/dvisvgm/src/BgColorSpecialHandler.cpp +++ b/dviware/dvisvgm/src/BgColorSpecialHandler.cpp @@ -62,7 +62,7 @@ void BgColorSpecialHandler::dviBeginPage (unsigned pageno, SpecialActions &actio } -vector<const char*> BgColorSpecialHandler::prefixes() const { +vector<const char*> BgColorSpecialHandler::prefixes () const { vector<const char*> pfx {"background"}; return pfx; } diff --git a/dviware/dvisvgm/src/BgColorSpecialHandler.hpp b/dviware/dvisvgm/src/BgColorSpecialHandler.hpp index 7a0290b05f..cb6146477e 100644 --- a/dviware/dvisvgm/src/BgColorSpecialHandler.hpp +++ b/dviware/dvisvgm/src/BgColorSpecialHandler.hpp @@ -31,8 +31,9 @@ class BgColorSpecialHandler : public SpecialHandler { void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; const char* info () const override {return "background color special";} - const char* name () const override {return "bgcolor";} - std::vector<const char*> prefixes() const override; + const char* name () const override {return handlerName();} + static const char* handlerName () {return "bgcolor";} + std::vector<const char*> prefixes () const override; protected: void dviBeginPage (unsigned pageno, SpecialActions &actions) override; diff --git a/dviware/dvisvgm/src/Color.cpp b/dviware/dvisvgm/src/Color.cpp index d5b6413081..42f786d869 100644 --- a/dviware/dvisvgm/src/Color.cpp +++ b/dviware/dvisvgm/src/Color.cpp @@ -55,6 +55,35 @@ void Color::setRGB (double r, double g, double b) { } +/** Sets the color value according to a given hex RGB string of the + * form "#123456" or "#123" where the latter is expanded to "#112233". + * The leading '#' character is optional. + * @param[in] hexString the RGB hex string + * @return true if the color value was assigned successfully */ +bool Color::setRGBHexString (string hexString) { + if (!hexString.empty()) { + if (hexString[0] == '#') + hexString = hexString.substr(1); + if (hexString.length() == 3) { + // expand short form "123" to "112233" + hexString.resize(6); + hexString[5] = hexString[4] = hexString[2]; + hexString[3] = hexString[2] = hexString[1]; + hexString[1] = hexString[0]; + } + if (hexString.length() == 6) { + try { + _rgb = stoi(hexString, nullptr, 16); + return true; + } + catch (...) { + } + } + } + return false; +} + + /** Expects a PostScript color name and sets the color accordingly. * @param[in] name PS color name * @param[in] case_sensitive if true, upper/lower case spelling is significant diff --git a/dviware/dvisvgm/src/Color.hpp b/dviware/dvisvgm/src/Color.hpp index e133db7436..156aa269e4 100644 --- a/dviware/dvisvgm/src/Color.hpp +++ b/dviware/dvisvgm/src/Color.hpp @@ -56,6 +56,7 @@ class Color { void setRGB (uint8_t r, uint8_t g, uint8_t b) {_rgb = (r << 16) | (g << 8) | b;} void setRGB (double r, double g, double b); void setRGB (const std::valarray<double> &rgb) {setRGB(rgb[0], rgb[1], rgb[2]);} + bool setRGBHexString (std::string hexString); bool setPSName (std::string name, bool case_sensitive=true); void setGray (uint8_t g) {setRGB(g,g,g);} void setGray (double g) {setRGB(g,g,g);} diff --git a/dviware/dvisvgm/src/ColorSpecialHandler.cpp b/dviware/dvisvgm/src/ColorSpecialHandler.cpp index 35bc111ee3..c6a3e2bfd6 100644 --- a/dviware/dvisvgm/src/ColorSpecialHandler.cpp +++ b/dviware/dvisvgm/src/ColorSpecialHandler.cpp @@ -113,7 +113,7 @@ bool ColorSpecialHandler::process (const string&, istream &is, SpecialActions &a } -vector<const char*> ColorSpecialHandler::prefixes() const { +vector<const char*> ColorSpecialHandler::prefixes () const { vector<const char*> pfx {"color"}; return pfx; } diff --git a/dviware/dvisvgm/src/ColorSpecialHandler.hpp b/dviware/dvisvgm/src/ColorSpecialHandler.hpp index ea542e1f06..72809e8e0f 100644 --- a/dviware/dvisvgm/src/ColorSpecialHandler.hpp +++ b/dviware/dvisvgm/src/ColorSpecialHandler.hpp @@ -33,9 +33,10 @@ class ColorSpecialHandler : public SpecialHandler { bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; static Color readColor (std::istream &is); static Color readColor (const std::string &model, std::istream &is); - const char* name () const override {return "color";} + const char* name () const override {return handlerName();} + static const char* handlerName () {return "color";} const char* info () const override {return "complete support of color specials";} - std::vector<const char*> prefixes() const override; + std::vector<const char*> prefixes () const override; private: std::stack<Color> _colorStack; diff --git a/dviware/dvisvgm/src/CommandLine.hpp b/dviware/dvisvgm/src/CommandLine.hpp index 72edde3ee1..d4f0745219 100644 --- a/dviware/dvisvgm/src/CommandLine.hpp +++ b/dviware/dvisvgm/src/CommandLine.hpp @@ -35,6 +35,7 @@ class CommandLine : public CL::CommandLine { Option colorOpt {"color", '\0', "colorize messages"}; Option colornamesOpt {"colornames", '\0', "prefer color names to RGB values if possible"}; Option commentsOpt {"comments", '\0', "add comments with additional information"}; + TypedOption<std::string, Option::ArgMode::OPTIONAL> currentcolorOpt {"currentcolor", '\0', "color", "#000", "replace given color with 'currentColor'"}; Option debugGlyphsOpt {"debug-glyphs", '\0', "create PS files for all glyphs converted to TTF"}; Option embedBitmapsOpt {"embed-bitmaps", '\0', "prevent references to external bitmap files"}; Option epsOpt {"eps", 'E', "convert EPS file to SVG"}; @@ -108,6 +109,7 @@ class CommandLine : public CL::CommandLine { #endif {&colornamesOpt, 1}, {&commentsOpt, 1}, + {¤tcolorOpt, 1}, {&embedBitmapsOpt, 1}, #if !defined(DISABLE_WOFF) {&fontFormatOpt, 1}, diff --git a/dviware/dvisvgm/src/DVIActions.hpp b/dviware/dvisvgm/src/DVIActions.hpp index 8e2e2a0829..ed3c012f58 100644 --- a/dviware/dvisvgm/src/DVIActions.hpp +++ b/dviware/dvisvgm/src/DVIActions.hpp @@ -21,6 +21,7 @@ #ifndef DVIACTIONS_HPP #define DVIACTIONS_HPP +#include <cstdint> #include <string> #include <vector> diff --git a/dviware/dvisvgm/src/DVIReader.cpp b/dviware/dvisvgm/src/DVIReader.cpp index 6dd7bebb69..2c52ba5210 100644 --- a/dviware/dvisvgm/src/DVIReader.cpp +++ b/dviware/dvisvgm/src/DVIReader.cpp @@ -206,8 +206,11 @@ void DVIReader::putVFChar (Font *font, uint32_t c) { FontManager &fm = FontManager::instance(); const vector<uint8_t> *dvi = vf->getDVI(c); // try to get DVI snippet that represents character c Font *firstFont = fm.vfFirstFont(vf); - if (!dvi && (!firstFont || !firstFont->getMetrics()->isJFM())) - return; + if (!dvi) { + const FontMetrics *ffm = firstFont ? firstFont->getMetrics() : nullptr; + if (!ffm || (!ffm->isJFM() && !ffm->isOFM())) + return; + } fm.enterVF(vf); // enter VF font number context int savedFontNum = _currFontNum; // save current font number setFont(fm.vfFirstFontNum(vf), SetFontMode::VF_ENTER); diff --git a/dviware/dvisvgm/src/DVIToSVG.cpp b/dviware/dvisvgm/src/DVIToSVG.cpp index f5501f33f6..ad809ca931 100644 --- a/dviware/dvisvgm/src/DVIToSVG.cpp +++ b/dviware/dvisvgm/src/DVIToSVG.cpp @@ -377,48 +377,67 @@ void DVIToSVG::embedFonts (XMLElement *svgElement) { } +static vector<string> extract_prefixes (const char *ignorelist) { + vector<string> prefixes; + if (ignorelist) { + const char *left = ignorelist; + while (*left) { + while (*left && !isalnum(*left)) + left++; + const char *right = left; + while (*right && isalnum(*right)) + right++; + if (*left) + prefixes.emplace_back(left, right-left); + left = right; + } + } + return prefixes; +} + + /** Enables or disables processing of specials. If ignorelist == 0, all * supported special handlers are loaded. To disable selected sets of specials, * the corresponding prefixes can be given separated by non alpha-numeric characters, * e.g. "color, ps, em" or "color: ps em" etc. * A single "*" in the ignore list disables all specials. - * @param[in] ignorelist list of special prefixes to ignore + * @param[in] ignorelist list of hanlder names to ignore * @param[in] pswarning if true, shows warning about disabled PS support * @return the SpecialManager that handles special statements */ void DVIToSVG::setProcessSpecials (const char *ignorelist, bool pswarning) { if (ignorelist && strcmp(ignorelist, "*") == 0) // ignore all specials? SpecialManager::instance().unregisterHandlers(); else { + auto ignoredHandlerName = extract_prefixes(ignorelist); // add special handlers - vector<unique_ptr<SpecialHandler>> handlers; - handlers.emplace_back(util::make_unique<BgColorSpecialHandler>()); // handles background color special - handlers.emplace_back(util::make_unique<ColorSpecialHandler>()); // handles color specials - handlers.emplace_back(util::make_unique<DvisvgmSpecialHandler>()); // handles raw SVG embeddings - handlers.emplace_back(util::make_unique<EmSpecialHandler>()); // handles emTeX specials - handlers.emplace_back(util::make_unique<HtmlSpecialHandler>()); // handles hyperref specials - handlers.emplace_back(util::make_unique<PapersizeSpecialHandler>()); // handles papersize special - handlers.emplace_back(util::make_unique<PdfSpecialHandler>()); // handles pdf specials - handlers.emplace_back(util::make_unique<TpicSpecialHandler>()); // handles tpic specials + SpecialManager::registerHandler<BgColorSpecialHandler>(ignoredHandlerName); // handles background color special + SpecialManager::registerHandler<ColorSpecialHandler>(ignoredHandlerName); // handles color specials + SpecialManager::registerHandler<DvisvgmSpecialHandler>(ignoredHandlerName); // handles raw SVG embeddings + SpecialManager::registerHandler<EmSpecialHandler>(ignoredHandlerName); // handles emTeX specials + SpecialManager::registerHandler<HtmlSpecialHandler>(ignoredHandlerName); // handles hyperref specials + SpecialManager::registerHandler<PapersizeSpecialHandler>(ignoredHandlerName); // handles papersize special + SpecialManager::registerHandler<PdfSpecialHandler>(ignoredHandlerName); // handles pdf specials + SpecialManager::registerHandler<TpicSpecialHandler>(ignoredHandlerName); // handles tpic specials + if (find(ignoredHandlerName.begin(), ignoredHandlerName.end(), PsSpecialHandler::handlerName()) == ignoredHandlerName.end()) { #ifndef DISABLE_GS - if (Ghostscript().available()) - handlers.emplace_back(util::make_unique<PsSpecialHandler>()); // handles PostScript specials - else + if (Ghostscript().available()) + SpecialManager::registerHandler<PsSpecialHandler>(ignoredHandlerName); // handles PostScript specials + else #endif - { + { #ifndef HAVE_LIBGS - // dummy PS special handler that only prints warning messages - handlers.emplace_back(util::make_unique<NoPsSpecialHandler>()); - if (pswarning) { + // dummy PS special handler that only prints warning messages + SpecialManager::registerHandler<NoPsSpecialHandler>(ignoredHandlerName); + if (pswarning) { #ifdef DISABLE_GS - Message::wstream() << "processing of PostScript specials has been disabled permanently\n"; + Message::wstream() << "processing of PostScript specials has been disabled permanently\n"; #else - Message::wstream() << "processing of PostScript specials is disabled (Ghostscript not found)\n"; + Message::wstream() << "processing of PostScript specials is disabled (Ghostscript not found)\n"; #endif - } + } #endif + } } - SpecialManager::instance().unregisterHandlers(); - SpecialManager::instance().registerHandlers(handlers, ignorelist); } } diff --git a/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp b/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp index abeb52be36..0a84038500 100644 --- a/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp +++ b/dviware/dvisvgm/src/DvisvgmSpecialHandler.cpp @@ -18,7 +18,6 @@ ** along with this program; if not, see <http://www.gnu.org/licenses/>. ** *************************************************************************/ -#include <array> #include <cstring> #include <utility> #include "Calculator.hpp" @@ -27,6 +26,7 @@ #include "InputBuffer.hpp" #include "InputReader.hpp" #include "Length.hpp" +#include "Message.hpp" #include "SpecialActions.hpp" #include "SVGTree.hpp" #include "utility.hpp" @@ -95,24 +95,23 @@ DvisvgmSpecialHandler::DvisvgmSpecialHandler () : _currentMacro(_macros.end()) void DvisvgmSpecialHandler::preprocess (const string&, istream &is, SpecialActions&) { - struct Command { + constexpr struct Command { const char *name; void (DvisvgmSpecialHandler::*handler)(InputReader&); - }; - constexpr array<Command, 5> commands {{ + } commands[] = { {"raw", &DvisvgmSpecialHandler::preprocessRaw}, {"rawdef", &DvisvgmSpecialHandler::preprocessRawDef}, {"rawset", &DvisvgmSpecialHandler::preprocessRawSet}, {"endrawset", &DvisvgmSpecialHandler::preprocessEndRawSet}, {"rawput", &DvisvgmSpecialHandler::preprocessRawPut} - }}; + }; StreamInputReader ir(is); const string cmdstr = ir.getWord(); - auto it = find_if(commands.begin(), commands.end(), [&](const Command &cmd) { + auto it = find_if(begin(commands), end(commands), [&](const Command &cmd) { return cmd.name == cmdstr; }); - if (it != commands.end()) { + if (it != end(commands)) { ir.skipSpace(); (this->*it->handler)(ir); } @@ -172,25 +171,26 @@ void DvisvgmSpecialHandler::preprocessRawPut (InputReader &ir) { * @param[in] is the special statement is read from this stream * @param[in] actions object providing the actions that can be performed by the SpecialHandler */ bool DvisvgmSpecialHandler::process (const string &prefix, istream &is, SpecialActions &actions) { - struct Command { + constexpr struct Command { const char *name; void (DvisvgmSpecialHandler::*handler)(InputReader&, SpecialActions&); + } commands[] = { + {"raw", &DvisvgmSpecialHandler::processRaw}, + {"rawdef", &DvisvgmSpecialHandler::processRawDef}, + {"rawset", &DvisvgmSpecialHandler::processRawSet}, + {"endrawset", &DvisvgmSpecialHandler::processEndRawSet}, + {"rawput", &DvisvgmSpecialHandler::processRawPut}, + {"bbox", &DvisvgmSpecialHandler::processBBox}, + {"img", &DvisvgmSpecialHandler::processImg}, + {"currentcolor", &DvisvgmSpecialHandler::processCurrentColor}, + {"message", &DvisvgmSpecialHandler::processMessage} }; - constexpr array<Command, 7> commands {{ - {"raw", &DvisvgmSpecialHandler::processRaw}, - {"rawdef", &DvisvgmSpecialHandler::processRawDef}, - {"rawset", &DvisvgmSpecialHandler::processRawSet}, - {"endrawset", &DvisvgmSpecialHandler::processEndRawSet}, - {"rawput", &DvisvgmSpecialHandler::processRawPut}, - {"bbox", &DvisvgmSpecialHandler::processBBox}, - {"img", &DvisvgmSpecialHandler::processImg} - }}; StreamInputReader ir(is); const string cmdstr = ir.getWord(); - auto it = find_if(commands.begin(), commands.end(), [&](const Command &cmd) { + auto it = find_if(begin(commands), end(commands), [&](const Command &cmd) { return cmd.name == cmdstr; }); - if (it != commands.end()) { + if (it != end(commands)) { ir.skipSpace(); (this->*it->handler)(ir, actions); } @@ -219,17 +219,16 @@ static void expand_constants (string &str, SpecialActions &actions) { } } } - struct Constant { + const struct Constant { const char *name; string val; - }; - const array<Constant, 5> constants {{ + } constants[] = { {"x", XMLString(actions.getX())}, {"y", XMLString(actions.getY())}, - {"color", actions.getColor().svgColorString()}, + {"color", SVGElement::USE_CURRENTCOLOR && SVGElement::CURRENTCOLOR == actions.getColor() ? "currentColor" : actions.getColor().svgColorString()}, {"matrix", actions.getMatrix().toSVG()}, {"nl", "\n"}, - }}; + }; for (const Constant &constant : constants) { const string pattern = string("{?")+constant.name+"}"; auto pos = str.find(pattern); @@ -452,6 +451,34 @@ void DvisvgmSpecialHandler::processImg (InputReader &ir, SpecialActions &actions } +void DvisvgmSpecialHandler::processCurrentColor (InputReader &ir, SpecialActions &actions) { + string param = ir.getString(); + Color color = actions.getColor(); + if (param.empty() || param == "on") { + SVGElement::CURRENTCOLOR = color; + SVGElement::USE_CURRENTCOLOR = true; + } + else if (param == "off") { + if (SVGElement::USE_CURRENTCOLOR) { + // force a color change to get the new currentColor setting recognized + actions.setColor(Color{uint32_t(color)+1}); + actions.setColor(color); + SVGElement::USE_CURRENTCOLOR = false; + } + } + else + throw SpecialException("currentcolor: unknown parameter '"+param+"'"); +} + + +void DvisvgmSpecialHandler::processMessage (InputReader &ir, SpecialActions &actions) { + string message = ir.getLine(); + evaluate_expressions(message, actions); + expand_constants(message, actions); + Message::mstream() << message << "\n"; +} + + void DvisvgmSpecialHandler::dviPreprocessingFinished () { string id; if (_currentMacro != _macros.end()) @@ -486,7 +513,7 @@ void DvisvgmSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) { } -vector<const char*> DvisvgmSpecialHandler::prefixes() const { +vector<const char*> DvisvgmSpecialHandler::prefixes () const { vector<const char*> pfx {"dvisvgm:"}; return pfx; } diff --git a/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp b/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp index 4c9c84b078..7edecb12c8 100644 --- a/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp +++ b/dviware/dvisvgm/src/DvisvgmSpecialHandler.hpp @@ -73,11 +73,12 @@ class DvisvgmSpecialHandler : public SpecialHandler { public: DvisvgmSpecialHandler (); - const char* name () const override {return "dvisvgm";} - const char* info () const override {return "special set for embedding raw SVG snippets";} - std::vector<const char*> prefixes() const override; void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; + const char* info () const override {return "special set for embedding raw SVG snippets";} + const char* name () const override {return handlerName();} + static const char* handlerName () {return "dvisvgm";} + std::vector<const char*> prefixes () const override; protected: void preprocessRaw (InputReader &ir); @@ -92,6 +93,8 @@ class DvisvgmSpecialHandler : public SpecialHandler { void processRawPut (InputReader &ir, SpecialActions &actions); void processBBox (InputReader &ir, SpecialActions &actions); void processImg (InputReader &ir, SpecialActions &actions); + void processCurrentColor (InputReader &ir, SpecialActions &actions); + void processMessage (InputReader &ir, SpecialActions &actions); void dviPreprocessingFinished () override; void dviBeginPage (unsigned pageno, SpecialActions &actions) override; void dviEndPage (unsigned pageno, SpecialActions &actions) override; diff --git a/dviware/dvisvgm/src/EmSpecialHandler.cpp b/dviware/dvisvgm/src/EmSpecialHandler.cpp index e116227ba9..919de50b2c 100644 --- a/dviware/dvisvgm/src/EmSpecialHandler.cpp +++ b/dviware/dvisvgm/src/EmSpecialHandler.cpp @@ -258,7 +258,7 @@ void EmSpecialHandler::dviEndPage (unsigned pageno, SpecialActions &actions) { } -vector<const char*> EmSpecialHandler::prefixes() const { +vector<const char*> EmSpecialHandler::prefixes () const { vector<const char*> pfx {"em:"}; return pfx; } diff --git a/dviware/dvisvgm/src/EmSpecialHandler.hpp b/dviware/dvisvgm/src/EmSpecialHandler.hpp index aee0219c12..5adb90aaca 100644 --- a/dviware/dvisvgm/src/EmSpecialHandler.hpp +++ b/dviware/dvisvgm/src/EmSpecialHandler.hpp @@ -38,10 +38,11 @@ class EmSpecialHandler : public SpecialHandler { }; public: - const char* name () const override {return "em";} - const char* info () const override {return "line drawing statements of the emTeX special set";} - std::vector<const char*> prefixes() const override; bool process (const std::string &prefix, std::istream &in, SpecialActions &actions) override; + const char* info () const override {return "line drawing statements of the emTeX special set";} + const char* name () const override {return handlerName();} + static const char* handlerName () {return "em";} + std::vector<const char*> prefixes () const override; protected: void dviEndPage (unsigned pageno, SpecialActions &actions) override; diff --git a/dviware/dvisvgm/src/FileFinder.cpp b/dviware/dvisvgm/src/FileFinder.cpp index 369ef33f49..f01b230af2 100644 --- a/dviware/dvisvgm/src/FileFinder.cpp +++ b/dviware/dvisvgm/src/FileFinder.cpp @@ -161,6 +161,7 @@ const char* FileFinder::findFile (const std::string &fname, const char *ftype) c #ifndef MIKTEX static std::map<std::string, kpse_file_format_type> types = { {"tfm", kpse_tfm_format}, + {"ofm", kpse_ofm_format}, {"pfb", kpse_type1_format}, {"vf", kpse_vf_format}, {"mf", kpse_mf_format}, diff --git a/dviware/dvisvgm/src/Font.cpp b/dviware/dvisvgm/src/Font.cpp index c60b162828..4645d33073 100644 --- a/dviware/dvisvgm/src/Font.cpp +++ b/dviware/dvisvgm/src/Font.cpp @@ -112,13 +112,14 @@ const FontMetrics* TFMFont::getMetrics () const { try { _metrics = FontMetrics::read(_fontname); if (!_metrics) { - _metrics = util::make_unique<NullFontMetric>(); + _metrics = util::make_unique<NullFontMetrics>(); Message::wstream(true) << "can't find "+_fontname+".tfm\n"; } } catch (FontMetricException &e) { - _metrics = util::make_unique<NullFontMetric>(); - Message::wstream(true) << e.what() << " in " << _fontname << ".tfm\n"; + const char *ext = (_metrics && _metrics->isOFM()) ? ".ofm" : ".tfm"; + _metrics = util::make_unique<NullFontMetrics>(); + Message::wstream(true) << e.what() << " in " << _fontname << ext << "\n"; } } return _metrics.get(); diff --git a/dviware/dvisvgm/src/Font.hpp b/dviware/dvisvgm/src/Font.hpp index 74f1b510cb..587a078500 100644 --- a/dviware/dvisvgm/src/Font.hpp +++ b/dviware/dvisvgm/src/Font.hpp @@ -291,6 +291,8 @@ class NativeFont : public PhysicalFont { protected: NativeFont (double ptsize, const FontStyle &style, Color color) : _ptsize(ptsize), _style(style), _color(color) {} + using PhysicalFont::hAdvance; + using PhysicalFont::vAdvance; private: double _ptsize; ///< font size in PS point units diff --git a/dviware/dvisvgm/src/FontCache.cpp b/dviware/dvisvgm/src/FontCache.cpp index 95155dc366..b89dc091c7 100644 --- a/dviware/dvisvgm/src/FontCache.cpp +++ b/dviware/dvisvgm/src/FontCache.cpp @@ -281,7 +281,7 @@ bool FontCache::fontinfo (const string &dirname, vector<FontInfo> &infos, vector string path = dirname+"/"+(fname.substr(1)); ifstream ifs(path, ios::binary); if (fontinfo(ifs, info)) - infos.push_back(move(info)); + infos.push_back(std::move(info)); else invalid.push_back(fname.substr(1)); } diff --git a/dviware/dvisvgm/src/FontManager.cpp b/dviware/dvisvgm/src/FontManager.cpp index bc6f88d322..3dcf87eae1 100644 --- a/dviware/dvisvgm/src/FontManager.cpp +++ b/dviware/dvisvgm/src/FontManager.cpp @@ -233,12 +233,13 @@ int FontManager::registerFont (uint32_t fontnum, const string &name, uint32_t ch else { // create dummy font as a placeholder if the proper font is not available newfont = util::make_unique<EmptyFont>(name); - if (filename.rfind('.') == string::npos) - filename += ".mf"; // print warning message about missing font file (only once for each filename) static set<string> missing_fonts; if (missing_fonts.find(filename) == missing_fonts.end()) { - Message::wstream(true) << "font file '" << filename << "' not found\n"; + if (filename.rfind('.') == string::npos) + Message::wstream(true) << "no font file found for '" << filename << "'\n"; + else + Message::wstream(true) << "font file '" << filename << "' not found\n"; missing_fonts.insert(filename); } } diff --git a/dviware/dvisvgm/src/FontMetrics.cpp b/dviware/dvisvgm/src/FontMetrics.cpp index 6147f36ab1..0edc1a6a79 100644 --- a/dviware/dvisvgm/src/FontMetrics.cpp +++ b/dviware/dvisvgm/src/FontMetrics.cpp @@ -23,19 +23,42 @@ #include "FileFinder.hpp" #include "FontMetrics.hpp" #include "JFM.hpp" +#include "OFM.hpp" #include "utility.hpp" using namespace std; +static inline uint16_t read_uint16 (istream &is) { + uint16_t val = 256*is.get(); + return val + is.get(); +} + +/** Reads the font metrics for a given font name from a TFM, JFM, or OFM file. + * @param[in] fontname name of font to get the metrics for. + * @return pointer to object that holds the font metrics, or nullptr if no matching file was found */ unique_ptr<FontMetrics> FontMetrics::read (const string &fontname) { - const char *path = FileFinder::instance().lookup(fontname + ".tfm"); + unique_ptr<FontMetrics> metrics; + const char *path = FileFinder::instance().lookup(fontname + ".ofm", false); + if (!path) + path = FileFinder::instance().lookup(fontname + ".tfm"); ifstream ifs(path, ios::binary); - if (!ifs) - return unique_ptr<FontMetrics>(); - uint16_t id = 256*ifs.get(); - id += ifs.get(); - if (id == 9 || id == 11) // Japanese font metric file? - return util::make_unique<JFM>(ifs); - return util::make_unique<TFM>(ifs); + if (ifs) { + auto id = read_uint16(ifs); + if (id == 0) { // OFM? + auto ofmLevel = read_uint16(ifs); + if (ofmLevel == 0) + metrics = util::make_unique<OFM0>(); + else if (ofmLevel == 1) + metrics = util::make_unique<OFM1>(); + else + throw FontMetricException("OFM level "+to_string(ofmLevel)+" not supported"); + } + else if (id == 9 || id == 11) // Japanese font metric file? + metrics = util::make_unique<JFM>(); + else + metrics = util::make_unique<TFM>(); + metrics->read(ifs); + } + return metrics; } diff --git a/dviware/dvisvgm/src/FontMetrics.hpp b/dviware/dvisvgm/src/FontMetrics.hpp index 79cb579b2e..85427720e7 100644 --- a/dviware/dvisvgm/src/FontMetrics.hpp +++ b/dviware/dvisvgm/src/FontMetrics.hpp @@ -21,6 +21,7 @@ #ifndef FONTMETRICS_HPP #define FONTMETRICS_HPP +#include <cstdint> #include <istream> #include <memory> #include <string> @@ -28,6 +29,7 @@ struct FontMetrics { virtual ~FontMetrics () =default; + virtual void read (std::istream &is) {} virtual double getDesignSize () const =0; virtual double getCharWidth (int c) const =0; virtual double getCharHeight (int c) const =0; @@ -41,14 +43,15 @@ struct FontMetrics { virtual double getDescent () const =0; virtual bool verticalLayout () const =0; virtual uint32_t getChecksum () const =0; - virtual uint16_t firstChar () const =0; - virtual uint16_t lastChar () const =0; + virtual uint32_t firstChar () const =0; + virtual uint32_t lastChar () const =0; virtual bool isJFM () const {return false;} + virtual bool isOFM () const {return false;} static std::unique_ptr<FontMetrics> read (const std::string &fontname); }; -struct NullFontMetric : public FontMetrics { +struct NullFontMetrics : public FontMetrics { double getDesignSize () const override {return 1;} double getCharWidth (int c) const override {return 0;} double getCharHeight (int c) const override {return 0;} @@ -62,8 +65,8 @@ struct NullFontMetric : public FontMetrics { double getDescent () const override {return 0;} bool verticalLayout () const override {return false;} uint32_t getChecksum () const override {return 0;} - uint16_t firstChar () const override {return 0;} - uint16_t lastChar () const override {return 0;} + uint32_t firstChar () const override {return 0;} + uint32_t lastChar () const override {return 0;} }; diff --git a/dviware/dvisvgm/src/Ghostscript.cpp b/dviware/dvisvgm/src/Ghostscript.cpp index fa6f1eb0eb..a428c01b9a 100644 --- a/dviware/dvisvgm/src/Ghostscript.cpp +++ b/dviware/dvisvgm/src/Ghostscript.cpp @@ -256,7 +256,10 @@ string Ghostscript::revisionstr () { int major = rev / 1000; int minor = (rev - major*1000)/10; int patch = rev % 10; - revstr = to_string(major) + "." + to_string(minor) + "." + to_string(patch); + revstr = to_string(major) + "."; + if (minor > 0 && minor < 10) + revstr += '0'; + revstr += to_string(minor) + "." + to_string(patch); } } return revstr; diff --git a/dviware/dvisvgm/src/GraphicsPathParser.hpp b/dviware/dvisvgm/src/GraphicsPathParser.hpp index 41a850db27..099614757d 100644 --- a/dviware/dvisvgm/src/GraphicsPathParser.hpp +++ b/dviware/dvisvgm/src/GraphicsPathParser.hpp @@ -27,6 +27,22 @@ #include "GraphicsPath.hpp" #include "MessageException.hpp" +namespace internal { + template <typename NumType> + bool parse_number (std::istream &is, NumType &value) { + is >> value; + return !is.fail(); + } + + template <> + bool parse_number (std::istream &is, double &value) { + // don't use operator >> for parsing floating point values because it's implemented + // differently in libstdc++ and libc++. Instead, use our own function to read the + // value from the input stream. + return util::read_double(is, value); + } +} + struct GraphicsPathParserException : public MessageException { explicit GraphicsPathParserException (const std::string &msg) : MessageException(msg) {} @@ -48,8 +64,7 @@ class GraphicsPathParser { NumType parseNumberOfType (std::istream &is) const { is >> std::ws; NumType number; - is >> number; - if (is.fail()) + if (!internal::parse_number(is, number)) error("number expected", is); is >> std::ws; return number; diff --git a/dviware/dvisvgm/src/HtmlSpecialHandler.cpp b/dviware/dvisvgm/src/HtmlSpecialHandler.cpp index e86de7fb96..2f43ebde79 100644 --- a/dviware/dvisvgm/src/HtmlSpecialHandler.cpp +++ b/dviware/dvisvgm/src/HtmlSpecialHandler.cpp @@ -80,7 +80,7 @@ void HtmlSpecialHandler::dviEndPage (unsigned pageno, SpecialActions &actions) { } -vector<const char*> HtmlSpecialHandler::prefixes() const { +vector<const char*> HtmlSpecialHandler::prefixes () const { vector<const char*> pfx {"html:"}; return pfx; } diff --git a/dviware/dvisvgm/src/HtmlSpecialHandler.hpp b/dviware/dvisvgm/src/HtmlSpecialHandler.hpp index f722060538..33f564f63b 100644 --- a/dviware/dvisvgm/src/HtmlSpecialHandler.hpp +++ b/dviware/dvisvgm/src/HtmlSpecialHandler.hpp @@ -31,9 +31,10 @@ class HtmlSpecialHandler : public SpecialHandler { public: void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; - const char* name () const override {return "html";} const char* info () const override {return "hyperref specials";} - std::vector<const char*> prefixes() const override; + const char* name () const override {return handlerName();} + static const char* handlerName () {return "html";} + std::vector<const char*> prefixes () const override; protected: void dviEndPage (unsigned pageno, SpecialActions &actions) override; diff --git a/dviware/dvisvgm/src/JFM.cpp b/dviware/dvisvgm/src/JFM.cpp index c801618178..fc8fe56d8a 100644 --- a/dviware/dvisvgm/src/JFM.cpp +++ b/dviware/dvisvgm/src/JFM.cpp @@ -19,7 +19,6 @@ *************************************************************************/ #include <algorithm> -#include <cstring> #include <fstream> #include <sstream> #include "JFM.hpp" @@ -28,27 +27,27 @@ using namespace std; -JFM::JFM (istream &is) { +void JFM::read (istream &is) { is.seekg(0); StreamReader reader(is); - uint16_t id = uint16_t(reader.readUnsigned(2)); // JFM ID (9 or 11) + auto id = uint16_t(reader.readUnsigned(2)); // JFM ID (9 or 11) 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 - uint16_t lh = uint16_t(reader.readUnsigned(2)); // length of header in 4 byte words - uint16_t bc = uint16_t(reader.readUnsigned(2)); // smallest character code in font - uint16_t ec = uint16_t(reader.readUnsigned(2)); // largest character code in font - uint16_t nw = uint16_t(reader.readUnsigned(2)); // number of words in width table - uint16_t nh = uint16_t(reader.readUnsigned(2)); // number of words in height table - uint16_t nd = uint16_t(reader.readUnsigned(2)); // number of words in depth table - uint16_t ni = uint16_t(reader.readUnsigned(2)); // number of words in italic corr. table - uint16_t nl = uint16_t(reader.readUnsigned(2)); // number of words in glue/kern table - uint16_t nk = uint16_t(reader.readUnsigned(2)); // number of words in kern table - uint16_t ng = uint16_t(reader.readUnsigned(2)); // number of words in glue table - uint16_t np = uint16_t(reader.readUnsigned(2)); // number of font parameter words + auto nt = uint16_t(reader.readUnsigned(2)); // length of character type table + auto lf = uint16_t(reader.readUnsigned(2)); // length of entire file in 4 byte words + auto lh = uint16_t(reader.readUnsigned(2)); // length of header in 4 byte words + auto bc = uint16_t(reader.readUnsigned(2)); // smallest character code in font + auto ec = uint16_t(reader.readUnsigned(2)); // largest character code in font + auto nw = uint16_t(reader.readUnsigned(2)); // number of words in width table + auto nh = uint16_t(reader.readUnsigned(2)); // number of words in height table + auto nd = uint16_t(reader.readUnsigned(2)); // number of words in depth table + auto ni = uint16_t(reader.readUnsigned(2)); // number of words in italic corr. table + auto nl = uint16_t(reader.readUnsigned(2)); // number of words in glue/kern table + auto nk = uint16_t(reader.readUnsigned(2)); // number of words in kern table + auto ng = uint16_t(reader.readUnsigned(2)); // number of words in glue table + auto np = uint16_t(reader.readUnsigned(2)); // number of font parameter words if (7+nt+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ng+np != lf) throw FontMetricException("inconsistent length values"); @@ -57,7 +56,7 @@ JFM::JFM (istream &is) { readHeader(reader); is.seekg(28+lh*4); readTables(reader, nt, nw, nh, nd, ni); - is.seekg(4*(lf-np), ios::beg); + is.seekg(4*(lf-np)); readParameters(reader, np); // JFM files provide 9 parameters but we don't need all of them } @@ -69,7 +68,7 @@ void JFM::readTables (StreamReader &reader, int nt, int nw, int nh, int nd, int // support new JFM spec by texjporg uint32_t c = reader.readUnsigned(2); c += 0x10000 * reader.readUnsigned(1); - uint8_t t = uint8_t(reader.readUnsigned(1)); + auto t = uint8_t(reader.readUnsigned(1)); if (t > 0) { minchar = min(minchar, c); maxchar = max(maxchar, c); @@ -84,7 +83,7 @@ void JFM::readTables (StreamReader &reader, int nt, int nw, int nh, int nd, int // support new JFM spec by texjporg uint32_t c = reader.readUnsigned(2); c += 0x10000 * reader.readUnsigned(1); - uint8_t t = uint8_t(reader.readUnsigned(1)); + auto t = uint8_t(reader.readUnsigned(1)); if (c >= minchar) _charTypeTable[c-minchar] = t; } @@ -93,7 +92,7 @@ void JFM::readTables (StreamReader &reader, int nt, int nw, int nh, int nd, int } -int JFM::charIndex (int c) const { +size_t JFM::charIndex (int c) const { uint8_t chartype = 0; if (!_charTypeTable.empty() && uint32_t(c) >= _minchar && uint32_t(c) < _minchar+_charTypeTable.size()) chartype = _charTypeTable[c-_minchar]; diff --git a/dviware/dvisvgm/src/JFM.hpp b/dviware/dvisvgm/src/JFM.hpp index 110f0f3e1a..c24798073c 100644 --- a/dviware/dvisvgm/src/JFM.hpp +++ b/dviware/dvisvgm/src/JFM.hpp @@ -27,7 +27,8 @@ class JFM : public TFM { public: - explicit JFM (std::istream &is); + JFM () =default; + void read (std::istream &is) override; bool verticalLayout () const override {return _vertical;} bool isJFM () const override {return true;} uint32_t minChar () const {return _minchar;} @@ -35,11 +36,11 @@ class JFM : public TFM { protected: void readTables (StreamReader &reader, int nt, int nw, int nh, int nd, int ni); - int charIndex (int c) const override; + size_t charIndex (int c) const override; private: - uint32_t _minchar; ///< character code of first entry in character type table - bool _vertical; ///< true if metrics refer to vertical text layout + uint32_t _minchar=0; ///< character code of first entry in character type table + bool _vertical=false; ///< true if metrics refer to vertical text layout std::vector<uint8_t> _charTypeTable; }; diff --git a/dviware/dvisvgm/src/Makefile.am b/dviware/dvisvgm/src/Makefile.am index 1272a216f3..4ccab654f0 100644 --- a/dviware/dvisvgm/src/Makefile.am +++ b/dviware/dvisvgm/src/Makefile.am @@ -105,6 +105,7 @@ libdvisvgm_la_SOURCES = \ MetafontWrapper.hpp MetafontWrapper.cpp \ NoPsSpecialHandler.hpp NoPsSpecialHandler.cpp \ NumericRanges.hpp \ + OFM.hpp OFM.cpp \ Opacity.hpp Opacity.cpp \ PageRanges.hpp PageRanges.cpp \ PageSize.hpp PageSize.cpp \ diff --git a/dviware/dvisvgm/src/Makefile.in b/dviware/dvisvgm/src/Makefile.in index 411c96252d..a576da9bc4 100644 --- a/dviware/dvisvgm/src/Makefile.in +++ b/dviware/dvisvgm/src/Makefile.in @@ -144,17 +144,17 @@ am_libdvisvgm_la_OBJECTS = BasicDVIReader.lo Bezier.lo \ Ghostscript.lo HashFunction.lo HtmlSpecialHandler.lo \ HyperlinkManager.lo ImageToSVG.lo InputBuffer.lo \ InputReader.lo JFM.lo Length.lo MapLine.lo Matrix.lo \ - Message.lo MetafontWrapper.lo NoPsSpecialHandler.lo Opacity.lo \ - PageRanges.lo PageSize.lo PapersizeSpecialHandler.lo \ - PathClipper.lo PDFHandler.lo PDFParser.lo PdfSpecialHandler.lo \ - PDFToSVG.lo PreScanDVIReader.lo Process.lo psdefs.lo \ - PSInterpreter.lo PSPattern.lo PSPreviewFilter.lo \ - PsSpecialHandler.lo RangeMap.lo ShadingPatch.lo \ - SignalHandler.lo SourceInput.lo SpecialManager.lo \ - StreamReader.lo StreamWriter.lo Subfont.lo SVGCharHandler.lo \ - SVGCharHandlerFactory.lo SVGCharPathHandler.lo \ - SVGCharTspanTextHandler.lo SVGElement.lo SVGOutput.lo \ - SVGSingleCharTextHandler.lo SVGTree.lo System.lo \ + Message.lo MetafontWrapper.lo NoPsSpecialHandler.lo OFM.lo \ + Opacity.lo PageRanges.lo PageSize.lo \ + PapersizeSpecialHandler.lo PathClipper.lo PDFHandler.lo \ + PDFParser.lo PdfSpecialHandler.lo PDFToSVG.lo \ + PreScanDVIReader.lo Process.lo psdefs.lo PSInterpreter.lo \ + PSPattern.lo PSPreviewFilter.lo PsSpecialHandler.lo \ + RangeMap.lo ShadingPatch.lo SignalHandler.lo SourceInput.lo \ + SpecialManager.lo StreamReader.lo StreamWriter.lo Subfont.lo \ + SVGCharHandler.lo SVGCharHandlerFactory.lo \ + SVGCharPathHandler.lo SVGCharTspanTextHandler.lo SVGElement.lo \ + SVGOutput.lo SVGSingleCharTextHandler.lo SVGTree.lo System.lo \ TensorProductPatch.lo Terminal.lo TFM.lo ToUnicodeMap.lo \ TpicSpecialHandler.lo TriangularPatch.lo Unicode.lo utility.lo \ VFReader.lo XMLDocument.lo XMLNode.lo XMLParser.lo \ @@ -213,11 +213,12 @@ am__depfiles_remade = ./$(DEPDIR)/BasicDVIReader.Plo \ ./$(DEPDIR)/JFM.Plo ./$(DEPDIR)/Length.Plo \ ./$(DEPDIR)/MapLine.Plo ./$(DEPDIR)/Matrix.Plo \ ./$(DEPDIR)/Message.Plo ./$(DEPDIR)/MetafontWrapper.Plo \ - ./$(DEPDIR)/NoPsSpecialHandler.Plo ./$(DEPDIR)/Opacity.Plo \ - ./$(DEPDIR)/PDFHandler.Plo ./$(DEPDIR)/PDFParser.Plo \ - ./$(DEPDIR)/PDFToSVG.Plo ./$(DEPDIR)/PSInterpreter.Plo \ - ./$(DEPDIR)/PSPattern.Plo ./$(DEPDIR)/PSPreviewFilter.Plo \ - ./$(DEPDIR)/PageRanges.Plo ./$(DEPDIR)/PageSize.Plo \ + ./$(DEPDIR)/NoPsSpecialHandler.Plo ./$(DEPDIR)/OFM.Plo \ + ./$(DEPDIR)/Opacity.Plo ./$(DEPDIR)/PDFHandler.Plo \ + ./$(DEPDIR)/PDFParser.Plo ./$(DEPDIR)/PDFToSVG.Plo \ + ./$(DEPDIR)/PSInterpreter.Plo ./$(DEPDIR)/PSPattern.Plo \ + ./$(DEPDIR)/PSPreviewFilter.Plo ./$(DEPDIR)/PageRanges.Plo \ + ./$(DEPDIR)/PageSize.Plo \ ./$(DEPDIR)/PapersizeSpecialHandler.Plo \ ./$(DEPDIR)/PathClipper.Plo ./$(DEPDIR)/PdfSpecialHandler.Plo \ ./$(DEPDIR)/PreScanDVIReader.Plo ./$(DEPDIR)/Process.Plo \ @@ -587,6 +588,7 @@ libdvisvgm_la_SOURCES = \ MetafontWrapper.hpp MetafontWrapper.cpp \ NoPsSpecialHandler.hpp NoPsSpecialHandler.cpp \ NumericRanges.hpp \ + OFM.hpp OFM.cpp \ Opacity.hpp Opacity.cpp \ PageRanges.hpp PageRanges.cpp \ PageSize.hpp PageSize.cpp \ @@ -820,6 +822,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Message.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MetafontWrapper.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NoPsSpecialHandler.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OFM.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Opacity.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PDFHandler.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PDFParser.Plo@am__quote@ # am--include-marker @@ -1156,6 +1159,7 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/Message.Plo -rm -f ./$(DEPDIR)/MetafontWrapper.Plo -rm -f ./$(DEPDIR)/NoPsSpecialHandler.Plo + -rm -f ./$(DEPDIR)/OFM.Plo -rm -f ./$(DEPDIR)/Opacity.Plo -rm -f ./$(DEPDIR)/PDFHandler.Plo -rm -f ./$(DEPDIR)/PDFParser.Plo @@ -1299,6 +1303,7 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/Message.Plo -rm -f ./$(DEPDIR)/MetafontWrapper.Plo -rm -f ./$(DEPDIR)/NoPsSpecialHandler.Plo + -rm -f ./$(DEPDIR)/OFM.Plo -rm -f ./$(DEPDIR)/Opacity.Plo -rm -f ./$(DEPDIR)/PDFHandler.Plo -rm -f ./$(DEPDIR)/PDFParser.Plo diff --git a/dviware/dvisvgm/src/NoPsSpecialHandler.cpp b/dviware/dvisvgm/src/NoPsSpecialHandler.cpp index 2c17e30690..644b2220d8 100644 --- a/dviware/dvisvgm/src/NoPsSpecialHandler.cpp +++ b/dviware/dvisvgm/src/NoPsSpecialHandler.cpp @@ -39,7 +39,7 @@ void NoPsSpecialHandler::dviEndPage (unsigned pageno, SpecialActions &actions) { } -vector<const char*> NoPsSpecialHandler::prefixes() const { +vector<const char*> NoPsSpecialHandler::prefixes () const { vector<const char*> pfx {"header=", "psfile=", "PSfile=", "ps:", "ps::", "!", "\""}; return pfx; } diff --git a/dviware/dvisvgm/src/NoPsSpecialHandler.hpp b/dviware/dvisvgm/src/NoPsSpecialHandler.hpp index f7132d2c75..3cd89b98f8 100644 --- a/dviware/dvisvgm/src/NoPsSpecialHandler.hpp +++ b/dviware/dvisvgm/src/NoPsSpecialHandler.hpp @@ -26,9 +26,10 @@ class NoPsSpecialHandler : public SpecialHandler { public: bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; - const char* name () const override {return nullptr;} const char* info () const override {return nullptr;} - std::vector<const char*> prefixes() const override; + const char* name () const override {return nullptr;} + static const char* handlerName () {return nullptr;} + std::vector<const char*> prefixes () const override; protected: void dviEndPage (unsigned pageno, SpecialActions &actions) override; diff --git a/dviware/dvisvgm/src/OFM.cpp b/dviware/dvisvgm/src/OFM.cpp new file mode 100644 index 0000000000..63872671f1 --- /dev/null +++ b/dviware/dvisvgm/src/OFM.cpp @@ -0,0 +1,270 @@ +/************************************************************************* +** OFM.cpp ** +** ** +** This file is part of dvisvgm -- a fast DVI to SVG converter ** +** Copyright (C) 2005-2023 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 <cstdint> +#include <string> +#include "Length.hpp" +#include "OFM.hpp" + +using namespace std; + + +static uint32_t read_uint32 (StreamReader &reader, const string &name) { + uint32_t quad = reader.readUnsigned(4); + if (quad & 0x80000000) + throw FontMetricException("negative '"+name+"' value not permitted"); + return quad; +} + +void OFM::FileDimensions::read (StreamReader &reader) { + lf = read_uint32(reader, "lf"); + lh = read_uint32(reader, "lh"); + bc = read_uint32(reader, "bc"); + ec = read_uint32(reader, "ec"); + nw = read_uint32(reader, "nw"); + nh = read_uint32(reader, "nh"); + nd = read_uint32(reader, "nd"); + ni = read_uint32(reader, "ni"); + nl = read_uint32(reader, "nl"); + nk = read_uint32(reader, "nk"); + ne = read_uint32(reader, "ne"); + np = read_uint32(reader, "np"); + dir = read_uint32(reader, "font-dir"); +} + + +/** Reads the common beginning of OFM level 0 and 1 files, and initializes + * the corresponding members. + * @param[in] is reads from this stream + * @param[in] maxNumWidths maximum number of width entries + * @return the collected data (file offsets, table sizes, writing direction) */ +OFM::FileDimensions OFM::read (istream &is, uint32_t maxNumWidths) { + OFM::FileDimensions dim; + if (is) { + is.seekg(0); + StreamReader sr(is); + uint32_t ofmLevel = sr.readUnsigned(4); + if (ofmLevel > 1) + throw FontMetricException("OFM level "+to_string(ofmLevel)+" not supported"); + if (int(ofmLevel) != level()) + throw FontMetricException("OFM level "+to_string(level())+" expected but found level "+to_string(ofmLevel)); + dim.read(sr); + if (dim.nw > maxNumWidths) + throw FontMetricException("invalid size of width table"); + _firstChar = dim.bc; // smallest character code in font + _lastChar = dim.ec; // largest character code in font + _vertical = (dim.dir > 0); + } + return dim; +} + + +/** Reads and stores the required data from the OFM header. */ +void OFM::readHeader (StreamReader &reader) { + _checksum = reader.readUnsigned(4); + _designSize = double(FixWord(reader.readSigned(4)))*Length::pt2bp; +} + + +/** Reads a sequence of values and stores them in a vector. + * @param[in] reader the TFM data is read from this object + * @param[out] words vector taking the read words + * @param[in] n number of entries to read */ +static void read_words (StreamReader &reader, vector<FixWord> &words, size_t n) { + words.resize(n); + for (size_t i=0; i < n; i++) + words[i] = reader.readSigned(4); +} + + +/** Reads and stores the required OFM tables (characters widths, heights, depths, ...). */ +void OFM::readTables (StreamReader &reader, const FileDimensions &dim) { + read_words(reader, _widthTable, dim.nw); + read_words(reader, _heightTable, dim.nh); + read_words(reader, _depthTable, dim.nd); + read_words(reader, _italicTable, dim.ni); + reader.skip(8*dim.nl + 4*dim.nk + 8*dim.ne); + read_words(reader, _params, min(dim.np, 7u)); // we only need params 0-6 + _params.resize(7, 0); // ensure 7 parameters + for (FixWord h : _heightTable) + _ascent = max(_ascent, h); + for (FixWord d : _depthTable) + _descent = max(_descent, d); +} + + +/** Returns the character index for a given character or -1 if + * the OFM file doesn't contain information on the character. */ +size_t OFM::charIndex (int c) const { + uint32_t chr=c; + if (chr < _firstChar || chr > _lastChar || size_t(chr-_firstChar) >= numCharInfos()) + return -1; + return chr-_firstChar; +} + + +/** Returns the width of a character in bp units. */ +double OFM::getCharWidth (int c) const { + auto cindex = charIndex(c); + if (cindex == size_t(-1)) + return 0; + return double(_widthTable[widthIndex(cindex)])*_designSize; +} + + +/** Returns the height of a character in bp units. */ +double OFM::getCharHeight (int c) const { + auto cindex = charIndex(c); + if (cindex == size_t(-1)) + return 0; + return double(_heightTable[heightIndex(cindex)])*_designSize; +} + + +/** Returns the depth of a character in bp units. */ +double OFM::getCharDepth (int c) const { + auto cindex = charIndex(c); + if (cindex == size_t(-1)) + return 0; + return double(_depthTable[depthIndex(cindex)])*_designSize; +} + + +/** Returns the italic correction width of a character in bp units. */ +double OFM::getItalicCorr (int c) const { + auto cindex = charIndex(c); + if (cindex == size_t(-1)) + return 0; + return double(_italicTable[italicIndex(cindex)])*_designSize; +} + +/** Returns the optimal space width between words in bp units. */ +double OFM::getSpace () const { + return _params.empty() ? 0 : double(_params[1])*_designSize; +} + + +/** Returns the amount of glue stretching between words in bp units. */ +double OFM::getSpaceStretch () const { + return _params.empty() ? 0 : double(_params[2])*_designSize; +} + + +/** Returns the amount of glue shrinking between words in bp units. */ +double OFM::getSpaceShrink () const { + return _params.empty() ? 0 : double(_params[3])*_designSize; +} + + +/** Returns the size of one EM unit in bp units. */ +double OFM::getQuad () const { + if (_params.empty() || _params[5] == 0) + return _designSize; + return double(_params[5])*_designSize; +} + +///////////////////////////////////////////////////////////////////////////// + +/** Reads and stores the required data from an OFM level 0 data stream. */ +void OFM0::read (std::istream &is) { + auto dim = OFM::read(is, 0xFFFF); + if (_firstChar >= _lastChar || _lastChar > 0xFFFF) + throw FontMetricException("character codes out of range"); + if (14 + dim.lh + 2*(dim.ec-dim.bc+1) + dim.nw + dim.nh + dim.nd + dim.ni +2*dim.nl + dim.nk + 2*dim.ne + dim.np != dim.lf) + throw FontMetricException("inconsistent table size values"); + StreamReader sr(is); + readHeader(sr); + sr.seek(56 + dim.lh*4); // move to char info table + readCharInfos(sr); + readTables(sr, dim); +} + + +/** Reads and stores the required data from the char-info section. + * Expects the stream pointer to be located on the first byte of the first char-info entry. */ +void OFM0::readCharInfos (StreamReader &reader) { + auto numChars = _lastChar-_firstChar+1; + _charInfos.resize(numChars); + for (size_t i=0; i < numChars; i++) { + _charInfos[i] = uint64_t(reader.readUnsigned(4)) << 32; + _charInfos[i] |= reader.readUnsigned(4); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/** Reads and stores the required data from an OFM level 1 data stream. */ +void OFM1::read (std::istream &is) { + auto dim = OFM::read(is, 0x110000); + if (_firstChar >= _lastChar || _lastChar > 0x10FFFF) + throw FontMetricException("character codes out of range"); + StreamReader sr(is); + uint32_t nco = sr.readUnsigned(4); // OFM1: offset of char-info table (in 4-byte words) + _ncw = sr.readUnsigned(4); // total number of character info words in OFM file + _npc = sr.readUnsigned(4); // number of parameters per character + // check validity of number entries for new (OFM level 1) table types + size_t sum=0, numTables=0; + for (int i=0; i < 12; i++) { + uint32_t num = sr.readUnsigned(4); + sum += num; + if (i%2 == 0) + numTables += num; + } + if (numTables > 32) + throw FontMetricException("too many OFM level 1 tables"); + if (29 + dim.lh + _ncw + dim.nw + dim.nh + dim.nd + dim.ni +2*dim.nl + dim.nk + 2*dim.ne + dim.np + sum != dim.lf) + throw FontMetricException("inconsistent table size values"); + readHeader(sr); + sr.seek(4*nco); // move to char info table + readCharInfos(sr); + readTables(sr, dim); +} + + +/** Reads and stores the required data from the char-info section. + * Expects the stream pointer to be located on the first byte of the first char-info entry. */ +void OFM1::readCharInfos (StreamReader &reader) { + auto numCharInfos = _ncw/(3 + _npc/2); // number of char info entries in file + auto numChars = _lastChar-_firstChar+1; + size_t numCharsRead=0; + _charInfos.reserve(numChars); + while (numCharsRead <= numChars && numCharInfos > 0) { + CharInfo info; + info.widthIndex = uint16_t(reader.readUnsigned(2)); + info.heightIndex = uint8_t(reader.readUnsigned(1)); + info.depthIndex = uint8_t(reader.readUnsigned(1)); + info.italicIndex = uint8_t(reader.readUnsigned(1)); + reader.skip(3); // skip RFU, ext-tag, tag, and remainder + auto repeats = uint16_t(reader.readUnsigned(2)); + if (numCharsRead + repeats > numChars) + throw FontMetricException("number of characters exceeded due to too many char-info repeats"); + reader.skip(_npc*2); // skip parameters + if (_npc%2 == 0) + reader.skip(2); // skip padding bytes + for (int j=0; j <= repeats; j++) + _charInfos.push_back(info); + numCharsRead += repeats+1; + numCharInfos--; + } + if (numCharInfos > 0) + throw FontMetricException("more char-info entries present than characters"); + if (numCharsRead < numChars) + throw FontMetricException("not all characters covered by char-info entries"); +} diff --git a/dviware/dvisvgm/src/OFM.hpp b/dviware/dvisvgm/src/OFM.hpp new file mode 100644 index 0000000000..560bc1f634 --- /dev/null +++ b/dviware/dvisvgm/src/OFM.hpp @@ -0,0 +1,135 @@ +/************************************************************************* +** OFM.hpp ** +** ** +** This file is part of dvisvgm -- a fast DVI to SVG converter ** +** Copyright (C) 2005-2023 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 OFM_HPP +#define OFM_HPP + +#include <vector> +#include "FixWord.hpp" +#include "FontMetrics.hpp" +#include "StreamReader.hpp" + +class OFM : public FontMetrics { + public: + virtual int level () const =0; + bool isOFM () const override {return true;} + uint32_t getChecksum () const override {return _checksum;} + uint32_t firstChar () const override {return _firstChar;} + uint32_t lastChar () const override {return _lastChar;} + bool verticalLayout () const override {return _vertical;} + double getDesignSize () const override {return _designSize;} + double getAscent () const override {return double(_ascent)*_designSize;} + double getDescent () const override {return double(_descent)*_designSize;} + double getCharWidth (int c) const override; + double getCharHeight (int c) const override; + double getCharDepth (int c) const override; + double getItalicCorr (int c) const override; + double getSpace () const override; + double getSpaceStretch () const override; + double getSpaceShrink () const override; + double getQuad () const override; + using FontMetrics::read; + + protected: + struct FileDimensions { + uint32_t lf=0; // length of file in 4-byte words + uint32_t lh=0; // length of header data in 4-byte words + uint32_t bc=0; // smallest character code + uint32_t ec=0; // largest character code + uint32_t nw=0; // number of width entries + uint32_t nh=0; // number of height entries + uint32_t nd=0; // number of depth entries + uint32_t ni=0; // number of italic correction entries + uint32_t nl=0; // number of lig-kern entries + uint32_t nk=0; // number of kern entries + uint32_t ne=0; // number of extensible character entries + uint32_t np=0; // number of font parameter words + uint32_t dir=0; // writing direction of font + void read (StreamReader &reader); + }; + + FileDimensions read (std::istream &is, uint32_t maxNumWidths); + void readHeader (StreamReader &reader); + void readTables (StreamReader &reader, const FileDimensions &dim); + size_t charIndex (int c) const; + virtual size_t numCharInfos () const =0; + virtual size_t widthIndex (size_t n) const =0; + virtual size_t heightIndex (size_t n) const =0; + virtual size_t depthIndex (size_t n) const =0; + virtual size_t italicIndex (size_t n) const =0; + + protected: + uint32_t _checksum=0; + uint32_t _firstChar=0, _lastChar=0; + double _designSize=0; ///< design size of the font in PS points (72bp = 1in) + std::vector<FixWord> _widthTable; ///< character widths in design size units + std::vector<FixWord> _heightTable; ///< character height in design size units + std::vector<FixWord> _depthTable; ///< character depth in design size units + std::vector<FixWord> _italicTable; ///< italic corrections in design size units + std::vector<FixWord> _params; ///< values of the OFM's param section + FixWord _ascent=0, _descent=0; ///< max. height and depth + bool _vertical=false; ///< true if metrics refer to vertical text layout +}; + + +class OFM0 : public OFM { + public: + void read (std::istream &is) override; + int level () const override {return 0;} + + protected: + void readCharInfos (StreamReader &reader); + size_t numCharInfos () const override {return _charInfos.size();} + size_t widthIndex (size_t n) const override {return (_charInfos[n] >> 48) & 0xFFFF;} + size_t heightIndex (size_t n) const override {return (_charInfos[n] >> 40) & 0xFF;} + size_t depthIndex (size_t n) const override {return (_charInfos[n] >> 32) & 0xFF;} + size_t italicIndex (size_t n) const override {return (_charInfos[n] >> 24) & 0xFF;} + + private: + std::vector<uint64_t> _charInfos; +}; + + +class OFM1 : public OFM { + struct CharInfo { + uint16_t widthIndex=0; + uint8_t heightIndex=0; + uint8_t depthIndex=0; + uint8_t italicIndex=0; + }; + public: + void read (std::istream &is) override; + int level () const override {return 1;} + + protected: + void readCharInfos (StreamReader &reader); + size_t numCharInfos () const override {return _charInfos.size();} + size_t widthIndex (size_t n) const override {return _charInfos[n].widthIndex;} + size_t heightIndex (size_t n) const override {return _charInfos[n].heightIndex;} + size_t depthIndex (size_t n) const override {return _charInfos[n].depthIndex;} + size_t italicIndex (size_t n) const override {return _charInfos[n].italicIndex;} + + private: + uint32_t _ncw=0; + uint32_t _npc=0; + std::vector<CharInfo> _charInfos; +}; + +#endif diff --git a/dviware/dvisvgm/src/PDFHandler.cpp b/dviware/dvisvgm/src/PDFHandler.cpp index ba88e69852..9d374e9874 100644 --- a/dviware/dvisvgm/src/PDFHandler.cpp +++ b/dviware/dvisvgm/src/PDFHandler.cpp @@ -139,6 +139,12 @@ string PDFHandler::mutoolVersion () { } +/** Returns the total number of pages of a PDF file. */ +int PDFHandler::numberOfPages (const string &fname) { + return parse_value<int>(mtShow("\""+fname+"\"", "trailer/Root/Pages/Count")); +} + + /** Converts a single page of a PDF file to SVG. If no context element is given, * the SVG page contents are added to a page group element of the SVG tree. * Otherwise, they are added to the context element which is not inserted into @@ -175,6 +181,7 @@ unique_ptr<SVGElement> PDFHandler::convert (const string &fname, int pageno, uni void PDFHandler::initFile (const string &fname) { finishFile(); _fname = FilePath(fname, true).absolute(); + _fname = "\"" + _fname + "\""; _numPages = parse_value<int>(mtShow("trailer/Root/Pages/Count")); // extract image and font files from the PDF string cwd = FileSystem::getcwd(); @@ -472,7 +479,7 @@ void PDFHandler::doClipPath (XMLElement *trcClipPathElement) { auto clipPathElement = util::make_unique<SVGElement>("clipPath"); clipPathElement->addAttribute("id", id); auto groupElement = util::make_unique<SVGElement>("g"); - _clipPathStack.emplace(ClipPathData{std::move(id), groupElement.get()}); + _clipPathStack.emplace(std::move(id), groupElement.get()); groupElement->setClipPathUrl(_clipPathStack.top().id); clipPathElement->append(std::move(pathElement)); _svg->appendToDefs(std::move(clipPathElement)); @@ -496,7 +503,7 @@ void PDFHandler::doClipStrokePath (XMLElement *trcClipStrokePathElement) { maskElement->append(std::move(pathElement)); auto groupElement = util::make_unique<SVGElement>("g"); groupElement->setMaskUrl(id); - _clipPathStack.emplace(ClipPathData{std::move(id), groupElement.get()}); + _clipPathStack.emplace(std::move(id), groupElement.get()); _svg->appendToDefs(std::move(maskElement)); _svg->pushPageContext(std::move(groupElement)); } @@ -508,7 +515,7 @@ void PDFHandler::doClipText (XMLElement *trcClipTextElement) { auto clipPathElement = util::make_unique<SVGElement>("clipPath"); clipPathElement->addAttribute("id", id); auto groupElement = util::make_unique<SVGElement>("g"); - _clipPathStack.emplace(ClipPathData{std::move(id), groupElement.get()}); + _clipPathStack.emplace(std::move(id), groupElement.get()); groupElement->setClipPathUrl(_clipPathStack.top().id); SVGElement *cpElementPtr = clipPathElement.get(); _svg->pushPageContext(std::move(clipPathElement)); @@ -630,9 +637,14 @@ void PDFHandler::doFillText (XMLElement *trcFillTextElement) { auto trm = parse_attr_value<vector<double>>(spanElement, "trm"); if (trm.size() < 4 || trm[0] == 0) continue; - auto fontname = strip_subset_prefix(parse_attr_value<string>(spanElement, "font")); - string filename; + auto fontname = parse_attr_value<string>(spanElement, "font"); auto it = _objDict.find(fontname); + if (it == _objDict.end()) { + // try to lookup font without subfont prefix + fontname = strip_subset_prefix(fontname); + it = _objDict.find(fontname); + } + string filename; if (it != _objDict.end()) filename = it->second.fname; if (filename.empty()) @@ -789,11 +801,10 @@ void PDFHandler::collectObjects () { for (auto &entry : _extractedFiles) { if (entry.second.substr(0, 5) == "font-") { string filepath = tmpdir+entry.second; // path to font file - string fontname = strip_subset_prefix(FontEngine::instance().getPSName(filepath)); - // If the extracted font file doesn't contain a font name, - // try to get it from the corresponding PDF object. - if (fontname.empty()) - fontname = mtShow(to_string(entry.first)+"/FontName", SearchPattern(R"(/(\w+))", "$1")); + string psFontname = FontEngine::instance().getPSName(filepath); + string fontname = mtShow(to_string(entry.first) + "/FontName", SearchPattern(R"(/((\w|[+-])+))", "$1")); + if (!psFontname.empty() && fontname.find('+') == string::npos) + fontname = std::move(psFontname); _objDict.emplace(fontname, ObjID(entry.first, 0, filepath)); } } diff --git a/dviware/dvisvgm/src/PDFHandler.hpp b/dviware/dvisvgm/src/PDFHandler.hpp index 3400a1d69d..708c9969ef 100644 --- a/dviware/dvisvgm/src/PDFHandler.hpp +++ b/dviware/dvisvgm/src/PDFHandler.hpp @@ -58,6 +58,7 @@ class PDFHandler { std::unique_ptr<SVGElement> convert (const std::string &fname, int pageno, std::unique_ptr<SVGElement> context=nullptr); static std::string mutoolVersion (); static bool available () {return !mutoolVersion().empty();} + static int numberOfPages (const std::string &fname); protected: static std::string mutool (const std::string &cmd, bool readFromStderr=false); diff --git a/dviware/dvisvgm/src/PDFParser.cpp b/dviware/dvisvgm/src/PDFParser.cpp index b92ce54e7f..6a8fa2f3cf 100644 --- a/dviware/dvisvgm/src/PDFParser.cpp +++ b/dviware/dvisvgm/src/PDFParser.cpp @@ -275,7 +275,7 @@ static PDFStream parse_stream (InputReader &ir, const char *delim) { do ir.skipUntil("endstream"); while (ir.peek() >= 0 && !strchr(delim, ir.peek())); // ensure delimiter after "endstream" - return PDFStream(); + return {}; } @@ -289,7 +289,7 @@ static PDFIndirectObject parse_indirect_object (InputReader &ir, const char *del const int *objno = objects.back().get<int>(); objects.pop_back(); if (objno && genno) - return PDFIndirectObject(*objno, *genno); + return {*objno, *genno}; } throw PDFException("object and generation number expected before 'obj'"); } @@ -302,7 +302,7 @@ static PDFObjectRef parse_object_ref (vector<PDFObject> &objects) { const int *objno = objects.back().get<int>(); objects.pop_back(); if (objno && genno) - return PDFObjectRef(*objno, *genno); + return {*objno, *genno}; } throw PDFException("object and generation number expected before 'R'"); } diff --git a/dviware/dvisvgm/src/PDFToSVG.cpp b/dviware/dvisvgm/src/PDFToSVG.cpp index b0e3df58ba..9cb30d3971 100644 --- a/dviware/dvisvgm/src/PDFToSVG.cpp +++ b/dviware/dvisvgm/src/PDFToSVG.cpp @@ -45,7 +45,7 @@ void PDFToSVG::checkGSAndFileFormat () { if (!PDFHandler::available()) { ostringstream oss; if (gsVersion() > 0) { - oss << "To process PDF files, either Ghostscript < 10.1 or mutool is required.\n"; + oss << "To process PDF files, either Ghostscript < 10.01.0 or mutool is required.\n"; oss << "The installed Ghostscript version " << Ghostscript().revisionstr() << " isn't supported.\n"; throw MessageException(oss.str()); } @@ -59,7 +59,10 @@ void PDFToSVG::checkGSAndFileFormat () { /** Returns the total number of pages in the PDF file. */ int PDFToSVG::totalPageCount () const { if (_totalPageCount < 0) { - _totalPageCount = psInterpreter().pdfPageCount(filename()); + if (_useGS) + _totalPageCount = psInterpreter().pdfPageCount(filename()); + else + _totalPageCount = PDFHandler::numberOfPages(filename()); if (_totalPageCount < 1) throw MessageException("can't retrieve number of pages from file " + filename()); } diff --git a/dviware/dvisvgm/src/Pair.hpp b/dviware/dvisvgm/src/Pair.hpp index c1fdd5b6d5..176183ecea 100644 --- a/dviware/dvisvgm/src/Pair.hpp +++ b/dviware/dvisvgm/src/Pair.hpp @@ -22,6 +22,7 @@ #define PAIR_HPP #include <cmath> +#include <cstdint> #include <ostream> #include "macros.hpp" diff --git a/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp b/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp index b070643f6d..81169dbbb0 100644 --- a/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp +++ b/dviware/dvisvgm/src/PapersizeSpecialHandler.cpp @@ -92,7 +92,7 @@ void PapersizeSpecialHandler::dviEndPage (unsigned pageno, SpecialActions &actio } -vector<const char*> PapersizeSpecialHandler::prefixes() const { +vector<const char*> PapersizeSpecialHandler::prefixes () const { vector<const char*> pfx {"papersize="}; return pfx; } diff --git a/dviware/dvisvgm/src/PapersizeSpecialHandler.hpp b/dviware/dvisvgm/src/PapersizeSpecialHandler.hpp index 621de709db..2f3e9e863f 100644 --- a/dviware/dvisvgm/src/PapersizeSpecialHandler.hpp +++ b/dviware/dvisvgm/src/PapersizeSpecialHandler.hpp @@ -34,8 +34,9 @@ class PapersizeSpecialHandler : public SpecialHandler { void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; const char* info () const override {return "special to set the page size";} - const char* name () const override {return "papersize";} - std::vector<const char*> prefixes() const override; + const char* name () const override {return handlerName();} + static const char* handlerName () {return "papersize";} + std::vector<const char*> prefixes () const override; void storePaperSize (unsigned pageno, Length width, Length height); void reset () {_pageSizes.clear();} diff --git a/dviware/dvisvgm/src/PathClipper.cpp b/dviware/dvisvgm/src/PathClipper.cpp index 50030f9657..14588f8981 100644 --- a/dviware/dvisvgm/src/PathClipper.cpp +++ b/dviware/dvisvgm/src/PathClipper.cpp @@ -58,7 +58,7 @@ class FlattenActions : public CurvedPath::IterationActions { if (p == _currentPoint && !_currentPoly.empty()) return; closepath(); - _currentPoly.emplace_back(IntPoint(to_cInt(p.x()), to_cInt(p.y()), 0)); + _currentPoly.emplace_back(to_cInt(p.x()), to_cInt(p.y()), 0); _currentPoint = _startPoint = p; } @@ -66,10 +66,10 @@ class FlattenActions : public CurvedPath::IterationActions { if (p == _currentPoint && !_currentPoly.empty()) return; if (_currentPoly.empty()) // this shouldn't happen but in case it does... - _currentPoly.emplace_back(IntPoint(0, 0, 0)); // ...add a start point first + _currentPoly.emplace_back(0, 0, 0); // ...add a start point first _numLines--; _currentPoly.back().Z.label2 = _numLines; - _currentPoly.emplace_back(IntPoint(to_cInt(p.x()), to_cInt(p.y()), ZType(_numLines, 0))); + _currentPoly.emplace_back(to_cInt(p.x()), to_cInt(p.y()), ZType(_numLines, 0)); _currentPoint = p; } @@ -100,7 +100,7 @@ class FlattenActions : public CurvedPath::IterationActions { protected: void addCurvePoints (const CubicBezier &bezier) { if (_currentPoly.empty()) // this shouldn't happen but in case it does, ... - _currentPoly.emplace_back(IntPoint(0, 0, 0)); // ...add a start point first + _currentPoly.emplace_back(0, 0, 0); // ...add a start point first vector<DPair> points; // points of flattened curve vector<double> t; // corresponding 'time' parameters bezier.approximate(0.01, points, &t); @@ -113,7 +113,7 @@ class FlattenActions : public CurvedPath::IterationActions { continue; _currentPoly.back().Z.label2 = ZLabel(_curves.size(), t[i-1]); ZLabel label(_curves.size(), t[i]); - _currentPoly.emplace_back(IntPoint(to_cInt(p.x()), to_cInt(p.y()), ZType(label, label))); + _currentPoly.emplace_back(to_cInt(p.x()), to_cInt(p.y()), ZType(label, label)); _currentPoint = p; } } @@ -211,7 +211,7 @@ inline ZLabel division_label (const IntPoint &p1, const IntPoint &p2, const IntP int32_t id = segment_id(p1, p2, t1, t2); if (id > 0) s = t1+(t2-t1)*division_ratio(p1, p2, q); - return ZLabel(id, s); + return {id, s}; } diff --git a/dviware/dvisvgm/src/PdfSpecialHandler.cpp b/dviware/dvisvgm/src/PdfSpecialHandler.cpp index 0af7303e8d..b3128512c9 100644 --- a/dviware/dvisvgm/src/PdfSpecialHandler.cpp +++ b/dviware/dvisvgm/src/PdfSpecialHandler.cpp @@ -283,7 +283,7 @@ void PdfSpecialHandler::dviEndPage (unsigned pageno, SpecialActions &actions) { } -vector<const char*> PdfSpecialHandler::prefixes() const { +vector<const char*> PdfSpecialHandler::prefixes () const { vector<const char*> pfx {"pdf:"}; return pfx; } diff --git a/dviware/dvisvgm/src/PdfSpecialHandler.hpp b/dviware/dvisvgm/src/PdfSpecialHandler.hpp index b5267939d7..4c5509d093 100644 --- a/dviware/dvisvgm/src/PdfSpecialHandler.hpp +++ b/dviware/dvisvgm/src/PdfSpecialHandler.hpp @@ -27,11 +27,12 @@ class StreamInputReader; class PdfSpecialHandler : public SpecialHandler { public: - const char* info () const override {return "PDF hyperlink, font map, and pagesize specials";} - const char* name () const override {return "pdf";} - std::vector<const char*> prefixes() const override; void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; + const char* info () const override {return "PDF hyperlink, font map, and pagesize specials";} + const char* name () const override {return handlerName();} + static const char* handlerName () {return "pdf";} + std::vector<const char*> prefixes () const override; protected: // handlers for corresponding PDF specials diff --git a/dviware/dvisvgm/src/Process.cpp b/dviware/dvisvgm/src/Process.cpp index c79f6e9526..b7fb3929b8 100644 --- a/dviware/dvisvgm/src/Process.cpp +++ b/dviware/dvisvgm/src/Process.cpp @@ -74,13 +74,13 @@ string Subprocess::read (const SearchPattern &pattern, bool *error) { const size_t bytesRead = readFromPipe(&_rdbuf[_bufstartpos], _rdbuf.size()-_bufstartpos, error); const size_t bufendpos = _bufstartpos + bytesRead; smatch matches; - size_t matchendpos= bufendpos; // buffer position after last match + size_t matchendpos = bufendpos; // buffer position after last match if (pattern.search.empty()) result.assign(_rdbuf.data(), matchendpos); else { bool matched=false; regex re(pattern.search); - for (auto it = cregex_iterator(&_rdbuf[0], &_rdbuf[bufendpos], re); it != cregex_iterator(); ++it) { + for (auto it = cregex_iterator(&_rdbuf[0], &_rdbuf[0]+bufendpos, re); it != cregex_iterator(); ++it) { result += it->format(pattern.replace, regex_constants::format_no_copy); matchendpos = it->position() + it->length(); matched = true; @@ -121,11 +121,8 @@ bool Process::run (string *out, const SearchPattern &pattern, PipeFlags flags) { if (out) out->clear(); for (;;) { - if (out) { - string chunk; - subprocess.readFromPipe(chunk, pattern); - *out += chunk; - } + if (out) + subprocess.readFromPipe(*out, pattern); Subprocess::State state = subprocess.state(); if (state != Subprocess::State::RUNNING) return state == Subprocess::State::FINISHED; diff --git a/dviware/dvisvgm/src/PsSpecialHandler.cpp b/dviware/dvisvgm/src/PsSpecialHandler.cpp index 5dfdfc0852..cb3a8a6bac 100644 --- a/dviware/dvisvgm/src/PsSpecialHandler.cpp +++ b/dviware/dvisvgm/src/PsSpecialHandler.cpp @@ -1378,9 +1378,9 @@ void PsSpecialHandler::ClippingStack::push (const Path &path, int saveID) { if (!_stack.empty()) prependedPath = _stack.top().prependedPath; if (path.empty()) - _stack.emplace(Entry(saveID)); + _stack.emplace(saveID); else - _stack.emplace(Entry(path, ++_maxID, saveID)); + _stack.emplace(path, ++_maxID, saveID); _stack.top().prependedPath = prependedPath; } @@ -1470,7 +1470,7 @@ void PsSpecialHandler::ClippingStack::setPrependedPath () { } -vector<const char*> PsSpecialHandler::prefixes() const { +vector<const char*> PsSpecialHandler::prefixes () const { vector<const char*> pfx { "header=", // read and execute PS header file prior to the following PS statements "pdffile=", // process PDF file diff --git a/dviware/dvisvgm/src/PsSpecialHandler.hpp b/dviware/dvisvgm/src/PsSpecialHandler.hpp index 695eb78d03..fb44e4bfa4 100644 --- a/dviware/dvisvgm/src/PsSpecialHandler.hpp +++ b/dviware/dvisvgm/src/PsSpecialHandler.hpp @@ -91,11 +91,12 @@ class PsSpecialHandler : public SpecialHandler, protected PSActions { public: PsSpecialHandler (); ~PsSpecialHandler () override; - const char* name () const override {return "ps";} - const char* info () const override {return "dvips PostScript specials";} - std::vector<const char*> prefixes() const override; void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; + const char* info () const override {return "dvips PostScript specials";} + const char* name () const override {return handlerName();} + static const char* handlerName () {return "ps";} + std::vector<const char*> prefixes () const override; void setDviScaleFactor (double dvi2bp) override {_previewFilter.setDviScaleFactor(dvi2bp);} void enterBodySection (); PSInterpreter& psInterpreter () {return _psi;} diff --git a/dviware/dvisvgm/src/SVGCharPathHandler.cpp b/dviware/dvisvgm/src/SVGCharPathHandler.cpp index fc59d2280e..2c753d2e5c 100644 --- a/dviware/dvisvgm/src/SVGCharPathHandler.cpp +++ b/dviware/dvisvgm/src/SVGCharPathHandler.cpp @@ -60,7 +60,7 @@ void SVGCharPathHandler::appendChar (uint32_t c, double x, double y) { // Apply text color changes only if the color of the entire font is black. // Glyphs of non-black fonts (e.g. defined in a XeTeX document) can't change their color. CharProperty<Color> &color = (_fontColor.get() != Color::BLACK) ? _fontColor : _color; - bool applyColor = color.get() != Color::BLACK; + bool applyColor = color.get() != Color::BLACK || (SVGElement::USE_CURRENTCOLOR && SVGElement::CURRENTCOLOR == Color::BLACK); bool applyMatrix = !_matrix->isIdentity(); bool applyOpacity = !_opacity->isFillDefault(); if (!_groupNode) { @@ -85,16 +85,19 @@ void SVGCharPathHandler::appendChar (uint32_t c, double x, double y) { GlyphMetrics metrics; font->getGlyphMetrics(c, _vertical, metrics); x -= metrics.wl; - if (auto pf = font_cast<const PhysicalFont*>(font)) { - // Center glyph between top and bottom border of the TFM box. - // This is just an approximation used until I find a way to compute - // the exact location in vertical mode. - GlyphMetrics exact_metrics; - pf->getExactGlyphBox(c, exact_metrics, false, nullptr); - y += exact_metrics.h+(metrics.d-exact_metrics.h-exact_metrics.d)/2; + if (_vertical) { + auto physicalFont = font_cast<const PhysicalFont *>(font); + if (!physicalFont) + y += metrics.d; + else { + // Center glyph between top and bottom border of the TFM box. + // This is just an approximation used until I find a way to compute + // the exact location in vertical mode. + GlyphMetrics exact_metrics; + physicalFont->getExactGlyphBox(c, exact_metrics, false, nullptr); + y += exact_metrics.h + (metrics.d - exact_metrics.h - exact_metrics.d) / 2; + } } - else - y += metrics.d; } Matrix rotation(1); if (_vertical && !font->verticalLayout()) { diff --git a/dviware/dvisvgm/src/SVGCharTspanTextHandler.cpp b/dviware/dvisvgm/src/SVGCharTspanTextHandler.cpp index 9c23f33050..426934f426 100644 --- a/dviware/dvisvgm/src/SVGCharTspanTextHandler.cpp +++ b/dviware/dvisvgm/src/SVGCharTspanTextHandler.cpp @@ -46,7 +46,7 @@ void SVGCharTspanTextHandler::appendChar (uint32_t c, double x, double y) { } // Apply text color changes only if the color of the entire font is black. // Glyphs of non-black fonts (e.g. defined in a XeTeX document) can't change their color. - bool applyColor = _color.get() != Color::BLACK && _font.get()->color() == Color::BLACK; + bool applyColor = _color.get() != Color::BLACK || (SVGElement::USE_CURRENTCOLOR && _font.get()->color() == Color::BLACK); bool applyOpacity = !_opacity->isFillDefault(); if (_xchanged || _ychanged || (_color.changed() && applyColor) || (_opacity.changed() && applyOpacity)) { _tspanNode = pushContextNode(util::make_unique<SVGElement>("tspan")); diff --git a/dviware/dvisvgm/src/SVGElement.cpp b/dviware/dvisvgm/src/SVGElement.cpp index 68f7d2231c..b0f1db925c 100644 --- a/dviware/dvisvgm/src/SVGElement.cpp +++ b/dviware/dvisvgm/src/SVGElement.cpp @@ -27,6 +27,9 @@ using namespace std; +bool SVGElement::USE_CURRENTCOLOR=false; +Color SVGElement::CURRENTCOLOR; + void SVGElement::setClipPathUrl (const string &url) { if (!url.empty()) @@ -41,7 +44,9 @@ void SVGElement::setClipRule (FillRule rule) { void SVGElement::setFillColor (Color color, bool skipBlack) { - if (color != Color::BLACK || !skipBlack) + if (USE_CURRENTCOLOR && color == CURRENTCOLOR) + addAttribute("fill", "currentColor"); + else if (color != Color::BLACK || !skipBlack) addAttribute("fill", color.svgColorString()); } @@ -108,7 +113,7 @@ void SVGElement::setPoints (const vector<DPair> &points) { void SVGElement::setStrokeColor (Color color) { - addAttribute("stroke", color.svgColorString()); + addAttribute("stroke", USE_CURRENTCOLOR && color == CURRENTCOLOR ? "currentColor" : color.svgColorString()); } diff --git a/dviware/dvisvgm/src/SVGElement.hpp b/dviware/dvisvgm/src/SVGElement.hpp index 632b6a4a84..3197cfc115 100644 --- a/dviware/dvisvgm/src/SVGElement.hpp +++ b/dviware/dvisvgm/src/SVGElement.hpp @@ -58,6 +58,9 @@ class SVGElement : public XMLElement { void setStrokeWidth (double width); void setStrokeMiterLimit (double limit); void setTransform (const Matrix &matrix); + + static bool USE_CURRENTCOLOR; ///< if true, apply color value "currentColor" + static Color CURRENTCOLOR; ///< color to be replaced with "currentColor" }; #endif diff --git a/dviware/dvisvgm/src/SVGSingleCharTextHandler.cpp b/dviware/dvisvgm/src/SVGSingleCharTextHandler.cpp index d2a4f53ad1..1dcab96317 100644 --- a/dviware/dvisvgm/src/SVGSingleCharTextHandler.cpp +++ b/dviware/dvisvgm/src/SVGSingleCharTextHandler.cpp @@ -29,7 +29,7 @@ void SVGSingleCharTextHandler::appendChar (uint32_t c, double x, double y) { textNode->append(XMLString(font->unicode(c), false)); // Apply color changes only if the color differs from black and if the font color itself is black. // Glyphs from non-black fonts (e.g. defined in a XeTeX document) can't change their color. - if (_color.get() != Color::BLACK && font->color() == Color::BLACK) + if (font->color() == Color::BLACK && (_color.get() != Color::BLACK || (SVGElement::USE_CURRENTCOLOR && SVGElement::CURRENTCOLOR == Color::BLACK))) textNode->setFillColor(_color); _color.changed(false); if (!_opacity->isFillDefault()) diff --git a/dviware/dvisvgm/src/SVGTree.hpp b/dviware/dvisvgm/src/SVGTree.hpp index 0453e53ac3..a368932d36 100644 --- a/dviware/dvisvgm/src/SVGTree.hpp +++ b/dviware/dvisvgm/src/SVGTree.hpp @@ -92,8 +92,8 @@ class SVGTree { private: XMLDocument _doc; - SVGElement *_root, *_page, *_defs; - XMLCData *_styleCDataNode; + SVGElement *_root=nullptr, *_page=nullptr, *_defs=nullptr; + XMLCData *_styleCDataNode=nullptr; std::unique_ptr<SVGCharHandler> _charHandler; std::stack<SVGElement*> _defsContextStack; std::stack<SVGElement*> _pageContextStack; diff --git a/dviware/dvisvgm/src/SpecialHandler.hpp b/dviware/dvisvgm/src/SpecialHandler.hpp index 81c3146d35..860cfdea61 100644 --- a/dviware/dvisvgm/src/SpecialHandler.hpp +++ b/dviware/dvisvgm/src/SpecialHandler.hpp @@ -37,15 +37,14 @@ struct SpecialException : public MessageException { class SpecialHandler { - friend class SpecialManager; public: virtual ~SpecialHandler () =default; - virtual const char* info () const=0; - virtual const char* name () const=0; - virtual std::vector<const char*> prefixes() const =0; + virtual const char* info () const =0; + virtual const char* name () const =0; + virtual std::vector<const char*> prefixes () const =0; virtual void setDviScaleFactor (double dvi2bp) {} virtual void preprocess (const std::string &prefix, std::istream &is, SpecialActions &actions) {} - virtual bool process (const std::string &prefix, std::istream &is, SpecialActions &actions)=0; + virtual bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) =0; virtual void dviPreprocessingFinished () {} virtual void dviBeginPage (unsigned pageno, SpecialActions &actions) {} virtual void dviEndPage (unsigned pageno, SpecialActions &actions) {} diff --git a/dviware/dvisvgm/src/SpecialManager.hpp b/dviware/dvisvgm/src/SpecialManager.hpp index 55da303b70..ed97f7f5be 100644 --- a/dviware/dvisvgm/src/SpecialManager.hpp +++ b/dviware/dvisvgm/src/SpecialManager.hpp @@ -21,12 +21,14 @@ #ifndef SPECIALMANAGER_HPP #define SPECIALMANAGER_HPP +#include <algorithm> #include <memory> #include <ostream> #include <string> #include <unordered_map> #include <vector> #include "SpecialHandler.hpp" +#include "utility.hpp" class SpecialActions; @@ -38,6 +40,14 @@ class SpecialManager { public: SpecialManager (const SpecialManager &) =delete; static SpecialManager& instance (); + + template <class Handler> + static void registerHandler (const std::vector<std::string> &ignoredHandlerNames) { + const char *name = Handler::handlerName(); + if (!name || find(ignoredHandlerNames.begin(), ignoredHandlerNames.end(), std::string(name)) == ignoredHandlerNames.end()) + instance().registerHandler(util::make_unique<Handler>()); + } + void registerHandler (std::unique_ptr<SpecialHandler> handler); void registerHandlers (std::vector<std::unique_ptr<SpecialHandler>> &handlers, const char *ignorelist); void unregisterHandlers (); diff --git a/dviware/dvisvgm/src/StreamReader.cpp b/dviware/dvisvgm/src/StreamReader.cpp index 196ce7affa..1513ae97db 100644 --- a/dviware/dvisvgm/src/StreamReader.cpp +++ b/dviware/dvisvgm/src/StreamReader.cpp @@ -40,7 +40,7 @@ istream& StreamReader::replaceStream (istream &in) { uint32_t StreamReader::readUnsigned (int bytes) { uint32_t ret = 0; for (bytes--; bytes >= 0 && !_is->eof(); bytes--) { - uint32_t b = uint32_t(_is->get()); + auto b = uint32_t(_is->get()); ret |= b << (8*bytes); } return ret; @@ -62,7 +62,7 @@ uint32_t StreamReader::readUnsigned (int n, HashFunction &hashfunc) { * @param[in] bytes number of bytes to read (max. 4) * @return read integer */ int32_t StreamReader::readSigned (int bytes) { - uint32_t ret = uint32_t(_is->get()); + auto ret = uint32_t(_is->get()); if (ret & 128) // negative value? ret |= 0xffffff00; for (bytes-=2; bytes >= 0 && !_is->eof(); bytes--) @@ -84,12 +84,12 @@ int32_t StreamReader::readSigned (int n, HashFunction &hashfunc) { /** Reads a string terminated by a 0-byte. */ string StreamReader::readString () { - if (!_is) - throw StreamReaderException("no stream assigned"); string ret; - while (!_is->eof() && _is->peek() > 0) - ret += char(_is->get()); - _is->get(); // skip 0-byte + if (_is) { + while (!_is->eof() && _is->peek() > 0) + ret += char(_is->get()); + _is->get(); // skip 0-byte + } return ret; } @@ -102,7 +102,7 @@ string StreamReader::readString (HashFunction &hashfunc, bool finalZero) { string ret = readString(); hashfunc.update(ret.data(), ret.length()); if (finalZero) - hashfunc.update(0, 1); + hashfunc.update(nullptr, 1); return ret; } @@ -111,11 +111,11 @@ string StreamReader::readString (HashFunction &hashfunc, bool finalZero) { * @param[in] length number of characters to read * @return the string read */ string StreamReader::readString (int length) { - if (!_is) - throw StreamReaderException("no stream assigned"); - length = max(0, length); - string str(length, '\0'); - _is->read(&str[0], length); // read 'length' bytes and append '\0' + string str; + if (_is) { + str.resize(max(0, length)); + _is->read(&str[0], streamsize(str.length())); // read 'length' bytes and append '\0' + } return str; } @@ -149,7 +149,7 @@ vector<uint8_t> StreamReader::readBytes (int n, HashFunction &hashfunc) { int StreamReader::readByte (HashFunction &hashfunc) { int ret = readByte(); if (ret >= 0) { - char c = ret & 0xff; + char c = char(ret & 0xff); hashfunc.update(&c, 1); } return ret; diff --git a/dviware/dvisvgm/src/StreamReader.hpp b/dviware/dvisvgm/src/StreamReader.hpp index 138751c33c..f775cd3bac 100644 --- a/dviware/dvisvgm/src/StreamReader.hpp +++ b/dviware/dvisvgm/src/StreamReader.hpp @@ -50,6 +50,7 @@ class StreamReader { 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);} + void skip (std::streampos n) {_is->seekg(n, std::ios::cur);} std::streampos tell () const {return _is->tellg();} int peek () const {return _is->peek();} diff --git a/dviware/dvisvgm/src/TFM.cpp b/dviware/dvisvgm/src/TFM.cpp index c29de267bb..e2ba673647 100644 --- a/dviware/dvisvgm/src/TFM.cpp +++ b/dviware/dvisvgm/src/TFM.cpp @@ -20,7 +20,6 @@ #include <fstream> #include <vector> -#include "FileFinder.hpp" #include "Length.hpp" #include "Message.hpp" #include "StreamReader.hpp" @@ -35,47 +34,46 @@ using namespace std; * @param[in] n number of words to be read */ template <typename T> static void read_words (StreamReader &reader, vector<T> &v, unsigned n) { - v.clear(); v.resize(n); for (unsigned i=0; i < n; i++) v[i] = reader.readUnsigned(4); } -TFM::TFM (istream &is) : _checksum(0), _firstChar(0), _lastChar(0), _designSize(0), _ascent(0), _descent(0) { +void TFM::read (istream &is) { 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 - uint16_t lh = uint16_t(reader.readUnsigned(2)); // length of header in 4 byte words - _firstChar= uint16_t(reader.readUnsigned(2)); // smallest character code in font - _lastChar = uint16_t(reader.readUnsigned(2)); // largest character code in font - uint16_t nw = uint16_t(reader.readUnsigned(2)); // number of words in width table - uint16_t nh = uint16_t(reader.readUnsigned(2)); // number of words in height table - uint16_t nd = uint16_t(reader.readUnsigned(2)); // number of words in depth table - uint16_t ni = uint16_t(reader.readUnsigned(2)); // number of words in italic corr. table - uint16_t nl = uint16_t(reader.readUnsigned(2)); // number of words in lig/kern table - uint16_t nk = uint16_t(reader.readUnsigned(2)); // number of words in kern table - uint16_t ne = uint16_t(reader.readUnsigned(2)); // number of words in ext. char table - uint16_t np = uint16_t(reader.readUnsigned(2)); // number of font parameter words - - if (6+lh+(_lastChar-_firstChar+1)+nw+nh+nd+ni+nl+nk+ne+np != lf) + auto lf = uint16_t(reader.readUnsigned(2)); // length of entire file in 4 byte words + auto lh = uint16_t(reader.readUnsigned(2)); // length of header in 4 byte words + auto bc = uint16_t(reader.readUnsigned(2)); // smallest character code in font + auto ec = uint16_t(reader.readUnsigned(2)); // largest character code in font + auto nw = uint16_t(reader.readUnsigned(2)); // number of words in width table + auto nh = uint16_t(reader.readUnsigned(2)); // number of words in height table + auto nd = uint16_t(reader.readUnsigned(2)); // number of words in depth table + auto ni = uint16_t(reader.readUnsigned(2)); // number of words in italic corr. table + auto nl = uint16_t(reader.readUnsigned(2)); // number of words in lig/kern table + auto nk = uint16_t(reader.readUnsigned(2)); // number of words in kern table + auto ne = uint16_t(reader.readUnsigned(2)); // number of words in ext. char table + auto np = uint16_t(reader.readUnsigned(2)); // number of font parameter words + if (6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np != lf) throw FontMetricException("inconsistent length values"); - if (_firstChar >= _lastChar || _lastChar > 255 || ne > 256) + if (bc >= ec || ec > 255 || ne > 256) throw FontMetricException("character codes out of range"); - + _firstChar = bc; + _lastChar = ec; readHeader(reader); is.seekg(24+lh*4); // move to char info table readTables(reader, nw, nh, nd, ni); - is.seekg(4*(lf-np), ios::beg); // move to param section + is.seekg(4*(lf-np)); // move to param section readParameters(reader, np); } void TFM::readHeader (StreamReader &reader) { _checksum = reader.readUnsigned(4); - _designSize = double(FixWord(reader.readUnsigned(4)))*Length::pt2bp; + _designSize = double(FixWord(reader.readSigned(4)))*Length::pt2bp; } @@ -99,7 +97,7 @@ void TFM::readParameters (StreamReader &reader, int np) { _params.resize(7); np = min(np, 7); for (int i=0; i < np; i++) - _params[i] = reader.readUnsigned(4); + _params[i] = reader.readSigned(4); for (int i=np; i < 7; i++) _params[i] = 0; } @@ -134,7 +132,7 @@ double TFM::getQuad () const { /** Returns the index to the entry of the character info table that describes the metric of a given character. * @param[in] c character whose index is retrieved * @return table index for character c, or -1 if there's no entry */ -int TFM::charIndex (int c) const { +size_t TFM::charIndex (int c) const { if (c < _firstChar || c > _lastChar || size_t(c-_firstChar) >= _charInfoTable.size()) return -1; return c-_firstChar; @@ -151,8 +149,8 @@ int TFM::charIndex (int c) const { /** Returns the width of char c in PS point units. */ double TFM::getCharWidth (int c) const { - int index = charIndex(c); - if (index < 0) + size_t index = charIndex(c); + if (index == size_t(-1)) return 0; index = (_charInfoTable[index] >> 24) & 0xFF; return double(_widthTable[index]) * _designSize; @@ -161,8 +159,8 @@ double TFM::getCharWidth (int c) const { /** Returns the height of char c in PS point units. */ double TFM::getCharHeight (int c) const { - int index = charIndex(c); - if (index < 0) + size_t index = charIndex(c); + if (index == size_t(-1)) return 0; index = (_charInfoTable[index] >> 20) & 0x0F; return double(_heightTable[index]) * _designSize; @@ -171,8 +169,8 @@ double TFM::getCharHeight (int c) const { /** Returns the depth of char c in PS point units. */ double TFM::getCharDepth (int c) const { - int index = charIndex(c); - if (index < 0) + size_t index = charIndex(c); + if (index == size_t(-1)) return 0; index = (_charInfoTable[index] >> 16) & 0x0F; return double(_depthTable[index]) * _designSize; @@ -181,8 +179,8 @@ double TFM::getCharDepth (int c) const { /** Returns the italic correction of char c in PS point units. */ double TFM::getItalicCorr (int c) const { - int index = charIndex(c); - if (index < 0) + size_t index = charIndex(c); + if (index == size_t(-1)) return 0; index = (_charInfoTable[index] >> 10) & 0x3F; return double(_italicTable[index]) * _designSize; diff --git a/dviware/dvisvgm/src/TFM.hpp b/dviware/dvisvgm/src/TFM.hpp index 5e968ff201..dd3fa32401 100644 --- a/dviware/dvisvgm/src/TFM.hpp +++ b/dviware/dvisvgm/src/TFM.hpp @@ -32,7 +32,8 @@ class StreamReader; class TFM : public FontMetrics { public: - explicit TFM (std::istream &is); + TFM () =default; + void read (std::istream &is) override; double getCharWidth (int c) const override; double getCharHeight (int c) const override; double getCharDepth (int c) const override; @@ -46,15 +47,14 @@ class TFM : public FontMetrics { double getDescent () const override {return double(_descent)*_designSize;} bool verticalLayout () const override {return false;} uint32_t getChecksum () const override {return _checksum;} - uint16_t firstChar () const override {return _firstChar;} - uint16_t lastChar () const override {return _lastChar;} + uint32_t firstChar () const override {return _firstChar;} + uint32_t lastChar () const override {return _lastChar;} protected: - TFM () : _params(7) {} void readHeader (StreamReader &reader); void readTables (StreamReader &reader, int nw, int nh, int nd, int ni); void readParameters (StreamReader &reader, int np); - virtual int charIndex (int c) const; + virtual size_t charIndex (int c) const; void setCharRange (int firstchar, int lastchar) {_firstChar=firstchar; _lastChar=lastchar;} private: @@ -67,7 +67,7 @@ class TFM : public FontMetrics { std::vector<FixWord> _depthTable; ///< character depth in design size units std::vector<FixWord> _italicTable; ///< italic corrections in design size units std::vector<FixWord> _params; ///< values of the TFM's param section - FixWord _ascent, _descent; ///< max. height and depth + FixWord _ascent=0, _descent=0; ///< max. height and depth }; #endif diff --git a/dviware/dvisvgm/src/TpicSpecialHandler.cpp b/dviware/dvisvgm/src/TpicSpecialHandler.cpp index 86b49b1d20..e22970c31a 100644 --- a/dviware/dvisvgm/src/TpicSpecialHandler.cpp +++ b/dviware/dvisvgm/src/TpicSpecialHandler.cpp @@ -354,7 +354,7 @@ bool TpicSpecialHandler::process (const string &prefix, istream &is, SpecialActi } -vector<const char*> TpicSpecialHandler::prefixes() const { +vector<const char*> TpicSpecialHandler::prefixes () const { vector<const char*> pfx {"ar", "bk", "da", "dt", "fp", "ia", "ip", "pa", "pn", "sh", "sp", "tx", "wh"}; return pfx; } diff --git a/dviware/dvisvgm/src/TpicSpecialHandler.hpp b/dviware/dvisvgm/src/TpicSpecialHandler.hpp index e8a56649f8..341a6a441c 100644 --- a/dviware/dvisvgm/src/TpicSpecialHandler.hpp +++ b/dviware/dvisvgm/src/TpicSpecialHandler.hpp @@ -28,10 +28,11 @@ class TpicSpecialHandler : public SpecialHandler { public: TpicSpecialHandler (); - const char* info () const override {return "TPIC specials";} - const char* name () const override {return "tpic";} - std::vector<const char*> prefixes() const override; bool process (const std::string &prefix, std::istream &is, SpecialActions &actions) override; + const char* info () const override {return "TPIC specials";} + const char* name () const override {return handlerName();} + static const char* handlerName () {return "tpic";} + std::vector<const char*> prefixes () const override; double penwidth () const {return _penwidth;} double grayLevel () const {return _grayLevel;} Color fillColor (bool grayOnly) const; diff --git a/dviware/dvisvgm/src/XMLNode.cpp b/dviware/dvisvgm/src/XMLNode.cpp index d5342e4d1e..b3d13e6a00 100644 --- a/dviware/dvisvgm/src/XMLNode.cpp +++ b/dviware/dvisvgm/src/XMLNode.cpp @@ -527,7 +527,7 @@ ostream& XMLCData::write (ostream &os) const { void XMLCData::append (string &&str) { if (_data.empty()) - _data = move(str); + _data = std::move(str); else _data += str; } diff --git a/dviware/dvisvgm/src/dvisvgm.cpp b/dviware/dvisvgm/src/dvisvgm.cpp index 4c5bf550b2..5ba5998408 100644 --- a/dviware/dvisvgm/src/dvisvgm.cpp +++ b/dviware/dvisvgm/src/dvisvgm.cpp @@ -309,11 +309,11 @@ static void init_fontmap (const CommandLine &cmdline) { * options affecting the SVG output. */ static string svg_options_hash (const CommandLine &cmdline) { // options affecting the SVG output - vector<const CL::Option*> svg_options = { + const CL::Option* svg_options[] = { &cmdline.bboxOpt, &cmdline.clipjoinOpt, &cmdline.colornamesOpt, &cmdline.commentsOpt, - &cmdline.exactBboxOpt, &cmdline.fontFormatOpt, &cmdline.fontmapOpt, &cmdline.gradOverlapOpt, - &cmdline.gradSegmentsOpt, &cmdline.gradSimplifyOpt, &cmdline.linkmarkOpt, &cmdline.magOpt, - &cmdline.noFontsOpt, &cmdline.noMergeOpt, &cmdline.noSpecialsOpt, &cmdline.noStylesOpt, + &cmdline.currentcolorOpt, &cmdline.exactBboxOpt, &cmdline.fontFormatOpt, &cmdline.fontmapOpt, + &cmdline.gradOverlapOpt, &cmdline.gradSegmentsOpt, &cmdline.gradSimplifyOpt, &cmdline.linkmarkOpt, + &cmdline.magOpt, &cmdline.noFontsOpt, &cmdline.noMergeOpt, &cmdline.noSpecialsOpt, &cmdline.noStylesOpt, &cmdline.optimizeOpt, &cmdline.precisionOpt, &cmdline.relativeOpt, &cmdline.zoomOpt }; string idString = get_transformation_string(cmdline); @@ -345,6 +345,13 @@ static void set_variables (const CommandLine &cmdline) { SpecialActions::PROGRESSBAR_DELAY = cmdline.progressOpt.value(); } Color::SUPPRESS_COLOR_NAMES = !cmdline.colornamesOpt.given(); + if ((SVGElement::USE_CURRENTCOLOR = cmdline.currentcolorOpt.given())) { + Color color; + if (color.setRGBHexString(cmdline.currentcolorOpt.value())) + SVGElement::CURRENTCOLOR = color; + else + throw CL::CommandLineException("invalid color string '"+cmdline.currentcolorOpt.value()+"'"); + } SVGTree::CREATE_CSS = !cmdline.noStylesOpt.given(); SVGTree::USE_FONTS = !cmdline.noFontsOpt.given(); if (!SVGTree::setFontFormat(cmdline.fontFormatOpt.value())) { diff --git a/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp b/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp index d6a7f35bc4..889e011571 100644 --- a/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp +++ b/dviware/dvisvgm/src/optimizer/SVGOptimizer.cpp @@ -39,12 +39,12 @@ string SVGOptimizer::MODULE_SEQUENCE; SVGOptimizer::SVGOptimizer (SVGTree *svg) : _svg(svg) { // optimizer modules available to the user; must be listed in default order // _moduleEntries.emplace_back(ModuleEntry("remove-ws", util::make_unique<WSNodeRemover>())); - _moduleEntries.emplace_back(ModuleEntry("simplify-text", util::make_unique<TextSimplifier>())); - _moduleEntries.emplace_back(ModuleEntry("simplify-transform", util::make_unique<TransformSimplifier>())); - _moduleEntries.emplace_back(ModuleEntry("group-attributes", util::make_unique<AttributeExtractor>())); - _moduleEntries.emplace_back(ModuleEntry("collapse-groups", util::make_unique<GroupCollapser>())); - _moduleEntries.emplace_back(ModuleEntry("remove-clippaths", util::make_unique<RedundantElementRemover>())); - _moduleEntries.emplace_back(ModuleEntry("reassign-clippaths", util::make_unique<ClipPathReassigner>())); + _moduleEntries.emplace_back("simplify-text", util::make_unique<TextSimplifier>()); + _moduleEntries.emplace_back("simplify-transform", util::make_unique<TransformSimplifier>()); + _moduleEntries.emplace_back("group-attributes", util::make_unique<AttributeExtractor>()); + _moduleEntries.emplace_back("collapse-groups", util::make_unique<GroupCollapser>()); + _moduleEntries.emplace_back("remove-clippaths", util::make_unique<RedundantElementRemover>()); + _moduleEntries.emplace_back("reassign-clippaths", util::make_unique<ClipPathReassigner>()); } diff --git a/dviware/dvisvgm/src/options.dtd b/dviware/dvisvgm/src/options.dtd index 43f75b1691..1a2349b002 100644 --- a/dviware/dvisvgm/src/options.dtd +++ b/dviware/dvisvgm/src/options.dtd @@ -41,7 +41,7 @@ <!ELEMENT arg EMPTY> <!ATTLIST arg - default NMTOKEN #IMPLIED + default CDATA #IMPLIED name CDATA #REQUIRED optional (yes|no) #IMPLIED type NMTOKEN #REQUIRED> diff --git a/dviware/dvisvgm/src/options.xml b/dviware/dvisvgm/src/options.xml index 889fe7f081..bd7867970f 100644 --- a/dviware/dvisvgm/src/options.xml +++ b/dviware/dvisvgm/src/options.xml @@ -66,6 +66,10 @@ <option long="comments"> <description>add comments with additional information</description> </option> + <option long="currentcolor"> + <arg type="string" name="color" default="#000" optional="yes"/> + <description>replace given color with 'currentColor'</description> + </option> <option long="embed-bitmaps"> <description>prevent references to external bitmap files</description> </option> diff --git a/dviware/dvisvgm/src/ttf/HmtxTable.cpp b/dviware/dvisvgm/src/ttf/HmtxTable.cpp index 6e849768fd..e5dc63c7e8 100644 --- a/dviware/dvisvgm/src/ttf/HmtxTable.cpp +++ b/dviware/dvisvgm/src/ttf/HmtxTable.cpp @@ -57,6 +57,6 @@ void HmtxTable::updateWidthData (uint32_t c, int16_t xmin, int16_t xmax) { _runLengthOfAdvWidth++; else _runLengthOfAdvWidth = 1; - _widthInfos.emplace_back(CharWidthInfo(xmin, w)); + _widthInfos.emplace_back(xmin, w); ttfWriter()->updateGlobalAdvanceWidth(w, _widthInfos.size()-_runLengthOfAdvWidth+1); }
\ No newline at end of file diff --git a/dviware/dvisvgm/src/ttf/TTFWriter.cpp b/dviware/dvisvgm/src/ttf/TTFWriter.cpp index 2dacdfc567..a64f1ff641 100644 --- a/dviware/dvisvgm/src/ttf/TTFWriter.cpp +++ b/dviware/dvisvgm/src/ttf/TTFWriter.cpp @@ -368,14 +368,14 @@ bool TTFWriter::convertTTFToWOFF (istream &is, ostream &os) { // read and process TTF header vector<char> bufvec(12, 0); is.read(&bufvec[0], 12); - buffers.emplace_back(TableBuffer(0, std::move(bufvec))); + buffers.emplace_back(0, std::move(bufvec)); int numTables = buffers.back().getUInt16(4); // read and process table records bufvec.clear(); bufvec.resize(4 * 4 * numTables); is.read(&bufvec[0], 4 * 4 * numTables); - buffers.emplace_back(TableBuffer(0, std::move(bufvec))); + buffers.emplace_back(0, std::move(bufvec)); struct TableRecord { TableRecord (const TableBuffer &buf, size_t ofs) @@ -389,7 +389,7 @@ bool TTFWriter::convertTTFToWOFF (istream &is, ostream &os) { tableRecords.reserve(numTables); const TableBuffer &recbuf = buffers.back(); for (int i=0; i < numTables; i++) - tableRecords.emplace_back(TableRecord(recbuf, 16*i)); + tableRecords.emplace_back(recbuf, 16*i); // read and process tables for (const TableRecord &record : tableRecords) { @@ -398,9 +398,9 @@ bool TTFWriter::convertTTFToWOFF (istream &is, ostream &os) { is.seekg(record.offset); is.read(&bufvec[0], record.length); if (record.tag == TTFTable::name2id("head")) - buffers.emplace_back(TableBuffer(record.tag, std::move(bufvec), HeadTable::offsetToChecksum())); + buffers.emplace_back(record.tag, std::move(bufvec), HeadTable::offsetToChecksum()); else - buffers.emplace_back(TableBuffer(record.tag, std::move(bufvec))); + buffers.emplace_back(record.tag, std::move(bufvec)); } return ttf_to_woff(std::move(buffers), os); } diff --git a/dviware/dvisvgm/src/ttf/VmtxTable.cpp b/dviware/dvisvgm/src/ttf/VmtxTable.cpp index 6873bac9d2..ad695763d1 100644 --- a/dviware/dvisvgm/src/ttf/VmtxTable.cpp +++ b/dviware/dvisvgm/src/ttf/VmtxTable.cpp @@ -52,6 +52,6 @@ void VmtxTable::updateHeightData (uint32_t c, int16_t ymin, int16_t ymax) { _runLengthOfAdvHeight++; else _runLengthOfAdvHeight = 1; - _heightInfos.emplace_back(CharHeightInfo(ymin, h)); + _heightInfos.emplace_back(ymin, h); ttfWriter()->updateGlobalAdvanceHeight(h, _heightInfos.size()-_runLengthOfAdvHeight+1); }
\ No newline at end of file diff --git a/dviware/dvisvgm/src/utility.cpp b/dviware/dvisvgm/src/utility.cpp index 947be91634..8492cbdeed 100644 --- a/dviware/dvisvgm/src/utility.cpp +++ b/dviware/dvisvgm/src/utility.cpp @@ -58,7 +58,7 @@ vector<double> math::svd (const double (&m)[2][2]) { theta = (a2-a1)/2; } } - return vector<double>{phi, sx, sy, theta}; + return {phi, sx, sy, theta}; } @@ -68,7 +68,7 @@ vector<double> math::svd (const double (&m)[2][2]) { * @param[in] t1 upper interval boundary * @param[in] n number of slices the interval is divided into * @param[in] f function to integrate */ -double math::integral (double t0, double t1, int n, const std::function<double(double)> &f) { +double math::integral (double t0, double t1, int n, const function<double(double)> &f) { double ti = t0, ui=0; double h = (t1-t0)/n; for (int i=0; i < n; i++) { @@ -103,7 +103,7 @@ double math::normalize_0_2pi (double rad) { * @param[in] str the string to process * @param[in] ws characters treated as whitespace * @return the trimmed string */ -string util::trim (const std::string &str, const char *ws) { +string util::trim (const string &str, const char *ws) { auto first = str.find_first_not_of(ws); if (first == string::npos) return ""; @@ -183,13 +183,8 @@ 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? - 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? - str.pop_back(); - } + str.erase(str.find_last_not_of('0')+1, string::npos); + str.erase(str.find_last_not_of('.')+1, string::npos); return str; } @@ -206,11 +201,69 @@ int util::ilog10 (int n) { } +/** Reads an integer string of the form (+|-)?[0-9]+ from an input stream + * ana appends it to a given one. + * @param[in] is stream to read from + * @param[out] str the read string is appended to this one + * @param[in] allow_leading_sign true if the first character may be '+' or '-' + * @return true if the string coule be read successfully */ +static bool read_int_string (istream &is, string &str, bool allow_leading_sign=true) { + string intstr; + if (is.peek() == '-' || is.peek() == '+') { + if (!allow_leading_sign) + return false; + intstr += char(is.get()); + if (!isdigit(is.peek())) + return false; + } + while (isdigit(is.peek())) + intstr += char(is.get()); + str += intstr; + return !intstr.empty(); +} + + +/** Reads and parses a double from an input stream. + * @param[in] is stream to read from + * @param[out] value the read double value + * @return true if the value was read and parsed successfully */ +bool util::read_double (istream &is, double &value) { + is >> ws; + string numstr; + // read optional leading sign + bool plusminus = (is.peek() == '-' || is.peek() == '+'); + if (plusminus) + numstr += char(is.get()); + // read optional integer part (before decimal dot) + read_int_string(is, numstr, !plusminus); + if (is.peek() == '.') { + numstr += char(is.get()); + // read fractional part (after decimal dot) + if (!read_int_string(is, numstr, false)) + return false; + } + // read optional exponential part + if (std::tolower(is.peek()) == 'e') { + numstr += char(is.get()); + if (!read_int_string(is, numstr)) + return false; + } + try { + size_t count; + value = stod(numstr, &count); + return count == numstr.length(); + } + catch (...) { + return false; + } +} + + /** Returns the contents of a file. * @param[in] fname name/path of the file */ string util::read_file_contents (const string &fname) { ifstream ifs(fname, ios::binary); - return string(istreambuf_iterator<char>(ifs.rdbuf()), istreambuf_iterator<char>()); + return {istreambuf_iterator<char>(ifs.rdbuf()), istreambuf_iterator<char>()}; } diff --git a/dviware/dvisvgm/src/utility.hpp b/dviware/dvisvgm/src/utility.hpp index 9b1b7006ad..ee272e22ec 100644 --- a/dviware/dvisvgm/src/utility.hpp +++ b/dviware/dvisvgm/src/utility.hpp @@ -72,6 +72,7 @@ std::string mimetype (const std::string &fname); std::vector<std::string> split (const std::string &str, const std::string &sep); int ilog10 (int n); +bool read_double (std::istream &is, double &value); 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); |