summaryrefslogtreecommitdiff
path: root/dviware/dvisvgm/src/PsSpecialHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dviware/dvisvgm/src/PsSpecialHandler.cpp')
-rw-r--r--dviware/dvisvgm/src/PsSpecialHandler.cpp125
1 files changed, 98 insertions, 27 deletions
diff --git a/dviware/dvisvgm/src/PsSpecialHandler.cpp b/dviware/dvisvgm/src/PsSpecialHandler.cpp
index 31f8aa55e7..2301eeb571 100644
--- a/dviware/dvisvgm/src/PsSpecialHandler.cpp
+++ b/dviware/dvisvgm/src/PsSpecialHandler.cpp
@@ -2,7 +2,7 @@
** PsSpecialHandler.cpp **
** **
** This file is part of dvisvgm -- a fast DVI to SVG converter **
-** Copyright (C) 2005-2019 Martin Gieseking <martin.gieseking@uos.de> **
+** Copyright (C) 2005-2020 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 **
@@ -23,7 +23,6 @@
#include <fstream>
#include <memory>
#include <sstream>
-#include "EPSFile.hpp"
#include "FileFinder.hpp"
#include "FilePath.hpp"
#include "FileSystem.hpp"
@@ -36,6 +35,7 @@
#include "SVGTree.hpp"
#include "TensorProductPatch.hpp"
#include "TriangularPatch.hpp"
+#include "utility.hpp"
using namespace std;
@@ -44,10 +44,12 @@ bool PsSpecialHandler::COMPUTE_CLIPPATHS_INTERSECTIONS = false;
bool PsSpecialHandler::SHADING_SEGMENT_OVERLAP = false;
int PsSpecialHandler::SHADING_SEGMENT_SIZE = 20;
double PsSpecialHandler::SHADING_SIMPLIFY_DELTA = 0.01;
+string PsSpecialHandler::BITMAP_FORMAT;
-PsSpecialHandler::PsSpecialHandler () : _psi(this), _actions(), _previewFilter(_psi), _xmlnode(), _savenode()
+PsSpecialHandler::PsSpecialHandler () : _psi(this), _previewFilter(_psi)
{
+ _psi.setImageDevice(BITMAP_FORMAT);
}
@@ -83,6 +85,7 @@ void PsSpecialHandler::initgraphics () {
_blendmode = 0; // "normal" mode (no blending)
_sx = _sy = _cos = 1.0;
_pattern = nullptr;
+ _patternEnabled = false;
_currentcolor = Color::BLACK;
_dashoffset = 0;
_dashpattern.clear();
@@ -381,6 +384,13 @@ void PsSpecialHandler::imgfile (FileType filetype, const string &fname, const ma
}
+/** Returns path + basename of temporary bitmap images. */
+static string image_base_path (SpecialActions &actions) {
+ FilePath imgpath = actions.getSVGFilePath(actions.getCurrentPageNumber());
+ return FileSystem::tmpdir() + "/" + imgpath.basename() + "-tmp-";
+}
+
+
/** 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
@@ -421,14 +431,15 @@ unique_ptr<XMLElement> PsSpecialHandler::createImageNode (FileType type, const s
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
+ "\n@beginspecial @setspecial" // enter special environment
+ "/setpagedevice{@setpagedevice}def " // activate processing of operator "setpagedevice"
+ "/@imgbase("+image_base_path(*_actions)+")store " // path and basename of image files
+ "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\n" // leave special environment
);
if (node->empty())
node.reset(nullptr);
@@ -468,6 +479,11 @@ static bool transform_box_extents (const Matrix &matrix, double &w, double &h, d
}
+void PsSpecialHandler::dviBeginPage (unsigned int pageno, SpecialActions &actions) {
+ _psi.execute("/@imgbase("+image_base_path(actions)+")store\n"); // path and basename of image files
+}
+
+
void PsSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) {
BoundingBox bbox;
if (_previewFilter.getBoundingBox(bbox)) { // is there any data written by preview package?
@@ -638,7 +654,7 @@ void PsSpecialHandler::stroke (vector<double> &p) {
}
else {
// compute bounding box
- _path.computeBBox(bbox);
+ bbox = _path.computeBBox();
bbox.expand(_linewidth/2);
ostringstream oss;
@@ -669,12 +685,10 @@ void PsSpecialHandler::stroke (vector<double> &p) {
path->addAttribute("stroke-dashoffset", _dashoffset);
}
}
- if (path && _clipStack.path()) {
+ if (path && _clipStack.path() && !_savenode) {
// assign clipping path and clip bounding box
path->addAttribute("clip-path", XMLString("url(#clip")+XMLString(_clipStack.topID())+")");
- BoundingBox clipbox;
- _clipStack.path()->computeBBox(clipbox);
- bbox.intersect(clipbox);
+ bbox.intersect(_clipStack.path()->computeBBox());
_clipStack.removePrependedPath();
}
if (_xmlnode)
@@ -696,8 +710,7 @@ void PsSpecialHandler::fill (vector<double> &p, bool evenodd) {
return;
// compute bounding box
- BoundingBox bbox;
- _path.computeBBox(bbox);
+ BoundingBox bbox = _path.computeBBox();
if (!_actions->getMatrix().isIdentity()) {
_path.transform(_actions->getMatrix());
if (!_xmlnode)
@@ -714,12 +727,10 @@ void PsSpecialHandler::fill (vector<double> &p, bool evenodd) {
path->addAttribute("fill", XMLString("url(#")+_pattern->svgID()+")");
else if (_actions->getColor() != Color::BLACK || _savenode)
path->addAttribute("fill", _actions->getColor().svgColorString());
- if (_clipStack.path()) {
+ if (_clipStack.path() && !_savenode) { // clip path active and not inside pattern definition?
// assign clipping path and clip bounding box
path->addAttribute("clip-path", XMLString("url(#clip")+XMLString(_clipStack.topID())+")");
- BoundingBox clipbox;
- _clipStack.path()->computeBBox(clipbox);
- bbox.intersect(clipbox);
+ bbox.intersect(_clipStack.path()->computeBBox());
_clipStack.removePrependedPath();
}
if (evenodd) // SVG default fill rule is "nonzero" algorithm
@@ -738,6 +749,62 @@ void PsSpecialHandler::fill (vector<double> &p, bool evenodd) {
}
+/** Postprocesses the 'image' operation performed by the PS interpreter. If
+ * the PS image operator succeeded, there's now a PNG file that must be embedded
+ * into the SVG file. */
+void PsSpecialHandler::image (std::vector<double> &p) {
+ int imgID = static_cast<int>(p[0]); // ID of PNG file written
+ if (imgID < 0) // no bitmap file written?
+ return;
+
+ double width = p[1];
+ double height = p[2];
+ string suffix = (BITMAP_FORMAT.substr(0, 3) == "png" ? ".png" : ".jpg");
+ string fname = image_base_path(*_actions)+to_string(imgID)+suffix;
+ ifstream ifs(fname, ios::binary);
+ if (ifs) {
+ ifs.close();
+ auto image = util::make_unique<XMLElement>("image");
+ double x = _actions->getX();
+ double y = _actions->getY();
+ image->addAttribute("x", x);
+ image->addAttribute("y", y);
+ image->addAttribute("width", util::to_string(width));
+ image->addAttribute("height", util::to_string(height));
+
+ // The current transformation matrix (CTM) maps the unit square to the rectangular region
+ // of the target canvas showing the bitmap (see PS Reference Manual, 4.10.3). Therefore,
+ // the local pixel coordinates of the original bitmap must be transformed by CTM*inv(M) to
+ // get the target coordinates. M is the matrix that maps the unit square to the bitmap rectangle.
+ Matrix matrix{width, 0, 0, 0, -height, height}; // maps unit square to bitmap rectangle
+ matrix = matrix.invert().lmultiply(_actions->getMatrix());
+ image->addAttribute("transform", matrix.toSVG());
+
+ // To prevent memory issues, only add the filename to the href attribute and tag it by '@'
+ // for later base64 encoding.
+ image->addAttribute("@xlink:href", string("data:image/")+(suffix == ".png" ? "png" : "jpeg")+";base64,"+fname);
+
+ // if set, assign clipping path to image
+ if (_clipStack.path()) {
+ auto group = util::make_unique<XMLElement>("g");
+ group->addAttribute("clip-path", XMLString("url(#clip")+XMLString(_clipStack.topID())+")");
+ group->append(std::move(image));
+ image = std::move(group); // handle the entire group as image to add
+ }
+ if (_xmlnode)
+ _xmlnode->append(std::move(image));
+ else {
+ _actions->svgTree().appendToPage(std::move(image));
+ BoundingBox bbox(x, y, x+width, y+height);
+ bbox.transform(matrix);
+ if (_clipStack.path())
+ bbox.intersect(_clipStack.path()->computeBBox());
+ _actions->embed(bbox);
+ }
+ }
+}
+
+
/** Creates a Matrix object from a given sequence of 6 double values.
* The given values must be arranged in PostScript matrix order.
* @param[in] v vector containing the matrix values
@@ -774,7 +841,6 @@ static void create_matrix (vector<double> &v, int startindex, Matrix &matrix) {
* 9-14: pattern matrix */
void PsSpecialHandler::makepattern (vector<double> &p) {
int pattern_type = static_cast<int>(p[0]);
- int id = static_cast<int>(p[1]);
switch (pattern_type) {
case 0:
// pattern definition completed
@@ -784,6 +850,7 @@ void PsSpecialHandler::makepattern (vector<double> &p) {
}
break;
case 1: { // tiling pattern
+ int id = static_cast<int>(p[1]);
BoundingBox bbox(p[2], p[3], p[4], p[5]);
const double &xstep=p[6], &ystep=p[7]; // horizontal and vertical distance of adjacent tiles
int paint_type = static_cast<int>(p[8]);
@@ -1184,7 +1251,8 @@ void PsSpecialHandler::rotate (vector<double> &p) {
void PsSpecialHandler::setgray (vector<double> &p) {
- _pattern = nullptr;
+ if (!_patternEnabled)
+ _pattern = nullptr;
_currentcolor.setGray(p[0]);
if (_actions)
_actions->setColor(_currentcolor);
@@ -1192,7 +1260,8 @@ void PsSpecialHandler::setgray (vector<double> &p) {
void PsSpecialHandler::setrgbcolor (vector<double> &p) {
- _pattern= nullptr;
+ if (!_patternEnabled)
+ _pattern= nullptr;
_currentcolor.setRGB(p[0], p[1], p[2]);
if (_actions)
_actions->setColor(_currentcolor);
@@ -1200,7 +1269,8 @@ void PsSpecialHandler::setrgbcolor (vector<double> &p) {
void PsSpecialHandler::setcmykcolor (vector<double> &p) {
- _pattern = nullptr;
+ if (!_patternEnabled)
+ _pattern = nullptr;
_currentcolor.setCMYK(p[0], p[1], p[2], p[3]);
if (_actions)
_actions->setColor(_currentcolor);
@@ -1208,7 +1278,8 @@ void PsSpecialHandler::setcmykcolor (vector<double> &p) {
void PsSpecialHandler::sethsbcolor (vector<double> &p) {
- _pattern = nullptr;
+ if (!_patternEnabled)
+ _pattern = nullptr;
_currentcolor.setHSB(p[0], p[1], p[2]);
if (_actions)
_actions->setColor(_currentcolor);