1 /*************************************************************************
2 ** PSPattern.cpp                                                        **
3 **                                                                      **
4 ** This file is part of dvisvgm -- the DVI to SVG converter             **
5 ** Copyright (C) 2005-2015 Martin Gieseking <martin.gieseking@uos.de>   **
6 **                                                                      **
7 ** This program is free software; you can redistribute it and/or        **
8 ** modify it under the terms of the GNU General Public License as       **
9 ** published by the Free Software Foundation; either version 3 of       **
10 ** the License, or (at your option) any later version.                  **
11 **                                                                      **
12 ** This program is distributed in the hope that it will be useful, but  **
13 ** WITHOUT ANY WARRANTY; without even the implied warranty of           **
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the         **
15 ** GNU General Public License for more details.                         **
16 **                                                                      **
17 ** You should have received a copy of the GNU General Public License    **
18 ** along with this program; if not, see <http://www.gnu.org/licenses/>. **
19 *************************************************************************/
20 
21 #include <config.h>
22 #include <sstream>
23 #include <vector>
24 #include "BoundingBox.h"
25 #include "PSPattern.h"
26 #include "SpecialActions.h"
27 #include "SVGTree.h"
28 #include "XMLNode.h"
29 
30 using namespace std;
31 
32 
svgID() const33 string PSPattern::svgID () const {
34 	return XMLString("pat")+XMLString(_id);
35 }
36 
37 
38 /** Appends the definition of this pattern to the "def" section of the SVG tree. */
apply(SpecialActions * actions)39 void PSPattern::apply (SpecialActions *actions) {
40 	if (XMLElementNode *pattern = createPatternNode())
41 		actions->appendToDefs(pattern);
42 }
43 
44 
45 /////////////////////////////////////////////////////////////////////////////
46 
PSTilingPattern(int id,BoundingBox & bbox,Matrix & matrix,double xstep,double ystep)47 PSTilingPattern::PSTilingPattern (int id, BoundingBox &bbox, Matrix &matrix, double xstep, double ystep)
48 	: PSPattern(id), _bbox(bbox), _matrix(matrix), _xstep(xstep), _ystep(ystep), _groupNode(0)
49 {
50 	_groupNode = PSTilingPattern::createGroupNode();
51 }
52 
53 
~PSTilingPattern()54 PSTilingPattern::~PSTilingPattern () {
55 	delete _groupNode;
56 }
57 
58 
59 /** Creates a new pattern element representing the pattern defined in the PS code. */
createPatternNode() const60 XMLElementNode* PSTilingPattern::createPatternNode () const {
61 	if (!_groupNode)
62 		return 0;
63 	BoundingBox box(_bbox.minX(), _bbox.minY(), _bbox.minX()+_xstep, _bbox.minY()+_ystep);
64 	XMLElementNode *pattern = new XMLElementNode("pattern");
65 	pattern->addAttribute("id", svgID());
66 	pattern->addAttribute("x", box.minX());
67 	pattern->addAttribute("y", box.minY());
68 	pattern->addAttribute("width", box.width());
69 	pattern->addAttribute("height", box.height());
70 	pattern->addAttribute("viewBox", box.toSVGViewBox());
71 	pattern->addAttribute("patternUnits", "userSpaceOnUse");
72 	if (!_matrix.isIdentity())
73 		pattern->addAttribute("patternTransform", _matrix.getSVG());
74 	if (_xstep < _bbox.width() || _ystep < _bbox.height()) {  // overlapping tiles?
75 		// disable clipping at the tile borders => tiles become "transparent"
76 		pattern->addAttribute("overflow", "visible");
77 	}
78 	if (XMLElementNode *clip=createClipNode())
79 		pattern->append(clip);
80 	pattern->append(_groupNode);
81 	return pattern;
82 }
83 
84 
85 /** Creates a new clip element restricting the drawing area to the
86  *  dimensions given in the definition of the pattern. */
createClipNode() const87 XMLElementNode* PSTilingPattern::createClipNode() const {
88 	XMLElementNode *clip = new XMLElementNode("clipPath");
89 	clip->addAttribute("id", "pc"+XMLString(psID()));
90 	XMLElementNode *rect = new XMLElementNode("rect");
91 	rect->addAttribute("x", _bbox.minX());
92 	rect->addAttribute("y", _bbox.minY());
93 	rect->addAttribute("width", _bbox.width());
94 	rect->addAttribute("height", _bbox.height());
95 	clip->append(rect);
96 	return clip;
97 }
98 
99 
100 /** Creates a new group element that contains all "drawing" elements that
101  *  define the pattern graphic. */
createGroupNode() const102 XMLElementNode* PSTilingPattern::createGroupNode () const {
103 	// add all succeeding path elements to this group
104 	XMLElementNode *group = new XMLElementNode("g");
105 	group->addAttribute("clip-path", XMLString("url(#pc")+XMLString(psID())+")");
106 	return group;
107 }
108 
109 
apply(SpecialActions * actions)110 void PSTilingPattern::apply (SpecialActions *actions) {
111 	PSPattern::apply(actions);
112 	_groupNode = 0;
113 }
114 
115 
116 /////////////////////////////////////////////////////////////////////////////
117 
PSColoredTilingPattern(int id,BoundingBox & bbox,Matrix & matrix,double xstep,double ystep)118 PSColoredTilingPattern::PSColoredTilingPattern (int id, BoundingBox &bbox, Matrix &matrix, double xstep, double ystep)
119 	: PSTilingPattern(id, bbox, matrix, xstep, ystep)
120 {
121 }
122 
123 
124 /////////////////////////////////////////////////////////////////////////////
125 
PSUncoloredTilingPattern(int id,BoundingBox & bbox,Matrix & matrix,double xstep,double ystep)126 PSUncoloredTilingPattern::PSUncoloredTilingPattern (int id, BoundingBox &bbox, Matrix &matrix, double xstep, double ystep)
127 	: PSTilingPattern(id, bbox, matrix, xstep, ystep), _applied(false)
128 {
129 }
130 
131 
~PSUncoloredTilingPattern()132 PSUncoloredTilingPattern::~PSUncoloredTilingPattern () {
133 	if (_applied)
134 		setGroupNode(0);  // prevent deleting the group node in the parent destructor
135 }
136 
137 
138 /** Returns an SVG id value that identifies this pattern with the current color applied. */
svgID() const139 string PSUncoloredTilingPattern::svgID () const {
140 	ostringstream oss;
141 	oss << PSPattern::svgID() << '-' << hex << _currentColor;
142 	return oss.str();
143 }
144 
145 
146 /** Appends the definition of this pattern with the current color applied
147  *  to the "def" section of the SVG tree. */
apply(SpecialActions * actions)148 void PSUncoloredTilingPattern::apply (SpecialActions* actions) {
149 	set<Color>::iterator it=_colors.find(_currentColor);
150 	if (it == _colors.end()) {
151 		if (_applied)
152 			setGroupNode(static_cast<XMLElementNode*>(getGroupNode()->clone()));
153 		// assign current color to the pattern graphic
154 		vector<XMLElementNode*> colored_elems;
155 		const char *attribs[] = {"fill", "stroke"};
156 		for (int i=0; i < 2; i++) {
157 			getGroupNode()->getDescendants(0, attribs[i], colored_elems);
158 			for (vector<XMLElementNode*>::iterator it=colored_elems.begin(); it != colored_elems.end(); ++it)
159 				if (string((*it)->getAttributeValue(attribs[i])) != "none")
160 					(*it)->addAttribute(attribs[i], _currentColor.rgbString());
161 			colored_elems.clear();
162 		}
163 		PSPattern::apply(actions);
164 		_colors.insert(_currentColor);
165 		_applied = true;
166 	}
167 }
168 
169 
createClipNode() const170 XMLElementNode* PSUncoloredTilingPattern::createClipNode() const {
171 	// only the first instance of this patterns get a clip element
172 	if (_colors.empty())
173 		return PSTilingPattern::createClipNode();
174 	return 0;
175 }
176