예제 #1
0
/** Handles the "point" command that adds a point to the point list. */
void EmSpecialHandler::point (InputReader &ir, SpecialActions &actions) {
	DPair pos(actions.getX(), actions.getY());
	int n = ir.getInt();
	if (ir.getPunct() == ',') {
		pos.x(ir.getDouble());
		if (ir.getPunct() == ',')
			pos.y(ir.getDouble());
	}
	_points[n] = pos;
}
예제 #2
0
/** Embeds the virtual rectangle (x, y ,w , h) into the current bounding box,
 *  where (x,y) is the lower left vertex composed of the current DVI position.
 *  @param[in] w width of the rectangle in PS point units
 *  @param[in] h height of the rectangle in PS point units
 *  @param[in] d depth of the rectangle in PS point units
 *  @param[in] transform if true, apply the current transformation matrix to the rectangle
 *  @param[in] actions object providing the actions that can be performed by the SpecialHandler */
static void update_bbox (Length w, Length h, Length d, bool transform, SpecialActions &actions) {
	double x = actions.getX();
	double y = actions.getY();
	BoundingBox bbox1(x, y, x+w.bp(), y-h.bp());
	BoundingBox bbox2(x, y, x+w.bp(), y+d.bp());
	if (transform) {
		bbox1.transform(actions.getMatrix());
		bbox2.transform(actions.getMatrix());
	}
	actions.embed(bbox1);
	actions.embed(bbox2);
}
void BgColorSpecialHandler::dviBeginPage (unsigned pageno, SpecialActions &actions) {
	// Ensure that the background color of the preceeding page is set as the
	// default background color of the current page because this special affects
	// the current and all subsequent pages until the next change.
	// See the documentation of the color package, section 3.5.
	if (_pageColors.empty())
		return;
	// find number of page with bg color change not lower than the current one
	vector<PageColor>::iterator it = lower_bound(_pageColors.begin(), _pageColors.end(), PageColor(pageno, Color::BLACK));
	if (it != _pageColors.end() && it->first == pageno)
		actions.setBgColor(it->second);
	else if (it != _pageColors.begin())
		actions.setBgColor((--it)->second);
}
/** Collect all background color changes while preprocessing the DVI file.
 *  We need them in order to apply the correct background colors even if
 *  not all but only selected DVI pages are converted. */
void BgColorSpecialHandler::preprocess (const char*, std::istream &is, SpecialActions &actions) {
	Color color = ColorSpecialHandler::readColor(is);
	unsigned pageno = actions.getCurrentPageNumber();
	if (_pageColors.empty() || _pageColors.back().second != color) {
		if (!_pageColors.empty() && _pageColors.back().first == pageno)
			_pageColors.back().second = color;
		else
			_pageColors.push_back(PageColor(pageno, color));
	}
}
예제 #5
0
bool ColorSpecialHandler::process (const string&, istream &is, SpecialActions &actions) {
	string cmd;
	is >> cmd;
	if (cmd == "push")               // color push <model> <params>
		_colorStack.push(readColor(is));
	else if (cmd == "pop") {
		if (!_colorStack.empty())     // color pop
			_colorStack.pop();
	}
	else {                           // color <model> <params>
		while (!_colorStack.empty())
			_colorStack.pop();
		_colorStack.push(readColor(cmd, is));
	}
	if (_colorStack.empty())
		actions.setColor(Color::BLACK);
	else
		actions.setColor(_colorStack.top());
	return true;
}
예제 #6
0
void DvisvgmSpecialHandler::processImg (InputReader &ir, SpecialActions &actions) {
	try {
		Length w = read_length(ir);
		Length h = read_length(ir);
		string f = ir.getString();
		update_bbox(w, h, Length(0), false, actions);
		auto img = util::make_unique<XMLElement>("image");
		img->addAttribute("x", actions.getX());
		img->addAttribute("y", actions.getY());
		img->addAttribute("width", w.bp());
		img->addAttribute("height", h.bp());
		img->addAttribute("xlink:href", f);
		if (!actions.getMatrix().isIdentity())
			img->addAttribute("transform", actions.getMatrix().toSVG());
		actions.svgTree().appendToPage(std::move(img));
	}
	catch (const UnitException &e) {
		throw SpecialException(string("dvisvgm:img: ") + e.what());
	}
}
예제 #7
0
/** Processes an opening element tag.
 *  @param[in] tag tag without leading and trailing angle brackets */
