Skip to content

Commit

Permalink
moved expandText() to class SpecialActions
Browse files Browse the repository at this point in the history
  • Loading branch information
mgieseki committed Jan 7, 2024
1 parent 0b7f162 commit 1e3f205
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 114 deletions.
4 changes: 2 additions & 2 deletions src/DVIToSVG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ void DVIToSVG::convert (unsigned first, unsigned last, HashFunction *hashFunc) {
else {
Message::mstream(false, Message::MC_PAGE_WRITTEN) << "\noutput written to " << fname << '\n';
if (!_userMessage.empty()) {
if (auto spcactions = dynamic_cast<SpecialActions*>(_actions.get())) {
string msg = DvisvgmSpecialHandler::expandText(_userMessage, *spcactions);
if (auto specialActions = dynamic_cast<SpecialActions*>(_actions.get())) {
string msg = specialActions->expandText(_userMessage);
Message::ustream(true) << msg << "\n";
}
}
Expand Down
96 changes: 4 additions & 92 deletions src/DvisvgmSpecialHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,90 +198,6 @@ bool DvisvgmSpecialHandler::process (const string &prefix, istream &is, SpecialA
}


/** Replaces constants of the form {?name} by their corresponding value.
* @param[in,out] str text to expand
* @param[in] actions interfcae to the world outside the special handler */
static void expand_constants (string &str, SpecialActions &actions) {
bool repl_bbox = true;
while (repl_bbox) {
const auto pos = str.find("{?bbox ");
if (pos == string::npos)
repl_bbox = false;
else {
auto endpos = pos+7;
while (endpos < str.length() && isalnum(str[endpos]))
++endpos;
if (str[endpos] != '}')
repl_bbox = false;
else {
BoundingBox &box = actions.bbox(str.substr(pos+7, endpos-pos-7));
str.replace(pos, endpos-pos+1, box.svgViewBoxString());
}
}
}
const struct Constant {
const char *name;
string val;
} constants[] = {
{"x", XMLString(actions.getX())},
{"y", XMLString(actions.getY())},
{"color", SVGElement::USE_CURRENTCOLOR && SVGElement::CURRENTCOLOR == actions.getColor() ? "currentColor" : actions.getColor().svgColorString()},
{"matrix", actions.getMatrix().toSVG()},
{"nl", "\n"},
{"pageno", to_string(actions.getCurrentPageNumber())},
{"svgfile", actions.getSVGFilePath(actions.getCurrentPageNumber()).relative()},
{"svgpath", actions.getSVGFilePath(actions.getCurrentPageNumber()).absolute()},
};
for (const Constant &constant : constants) {
const string pattern = string("{?")+constant.name+"}";
auto pos = str.find(pattern);
while (pos != string::npos) {
str.replace(pos, strlen(constant.name)+3, constant.val);
pos = str.find(pattern, pos+constant.val.length()); // look for further matches
}
}
}


/** Evaluates substrings of the form {?(expr)} where 'expr' is a math expression,
* and replaces the substring by the computed value.
* @param[in,out] str string to scan for expressions */
static void evaluate_expressions (string &str, const SpecialActions &actions) {
auto left = str.find("{?("); // start position of expression macro
while (left != string::npos) {
auto right = str.find(")}", left+2); // end position of expression macro
if (right == string::npos)
break;
Calculator calc;
calc.setVariable("x", actions.getX());
calc.setVariable("y", actions.getY());
string expr = str.substr(left+3, right-left-3); // math expression to evaluate
if (util::normalize_space(expr).empty()) // no expression given, e.g. {?( )}
str.erase(left, right-left+2); // => replace with empty string
else {
try {
double val = calc.eval(expr);
XMLString valstr(val);
str.replace(left, right-left+2, valstr);
right = left+valstr.length()-1;
}
catch (CalculatorException &e) {
throw SpecialException(string(e.what())+" in '{?("+expr+")}'");
}
}
left = str.find("{?(", right+1); // find next expression macro
}
}


string DvisvgmSpecialHandler::expandText (const string &text, SpecialActions &actions) {
string ret = text;
evaluate_expressions(ret, actions);
expand_constants(ret, actions);
return ret;
}


/** Processes raw SVG fragments from the input stream. The SVG data must represent
* a single or multiple syntactically complete XML parts, like opening/closing tags,
* comments, or CDATA blocks. These must not be split and distributed over several
Expand All @@ -291,8 +207,7 @@ void DvisvgmSpecialHandler::processRaw (InputReader &ir, SpecialActions &actions
if (_nestingLevel == 0) {
string xml = ir.getLine();
if (!xml.empty()) {
evaluate_expressions(xml, actions);
expand_constants(xml, actions);
xml = actions.expandText(xml);
_pageParser.parse(std::move(xml));
}
}
Expand All @@ -303,8 +218,7 @@ void DvisvgmSpecialHandler::processRawDef (InputReader &ir, SpecialActions &acti
if (_nestingLevel == 0) {
string xml = ir.getLine();
if (!xml.empty()) {
evaluate_expressions(xml, actions);
expand_constants(xml, actions);
xml = actions.expandText(xml);
_defsParser.parse(std::move(xml));
}
}
Expand Down Expand Up @@ -335,7 +249,7 @@ void DvisvgmSpecialHandler::processRawPut (InputReader &ir, SpecialActions &acti
char &type = defstr[0];
string def = defstr.substr(1);
if ((type == 'P' || type == 'D') && !def.empty()) {
expand_constants(def, actions);
def = actions.expandText(def);
if (type == 'P')
_pageParser.parse(std::move(def));
else { // type == 'D'
Expand Down Expand Up @@ -483,9 +397,7 @@ void DvisvgmSpecialHandler::processCurrentColor (InputReader &ir, SpecialActions


void DvisvgmSpecialHandler::processMessage (InputReader &ir, SpecialActions &actions) {
string message = ir.getLine();
evaluate_expressions(message, actions);
expand_constants(message, actions);
string message = actions.expandText(ir.getLine());
Message::ustream() << message << "\n";
}

Expand Down
1 change: 0 additions & 1 deletion src/DvisvgmSpecialHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ class DvisvgmSpecialHandler : public SpecialHandler {
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 std::string expandText (const std::string &text, SpecialActions &actions);
static const char* handlerName () {return "dvisvgm";}
std::vector<const char*> prefixes () const override;

Expand Down
2 changes: 1 addition & 1 deletion src/ImageToSVG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ void ImageToSVG::writeSVG (int pageno) {
<< " x " << XMLString(_bbox.height()*bp2mm) << "mm)\n";
Message::mstream(false, Message::MC_PAGE_WRITTEN) << "output written to " << svgfname << '\n';
if (!_userMessage.empty()) {
string msg = DvisvgmSpecialHandler::expandText(_userMessage, *this);
string msg = expandText(_userMessage);
Message::ustream(true) << msg << "\n";
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ libdvisvgm_la_SOURCES = \
ShadingPatch.hpp ShadingPatch.cpp \
SignalHandler.hpp SignalHandler.cpp \
SourceInput.hpp SourceInput.cpp \
SpecialActions.hpp \
SpecialActions.hpp SpecialActions.cpp \
SpecialHandler.hpp \
SpecialManager.hpp SpecialManager.cpp \
StreamReader.hpp StreamReader.cpp \
Expand Down
111 changes: 111 additions & 0 deletions src/SpecialActions.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*************************************************************************
** SpecialActions.cpp **
** **
** This file is part of dvisvgm -- a fast DVI to SVG converter **
** Copyright (C) 2005-2024 Martin Gieseking <[email protected]> **
** **
** This program is free software; you can redistribute it and/or **
** modify it under the terms of the GNU General Public License as **
** published by the Free Software Foundation; either version 3 of **
** the License, or (at your option) any later version. **
** **
** This program is distributed in the hope that it will be useful, but **
** WITHOUT ANY WARRANTY; without even the implied warranty of **
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
** GNU General Public License for more details. **
** **
** You should have received a copy of the GNU General Public License **
** along with this program; if not, see <http://www.gnu.org/licenses/>. **
*************************************************************************/

#include <cstring>
#include "Calculator.hpp"
#include "SpecialActions.hpp"

using namespace std;

/** Replaces constants of the form {?name} by their corresponding value.
* @param[in,out] str text to expand
* @param[in] actions interfcae to the world outside the special handler */
static void expand_constants (string &str, SpecialActions &actions) {
bool repl_bbox = true;
while (repl_bbox) {
const auto pos = str.find("{?bbox ");
if (pos == string::npos)
repl_bbox = false;
else {
auto endpos = pos+7;
while (endpos < str.length() && isalnum(str[endpos]))
++endpos;
if (str[endpos] != '}')
repl_bbox = false;
else {
BoundingBox &box = actions.bbox(str.substr(pos+7, endpos-pos-7));
str.replace(pos, endpos-pos+1, box.svgViewBoxString());
}
}
}
const struct Constant {
const char *name;
string val;
} constants[] = {
{"x", XMLString(actions.getX())},
{"y", XMLString(actions.getY())},
{"color", SVGElement::USE_CURRENTCOLOR && SVGElement::CURRENTCOLOR == actions.getColor() ? "currentColor" : actions.getColor().svgColorString()},
{"matrix", actions.getMatrix().toSVG()},
{"nl", "\n"},
{"pageno", to_string(actions.getCurrentPageNumber())},
{"svgfile", actions.getSVGFilePath(actions.getCurrentPageNumber()).relative()},
{"svgpath", actions.getSVGFilePath(actions.getCurrentPageNumber()).absolute()},
};
for (const Constant &constant : constants) {
const string pattern = string("{?")+constant.name+"}";
auto pos = str.find(pattern);
while (pos != string::npos) {
str.replace(pos, strlen(constant.name)+3, constant.val);
pos = str.find(pattern, pos+constant.val.length()); // look for further matches
}
}
}


/** Evaluates substrings of the form {?(expr)} where 'expr' is a math expression,
* and replaces the substring by the computed value.
* @param[in,out] str string to scan for expressions */
static void evaluate_expressions (string &str, const SpecialActions &actions) {
auto left = str.find("{?("); // start position of expression macro
while (left != string::npos) {
auto right = str.find(")}", left+2); // end position of expression macro
if (right == string::npos)
break;
Calculator calc;
calc.setVariable("x", actions.getX());
calc.setVariable("y", actions.getY());
string expr = str.substr(left+3, right-left-3); // math expression to evaluate
if (util::normalize_space(expr).empty()) // no expression given, e.g. {?( )}
str.erase(left, right-left+2); // => replace with empty string
else {
try {
double val = calc.eval(expr);
XMLString valstr(val);
str.replace(left, right-left+2, valstr);
right = left+valstr.length()-1;
}
catch (CalculatorException &e) {
throw SpecialException(string(e.what())+" in '{?("+expr+")}'");
}
}
left = str.find("{?(", right+1); // find next expression macro
}
}


/** Returns a given string with macros and arithmetic expressions expanded.
* @param[in] text string to be processed
* @return the expanded text */
string SpecialActions::expandText (const string &text) {
string ret = text;
evaluate_expressions(ret, *this);
expand_constants(ret, *this);
return ret;
}
6 changes: 6 additions & 0 deletions src/SpecialActions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@
#include "Color.hpp"
#include "FilePath.hpp"
#include "Matrix.hpp"
#include "MessageException.hpp"
#include "Opacity.hpp"
#include "SVGTree.hpp"

class XMLElement;
class XMLNode;

struct SpecialException : public MessageException {
explicit SpecialException (const std::string &msg) : MessageException(msg) {}
};

class SpecialActions {
public:
virtual ~SpecialActions () =default;
Expand Down Expand Up @@ -63,6 +68,7 @@ class SpecialActions {
virtual void lockOutput () {}
virtual void unlockOutput () {}
virtual bool outputLocked () const {return false;}
std::string expandText (const std::string &text);

static double PROGRESSBAR_DELAY; ///< progress bar doesn't appear before this time has elapsed (in sec)
};
Expand Down
8 changes: 0 additions & 8 deletions src/SpecialHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,10 @@
#include <istream>
#include <list>
#include <vector>
#include "MessageException.hpp"


class SpecialActions;
class SpecialManager;


struct SpecialException : public MessageException {
explicit SpecialException (const std::string &msg) : MessageException(msg) {}
};


class SpecialHandler {
public:
virtual ~SpecialHandler () =default;
Expand Down
28 changes: 19 additions & 9 deletions tests/DvisvgmSpecialTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,17 @@ class DvisvgmSpecialTest : public ::testing::Test {
protected:
class ActionsRecorder : public EmptySpecialActions {
public:
void embed (const BoundingBox &bb) override {bbox.embed(bb);}
double getX () const override {return -42;}
double getY () const override {return 14;}
bool defsEquals (const string &str) const {return defsString() == str;}
bool pageEquals (const string &str) const {return pageString() == str;}
bool bboxEquals (const string &str) const {return bbox.svgViewBoxString() == str;}
string bboxString () const {return bbox.svgViewBoxString();}
string defsString () const {return toString(svgTree().defsNode());}
string pageString () const {return toString(svgTree().pageNode());}
void embed (const BoundingBox &bb) override {bbox.embed(bb);}
double getX () const override {return -42;}
double getY () const override {return 14;}
unsigned getCurrentPageNumber() const override {return 1;}
FilePath getSVGFilePath(unsigned pageno) const override {return FilePath("test.svg");}
bool defsEquals (const string &str) const {return defsString() == str;}
bool pageEquals (const string &str) const {return pageString() == str;}
bool bboxEquals (const string &str) const {return bbox.svgViewBoxString() == str;}
string bboxString () const {return bbox.svgViewBoxString();}
string defsString () const {return toString(svgTree().defsNode());}
string pageString () const {return toString(svgTree().pageNode());}

void clear () {
SpecialActions::svgTree().reset();
Expand Down Expand Up @@ -369,6 +371,14 @@ TEST_F(DvisvgmSpecialTest, processBBox) {
}


TEST_F(DvisvgmSpecialTest, expandText) {
EXPECT_EQ(recorder.expandText(""), "");
EXPECT_EQ(recorder.expandText("static text"), "static text");
EXPECT_EQ(recorder.expandText("x={?x}, y={?y}"), "x=-42, y=14");
EXPECT_EQ(recorder.expandText("page:{?pageno}, file:{?svgfile}"), "page:1, file:test.svg");
}


TEST_F(DvisvgmSpecialTest, fail4) {
std::istringstream iss("bbox abs 0 0 72.27xx 72.27"); // unknown unit
EXPECT_THROW(handler.process("", iss, recorder), SpecialException);
Expand Down

0 comments on commit 1e3f205

Please sign in to comment.