summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-1.0.1/src/DVIToSVG.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-1.0.1/src/DVIToSVG.cpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-1.0.1/src/DVIToSVG.cpp336
1 files changed, 336 insertions, 0 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-1.0.1/src/DVIToSVG.cpp b/Build/source/texk/dvisvgm/dvisvgm-1.0.1/src/DVIToSVG.cpp
new file mode 100644
index 00000000000..ec35140fdc8
--- /dev/null
+++ b/Build/source/texk/dvisvgm/dvisvgm-1.0.1/src/DVIToSVG.cpp
@@ -0,0 +1,336 @@
+/*************************************************************************
+** DVIToSVG.cpp **
+** **
+** This file is part of dvisvgm -- the DVI to SVG converter **
+** Copyright (C) 2005-2010 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/>. **
+*************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#define VERSION_STR VERSION " (" TARGET_SYSTEM ")"
+#else
+#define VERSION_STR ""
+#endif
+
+#include <cstdlib>
+#include <ctime>
+#include <fstream>
+#include <sstream>
+#include "Calculator.h"
+#include "DVIToSVG.h"
+#include "DVIToSVGActions.h"
+#include "Font.h"
+#include "FontManager.h"
+#include "FileFinder.h"
+#include "GlyphTracerMessages.h"
+#include "InputBuffer.h"
+#include "InputReader.h"
+#include "Matrix.h"
+#include "Message.h"
+#include "PageRanges.h"
+#include "PageSize.h"
+#include "SVGTree.h"
+#include "TFM.h"
+#include "XMLDocument.h"
+#include "XMLDocTypeNode.h"
+#include "XMLNode.h"
+#include "XMLString.h"
+//
+///////////////////////////////////
+// special handlers
+
+#include "BgColorSpecialHandler.h"
+#include "ColorSpecialHandler.h"
+#include "DvisvgmSpecialHandler.h"
+#include "EmSpecialHandler.h"
+//#include "HtmlSpecialHandler.h"
+#if !defined(DISABLE_GS)
+ #include "PsSpecialHandler.h"
+#endif
+#include "TpicSpecialHandler.h"
+///////////////////////////////////
+
+
+using namespace std;
+
+
+/** Returns time stamp of current date/time. */
+static string datetime () {
+ time_t t;
+ time(&t);
+ struct tm *tm = localtime(&t);
+ char *timestr = asctime(tm);
+ timestr[24] = 0; // remove newline
+ return timestr;
+}
+
+class PSHeaderActions : public DVIActions
+{
+ public :
+ PSHeaderActions (DVIToSVG &dvisvg) : _dvisvg(dvisvg) {}
+
+ void special (const std::string &s) {
+ // execute PS headers only
+ if (!s.empty() && (s[0] == '!' || s.substr(0, 7) == "header="))
+ _dvisvg.specialManager().process(s, 0);
+ }
+
+ BoundingBox& bbox () {return _bbox;}
+
+ private:
+ DVIToSVG &_dvisvg;
+ BoundingBox _bbox;
+};
+
+
+/** 'a': trace all glyphs even if some of them are already cached
+ * 'm': trace missing glyphs, i.e. glyphs not yet cached
+ * 0 : only trace actually required glyphs */
+char DVIToSVG::TRACE_MODE = 0;
+
+
+DVIToSVG::DVIToSVG (istream &is, DVIToSVG::Output &out) : DVIReader(is), _out(out)
+{
+ replaceActions(new DVIToSVGActions(*this, _svg));
+}
+
+
+DVIToSVG::~DVIToSVG () {
+ delete replaceActions(0);
+}
+
+
+/** Starts the conversion process.
+ * @param[in] first number of first page to convert
+ * @param[in] last number of last page to convert
+ * @param[out] pageinfo (number of converted pages, number of total pages) */
+void DVIToSVG::convert (unsigned first, unsigned last, pair<int,int> *pageinfo) {
+ if (getTotalPages() == 0)
+ executePostamble(); // collect scaling and font information
+ if (first > last)
+ swap(first, last);
+ if (first > getTotalPages()) {
+ ostringstream oss;
+ oss << "file contains only " << getTotalPages() << " page";
+ if (getTotalPages() > 1)
+ oss << 's';
+ throw DVIException(oss.str());
+ }
+ last = min(last, getTotalPages());
+
+ if (first > 1) {
+ // ensure loading of PostScript prologues given at the beginning of the first page
+ // (prologue files are always referenced in first page)
+ PSHeaderActions headerActions(*this);
+ DVIActions *save = replaceActions(&headerActions);
+ executePage(1);
+ replaceActions(save);
+ }
+ for (unsigned i=first; i <= last; ++i) {
+ executePage(i);
+ embedFonts(_svg.rootNode());
+ _svg.write(_out.getPageStream(getCurrentPageNumber(), getTotalPages()));
+ string fname = _out.filename(i, getTotalPages());
+ Message::mstream(false, Terminal::GREEN) << "\npage written to " << (fname.empty() ? "<stdout>" : fname) << '\n';
+ _svg.reset();
+ static_cast<DVIToSVGActions*>(getActions())->reset();
+ }
+ if (pageinfo) {
+ pageinfo->first = last-first+1;
+ pageinfo->second = getTotalPages();
+ }
+}
+
+
+/** Convert DVI pages specified by a page range string.
+ * @param[in] rangestr string describing the pages to convert
+ * @param[out] pageinfo (number of converted pages, number of total pages) */
+void DVIToSVG::convert (const string &rangestr, pair<int,int> *pageinfo) {
+ if (getTotalPages() == 0)
+ executePostamble(); // collect scaling and font information
+ PageRanges ranges;
+ if (!ranges.parse(rangestr, getTotalPages()))
+ throw MessageException("invalid page range format");
+ FORALL(ranges, PageRanges::ConstIterator, it)
+ convert(it->first, it->second);
+ if (pageinfo) {
+ pageinfo->first = ranges.pages();
+ pageinfo->second = getTotalPages();
+ }
+}
+
+
+/** This template method is called by parent class DVIReader before
+ * executing the BOP actions.
+ * @param[in] c contains information about the page (page number etc.) */
+void DVIToSVG::beginPage (unsigned n, Int32 *c) {
+ if (dynamic_cast<DVIToSVGActions*>(getActions())) {
+ Message::mstream().indent(0);
+ Message::mstream(false, Terminal::BLUE, true) << "processing page " << n << " [" << c[0] << ']';
+ Message::mstream().indent(1);
+ _svg.appendToDoc(new XMLCommentNode(" This file was generated by dvisvgm "VERSION_STR" "));
+ _svg.appendToDoc(new XMLCommentNode(" " + datetime() + " "));
+ }
+}
+
+
+/** This template method is called by parent class DVIReader before
+ * executing the EOP actions. */
+void DVIToSVG::endPage () {
+ if (!dynamic_cast<DVIToSVGActions*>(getActions()))
+ return;
+
+ _specialManager.notifyEndPage();
+ // set bounding box and apply page transformations
+ BoundingBox &bbox = getActions()->bbox();
+ if (!_transCmds.empty()) {
+ Calculator calc;
+ calc.setVariable("ux", bbox.minX());
+ calc.setVariable("uy", bbox.minY());
+ calc.setVariable("w", bbox.width());
+ calc.setVariable("h", bbox.height());
+ calc.setVariable("pt", 1);
+ calc.setVariable("in", 72.27);
+ calc.setVariable("cm", 72.27/2.54);
+ calc.setVariable("mm", 72.27/25.4);
+ Matrix matrix(_transCmds, calc);
+ static_cast<DVIToSVGActions*>(getActions())->setPageMatrix(matrix);
+ if (_bboxString == "min")
+ bbox.transform(matrix);
+ }
+ if (string("dvi none min").find(_bboxString) == string::npos) {
+ istringstream iss(_bboxString);
+ StreamInputReader ir(iss);
+ ir.skipSpace();
+ if (isalpha(ir.peek())) {
+ // set explicitly given page format
+ PageSize size(_bboxString);
+ if (size.valid()) {
+ // convention: DVI position (0,0) equals (1in, 1in) relative
+ // to the upper left vertex of the page (see DVI specification)
+ const double border = -72.27;
+ bbox = BoundingBox(border, border, size.widthInPT()+border, size.heightInPT()+border);
+ }
+ }
+ else { // set/modify bounding box by explicitly given values
+ try {
+ bbox.set(_bboxString);
+ }
+ catch (const MessageException &e) {
+ }
+ }
+ }
+ else if (_bboxString == "dvi") {
+ // center page content
+ double dx = (getPageWidth()-bbox.width())/2;
+ double dy = (getPageHeight()-bbox.height())/2;
+ bbox += BoundingBox(-dx, -dy, dx, dy);
+ }
+ if (_bboxString != "none") {
+ if (bbox.width() == 0)
+ Message::mstream(false, Terminal::YELLOW) << "\npage is empty\n";
+ else {
+ _svg.setBBox(bbox);
+
+ Message::mstream(false, Terminal::MAGENTA) << "\npage size: " << bbox.width() << "pt"
+ " x " << bbox.height() << "pt"
+ " (" << bbox.width()/72.27*25.4 << "mm"
+ " x " << bbox.height()/72.27*25.4 << "mm)\n";
+ }
+ }
+}
+
+
+static void collect_chars (map<const Font*, set<int> > &fm) {
+ typedef const map<const Font*, set<int> > UsedCharsMap;
+ FORALL(fm, UsedCharsMap::const_iterator, it) {
+ if (it->first->uniqueFont() != it->first) {
+ FORALL(it->second, set<int>::const_iterator, cit)
+ fm[it->first->uniqueFont()].insert(*cit);
+ }
+ }
+}
+
+
+/** Adds the font information to the SVG tree.
+ * @param[in] svgElement the font nodes are added to this node */
+void DVIToSVG::embedFonts (XMLElementNode *svgElement) {
+ if (!svgElement)
+ return;
+ if (!getActions()) // no dvi actions => no chars written => no fonts to embed
+ return;
+
+ typedef map<const Font*, set<int> > UsedCharsMap;
+ const DVIToSVGActions *svgActions = static_cast<DVIToSVGActions*>(getActions());
+ UsedCharsMap &usedChars = svgActions->getUsedChars();
+
+ collect_chars(usedChars);
+
+ GlyphTracerMessages messages;
+ FORALL(usedChars, UsedCharsMap::const_iterator, it) {
+ const Font *font = it->first;
+ if (const PhysicalFont *ph_font = dynamic_cast<const PhysicalFont*>(font)) {
+ if (TRACE_MODE)
+ ph_font->traceAllGlyphs(TRACE_MODE == 'a', &messages);
+
+ if (font->path()) // does font file exist?
+ _svg.append(*ph_font, it->second, &messages);
+ else
+ Message::wstream(true) << "can't embed font '" << font->name() << "'\n";
+ }
+ else
+ Message::wstream(true) << "can't embed font '" << font->name() << "'\n";
+ }
+ _svg.appendFontStyles(svgActions->getUsedFonts());
+}
+
+
+/** Enables or disables processing of specials. If ignorelist == 0, all
+ * supported special handlers are loaded. To disable selected sets of specials,
+ * the corresponding prefixes can be given separated by non alpha-numeric characters,
+ * e.g. "color, ps, em" or "color: ps em" etc.
+ * A single "*" in the ignore list disables all specials.
+ * @param[in] ignorelist list of special prefixes to ignore
+ * @return the SpecialManager that handles special statements */
+const SpecialManager* DVIToSVG::setProcessSpecials (const char *ignorelist) {
+ if (ignorelist && strcmp(ignorelist, "*") == 0) { // ignore all specials?
+ _specialManager.unregisterHandlers();
+ }
+ else {
+ // add special handlers
+ SpecialHandler *handlers[] = {
+ 0, // placeholder for PsSpecialHandler
+ new BgColorSpecialHandler, // handles background color special
+ new ColorSpecialHandler, // handles color specials
+ new DvisvgmSpecialHandler, // handles raw SVG embeddings
+ new EmSpecialHandler, // handles emTeX specials
+// new HtmlSpecialHandler, // handles hyperref specials
+ new TpicSpecialHandler, // handles tpic specials
+ 0
+ };
+ SpecialHandler **p = handlers;
+#if !defined(DISABLE_GS)
+ if (Ghostscript().available())
+ *p = new PsSpecialHandler;
+ else
+#endif
+ p++;
+ _specialManager.unregisterHandlers();
+ _specialManager.registerHandlers(p, ignorelist);
+ }
+ return &_specialManager;
+}
+