void DvisvgmSpecialHandler::XMLParser::openElement (const string &tag, SpecialActions &actions) {
	StringInputBuffer ib(tag);
	BufferInputReader ir(ib);
	string name = ir.getString("/ \t\n\r");
	ir.skipSpace();
	auto elemNode = util::make_unique<XMLElement>(name);
	map<string, string> attribs;
	if (ir.parseAttributes(attribs, true, "\"'")) {
		for (const auto &attrpair : attribs)
			elemNode->addAttribute(attrpair.first, attrpair.second);
	}
	ir.skipSpace();
	if (ir.peek() == '/')       // end of empty element tag
		(actions.svgTree().*_append)(std::move(elemNode));
	else if (ir.peek() < 0) {   // end of opening tag
		_nameStack.push_back(name);
		(actions.svgTree().*_pushContext)(std::move(elemNode));
	}
	else
		throw SpecialException("'>' or '/>' expected at end of opening tag <"+name);
}
예제 #8
0
void DvisvgmSpecialHandler::dviEndPage (unsigned, SpecialActions &actions) {
	_defsParser.flush(actions);
	_pageParser.flush(actions);
	actions.bbox().unlock();
	for (auto &strvecpair : _macros) {
		StringVector &vec = strvecpair.second;
		for (string &str : vec) {
			// activate locked parts of a pattern again
			if (str[0] == 'L')
				str[0] = 'D';
		}
	}
}
예제 #9
0
/** 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) {
		size_t pos = str.find("{?bbox ");
		if (pos == string::npos)
			repl_bbox = false;
		else {
			size_t endpos = pos+7;
			while (endpos < str.length() && isalnum(str[endpos]))
				++endpos;
			if (str[endpos] == '}') {
				BoundingBox &box=actions.bbox(str.substr(pos+7, endpos-pos-7));
				str.replace(pos, endpos-pos+1, box.toSVGViewBox());
			}
			else
				repl_bbox = false;
		}
	}
	struct Constant {
		const char *name;
		string val;
	};
	const array<Constant, 5> constants {{
		{"x",      XMLString(actions.getX())},
		{"y",      XMLString(actions.getY())},
		{"color",  actions.getColor().svgColorString()},
		{"matrix", actions.getMatrix().toSVG()},
		{"nl",    "\n"},
	}};
	for (const Constant &constant : constants) {
		const string pattern = string("{?")+constant.name+"}";
		size_t 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
		}
	}
}
예제 #10
0
/** Processes a closing element tag.
 *  @param[in] tag tag without leading and trailing angle brackets */
void DvisvgmSpecialHandler::XMLParser::closeElement (const string &tag, SpecialActions &actions) {
	StringInputBuffer ib(tag);
	BufferInputReader ir(ib);
	string name = ir.getString(" \t\n\r");
	ir.skipSpace();
	if (ir.peek() >= 0)
		throw SpecialException("'>' expected at end of closing tag </"+name);
	if (_nameStack.empty())
		throw SpecialException("spurious closing tag </" + name + ">");
	if (_nameStack.back() != name)
		throw SpecialException("expected </" + name + "> but found </" + _nameStack.back() + ">");
	(actions.svgTree().*_popContext)();
	_nameStack.pop_back();
}
예제 #11
0
/** Evaluates the special dvisvgm:bbox.
 *  variant 1: dvisvgm:bbox [r[el]] <width> <height> [<depth>] [transform]
 *  variant 2: dvisvgm:bbox a[bs] <x1> <y1> <x2> <y2> [transform]
 *  variant 3: dvisvgm:bbox f[ix] <x1> <y1> <x2> <y2> [transform]
 *  variant 4: dvisvgm:bbox n[ew] <name>
 *  variant 5: dvisvgm:bbox lock | unlock */
