diff options
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp')
-rw-r--r-- | Build/source/texk/dvisvgm/dvisvgm-src/src/PsSpecialHandler.cpp | 230 |
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> ¶ms) { 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 () { |