summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-1.5/src/DVIToSVGActions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-1.5/src/DVIToSVGActions.cpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-1.5/src/DVIToSVGActions.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-1.5/src/DVIToSVGActions.cpp b/Build/source/texk/dvisvgm/dvisvgm-1.5/src/DVIToSVGActions.cpp
new file mode 100644
index 00000000000..d5d8fb2e768
--- /dev/null
+++ b/Build/source/texk/dvisvgm/dvisvgm-1.5/src/DVIToSVGActions.cpp
@@ -0,0 +1,370 @@
+/*************************************************************************
+** DVIToSVGActions.cpp **
+** **
+** This file is part of dvisvgm -- the DVI to SVG converter **
+** Copyright (C) 2005-2013 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 **
+** published by the Free Software Foundation; either version 3 of **
+** the License, or (at your option) any later version. **
+** **
+** This program is distributed in the hope that it will be useful, but **
+** WITHOUT ANY WARRANTY; without even the implied warranty of **
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
+** GNU General Public License for more details. **
+** **
+** You should have received a copy of the GNU General Public License **
+** along with this program; if not, see <http://www.gnu.org/licenses/>. **
+*************************************************************************/
+
+#include <config.h>
+#include <cstring>
+#include <ctime>
+#include "BoundingBox.h"
+#include "DVIToSVG.h"
+#include "DVIToSVGActions.h"
+#include "Font.h"
+#include "FontManager.h"
+#include "GlyphTracerMessages.h"
+#include "SpecialManager.h"
+#include "System.h"
+#include "XMLNode.h"
+#include "XMLString.h"
+
+
+using namespace std;
+
+
+double DVIToSVGActions::PROGRESSBAR_DELAY=1000; // initial delay in seconds (values >= 1000 disable the progressbar)
+
+
+DVIToSVGActions::DVIToSVGActions (DVIToSVG &dvisvg, SVGTree &svg)
+ : _svg(svg), _dvisvg(dvisvg), _pageMatrix(0), _bgcolor(Color::TRANSPARENT), _boxes(0)
+{
+ _currentFontNum = -1;
+ _pageCount = 0;
+}
+
+
+DVIToSVGActions::~DVIToSVGActions () {
+ delete _pageMatrix;
+ delete _boxes;
+}
+
+
+void DVIToSVGActions::reset() {
+ _usedChars.clear();
+ _usedFonts.clear();
+ _bbox = BoundingBox();
+ _currentFontNum = -1;
+ _bgcolor = Color::TRANSPARENT;
+}
+
+
+void DVIToSVGActions::setPageMatrix (const Matrix &matrix) {
+ delete _pageMatrix;
+ _pageMatrix = new Matrix(matrix);
+}
+
+
+void DVIToSVGActions::moveToX (double x) {
+ _dvisvg.specialManager().notifyPositionChange(getX(), getY());
+ _svg.setX(getX()); // get current horizontal DVI position
+}
+
+
+void DVIToSVGActions::moveToY (double y) {
+ _dvisvg.specialManager().notifyPositionChange(getX(), getY());
+ _svg.setY(y);
+}
+
+
+/** This method is called when a "set char" command was found in the DVI file.
+ * It draws a character of the current font.
+ * @param[in] x horizontal position of left bounding box edge
+ * @param[in] y vertical position of the character's baseline
+ * @param[in] c character code relative to the current font
+ * @param[in] font font to be used */
+void DVIToSVGActions::setChar (double x, double y, unsigned c, bool vertical, const Font *font) {
+ // If we use SVG fonts there is no need to record all font name/char/size combinations
+ // because the SVG font mechanism handles this automatically. It's sufficient to
+ // record font names and chars. The various font sizes can be ignored here.
+ // For a given font object, Font::uniqueFont() returns the same unique font object for
+ // all fonts with the same name.
+ _usedChars[SVGTree::USE_FONTS ? font->uniqueFont() : font].insert(c);
+
+ // However, we record all required fonts
+ _usedFonts.insert(font);
+ _svg.appendChar(c, x, y, *font);
+
+ static string fontname;
+ GlyphTracerMessages callback(fontname != font->name(), false);
+ fontname = font->name();
+
+ BoundingBox charbox;
+ GlyphMetrics metrics;
+ font->getGlyphMetrics(c, vertical, metrics);
+ const PhysicalFont* pf = dynamic_cast<const PhysicalFont*>(font);
+ if (PhysicalFont::EXACT_BBOX && pf) {
+ GlyphMetrics exact_metrics;
+ pf->getExactGlyphBox(c, exact_metrics, vertical, &callback);
+ if (vertical) {
+ // move top of bbox to upper margin of glyph (just an approximation yet)
+ y += (metrics.d-exact_metrics.h-exact_metrics.d)/2;
+ }
+ metrics = exact_metrics;
+ }
+ BoundingBox bbox(x-metrics.wl, y-metrics.h, x+metrics.wr, y+metrics.d);
+
+ // update bounding box
+ if (!getMatrix().isIdentity())
+ bbox.transform(getMatrix());
+ embed(bbox);
+#if 0
+ XMLElementNode *rect = new XMLElementNode("rect");
+ rect->addAttribute("x", x-metrics.wl);
+ rect->addAttribute("y", y-metrics.h);
+ rect->addAttribute("width", metrics.wl+metrics.wr);
+ rect->addAttribute("height", metrics.h+metrics.d);
+ rect->addAttribute("fill", "none");
+ rect->addAttribute("stroke", "red");
+ rect->addAttribute("stroke-width", "0.5");
+ _svg.appendToPage(rect);
+ if (metrics.d > 0) {
+ XMLElementNode *line = new XMLElementNode("line");
+ line->addAttribute("x1", x-metrics.wl);
+ line->addAttribute("y1", y);
+ line->addAttribute("x2", x+metrics.wr);
+ line->addAttribute("y2", y);
+ line->addAttribute("stroke", "blue");
+ line->addAttribute("stroke-width", "0.5");
+ _svg.appendToPage(line);
+ }
+ if (metrics.wl > 0) {
+ XMLElementNode *line = new XMLElementNode("line");
+ line->addAttribute("x1", x);
+ line->addAttribute("y1", y-metrics.h);
+ line->addAttribute("x2", x);
+ line->addAttribute("y2", y+metrics.d);
+ line->addAttribute("stroke", "blue");
+ line->addAttribute("stroke-width", "0.5");
+ _svg.appendToPage(line);
+ }
+#endif
+}
+
+
+/** This method is called when a "set rule" or "put rule" command was found in the
+ * DVI file. It draws a solid unrotated rectangle.
+ * @param[in] x horizontal position of left edge
+ * @param[in] y vertical position of bottom(!) edge
+ * @param[in] height length of the vertical edges
+ * @param[in] width length of the horizontal edges */
+void DVIToSVGActions::setRule (double x, double y, double height, double width) {
+/* x *= BP;
+ y *= BP;
+ height *= BP;
+ width *= BP; */
+ // (x,y) is the lower left corner of the rectangle
+ XMLElementNode *rect = new XMLElementNode("rect");
+ rect->addAttribute("x", x);
+ rect->addAttribute("y", y-height);
+ rect->addAttribute("height", height);
+ rect->addAttribute("width", width);
+ if (!getMatrix().isIdentity())
+ rect->addAttribute("transform", getMatrix().getSVG());
+ if (getColor() != Color::BLACK)
+ rect->addAttribute("fill", _svg.getColor().rgbString());
+ _svg.appendToPage(rect);
+
+ // update bounding box
+ BoundingBox bb(x, y-height, x+width, y);
+ if (!getMatrix().isIdentity())
+ bb.transform(getMatrix());
+ embed(bb);
+}
+
+
+/** This method is called when a "set font" command was found in the DVI file. The
+ * font must be previously defined.
+ * @param[in] num unique number of the font in the DVI file (not necessarily equal to the DVI font number)
+ * @param[in] font pointer to the font object (always represents a physical font and never a virtual font) */
+void DVIToSVGActions::setFont (int num, const Font *font) {
+ _currentFontNum = num;
+ _svg.setFont(num, font);
+}
+
+
+/** This method is called when a "special" command was found in the DVI file.
+ * @param[in] s the special expression
+ * @param[in] dvi2pt factor to scale DVI units to TeX points */
+void DVIToSVGActions::special (const string &s, double dvi2pt) {
+ try {
+ _dvisvg.specialManager().process(s, dvi2pt, this, this);
+ // @@ output message in case of unsupported specials?
+ }
+ catch (const SpecialException &e) {
+ Message::estream(true) << "error in special '" << s << "': " << e.what() << '\n';
+ }
+}
+
+
+void DVIToSVGActions::beginSpecial (const char *prefix) {
+}
+
+
+void DVIToSVGActions::endSpecial (const char *) {
+}
+
+
+/** This method is called when the DVI preamble was read
+ * @param[in] cmt preamble comment text. */
+void DVIToSVGActions::preamble (const string &cmt) {
+}
+
+
+void DVIToSVGActions::postamble () {
+}
+
+
+/** This method is called when a "begin of page (bop)" command was found in the DVI file.
+ * @param[in] c array with 10 components representing \count0 ... \count9. c[0] contains the
+ * current (printed) page number (may differ from page count) */
+void DVIToSVGActions::beginPage (unsigned pageno, Int32 *c) {
+ _svg.newPage(++_pageCount);
+ _bbox = BoundingBox(); // clear bounding box
+ if (_boxes)
+ _boxes->clear();
+}
+
+
+/** This method is called when an "end of page (eop)" command was found in the DVI file. */
+void DVIToSVGActions::endPage (unsigned pageno) {
+ _svg.transformPage(_pageMatrix);
+ if (_bgcolor != Color::TRANSPARENT) {
+ // create a rectangle filled with the background color
+ XMLElementNode *r = new XMLElementNode("rect");
+ r->addAttribute("x", _bbox.minX());
+ r->addAttribute("y", _bbox.minY());
+ r->addAttribute("width", _bbox.width());
+ r->addAttribute("height", _bbox.height());
+ r->addAttribute("fill", _bgcolor.rgbString());
+ _svg.prependToPage(r);
+ }
+}
+
+
+void DVIToSVGActions::setBgColor (const Color &color) {
+ _bgcolor = color;
+}
+
+
+void DVIToSVGActions::embed(const BoundingBox& bbox) {
+ _bbox.embed(bbox);
+ if (_boxes) {
+ FORALL(*_boxes, BoxMap::iterator, it)
+ it->second.embed(bbox);
+ }
+}
+
+
+void DVIToSVGActions::embed(const DPair& p, double r) {
+ if (r == 0)
+ _bbox.embed(p);
+ else
+ _bbox.embed(p, r);
+ if (_boxes)
+ FORALL(*_boxes, BoxMap::iterator, it)
+ it->second.embed(p, r);
+}
+
+
+BoundingBox& DVIToSVGActions::bbox(const string& name, bool reset) {
+ if (!_boxes)
+ _boxes = new BoxMap;
+ BoundingBox &box = (*_boxes)[name];
+ if (reset)
+ box = BoundingBox();
+ return box;
+}
+
+
+/** This method is called by subprocesses like the PS handler when
+ * a computation step has finished. Since the total number of steps
+ * can't be determined in advance, we don't show a percent value but
+ * a rotating dash. */
+void DVIToSVGActions::progress (const char *id) {
+ if (PROGRESSBAR_DELAY < 1000) {
+ static double time=0;
+ // slow down updating of the progress indicator to prevent flickering
+ if (System::time() - time > 0.1) {
+ progress(0, 0, id);
+ time = System::time();
+ }
+ }
+}
+
+
+/** Returns the number of digits of a given integer. */
+static int digits (int n) {
+ if (n == 0)
+ return 1;
+ if (n > 0)
+ return int(log10(double(n))+1);
+ return int(log10(double(-n))+2);
+}
+
+
+/** Draws a simple progress indicator.
+ * @param[in] current current iteration step (of 'total' steps)
+ * @param[in] total total number of iteration steps
+ * @param[in] id ID of the subprocess providing the information */
+void DVIToSVGActions::progress (size_t current, size_t total, const char *id) {
+ static double time=0;
+ static bool draw=false; // show progress indicator?
+ static const char *prev_id=0;
+ if (current == 0 && total > 0) {
+ time = System::time();
+ draw = false;
+ Message::mstream() << '\n';
+ }
+ // don't show the progress indicator before the given time has elapsed
+ if (!draw && System::time()-time > PROGRESSBAR_DELAY) {
+ draw = true;
+ Terminal::cursor(false);
+ }
+ if (draw && (System::time() - time > 0.1 || (total > 0 && current == total) || prev_id != id)) {
+ static int step = -1; // >=0: rotating dash
+ static size_t prev_current=0, prev_total=1;
+ const char *tips = "-\\|/";
+ if (total == 0) {
+ current = prev_current;
+ total = prev_total;
+ step = (step+1) % 4;
+ }
+ else {
+ prev_current = current;
+ prev_total = total;
+ step = -1;
+ }
+ // adapt length of progress indicator to terminal width
+ int cols = Terminal::columns();
+ int width = (cols == 0 || cols > 60) ? 50 : 49-60+cols;
+ double factor = double(current)/double(total);
+ int length = int(width*factor);
+ Message::mstream(false, Message::MC_PROGRESS)
+ << '[' << string(length, '=')
+ << (factor < 1.0 ? (step < 0 ? ' ' : tips[step]) : '=')
+ << string(width-length, ' ')
+ << "] " << string(3-digits(int(100*factor)), ' ') << int(100*factor)
+ << "%\r";
+ // overprint indicator when finished
+ if (factor == 1.0) {
+ Message::estream().clearline();
+ Terminal::cursor(true);
+ }
+ time = System::time();
+ prev_id = id;
+ }
+}