void DvisvgmSpecialHandler::processBBox (InputReader &ir, SpecialActions &actions) {
	ir.skipSpace();
	if (ir.check("lock"))
		actions.bbox().lock();
	else if (ir.check("unlock"))
		actions.bbox().unlock();
	else {
		int c = ir.peek();
		try {
			if (!isalpha(c))
				c = 'r';   // no mode specifier => relative box parameters
			else {
				while (!isspace(ir.peek()))  // skip trailing characters
					ir.get();
				if (c == 'n') {   // "new": create new local bounding box
					ir.skipSpace();
					string name;
					while (isalnum(ir.peek()))
						name += char(ir.get());
					ir.skipSpace();
					if (!name.empty() && ir.eof())
						actions.bbox(name, true); // create new user box
				}
				else if (c == 'a' || c == 'f') {  // "abs" or "fix"
					Length lengths[4];
					for (Length &len : lengths)
						len = read_length(ir);
					BoundingBox b(lengths[0], lengths[1], lengths[2], lengths[3]);
					ir.skipSpace();
					if (ir.check("transform"))
						b.transform(actions.getMatrix());
					if (c == 'a')
						actions.embed(b);
					else {
						actions.bbox() = b;
						actions.bbox().lock();
					}
				}
			}
			if (c == 'r') {
				Length w = read_length(ir);
				Length h = read_length(ir);
				Length d = read_length(ir);
				ir.skipSpace();
				update_bbox(w, h, d, ir.check("transform"), actions);
			}
		}
		catch (const UnitException &e) {
			throw SpecialException(string("dvisvgm:bbox: ") + e.what());
		}
	}
}
예제 #12
0
/** Creates the SVG element that will a the line.
 * @param[in] p1 first endpoint in PS point units
 * @param[in] p2 second endpoint in PS point units
 * @param[in] c1 cut method of first endpoint ('h', 'v' or 'p')
 * @param[in] c2 cut method of second endpoint ('h', 'v' or 'p')
 * @param[in] lw line width in PS point units
 * @param[in] actions object providing the actions that can be performed by the SpecialHandler */
static void create_line (const DPair &p1, const DPair &p2, char c1, char c2, double lw, SpecialActions &actions) {
	unique_ptr<XMLElementNode> node;
	DPair dir = p2-p1;
	if (dir.x() == 0 || dir.y() == 0 || (c1 == 'p' && c2 == 'p')) {
		// draw regular line
		node = util::make_unique<XMLElementNode>("line");
		node->addAttribute("x1", p1.x());
		node->addAttribute("y1", p1.y());
		node->addAttribute("x2", p2.x());
		node->addAttribute("y2", p2.y());
		node->addAttribute("stroke-width", lw);
		node->addAttribute("stroke", actions.getColor().svgColorString());
		// update bounding box
		DPair cv = cut_vector('p', dir, lw);
		actions.embed(p1+cv);
		actions.embed(p1-cv);
		actions.embed(p2+cv);
		actions.embed(p2-cv);
	}
	else {
		// draw polygon
		DPair cv1 = cut_vector(c1, dir, lw);
		DPair cv2 = cut_vector(c2, dir, lw);
		DPair q11 = p1+cv1, q12 = p1-cv1;
		DPair q21 = p2+cv2, q22 = p2-cv2;
		ostringstream oss;
		oss << XMLString(q11.x()) << ',' << XMLString(q11.y()) << ' '
			 << XMLString(q12.x()) << ',' << XMLString(q12.y()) << ' '
			 << XMLString(q22.x()) << ',' << XMLString(q22.y()) << ' '
			 << XMLString(q21.x()) << ',' << XMLString(q21.y());
		node = util::make_unique<XMLElementNode>("polygon");
		node->addAttribute("points", oss.str());
		if (actions.getColor() != Color::BLACK)
			node->addAttribute("fill", actions.getColor().svgColorString());
		// update bounding box
		actions.embed(q11);
		actions.embed(q12);
		actions.embed(q21);
		actions.embed(q22);
	}
	actions.appendToPage(std::move(node));
}
예제 #13
0
void PapersizeSpecialHandler::preprocess (const string&, std::istream &is, SpecialActions &actions) {
	string params;
	is >> params;
	Length w, h;
	const size_t splitpos = params.find(',');
	try {
		if (splitpos == string::npos) {
			w.set(params);
			h.set(params);
		}
		else {
			w.set(params.substr(0, splitpos));
			h.set(params.substr(splitpos+1));
		}
		storePaperSize(actions.getCurrentPageNumber(), w, h);
	}
	catch (UnitException &e) { // ignore invalid length units for now
	}
}
예제 #14
0
/** Applies the previously recorded size to a given page. */
void PapersizeSpecialHandler::applyPaperSize (unsigned pageno, SpecialActions &actions) {
	// find page n >= pageno that contains a papersize special
	auto lb_it = lower_bound(_pageSizes.begin(), _pageSizes.end(), PageSize(pageno, DoublePair()),
		[](const PageSize &ps1, const PageSize &ps2) {
			// order PageSize objects by page number
			return ps1.first < ps2.first;
		});
	auto it = _pageSizes.end();
	if (lb_it != _pageSizes.end() && lb_it->first == pageno)
		it = lb_it;                        // if current page contains a papersize special, use it
	else if (lb_it != _pageSizes.begin()) // no papersize special on current page?
		it = lb_it-1;                      // => use the one on the nearest preceding page
	if (it == _pageSizes.end())
		Message::wstream(true) << "no valid papersize special found\n";
	else {
		DoublePair size = it->second;
		const double border = -72;  // DVI standard: coordinates of upper left paper corner are (-72bp, -72bp)
		actions.bbox() = BoundingBox(border, border, size.first+border, size.second+border);
	}
}
예제 #15
0
void PapersizeSpecialHandler::dviEndPage (unsigned pageno, SpecialActions &actions) {
	if (actions.getBBoxFormatString() == "papersize")
		applyPaperSize(pageno, actions);
}
예제 #16
0
/** Handles the "lineto" command that sraws a straight line from the current drawing position
 *  to the current DVI position, and sets the drawing position to the DVI position afterwards. */
