From 6825a6097da27fdfdf22859c0f81c208e7e22bb4 Mon Sep 17 00:00:00 2001 From: Johan Engelen Date: Sat, 20 Feb 2010 23:07:44 +0100 Subject: [PATCH] initial work, plugging in a LaTeX renderer to write the text stuff to a .tex file. implementation of renderer is all that is left to do :) --- src/extension/internal/CMakeLists.txt | 1 + src/extension/internal/Makefile_insert | 2 + .../internal/cairo-renderer-pdf-out.cpp | 76 +++- src/extension/internal/cairo-renderer.cpp | 2 + src/extension/internal/pdflatex-renderer.cpp | 428 ++++++++++++++++++ src/extension/internal/pdflatex-renderer.h | 82 ++++ 6 files changed, 581 insertions(+), 10 deletions(-) create mode 100644 src/extension/internal/pdflatex-renderer.cpp create mode 100644 src/extension/internal/pdflatex-renderer.h diff --git a/src/extension/internal/CMakeLists.txt b/src/extension/internal/CMakeLists.txt index c9c00e05b..3412b740c 100644 --- a/src/extension/internal/CMakeLists.txt +++ b/src/extension/internal/CMakeLists.txt @@ -20,6 +20,7 @@ latex-pstricks.cpp latex-pstricks-out.cpp odf.cpp pdfinput +pdflatex-renderer.cpp pdf-input-cairo.cpp pov-out.cpp javafx-out.cpp diff --git a/src/extension/internal/Makefile_insert b/src/extension/internal/Makefile_insert index d2ba9b3eb..881b3ec22 100644 --- a/src/extension/internal/Makefile_insert +++ b/src/extension/internal/Makefile_insert @@ -109,6 +109,8 @@ ink_common_sources += \ extension/internal/javafx-out.h \ extension/internal/gdkpixbuf-input.h \ extension/internal/gdkpixbuf-input.cpp \ + extension/internal/pdflatex-renderer.h \ + extension/internal/pdflatex-renderer.cpp \ extension/internal/pdfinput/svg-builder.h \ extension/internal/pdfinput/svg-builder.cpp \ extension/internal/pdfinput/pdf-parser.h \ diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp index a62d2cb14..ba23f4bcc 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -20,6 +20,7 @@ #include "cairo-renderer-pdf-out.h" #include "cairo-render-context.h" #include "cairo-renderer.h" +#include "pdflatex-renderer.h" #include #include "extension/system.h" #include "extension/print.h" @@ -33,6 +34,8 @@ #include "sp-item.h" #include "sp-root.h" +#include <2geom/matrix.h> + namespace Inkscape { namespace Extension { namespace Internal { @@ -48,7 +51,7 @@ CairoRendererPdfOutput::check (Inkscape::Extension::Extension * module) static bool pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, - bool texttopath, bool texttolatex, bool filtertobitmap, int resolution, + bool texttopath, bool omittext, bool filtertobitmap, int resolution, const gchar * const exportId, bool exportDrawing, bool exportCanvas) { sp_document_ensure_up_to_date(doc); @@ -83,7 +86,7 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int CairoRenderContext *ctx = renderer->createContext(); ctx->setPDFLevel(level); ctx->setTextToPath(texttopath); - renderer->_omitText = texttolatex; + renderer->_omitText = omittext; ctx->setFilterToBitmap(filtertobitmap); ctx->setBitmapResolution(resolution); @@ -107,6 +110,45 @@ pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int return ret; } +static bool +latex_render_document_text_to_file( SPDocument *doc, gchar const *filename, + const gchar * const exportId, bool exportDrawing, bool exportCanvas) +{ + sp_document_ensure_up_to_date(doc); + +/* Start */ + + SPItem *base = NULL; + + bool pageBoundingBox = true; + if (exportId && strcmp(exportId, "")) { + // we want to export the given item only + base = SP_ITEM(doc->getObjectById(exportId)); + pageBoundingBox = exportCanvas; + } + else { + // we want to export the entire document from root + base = SP_ITEM(sp_document_root(doc)); + pageBoundingBox = !exportDrawing; + } + + if (!base) + return false; + + /* Create renderer */ + PDFLaTeXRenderer *renderer = new PDFLaTeXRenderer(); + + /* Render document */ + bool ret = renderer->setupDocument(doc, pageBoundingBox, base); + if (ret) { + renderer->renderItem(base); + } + + delete renderer; + + return ret; +} + /** \brief This function calls the output module with the filename @@ -195,15 +237,29 @@ CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, g_warning("Parameter might not exist"); } - gchar * final_name; - final_name = g_strdup_printf("> %s", filename); - ret = pdf_render_document_to_file(doc, final_name, level, - new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, - new_exportId, new_exportDrawing, new_exportCanvas); - g_free(final_name); + // Create PDF file + { + gchar * final_name; + final_name = g_strdup_printf("> %s", filename); + ret = pdf_render_document_to_file(doc, final_name, level, + new_textToPath, new_textToLaTeX, new_blurToBitmap, new_bitmapResolution, + new_exportId, new_exportDrawing, new_exportCanvas); + g_free(final_name); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } + + // Create LaTeX file (if requested) + if (new_textToLaTeX) { + gchar * tex_filename; + tex_filename = g_strdup_printf("%s.tex", filename); + ret = latex_render_document_text_to_file(doc, tex_filename, new_exportId, new_exportDrawing, new_exportCanvas); + g_free(tex_filename); - if (!ret) - throw Inkscape::Extension::Output::save_failed(); + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } } #include "clear-n_.h" diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 6d75c1e5d..6e4bb3b7e 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -598,6 +598,8 @@ CairoRenderer::renderItem(CairoRenderContext *ctx, SPItem *item) bool CairoRenderer::setupDocument(CairoRenderContext *ctx, SPDocument *doc, bool pageBoundingBox, SPItem *base) { +// PLEASE note when making changes to the boundingbox and transform calculation, corresponding changes should be made to PDFLaTeXRenderer::setupDocument !!! + g_assert( ctx != NULL ); if (!base) diff --git a/src/extension/internal/pdflatex-renderer.cpp b/src/extension/internal/pdflatex-renderer.cpp new file mode 100644 index 000000000..bcababf8e --- /dev/null +++ b/src/extension/internal/pdflatex-renderer.cpp @@ -0,0 +1,428 @@ +#define EXTENSION_INTERNAL_PDF_LATEX_RENDERER_CPP + +/** \file + * Rendering LaTeX file (pdf+latex output) + */ +/* + * Authors: + * Johan Engelen + * Miklos Erdelyi + * + * Copyright (C) 2006-2010 Authors + * + * Licensed under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef PANGO_ENABLE_BACKEND +#define PANGO_ENABLE_BACKEND +#endif + +#ifndef PANGO_ENABLE_ENGINE +#define PANGO_ENABLE_ENGINE +#endif + + +#include +#include + +#include "libnr/nr-rect.h" +#include "libnrtype/Layout-TNG.h" +#include <2geom/transforms.h> +#include <2geom/pathvector.h> + +#include + +#include +#include "display/nr-arena.h" +#include "display/nr-arena-item.h" +#include "display/nr-arena-group.h" +#include "display/curve.h" +#include "display/canvas-bpath.h" +#include "sp-item.h" +#include "sp-item-group.h" +#include "style.h" +#include "marker.h" +#include "sp-linear-gradient.h" +#include "sp-radial-gradient.h" +#include "sp-root.h" +#include "sp-use.h" +#include "sp-text.h" +#include "sp-flowtext.h" +#include "sp-mask.h" +#include "sp-clippath.h" + +#include +#include "helper/png-write.h" +#include "helper/pixbuf-ops.h" + +#include "pdflatex-renderer.h" +#include "extension/system.h" + +#include "io/sys.h" + +#include + +// include support for only the compiled-in surface types +#ifdef CAIRO_HAS_PDF_SURFACE +#include +#endif +#ifdef CAIRO_HAS_PS_SURFACE +#include +#endif + +//#define TRACE(_args) g_printf _args +#define TRACE(_args) +//#define TEST(_args) _args +#define TEST(_args) + +// FIXME: expose these from sp-clippath/mask.cpp +struct SPClipPathView { + SPClipPathView *next; + unsigned int key; + NRArenaItem *arenaitem; + NRRect bbox; +}; + +struct SPMaskView { + SPMaskView *next; + unsigned int key; + NRArenaItem *arenaitem; + NRRect bbox; +}; + +namespace Inkscape { +namespace Extension { +namespace Internal { + +PDFLaTeXRenderer::PDFLaTeXRenderer(void) + : _m(Geom::identity()) +{} + +PDFLaTeXRenderer::~PDFLaTeXRenderer(void) +{ + /* restore default signal handling for SIGPIPE */ +#if !defined(_WIN32) && !defined(__WIN32__) + (void) signal(SIGPIPE, SIG_DFL); +#endif + + return; +} + +void +PDFLaTeXRenderer::sp_group_render(SPItem *item) +{ + SPGroup *group = SP_GROUP(item); + + GSList *l = g_slist_reverse(group->childList(false)); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM(o)) { + renderItem (SP_ITEM (o)); + } + l = g_slist_remove (l, o); + } +} + +void +PDFLaTeXRenderer::sp_use_render(SPItem *item) +{ +/* + bool translated = false; + SPUse *use = SP_USE(item); + + if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) { + Geom::Matrix tp(Geom::Translate(use->x.computed, use->y.computed)); + ctx->pushState(); + ctx->transform(&tp); + translated = true; + } + + if (use->child && SP_IS_ITEM(use->child)) { + renderItem(SP_ITEM(use->child)); + } + + if (translated) { + ctx->popState(); + } +*/ +} + +void +PDFLaTeXRenderer::sp_text_render(SPItem *item) +{ + SPText *group = SP_TEXT (item); +/* +implement +*/ +} + +void +PDFLaTeXRenderer::sp_flowtext_render(SPItem *item) +{ + SPFlowtext *group = SP_FLOWTEXT(item); +/* +implement +*/ +} + +void +PDFLaTeXRenderer::sp_root_render(SPItem *item) +{ + SPRoot *root = SP_ROOT(item); + +/* + if (!ctx->getCurrentState()->has_overflow && SP_OBJECT(item)->parent) + ctx->addClippingRect(root->x.computed, root->y.computed, root->width.computed, root->height.computed); + + ctx->pushState(); + setStateForItem(ctx, item); + Geom::Matrix tempmat (root->c2p); + ctx->transform(&tempmat); + sp_group_render(item, ctx); + ctx->popState(); +*/ +} + +void +PDFLaTeXRenderer::sp_item_invoke_render(SPItem *item) +{ + // Check item's visibility + if (item->isHidden()) { + return; + } + + if (SP_IS_ROOT(item)) { + TRACE(("root\n")); + return sp_root_render(item); + } else if (SP_IS_GROUP(item)) { + TRACE(("group\n")); + return sp_group_render(item); + } else if (SP_IS_USE(item)) { + TRACE(("use begin---\n")); + sp_use_render(item); + TRACE(("---use end\n")); + } else if (SP_IS_TEXT(item)) { + TRACE(("text\n")); + return sp_text_render(item); + } else if (SP_IS_FLOWTEXT(item)) { + TRACE(("flowtext\n")); + return sp_flowtext_render(item); + } + // We are not interested in writing the other SPItem types to LaTeX +} + +void +PDFLaTeXRenderer::setStateForItem(SPItem const *item) +{ +/* + SPStyle const *style = SP_OBJECT_STYLE(item); + ctx->setStateForStyle(style); + + CairoRenderState *state = ctx->getCurrentState(); + state->clip_path = item->clip_ref->getObject(); + state->mask = item->mask_ref->getObject(); + state->item_transform = Geom::Matrix (item->transform); + + // If parent_has_userspace is true the parent state's transform + // has to be used for the mask's/clippath's context. + // This is so because we use the image's/(flow)text's transform for positioning + // instead of explicitly specifying it and letting the renderer do the + // transformation before rendering the item. + if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item) || SP_IS_IMAGE(item)) + state->parent_has_userspace = TRUE; + TRACE(("setStateForItem opacity: %f\n", state->opacity)); +*/ +} + +void +PDFLaTeXRenderer::renderItem(SPItem *item) +{ +/* + ctx->pushState(); + setStateForItem(ctx, item); + + CairoRenderState *state = ctx->getCurrentState(); + state->need_layer = ( state->mask || state->clip_path || state->opacity != 1.0 ); + + // Draw item on a temporary surface so a mask, clip path, or opacity can be applied to it. + if (state->need_layer) { + state->merge_opacity = FALSE; + ctx->pushLayer(); + } + Geom::Matrix tempmat (item->transform); + ctx->transform(&tempmat); + sp_item_invoke_render(item, ctx); + + if (state->need_layer) + ctx->popLayer(); + + ctx->popState(); +*/ +} + +bool +PDFLaTeXRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base) +{ +// The boundingbox calculation here should be exactly the same as the one by CairoRenderer::setupDocument ! + + if (!base) + base = SP_ITEM(sp_document_root(doc)); + + NRRect d; + if (pageBoundingBox) { + d.x0 = d.y0 = 0; + d.x1 = ceil(sp_document_width(doc)); + d.y1 = ceil(sp_document_height(doc)); + } else { + sp_item_invoke_bbox(base, &d, sp_item_i2d_affine(base), TRUE, SPItem::RENDERING_BBOX); + } + + // convert from px to pt + d.x0 *= PT_PER_PX; + d.x1 *= PT_PER_PX; + d.y0 *= PT_PER_PX; + d.y1 *= PT_PER_PX; + + double _width = d.x1-d.x0; + double _height = d.y1-d.y0; + + if (!pageBoundingBox) + { + double high = sp_document_height(doc); + high *= PT_PER_PX; + + transform( Geom::Translate( -d.x0 * PX_PER_PT, + (d.y1 - high) * PX_PER_PT ) ); + } + + return true; +} + +void +PDFLaTeXRenderer::transform(Geom::Matrix const &transform) +{ + _m *= transform; +} + + +/* +#include "macros.h" // SP_PRINT_* + +// Apply an SVG clip path +void +PDFLaTeXRenderer::applyClipPath(CairoRenderContext *ctx, SPClipPath const *cp) +{ + g_assert( ctx != NULL && ctx->_is_valid ); + + if (cp == NULL) + return; + + CairoRenderContext::CairoRenderMode saved_mode = ctx->getRenderMode(); + ctx->setRenderMode(CairoRenderContext::RENDER_MODE_CLIP); + + Geom::Matrix saved_ctm; + if (cp->clipPathUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) { + //SP_PRINT_DRECT("clipd", cp->display->bbox); + NRRect clip_bbox(cp->display->bbox); + Geom::Matrix t(Geom::Scale(clip_bbox.x1 - clip_bbox.x0, clip_bbox.y1 - clip_bbox.y0)); + t[4] = clip_bbox.x0; + t[5] = clip_bbox.y0; + t *= ctx->getCurrentState()->transform; + ctx->getTransform(&saved_ctm); + ctx->setTransform(&t); + } + + TRACE(("BEGIN clip\n")); + SPObject *co = SP_OBJECT(cp); + for (SPObject *child = sp_object_first_child(co) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + if (SP_IS_ITEM(child)) { + SPItem *item = SP_ITEM(child); + + // combine transform of the item in clippath and the item using clippath: + Geom::Matrix tempmat (item->transform); + tempmat = tempmat * (ctx->getCurrentState()->item_transform); + + // render this item in clippath + ctx->pushState(); + ctx->transform(&tempmat); + setStateForItem(ctx, item); + sp_item_invoke_render(item, ctx); + ctx->popState(); + } + } + TRACE(("END clip\n")); + + // do clipping only if this was the first call to applyClipPath + if (ctx->getClipMode() == CairoRenderContext::CLIP_MODE_PATH + && saved_mode == CairoRenderContext::RENDER_MODE_NORMAL) + cairo_clip(ctx->_cr); + + if (cp->clipPathUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) + ctx->setTransform(&saved_ctm); + + ctx->setRenderMode(saved_mode); +} + +// Apply an SVG mask +void +PDFLaTeXRenderer::applyMask(CairoRenderContext *ctx, SPMask const *mask) +{ + g_assert( ctx != NULL && ctx->_is_valid ); + + if (mask == NULL) + return; + + //SP_PRINT_DRECT("maskd", &mask->display->bbox); + NRRect mask_bbox(mask->display->bbox); + // TODO: should the bbox be transformed if maskUnits != userSpaceOnUse ? + if (mask->maskContentUnits == SP_CONTENT_UNITS_OBJECTBOUNDINGBOX) { + Geom::Matrix t(Geom::Scale(mask_bbox.x1 - mask_bbox.x0, mask_bbox.y1 - mask_bbox.y0)); + t[4] = mask_bbox.x0; + t[5] = mask_bbox.y0; + t *= ctx->getCurrentState()->transform; + ctx->setTransform(&t); + } + + // Clip mask contents... but... + // The mask's bounding box is the "geometric bounding box" which doesn't allow for + // filters which extend outside the bounding box. So don't clip. + // ctx->addClippingRect(mask_bbox.x0, mask_bbox.y0, mask_bbox.x1 - mask_bbox.x0, mask_bbox.y1 - mask_bbox.y0); + + ctx->pushState(); + + TRACE(("BEGIN mask\n")); + SPObject *co = SP_OBJECT(mask); + for (SPObject *child = sp_object_first_child(co) ; child != NULL; child = SP_OBJECT_NEXT(child) ) { + if (SP_IS_ITEM(child)) { + SPItem *item = SP_ITEM(child); + renderItem(ctx, item); + } + } + TRACE(("END mask\n")); + + ctx->popState(); +} +*/ + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#undef TRACE + +/* End of GNU GPL code */ + +/* + 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:encoding=utf-8:textwidth=99 : diff --git a/src/extension/internal/pdflatex-renderer.h b/src/extension/internal/pdflatex-renderer.h new file mode 100644 index 000000000..21609e892 --- /dev/null +++ b/src/extension/internal/pdflatex-renderer.h @@ -0,0 +1,82 @@ +#ifndef EXTENSION_INTERNAL_PDF_LATEX_RENDERER_H_SEEN +#define EXTENSION_INTERNAL_PDF_LATEX_RENDERER_H_SEEN + +/** \file + * Declaration of PDFLaTeXRenderer, used for rendering the accompanying LaTeX file when saving PDF output + LaTeX + */ +/* + * Authors: + * Johan Engelen + * + * Copyright (C) 2010 Authors + * + * Licensed under GNU GPL + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "extension/extension.h" +#include +#include + +#include "style.h" + +#include + +#include <2geom/matrix.h> + +class SPClipPath; +class SPMask; +class SPItem; + +namespace Inkscape { +namespace Extension { +namespace Internal { + +class PDFLaTeXRenderer { +public: + PDFLaTeXRenderer(); + virtual ~PDFLaTeXRenderer(); + + void setStateForItem(SPItem const *item); + +// void applyClipPath(CairoRenderContext *ctx, SPClipPath const *cp); +// void applyMask(CairoRenderContext *ctx, SPMask const *mask); + + /** Initializes the PDFLaTeXRenderer according to the specified + SPDocument. Important to set the boundingbox to the pdf boundingbox */ + bool setupDocument(SPDocument *doc, bool pageBoundingBox, SPItem *base); + + /** Traverses the object tree and invokes the render methods. */ + void renderItem(SPItem *item); + +protected: + void transform(Geom::Matrix const &transform); + Geom::Matrix _m; // the transform for current item + + void sp_item_invoke_render(SPItem *item); + void sp_root_render(SPItem *item); + void sp_group_render(SPItem *item); + void sp_use_render(SPItem *item); + void sp_text_render(SPItem *item); + void sp_flowtext_render(SPItem *item); +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + +#endif /* !EXTENSION_INTERNAL_CAIRO_RENDERER_H_SEEN */ + +/* + 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:encoding=utf-8:textwidth=99 : -- 2.30.2