summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp230
1 files changed, 153 insertions, 77 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp
index 4b503d34bee..405da48434b 100644
--- a/Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp
+++ b/Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp
@@ -25,6 +25,7 @@
#include <sstream>
#include "EPSFile.hpp"
#include "FileFinder.hpp"
+#include "FilePath.hpp"
#include "FileSystem.hpp"
#include "Message.hpp"
#include "PathClipper.hpp"
@@ -183,6 +184,15 @@ void PsSpecialHandler::preprocess (const string &prefix, istream &is, SpecialAct
}
+static string filename_suffix (const string &fname) {
+ string ret;
+ size_t pos = fname.rfind('.');
+ if (pos != string::npos)
+ ret = util::tolower(fname.substr(pos+1));
+ return ret;
+}
+
+
bool PsSpecialHandler::process (const string &prefix, istream &is, SpecialActions &actions) {
// process PS headers only once (in prescan)
if (prefix == "!" || prefix == "header=")
@@ -203,10 +213,24 @@ bool PsSpecialHandler::process (const string &prefix, istream &is, SpecialAction
else if (prefix == "psfile=" || prefix == "PSfile=" || prefix == "pdffile=") {
if (_actions) {
StreamInputReader in(is);
- const string fname = in.getQuotedString(in.peek() == '"' ? "\"" : nullptr);
+ string fname = in.getQuotedString(in.peek() == '"' ? "\"" : nullptr);
+ fname = FileSystem::adaptPathSeperators(fname);
+ FileType fileType = FileType::EPS;
+ if (prefix == "pdffile")
+ fileType = FileType::PDF;
+ else {
+ // accept selected non-PS files in psfile special
+ string ext = filename_suffix(fname);
+ if (ext == "pdf")
+ fileType = FileType::PDF;
+ else if (ext == "svg")
+ fileType = FileType::SVG;
+ else if (ext == "jpg" || ext == "jpeg" || ext == "png")
+ fileType = FileType::BITMAP;
+ }
map<string,string> attr;
- in.parseAttributes(attr);
- imgfile(prefix == "pdffile=" ? FileType::PDF : FileType::EPS, fname, attr);
+ in.parseAttributes(attr, false);
+ imgfile(fileType, fname, attr);
}
}
else if (prefix == "ps::") {
@@ -216,9 +240,9 @@ bool PsSpecialHandler::process (const string &prefix, istream &is, SpecialAction
// collect characters inside the brackets
string code;
for (int i=0; i < 9 && is.peek() != ']' && !is.eof(); ++i)
- code += is.get();
+ code += char(is.get());
if (is.peek() == ']')
- code += is.get();
+ code += char(is.get());
if (code == "[begin]" || code == "[nobreak]") {
moveToDVIPos();
@@ -272,15 +296,6 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
if (fname == "/dev/null")
return;
- string filepath;
- if (const char *path = FileFinder::instance().lookup(fname, false))
- filepath = FileSystem::adaptPathSeperators(path);
- if ((filepath.empty() || !FileSystem::exists(filepath)) && FileSystem::exists(fname))
- filepath = fname;
- if (filepath.empty()) {
- Message::wstream(true) << "file '" << fname << "' not found\n";
- return;
- }
map<string,string>::const_iterator it;
// bounding box of EPS figure in PS point units (lower left and upper right corner)
@@ -290,7 +305,9 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
double ury = (it = attr.find("ury")) != attr.end() ? stod(it->second) : 0;
int pageno = (it = attr.find("page")) != attr.end() ? stoi(it->second, nullptr, 10) : 1;
- if (filetype == FileType::PDF && llx == 0 && lly == 0 && urx == 0 && ury == 0) {
+ if (filetype == FileType::BITMAP || filetype == FileType::SVG)
+ swap(lly, ury);
+ else if (filetype == FileType::PDF && llx == 0 && lly == 0 && urx == 0 && ury == 0) {
BoundingBox pagebox = _psi.pdfPageBox(fname, pageno);
if (pagebox.valid()) {
llx = pagebox.minX();
@@ -316,6 +333,8 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
double vscale = (it = attr.find("vscale")) != attr.end() ? stod(it->second) : 100;
double angle = (it = attr.find("angle")) != attr.end() ? stod(it->second) : 0;
+ bool clipToBbox = (attr.find("clip") != attr.end());
+
// compute factors to scale the bounding box to width rwi and height rhi
double sx = rwi/abs(llx-urx);
double sy = rhi/abs(lly-ury);
@@ -335,36 +354,26 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
_actions->setY(0);
moveToDVIPos();
- auto groupNode = util::make_unique<XMLElementNode>("g"); // put SVG nodes created from the EPS/PDF file in this group
- _xmlnode = groupNode.get();
- _psi.execute(
- "\n@beginspecial @setspecial" // enter special environment
- "/setpagedevice{@setpagedevice}def " // activate processing of operator "setpagedevice"
- "matrix setmatrix" // don't apply outer PS transformations
- "/FirstPage "+to_string(pageno)+" def" // set number of fisrt page to convert (PDF only)
- "/LastPage "+to_string(pageno)+" def" // set number of last page to convert (PDF only)
- "("+ filepath + ")run " // execute file content
- "@endspecial " // leave special environment
- );
- if (!groupNode->empty()) { // has anything been drawn?
+ auto imgNode = createImageNode(filetype, fname, pageno, BoundingBox(llx, lly, urx, ury), clipToBbox);
+ if (imgNode) { // has anything been drawn?
Matrix matrix(1);
- matrix.scale(sx, -sy).rotate(-angle).scale(hscale/100, vscale/100); // apply transformation attributes
+ if (filetype == FileType::EPS || filetype == FileType::PDF)
+ sy = -sy; // adapt orientation of y-coordinates
+ matrix.scale(sx, sy).rotate(-angle).scale(hscale/100, vscale/100); // apply transformation attributes
matrix.translate(x+hoffset, y-voffset); // move image to current DVI position
- matrix.rmultiply(_actions->getMatrix());
+ matrix.lmultiply(_actions->getMatrix());
// update bounding box
BoundingBox bbox(0, 0, urx-llx, ury-lly);
bbox.transform(matrix);
_actions->embed(bbox);
- // insert group containing SVG nodes created from image
- matrix.lmultiply(TranslationMatrix(-llx, -lly)); // move lower left corner of image to origin
+ // insert element containing the image data
+ matrix.rmultiply(TranslationMatrix(-llx, -lly)); // move lower left corner of image to origin
if (!matrix.isIdentity())
- groupNode->addAttribute("transform", matrix.getSVG());
- _actions->appendToPage(std::move(groupNode));
+ imgNode->addAttribute("transform", matrix.toSVG());
+ _actions->svgTree().appendToPage(std::move(imgNode));
}
- _xmlnode = nullptr; // append following elements to page group again
-
// restore DVI position
_actions->setX(x);
_actions->setY(y);
@@ -372,6 +381,63 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
}
+/** Creates an XML element containing the image data depending on the file type.
+ * @param[in] type file type of the image
+ * @param[in] fname file name/path of image file
+ * @param[in] pageno number of page to process (PDF only)
+ * @param[in] bbox bounding box of the image
+ * @param[in] clip if true, the image is clipped to its bounding box
+ * @return pointer to the element or nullptr if there's no image data */
+unique_ptr<XMLElement> PsSpecialHandler::createImageNode (FileType type, const string &fname, int pageno, BoundingBox bbox, bool clip) {
+ unique_ptr<XMLElement> node;
+ string pathstr;
+ if (const char *path = FileFinder::instance().lookup(fname, false))
+ pathstr = FileSystem::adaptPathSeperators(path);
+ if ((pathstr.empty() || !FileSystem::exists(pathstr)) && FileSystem::exists(fname))
+ pathstr = fname;
+ if (pathstr.empty())
+ Message::wstream(true) << "file '" << fname << "' not found\n";
+ else if (type == FileType::BITMAP || type == FileType::SVG) {
+ node = util::make_unique<XMLElement>("image");
+ node->addAttribute("x", 0);
+ node->addAttribute("y", 0);
+ node->addAttribute("width", bbox.width());
+ node->addAttribute("height", bbox.height());
+
+ // Only reference the image with an absolute path if either an absolute path was given by the user
+ // or a given plain filename is not present in the current working directory but was found through
+ // the FileFinder, i.e. it's usually located somewhere in the texmf tree.
+ string href = pathstr;
+ if (!FilePath::isAbsolute(fname) && (fname.find('/') != string::npos || FilePath(fname).exists()))
+ href = FilePath(pathstr).relative(FilePath(_actions->getSVGFilePath(pageno)));
+ node->addAttribute("xlink:href", href);
+ }
+ else { // PostScript or PDF
+ // clip image to its bounding box if flag 'clip' is given
+ string rectclip;
+ if (clip)
+ rectclip = to_string(bbox.minX())+" "+to_string(bbox.minY())+" "+to_string(bbox.width())+" "+to_string(bbox.height())+" rectclip";
+
+ node = util::make_unique<XMLElement>("g"); // put SVG nodes created from the EPS/PDF file in this group
+ _xmlnode = node.get();
+ _psi.execute(
+ "\n@beginspecial @setspecial" // enter special environment
+ "/setpagedevice{@setpagedevice}def " // activate processing of operator "setpagedevice"
+ "matrix setmatrix" // don't apply outer PS transformations
+ "/FirstPage "+to_string(pageno)+" def" // set number of first page to convert (PDF only)
+ "/LastPage "+to_string(pageno)+" def " // set number of last page to convert (PDF only)
+ +rectclip+ // clip to bounding box (if requexted by attribute 'clip')
+ "(" + pathstr + ")run " // execute file content
+ "@endspecial " // leave special environment
+ );
+ if (node->empty())
+ node.reset(nullptr);
+ _xmlnode = nullptr; // append following elements to page group again
+ }
+ return node;
+}
+
+
/** Apply transformation to width, height, and depth set by preview package.
* @param[in] matrix transformation matrix to apply
* @param[out] w width
@@ -386,15 +452,17 @@ static bool transform_box_extents (const Matrix &matrix, double &w, double &h, d
return false; // => non-horizontal baseline, can't compute meaningful extents
if (ex.y() == 0) // horizontal scaling or skewing?
- w *= fabs(ex.x());
+ w *= abs(ex.x());
if (ey.x()==0 || ex.y()==0) { // vertical scaling?
- if (ey.y() < 0) swap(h, d);
- if (double sy = fabs(ey.y())/ey.length()) {
- h *= fabs(ey.y()/sy);
- d *= fabs(ey.y()/sy);
- }
- else
+ if (ey.y() < 0)
+ swap(h, d);
+ double sy = abs(ey.y())/ey.length();
+ if (sy < 1e-8)
h = d = 0;
+ else {
+ h *= abs(ey.y()/sy);
+ d *= abs(ey.y()/sy);
+ }
}
return true;
}
@@ -435,7 +503,7 @@ void PsSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) {
"depth=" << XMLString(d*bp2pt) << "pt\n";
}
#if 0
- auto rect = util::make_unique<XMLElementNode>("rect");
+ auto rect = util::make_unique<XMLElement>("rect");
rect->addAttribute("x", actions.bbox().minX());
rect->addAttribute("y", actions.bbox().minY());
rect->addAttribute("width", w);
@@ -445,7 +513,7 @@ void PsSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) {
rect->addAttribute("stroke-width", "0.1");
actions.appendToPage(std::move(rect));
if (d > 0) {
- auto line = util::make_unique<XMLElementNode>("line");
+ auto line = util::make_unique<XMLElement>("line");
line->addAttribute("x1", actions.bbox().minX());
line->addAttribute("y1", actions.bbox().minY()+h);
line->addAttribute("x2", actions.bbox().maxX());
@@ -553,14 +621,14 @@ void PsSpecialHandler::stroke (vector<double> &p) {
}
if (_clipStack.prependedPath())
_path.prepend(*_clipStack.prependedPath());
- unique_ptr<XMLElementNode> path;
+ unique_ptr<XMLElement> path;
Pair<double> point;
if (_path.isDot(point)) { // zero-length path?
if (_linecap == 1) { // round line ends? => draw dot
double x = point.x();
double y = point.y();
double r = _linewidth/2.0;
- path = util::make_unique<XMLElementNode>("circle");
+ path = util::make_unique<XMLElement>("circle");
path->addAttribute("cx", x);
path->addAttribute("cy", y);
path->addAttribute("r", r);
@@ -575,7 +643,7 @@ void PsSpecialHandler::stroke (vector<double> &p) {
ostringstream oss;
_path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
- path = util::make_unique<XMLElementNode>("path");
+ path = util::make_unique<XMLElement>("path");
path->addAttribute("d", oss.str());
path->addAttribute("stroke", _actions->getColor().svgColorString());
path->addAttribute("fill", "none");
@@ -592,13 +660,11 @@ void PsSpecialHandler::stroke (vector<double> &p) {
if (_blendmode > 0 && _blendmode < 16)
path->addAttribute("style", "mix-blend-mode:"+css_blendmode_name(_blendmode));
if (!_dashpattern.empty()) {
- ostringstream oss;
- for (size_t i=0; i < _dashpattern.size(); i++) {
- if (i > 0)
- oss << ',';
- oss << XMLString(_dashpattern[i]);
- }
- path->addAttribute("stroke-dasharray", oss.str());
+ string patternStr;
+ for (double dashValue : _dashpattern)
+ patternStr += XMLString(dashValue)+",";
+ patternStr.pop_back();
+ path->addAttribute("stroke-dasharray", patternStr);
if (_dashoffset != 0)
path->addAttribute("stroke-dashoffset", _dashoffset);
}
@@ -614,7 +680,7 @@ void PsSpecialHandler::stroke (vector<double> &p) {
if (_xmlnode)
_xmlnode->append(std::move(path));
else {
- _actions->appendToPage(std::move(path));
+ _actions->svgTree().appendToPage(std::move(path));
_actions->embed(bbox);
}
_path.clear();
@@ -642,7 +708,7 @@ void PsSpecialHandler::fill (vector<double> &p, bool evenodd) {
ostringstream oss;
_path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
- unique_ptr<XMLElementNode> path = util::make_unique<XMLElementNode>("path");
+ unique_ptr<XMLElement> path = util::make_unique<XMLElement>("path");
path->addAttribute("d", oss.str());
if (_pattern)
path->addAttribute("fill", XMLString("url(#")+_pattern->svgID()+")");
@@ -665,7 +731,7 @@ void PsSpecialHandler::fill (vector<double> &p, bool evenodd) {
if (_xmlnode)
_xmlnode->append(std::move(path));
else {
- _actions->appendToPage(std::move(path));
+ _actions->svgTree().appendToPage(std::move(path));
_actions->embed(bbox);
}
_path.clear();
@@ -682,7 +748,7 @@ static void create_matrix (vector<double> &v, int startindex, Matrix &matrix) {
// using corresponding values of the identity matrix.
if (v.size()-startindex < 6) {
v.resize(6+startindex);
- for (int i=v.size()-startindex; i < 6; ++i)
+ for (size_t i=v.size()-startindex; i < 6; ++i)
v[i+startindex] = (i%3 ? 0 : 1);
}
// PS matrix [a b c d e f] equals ((a,b,0),(c,d,0),(e,f,1)).
@@ -724,7 +790,7 @@ void PsSpecialHandler::makepattern (vector<double> &p) {
Matrix matrix; // transformation matrix given together with pattern definition
create_matrix(p, 9, matrix);
- matrix.rmultiply(_actions->getMatrix());
+ matrix.lmultiply(_actions->getMatrix());
unique_ptr<PSTilingPattern> pattern;
if (paint_type == 1)
@@ -756,10 +822,10 @@ void PsSpecialHandler::setpattern (vector<double> &p) {
if (it == _patterns.end())
_pattern = nullptr;
else {
- if (auto *pattern = dynamic_cast<PSUncoloredTilingPattern*>(it->second.get()))
+ if (auto pattern = dynamic_cast<PSUncoloredTilingPattern*>(it->second.get()))
pattern->setColor(color);
it->second->apply(*_actions);
- if (auto *pattern = dynamic_cast<PSTilingPattern*>(it->second.get()))
+ if (auto pattern = dynamic_cast<PSTilingPattern*>(it->second.get()))
_pattern = pattern;
else
_pattern = nullptr;
@@ -828,19 +894,19 @@ void PsSpecialHandler::clip (Path path, bool evenodd) {
intersectedPath.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
}
if (pathReplaced) {
- auto pathElem = util::make_unique<XMLElementNode>("path");
+ auto pathElem = util::make_unique<XMLElement>("path");
pathElem->addAttribute("d", oss.str());
if (evenodd)
pathElem->addAttribute("clip-rule", "evenodd");
int newID = _clipStack.topID();
- auto clipElem = util::make_unique<XMLElementNode>("clipPath");
+ auto clipElem = util::make_unique<XMLElement>("clipPath");
clipElem->addAttribute("id", XMLString("clip")+XMLString(newID));
if (!COMPUTE_CLIPPATHS_INTERSECTIONS && oldID)
clipElem->addAttribute("clip-path", XMLString("url(#clip")+XMLString(oldID)+")");
clipElem->append(std::move(pathElem));
- _actions->appendToDefs(std::move(clipElem));
+ _actions->svgTree().appendToDefs(std::move(clipElem));
}
}
@@ -864,7 +930,7 @@ void PsSpecialHandler::shfill (vector<double> &params) {
case 3: colorSpace = Color::ColorSpace::RGB; break;
case 4: colorSpace = Color::ColorSpace::CMYK; break;
}
- VectorIterator<double> it = params;
+ VectorIterator<double> it(params);
it += 2; // skip shading type and color space
// Get color to fill the whole mesh area before drawing the gradient colors on top of that background.
// This is an optional parameter to shfill.
@@ -951,15 +1017,15 @@ static void read_patch_data (ShadingPatch &patch, int edgeflag,
class ShadingCallback : public ShadingPatch::Callback {
public:
- ShadingCallback (SpecialActions &actions, XMLElementNode *parent, int clippathID)
+ ShadingCallback (SpecialActions &actions, XMLElement *parent, int clippathID)
: _actions(actions)
{
- auto group = util::make_unique<XMLElementNode>("g");
+ auto group = util::make_unique<XMLElement>("g");
_group = group.get();
if (parent)
parent->append(std::move(group));
else
- actions.appendToPage(std::move(group));
+ actions.svgTree().appendToPage(std::move(group));
if (clippathID > 0)
_group->addAttribute("clip-path", XMLString("url(#clip")+XMLString(clippathID)+")");
}
@@ -971,7 +1037,7 @@ class ShadingCallback : public ShadingPatch::Callback {
// draw a single patch segment
ostringstream oss;
path.writeSVG(oss, SVGTree::RELATIVE_PATH_CMDS);
- auto pathElem = util::make_unique<XMLElementNode>("path");
+ auto pathElem = util::make_unique<XMLElement>("path");
pathElem->addAttribute("d", oss.str());
pathElem->addAttribute("fill", color.svgColorString());
_group->append(std::move(pathElem));
@@ -979,7 +1045,7 @@ class ShadingCallback : public ShadingPatch::Callback {
private:
SpecialActions &_actions;
- XMLElementNode *_group;
+ XMLElement *_group;
};
@@ -999,16 +1065,14 @@ void PsSpecialHandler::processSequentialPatchMesh (int shadingTypeID, ColorSpace
#if 0
if (bgcolorGiven) {
// fill whole patch area with given background color
- GraphicsPath<double> outline;
- patch->getBoundaryPath(outline);
+ GraphicsPath<double> outline = patch->getBoundaryPath();
callback.patchSegment(outline, bgcolor);
}
#endif
patch->approximate(SHADING_SEGMENT_SIZE, SHADING_SEGMENT_OVERLAP, SHADING_SIMPLIFY_DELTA, callback);
if (!_xmlnode) {
// update bounding box
- BoundingBox bbox;
- patch->getBBox(bbox);
+ BoundingBox bbox = patch->getBBox();
bbox.transform(_actions->getMatrix());
_actions->embed(bbox);
}
@@ -1093,7 +1157,7 @@ void PsSpecialHandler::scale (vector<double> &p) {
if (_actions) {
Matrix m = _actions->getMatrix();
ScalingMatrix s(p[0], p[1]);
- m.lmultiply(s);
+ m.rmultiply(s);
_actions->setMatrix(m);
}
}
@@ -1103,7 +1167,7 @@ void PsSpecialHandler::translate (vector<double> &p) {
if (_actions) {
Matrix m = _actions->getMatrix();
TranslationMatrix t(p[0], p[1]);
- m.lmultiply(t);
+ m.rmultiply(t);
_actions->setMatrix(m);
}
}
@@ -1113,7 +1177,7 @@ void PsSpecialHandler::rotate (vector<double> &p) {
if (_actions) {
Matrix m = _actions->getMatrix();
RotationMatrix r(p[0]);
- m.lmultiply(r);
+ m.rmultiply(r);
_actions->setMatrix(m);
}
}
@@ -1167,6 +1231,18 @@ void PsSpecialHandler::executed () {
_actions->progress("ps");
}
+
+/** This method is called by PSInterpreter if the status of the output devices has changed.
+ * @param[in] p 1 if output device is the nulldevice, 1 otherwise */
+void PsSpecialHandler::setnulldevice (vector<double> &p) {
+ if (_actions) {
+ if (p[0] != 0)
+ _actions->lockOutput(); // prevent further SVG output
+ else
+ _actions->unlockOutput(); // enable SVG output again
+ }
+}
+
////////////////////////////////////////////
void PsSpecialHandler::ClippingStack::pushEmptyPath () {