void EmSpecialHandler::lineto (InputReader&, SpecialActions &actions) {
	DPair currpos(actions.getX(), actions.getY());
	create_line(_pos, currpos, 'p', 'p', _linewidth, actions);
	_pos = currpos;
}
예제 #17
0
/** Parses a fragment of XML code, creates corresponding XML nodes and adds them
 *  to the SVG tree. The code may be split and processed by several calls of this
 *  function. Incomplete chunks that can't be processed yet are stored and picked
 *  up again together with the next incoming XML fragment. If no further code should
 *  be appended, parameter 'finish' must be set.
 *  @param[in] xml XML fragment to parse
 *  @param[in] actions object providing the SVG tree functions
 *  @param[in] finish if true, no more XML is expected and parsing is finished */
void DvisvgmSpecialHandler::XMLParser::parse (const string &xml, SpecialActions &actions, bool finish) {
	// collect/extract an XML fragment that only contains complete tags
	// incomplete tags are held back
	_xmlbuf += xml;
	size_t left=0, right;
	while (left != string::npos) {
		right = _xmlbuf.find('<', left);
		if (left < right && left < _xmlbuf.length())  // plain text found?
			(actions.svgTree().*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right-left)));
		if (right != string::npos) {
			left = right;
			if (_xmlbuf.compare(left, 9, "<![CDATA[") == 0) {
				right = _xmlbuf.find("]]>", left+9);
				if (right == string::npos) {
					if (finish)	throw SpecialException("expected ']]>' at end of CDATA block");
					break;
				}
				(actions.svgTree().*_append)(util::make_unique<XMLCData>(_xmlbuf.substr(left+9, right-left-9)));
				right += 2;
			}
			else if (_xmlbuf.compare(left, 4, "<!--") == 0) {
				right = _xmlbuf.find("-->", left+4);
				if (right == string::npos) {
					if (finish)	throw SpecialException("expected '-->' at end of comment");
					break;
				}
				(actions.svgTree().*_append)(util::make_unique<XMLComment>(_xmlbuf.substr(left+4, right-left-4)));
				right += 2;
			}
			else if (_xmlbuf.compare(left, 2, "<?") == 0) {
				right = _xmlbuf.find("?>", left+2);
				if (right == string::npos) {
					if (finish)	throw SpecialException("expected '?>' at end of processing instruction");
					break;
				}
				(actions.svgTree().*_append)(util::make_unique<XMLText>(_xmlbuf.substr(left, right-left+2)));
				right++;
			}
			else if (_xmlbuf.compare(left, 2, "</") == 0) {
				right = _xmlbuf.find('>', left+2);
				if (right == string::npos) {
					if (finish)	throw SpecialException("missing '>' at end of closing XML tag");
					break;
				}
				closeElement(_xmlbuf.substr(left+2, right-left-2), actions);
			}
			else {
				right = _xmlbuf.find('>', left+1);
				if (right == string::npos) {
					if (finish)	throw SpecialException("missing '>' or '/>' at end of opening XML tag");
					break;
				}
				openElement(_xmlbuf.substr(left+1, right-left-1), actions);
			}
		}
		left = right;
		if (right != string::npos)
			left++;
	}
	if (left == string::npos)
		_xmlbuf.clear();
	else
		_xmlbuf.erase(0, left);
}
예제 #18
0
/** Handles the "moveto" command that sets the drawing position to the current DVI position. */
void EmSpecialHandler::moveto (InputReader&, SpecialActions &actions) {
	_pos.x(actions.getX());
	_pos.y(actions.getY());
}