summaryrefslogtreecommitdiffstats
path: root/src/extension/internal/latex-text-renderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/extension/internal/latex-text-renderer.cpp755
1 files changed, 755 insertions, 0 deletions
diff --git a/src/extension/internal/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp
new file mode 100644
index 0000000..12fa66f
--- /dev/null
+++ b/src/extension/internal/latex-text-renderer.cpp
@@ -0,0 +1,755 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** \file
+ * Rendering LaTeX file (pdf/eps/ps+latex output)
+ *
+ * The idea stems from GNUPlot's epslatex terminal output :-)
+ */
+/*
+ * Authors:
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Miklos Erdelyi <erdelyim@gmail.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2006-2011 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "latex-text-renderer.h"
+
+#include <csignal>
+#include <cerrno>
+
+#include <glibmm/i18n.h>
+#include <glibmm/regex.h>
+
+#include "libnrtype/Layout-TNG.h"
+#include <2geom/transforms.h>
+#include <2geom/rect.h>
+
+#include "object/sp-item.h"
+#include "object/sp-item-group.h"
+#include "object/sp-root.h"
+#include "object/sp-use.h"
+#include "object/sp-text.h"
+#include "object/sp-flowtext.h"
+#include "object/sp-rect.h"
+#include "style.h"
+
+#include "text-editing.h"
+
+#include "util/units.h"
+
+#include "extension/output.h"
+#include "extension/system.h"
+
+#include "inkscape-version.h"
+#include "io/sys.h"
+#include "document.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Internal {
+
+/**
+ * This method is called by the PDF, EPS and PS output extensions.
+ * @param filename This should be the filename without '_tex' extension to which the tex code should be written. Output goes to <filename>_tex, note the underscore instead of period.
+ */
+bool
+latex_render_document_text_to_file( SPDocument *doc, gchar const *filename,
+ const gchar * const exportId, bool exportDrawing, bool exportCanvas, double bleedmargin_px,
+ bool pdflatex)
+{
+ doc->ensureUpToDate();
+
+ SPRoot *root = doc->getRoot();
+ SPItem *base = nullptr;
+
+ bool pageBoundingBox = true;
+ if (exportId && strcmp(exportId, "")) {
+ // we want to export the given item only
+ base = dynamic_cast<SPItem *>(doc->getObjectById(exportId));
+ if (!base) {
+ throw Inkscape::Extension::Output::export_id_not_found(exportId);
+ }
+ root->cropToObject(base); // TODO: This is inconsistent in CLI (should only happen for --export-id-only)
+ pageBoundingBox = exportCanvas;
+ }
+ else {
+ // we want to export the entire document from root
+ base = root;
+ pageBoundingBox = !exportDrawing;
+ }
+
+ if (!base)
+ return false;
+
+ /* Create renderer */
+ LaTeXTextRenderer *renderer = new LaTeXTextRenderer(pdflatex);
+
+ bool ret = renderer->setTargetFile(filename);
+ if (ret) {
+ /* Render document */
+ bool ret = renderer->setupDocument(doc, pageBoundingBox, bleedmargin_px, base);
+ if (ret) {
+ renderer->renderItem(root);
+ }
+ }
+
+ delete renderer;
+
+ return ret;
+}
+
+LaTeXTextRenderer::LaTeXTextRenderer(bool pdflatex)
+ : _stream(nullptr),
+ _filename(nullptr),
+ _pdflatex(pdflatex),
+ _omittext_state(EMPTY),
+ _omittext_page(1)
+{
+ push_transform(Geom::identity());
+}
+
+LaTeXTextRenderer::~LaTeXTextRenderer()
+{
+ if (_stream) {
+ writePostamble();
+
+ fclose(_stream);
+ }
+
+ /* restore default signal handling for SIGPIPE */
+#if !defined(_WIN32) && !defined(__WIN32__)
+ (void) signal(SIGPIPE, SIG_DFL);
+#endif
+
+ if (_filename) {
+ g_free(_filename);
+ }
+
+ return;
+}
+
+/** This should create the output LaTeX file, and assign it to _stream.
+ * @return Returns true when successful
+ */
+bool
+LaTeXTextRenderer::setTargetFile(gchar const *filename) {
+ if (filename != nullptr) {
+ while (isspace(*filename)) filename += 1;
+
+ _filename = g_path_get_basename(filename);
+
+ gchar *filename_ext = g_strdup_printf("%s_tex", filename);
+ Inkscape::IO::dump_fopen_call(filename_ext, "K");
+ FILE *osf = Inkscape::IO::fopen_utf8name(filename_ext, "w+");
+ if (!osf) {
+ fprintf(stderr, "inkscape: fopen(%s): %s\n", filename_ext, strerror(errno));
+ g_free(filename_ext);
+ return false;
+ }
+ _stream = osf;
+ g_free(filename_ext);
+ }
+
+ /* fixme: this is kinda icky */
+#if !defined(_WIN32) && !defined(__WIN32__)
+ (void) signal(SIGPIPE, SIG_IGN);
+#endif
+
+ fprintf(_stream, "%%%% Creator: Inkscape %s, www.inkscape.org\n", Inkscape::version_string);
+ fprintf(_stream, "%%%% PDF/EPS/PS + LaTeX output extension by Johan Engelen, 2010\n");
+ fprintf(_stream, "%%%% Accompanies image file '%s' (pdf, eps, ps)\n", _filename);
+ fprintf(_stream, "%%%%\n");
+ /* flush this to test output stream as early as possible */
+ if (fflush(_stream)) {
+ if (ferror(_stream)) {
+ g_print("Error %d on LaTeX file output stream: %s\n", errno,
+ g_strerror(errno));
+ }
+ g_print("Output to LaTeX file failed\n");
+ /* fixme: should use pclose() for pipes */
+ fclose(_stream);
+ _stream = nullptr;
+ fflush(stdout);
+ return false;
+ }
+
+ writePreamble();
+
+ return true;
+}
+
+static char const preamble[] =
+"%% To include the image in your LaTeX document, write\n"
+"%% \\input{<filename>.pdf_tex}\n"
+"%% instead of\n"
+"%% \\includegraphics{<filename>.pdf}\n"
+"%% To scale the image, write\n"
+"%% \\def\\svgwidth{<desired width>}\n"
+"%% \\input{<filename>.pdf_tex}\n"
+"%% instead of\n"
+"%% \\includegraphics[width=<desired width>]{<filename>.pdf}\n"
+"%%\n"
+"%% Images with a different path to the parent latex file can\n"
+"%% be accessed with the `import' package (which may need to be\n"
+"%% installed) using\n"
+"%% \\usepackage{import}\n"
+"%% in the preamble, and then including the image with\n"
+"%% \\import{<path to file>}{<filename>.pdf_tex}\n"
+"%% Alternatively, one can specify\n"
+"%% \\graphicspath{{<path to file>/}}\n"
+"%% \n"
+"%% For more information, please see info/svg-inkscape on CTAN:\n"
+"%% http://tug.ctan.org/tex-archive/info/svg-inkscape\n"
+"%%\n"
+"\\begingroup%\n"
+" \\makeatletter%\n"
+" \\providecommand\\color[2][]{%\n"
+" \\errmessage{(Inkscape) Color is used for the text in Inkscape, but the package \'color.sty\' is not loaded}%\n"
+" \\renewcommand\\color[2][]{}%\n"
+" }%\n"
+" \\providecommand\\transparent[1]{%\n"
+" \\errmessage{(Inkscape) Transparency is used (non-zero) for the text in Inkscape, but the package \'transparent.sty\' is not loaded}%\n"
+" \\renewcommand\\transparent[1]{}%\n"
+" }%\n"
+" \\providecommand\\rotatebox[2]{#2}%\n"
+" \\newcommand*\\fsize{\\dimexpr\\f@size pt\\relax}%\n"
+" \\newcommand*\\lineheight[1]{\\fontsize{\\fsize}{#1\\fsize}\\selectfont}%\n";
+
+static char const postamble[] =
+" \\end{picture}%\n"
+"\\endgroup%\n";
+
+void
+LaTeXTextRenderer::writePreamble()
+{
+ fprintf(_stream, "%s", preamble);
+}
+void
+LaTeXTextRenderer::writePostamble()
+{
+ fprintf(_stream, "%s", postamble);
+}
+
+void LaTeXTextRenderer::sp_group_render(SPGroup *group)
+{
+ std::vector<SPObject*> l = (group->childList(false));
+ for(auto x : l){
+ SPItem *item = dynamic_cast<SPItem*>(x);
+ if (item) {
+ renderItem(item);
+ }
+ }
+}
+
+void LaTeXTextRenderer::sp_use_render(SPUse *use)
+{
+ bool translated = false;
+
+ if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) {
+ Geom::Affine tp(Geom::Translate(use->x.computed, use->y.computed));
+ push_transform(tp);
+ translated = true;
+ }
+
+ SPItem *childItem = dynamic_cast<SPItem *>(use->child);
+ if (childItem) {
+ renderItem(childItem);
+ }
+
+ if (translated) {
+ pop_transform();
+ }
+}
+
+void LaTeXTextRenderer::sp_text_render(SPText *textobj)
+{
+ // Nothing to do here... (so don't emit an empty box)
+ // Also avoids falling out of sync with the CairoRenderer (which won't render anything in this case either)
+ if (textobj->layout.getActualLength() == 0)
+ return;
+
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_pdflatex && _omittext_state == GRAPHIC_ON_TOP)
+ _omittext_state = NEW_PAGE_ON_GRAPHIC;
+
+ SPStyle *style = textobj->style;
+
+ // get position and alignment
+ // Align vertically on the baseline of the font (retrieved from the anchor point)
+ // Align horizontally on anchorpoint
+ gchar const *alignment = nullptr;
+ gchar const *aligntabular = nullptr;
+ switch (style->text_anchor.computed) {
+ case SP_CSS_TEXT_ANCHOR_START:
+ alignment = "[lt]";
+ aligntabular = "{l}";
+ break;
+ case SP_CSS_TEXT_ANCHOR_END:
+ alignment = "[rt]";
+ aligntabular = "{r}";
+ break;
+ case SP_CSS_TEXT_ANCHOR_MIDDLE:
+ default:
+ alignment = "[t]";
+ aligntabular = "{c}";
+ break;
+ }
+
+ Geom::Point anchor;
+ const auto baseline_anchor_point = textobj->layout.baselineAnchorPoint();
+ if (baseline_anchor_point) {
+ anchor = (*baseline_anchor_point) * transform();
+ } else {
+ g_warning("LaTeXTextRenderer::sp_text_render: baselineAnchorPoint unset, text position will be wrong. Please report the issue.");
+ }
+
+ // determine color and transparency (for now, use rgb color model as it is most native to Inkscape)
+ bool has_color = false; // if the item has no color set, don't force black color
+ bool has_transparency = false;
+ // TODO: how to handle ICC colors?
+ // give priority to fill color
+ guint32 rgba = 0;
+ float opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
+ if (style->fill.set && style->fill.isColor()) {
+ has_color = true;
+ rgba = style->fill.value.color.toRGBA32(1.);
+ opacity *= SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
+ } else if (style->stroke.set && style->stroke.isColor()) {
+ has_color = true;
+ rgba = style->stroke.value.color.toRGBA32(1.);
+ opacity *= SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
+ }
+ if (opacity < 1.0) {
+ has_transparency = true;
+ }
+
+ // get rotation
+ Geom::Affine i2doc = textobj->i2doc_affine();
+ Geom::Affine wotransl = i2doc.withoutTranslation();
+ double degrees = -180/M_PI * Geom::atan2(wotransl.xAxis());
+ bool has_rotation = !Geom::are_near(degrees,0.);
+
+ // get line-height
+ float line_height;
+ if (style->line_height.unit == SP_CSS_UNIT_NONE) {
+ // unitless 'line-height' (use as-is, computed value is relative value)
+ line_height = style->line_height.computed;
+ } else {
+ // 'line-height' with unit (make relative, computed value is absolute value)
+ line_height = style->line_height.computed / style->font_size.computed;
+ }
+
+ // write to LaTeX
+ Inkscape::SVGOStringStream os;
+ os.setf(std::ios::fixed); // don't use scientific notation
+
+ os << " \\put(" << anchor[Geom::X] << "," << anchor[Geom::Y] << "){";
+ if (has_color) {
+ os << "\\color[rgb]{" << SP_RGBA32_R_F(rgba) << "," << SP_RGBA32_G_F(rgba) << "," << SP_RGBA32_B_F(rgba) << "}";
+ }
+ if (_pdflatex && has_transparency) {
+ os << "\\transparent{" << opacity << "}";
+ }
+ if (has_rotation) {
+ os << "\\rotatebox{" << degrees << "}{";
+ }
+ os << "\\makebox(0,0)" << alignment << "{";
+ if (line_height != 1) {
+ os << "\\lineheight{" << line_height << "}";
+ }
+ os << "\\smash{";
+ os << "\\begin{tabular}[t]" << aligntabular;
+
+ // Walk through all spans in the text object.
+ // Write span strings to LaTeX, associated with font weight and style.
+ Inkscape::Text::Layout const &layout = *(te_get_layout (textobj));
+ for (Inkscape::Text::Layout::iterator li = layout.begin(), le = layout.end();
+ li != le; li.nextStartOfSpan())
+ {
+ Inkscape::Text::Layout::iterator ln = li;
+ ln.nextStartOfSpan();
+ Glib::ustring uspanstr = sp_te_get_string_multiline (textobj, li, ln);
+
+ // escape ampersands
+ uspanstr = Glib::Regex::create("&")->replace_literal(uspanstr, 0, "\\&", (Glib::RegexMatchFlags)0);
+ // escape percent
+ uspanstr = Glib::Regex::create("%")->replace_literal(uspanstr, 0, "\\%", (Glib::RegexMatchFlags)0);
+
+ const gchar *spanstr = uspanstr.c_str();
+ if (!spanstr) {
+ continue;
+ }
+
+ bool is_bold = false, is_italic = false, is_oblique = false;
+
+ // newline character only -> don't attempt to add style (will break compilation in LaTeX)
+ if (g_strcmp0(spanstr, "\n")) {
+ SPStyle const &spanstyle = *(sp_te_style_at_position (textobj, li));
+ if (spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_500 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_600 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_700 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_800 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_900 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLD ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLDER)
+ {
+ is_bold = true;
+ os << "\\textbf{";
+ }
+ if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_ITALIC)
+ {
+ is_italic = true;
+ os << "\\textit{";
+ }
+ if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_OBLIQUE)
+ {
+ is_oblique = true;
+ os << "\\textsl{"; // this is an accurate choice if the LaTeX chosen font matches the font in Inkscape. Gives bad results when it is not so...
+ }
+ }
+
+ // replace carriage return with double slash
+ gchar ** splitstr = g_strsplit(spanstr, "\n", 2);
+ os << splitstr[0];
+ if (g_strv_length(splitstr) > 1)
+ {
+ os << "\\\\";
+ }
+ g_strfreev(splitstr);
+
+ if (is_oblique) { os << "}"; } // oblique end
+ if (is_italic) { os << "}"; } // italic end
+ if (is_bold) { os << "}"; } // bold end
+ }
+
+ os << "\\end{tabular}"; // tabular end
+ os << "}"; // smash end
+ if (has_rotation) { os << "}"; } // rotatebox end
+ os << "}"; //makebox end
+ os << "}%\n"; // put end
+
+ fprintf(_stream, "%s", os.str().c_str());
+}
+
+void LaTeXTextRenderer::sp_flowtext_render(SPFlowtext *flowtext)
+{
+/*
+Flowtext is possible by using a minipage! :)
+Flowing in rectangle is possible, not in arb shape.
+*/
+
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_pdflatex && _omittext_state == GRAPHIC_ON_TOP)
+ _omittext_state = NEW_PAGE_ON_GRAPHIC;
+
+ SPStyle *style = flowtext->style;
+
+ SPItem *frame_item = flowtext->get_frame(nullptr);
+ SPRect *frame = dynamic_cast<SPRect *>(frame_item);
+ if (!frame_item || !frame) {
+ g_warning("LaTeX export: non-rectangular flowed text shapes are not supported, skipping text.");
+ return; // don't know how to handle non-rect frames yet. is quite uncommon for latex users i think
+ }
+
+ // We will transform the coordinates
+ Geom::Rect framebox = frame->getRect();
+
+ // get position and alignment
+ // Align on topleft corner.
+ gchar const *alignment = "[lt]";
+ gchar const *justification = "";
+ switch (flowtext->layout.paragraphAlignment(flowtext->layout.begin())) {
+ case Inkscape::Text::Layout::LEFT:
+ justification = "\\raggedright ";
+ break;
+ case Inkscape::Text::Layout::RIGHT:
+ justification = "\\raggedleft ";
+ break;
+ case Inkscape::Text::Layout::CENTER:
+ justification = "\\centering ";
+ case Inkscape::Text::Layout::FULL:
+ default:
+ // no need to add LaTeX code for standard justified output :)
+ break;
+ }
+
+ // The topleft Corner was calculated after rotating the text which results in a wrong Coordinate.
+ // Now, the topleft Corner is rotated after calculating it
+ Geom::Point pos(framebox.corner(0) * transform()); //topleft corner
+
+ // determine color and transparency (for now, use rgb color model as it is most native to Inkscape)
+ bool has_color = false; // if the item has no color set, don't force black color
+ bool has_transparency = false;
+ // TODO: how to handle ICC colors?
+ // give priority to fill color
+ guint32 rgba = 0;
+ float opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
+ if (style->fill.set && style->fill.isColor()) {
+ has_color = true;
+ rgba = style->fill.value.color.toRGBA32(1.);
+ opacity *= SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
+ } else if (style->stroke.set && style->stroke.isColor()) {
+ has_color = true;
+ rgba = style->stroke.value.color.toRGBA32(1.);
+ opacity *= SP_SCALE24_TO_FLOAT(style->stroke_opacity.value);
+ }
+ if (opacity < 1.0) {
+ has_transparency = true;
+ }
+
+ // get rotation
+ Geom::Affine i2doc = flowtext->i2doc_affine();
+ Geom::Affine wotransl = i2doc.withoutTranslation();
+ double degrees = -180/M_PI * Geom::atan2(wotransl.xAxis());
+ bool has_rotation = !Geom::are_near(degrees,0.);
+
+ // write to LaTeX
+ Inkscape::SVGOStringStream os;
+ os.setf(std::ios::fixed); // don't use scientific notation
+
+ os << " \\put(" << pos[Geom::X] << "," << pos[Geom::Y] << "){";
+ if (has_color) {
+ os << "\\color[rgb]{" << SP_RGBA32_R_F(rgba) << "," << SP_RGBA32_G_F(rgba) << "," << SP_RGBA32_B_F(rgba) << "}";
+ }
+ if (_pdflatex && has_transparency) {
+ os << "\\transparent{" << opacity << "}";
+ }
+ if (has_rotation) {
+ os << "\\rotatebox{" << degrees << "}{";
+ }
+ os << "\\makebox(0,0)" << alignment << "{";
+
+ // Scale the x width correctly
+ os << "\\begin{minipage}{" << framebox.width() * transform().expansionX() << "\\unitlength}";
+ os << justification;
+
+ // Walk through all spans in the text object.
+ // Write span strings to LaTeX, associated with font weight and style.
+ Inkscape::Text::Layout const &layout = *(te_get_layout(flowtext));
+ for (Inkscape::Text::Layout::iterator li = layout.begin(), le = layout.end();
+ li != le; li.nextStartOfSpan())
+ {
+ SPStyle const &spanstyle = *(sp_te_style_at_position(flowtext, li));
+ bool is_bold = false, is_italic = false, is_oblique = false;
+
+ if (spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_500 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_600 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_700 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_800 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_900 ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLD ||
+ spanstyle.font_weight.computed == SP_CSS_FONT_WEIGHT_BOLDER)
+ {
+ is_bold = true;
+ os << "\\textbf{";
+ }
+ if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_ITALIC)
+ {
+ is_italic = true;
+ os << "\\textit{";
+ }
+ if (spanstyle.font_style.computed == SP_CSS_FONT_STYLE_OBLIQUE)
+ {
+ is_oblique = true;
+ os << "\\textsl{"; // this is an accurate choice if the LaTeX chosen font matches the font in Inkscape. Gives bad results when it is not so...
+ }
+
+ Inkscape::Text::Layout::iterator ln = li;
+ ln.nextStartOfSpan();
+ Glib::ustring uspanstr = sp_te_get_string_multiline(flowtext, li, ln);
+ const gchar *spanstr = uspanstr.c_str();
+ if (!spanstr) {
+ continue;
+ }
+ // replace carriage return with double slash
+ gchar ** splitstr = g_strsplit(spanstr, "\n", -1);
+ gchar *spanstr_new = g_strjoinv("\\\\ ", splitstr);
+ os << spanstr_new;
+ g_strfreev(splitstr);
+ g_free(spanstr_new);
+
+ if (is_oblique) { os << "}"; } // oblique end
+ if (is_italic) { os << "}"; } // italic end
+ if (is_bold) { os << "}"; } // bold end
+ }
+
+ os << "\\end{minipage}";
+ if (has_rotation) {
+ os << "}"; // rotatebox end
+ }
+ os << "}"; //makebox end
+ os << "}%\n"; // put end
+
+ fprintf(_stream, "%s", os.str().c_str());
+}
+
+void LaTeXTextRenderer::sp_root_render(SPRoot *root)
+{
+ push_transform(root->c2p);
+ sp_group_render(root);
+ pop_transform();
+}
+
+void
+LaTeXTextRenderer::sp_item_invoke_render(SPItem *item)
+{
+ // Check item's visibility
+ if (item->isHidden()) {
+ return;
+ }
+
+ SPRoot *root = dynamic_cast<SPRoot *>(item);
+ if (root) {
+ return sp_root_render(root);
+ }
+ SPGroup *group = dynamic_cast<SPGroup *>(item);
+ if (group) {
+ return sp_group_render(group);
+ }
+ SPUse *use = dynamic_cast<SPUse *>(item);
+ if (use) {
+ return sp_use_render(use);
+ }
+ SPText *text = dynamic_cast<SPText *>(item);
+ if (text) {
+ return sp_text_render(text);
+ }
+ SPFlowtext *flowtext = dynamic_cast<SPFlowtext *>(item);
+ if (flowtext) {
+ return sp_flowtext_render(flowtext);
+ }
+ // Only PDFLaTeX supports importing a single page of a graphics file,
+ // so only PDF backend gets interleaved text/graphics
+ if (_pdflatex && (_omittext_state == EMPTY || _omittext_state == NEW_PAGE_ON_GRAPHIC)) {
+ writeGraphicPage();
+ }
+ _omittext_state = GRAPHIC_ON_TOP;
+}
+
+void
+LaTeXTextRenderer::renderItem(SPItem *item)
+{
+ push_transform(item->transform);
+ sp_item_invoke_render(item);
+ pop_transform();
+}
+
+void
+LaTeXTextRenderer::writeGraphicPage() {
+ Inkscape::SVGOStringStream os;
+ os.setf(std::ios::fixed); // no scientific notation
+
+ // strip pathname, as it is probably desired. Having a specific path in the TeX file is not convenient.
+ if (_pdflatex)
+ os << " \\put(0,0){\\includegraphics[width=\\unitlength,page=" << _omittext_page++ << "]{" << _filename << "}}%\n";
+ else
+ os << " \\put(0,0){\\includegraphics[width=\\unitlength]{" << _filename << "}}%\n";
+
+ fprintf(_stream, "%s", os.str().c_str());
+}
+
+bool
+LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, double bleedmargin_px, SPItem *base)
+{
+// The boundingbox calculation here should be exactly the same as the one by CairoRenderer::setupDocument !
+
+ if (!base) {
+ base = doc->getRoot();
+ }
+
+ Geom::Rect d;
+ if (pageBoundingBox) {
+ d = Geom::Rect::from_xywh(Geom::Point(0,0), doc->getDimensions());
+ } else {
+ Geom::OptRect bbox = base->documentVisualBounds();
+ if (!bbox) {
+ g_message("CairoRenderer: empty bounding box.");
+ return false;
+ }
+ d = *bbox;
+ }
+ d.expandBy(bleedmargin_px);
+
+ // scale all coordinates, such that the width of the image is 1, this is convenient for scaling the image in LaTeX
+ double scale = 1/(d.width());
+ double _width = d.width() * scale;
+ double _height = d.height() * scale;
+ push_transform(Geom::Translate(-d.corner(3)) * Geom::Scale(scale, -scale));
+
+ // write the info to LaTeX
+ Inkscape::SVGOStringStream os;
+ os.setf(std::ios::fixed); // no scientific notation
+
+ // scaling of the image when including it in LaTeX
+ os << " \\ifx\\svgwidth\\undefined%\n";
+ os << " \\setlength{\\unitlength}{" << Inkscape::Util::Quantity::convert(d.width(), "px", "pt") << "bp}%\n"; // note: 'bp' is the Postscript pt unit in LaTeX, see LP bug #792384
+ os << " \\ifx\\svgscale\\undefined%\n";
+ os << " \\relax%\n";
+ os << " \\else%\n";
+ os << " \\setlength{\\unitlength}{\\unitlength * \\real{\\svgscale}}%\n";
+ os << " \\fi%\n";
+ os << " \\else%\n";
+ os << " \\setlength{\\unitlength}{\\svgwidth}%\n";
+ os << " \\fi%\n";
+ os << " \\global\\let\\svgwidth\\undefined%\n";
+ os << " \\global\\let\\svgscale\\undefined%\n";
+ os << " \\makeatother%\n";
+
+ os << " \\begin{picture}(" << _width << "," << _height << ")%\n";
+
+ // set \baselineskip equal to fontsize (the closest we can seem to get to CSS "line-height: 1;")
+ // and remove column spacing from tabular
+ os << " \\lineheight{1}%\n";
+ os << " \\setlength\\tabcolsep{0pt}%\n";
+
+ fprintf(_stream, "%s", os.str().c_str());
+
+ if (!_pdflatex)
+ writeGraphicPage();
+
+ return true;
+}
+
+Geom::Affine const &
+LaTeXTextRenderer::transform()
+{
+ return _transform_stack.top();
+}
+
+void
+LaTeXTextRenderer::push_transform(Geom::Affine const &tr)
+{
+ if(!_transform_stack.empty()){
+ Geom::Affine tr_top = _transform_stack.top();
+ _transform_stack.push(tr * tr_top);
+ } else {
+ _transform_stack.push(tr);
+ }
+}
+
+void
+LaTeXTextRenderer::pop_transform()
+{
+ _transform_stack.pop();
+}
+
+} /* namespace Internal */
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :