From cca66b9ec4e494c1d919bff0f71a820d8afab1fa Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:24:48 +0200 Subject: Adding upstream version 1.2.2. Signed-off-by: Daniel Baumann --- src/extension/internal/cairo-renderer-pdf-out.cpp | 335 ++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 src/extension/internal/cairo-renderer-pdf-out.cpp (limited to 'src/extension/internal/cairo-renderer-pdf-out.cpp') diff --git a/src/extension/internal/cairo-renderer-pdf-out.cpp b/src/extension/internal/cairo-renderer-pdf-out.cpp new file mode 100644 index 0000000..f5adc09 --- /dev/null +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * A quick hack to use the Cairo renderer to write out a file. This + * then makes 'save as...' PDF. + * + * Authors: + * Ted Gould + * Ulf Erikson + * Johan Engelen + * Jon A. Cruz + * Abhishek Sharma + * + * Copyright (C) 2004-2010 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#ifdef CAIRO_HAS_PDF_SURFACE + +#include "cairo-renderer-pdf-out.h" +#include "cairo-render-context.h" +#include "cairo-renderer.h" +#include "latex-text-renderer.h" +#include "path-chemistry.h" +#include +#include "extension/system.h" +#include "extension/print.h" +#include "extension/db.h" +#include "extension/output.h" + +#include "display/drawing.h" +#include "display/curve.h" + +#include "object/sp-item.h" +#include "object/sp-root.h" +#include "object/sp-page.h" + +#include <2geom/affine.h> +#include "page-manager.h" +#include "document.h" + +#include "util/units.h" + +namespace Inkscape { +namespace Extension { +namespace Internal { + +bool CairoRendererPdfOutput::check(Inkscape::Extension::Extension * /*module*/) +{ + bool result = true; + + if (nullptr == Inkscape::Extension::db.get("org.inkscape.output.pdf.cairorenderer")) { + result = false; + } + + return result; +} + +static bool +pdf_render_document_to_file(SPDocument *doc, gchar const *filename, unsigned int level, + bool texttopath, bool omittext, bool filtertobitmap, int resolution, + const gchar * const exportId, bool exportDrawing, bool exportCanvas, double bleedmargin_px) +{ + if (texttopath) { + assert(!omittext); + // Cairo's text-to-path method has numerical precision and font matching + // issues (https://gitlab.com/inkscape/inkscape/-/issues/1979). + // We get better results by using Inkscape's Object-to-Path method. + Inkscape::convert_text_to_curves(doc); + } + + 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 = SP_ITEM(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 new arena */ + Inkscape::Drawing drawing; + drawing.setExact(true); + unsigned dkey = SPItem::display_key_new(1); + root->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY); + + /* Create renderer and context */ + CairoRenderer *renderer = new CairoRenderer(); + CairoRenderContext *ctx = renderer->createContext(); + ctx->setPDFLevel(level); + ctx->setTextToPath(texttopath); + ctx->setOmitText(omittext); + ctx->setFilterToBitmap(filtertobitmap); + ctx->setBitmapResolution(resolution); + + bool ret = ctx->setPdfTarget (filename); + if(ret) { + /* Render document */ + ret = renderer->setupDocument(ctx, doc, pageBoundingBox, bleedmargin_px, base); + + auto pages = doc->getPageManager().getPages(); + if (pages.size() == 0) { + // Output the page bounding box as already set up in the initial setupDocument. + renderer->renderItem(ctx, root); + ret = ctx->finish(); + } else { + auto scale = doc->getDocumentScale(); + + // Set root transformation, which copies a bit of what happens above the + // reason for this manual process is because we need to slide in the page + // offset transformations so need fine-control over placing the children. + ctx->transform(scale); + ctx->transform(root->transform); + + int index = 1; + for (auto &page : pages) { + ctx->pushState(); + + auto pt = Inkscape::Util::Quantity::convert(1, "px", "pt"); + auto rect = page->getRect(); + // Conclude previous page and set new page width and height. + auto big_rect = rect * scale; + ctx->nextPage(big_rect.width() * pt, big_rect.height() * pt, page->label()); + + // Set up page transformation which pushes objects back into the 0,0 location + ctx->transform(Geom::Translate(rect.corner(0)).inverse()); + + for (auto &child : page->getOverlappingItems(false)) { + ctx->pushState(); + + // This process does not return layers, so those affines are added manually. + for (auto anc : child->ancestorList(true)) { + if (auto layer = dynamic_cast(anc)) { + if (layer != child && layer != root) { + ctx->transform(layer->transform); + } + } + } + + // Render the page into the context in the new location. + renderer->renderItem(ctx, child, nullptr, page); + ctx->popState(); + } + ret = ctx->finishPage(); + index += 1; + + ctx->popState(); + } + ret = ctx->finish(); + } + } + + root->invoke_hide(dkey); + + renderer->destroyContext(ctx); + delete renderer; + + return ret; +} + +/** + \brief This function calls the output module with the filename + \param mod unused + \param doc Document to be saved + \param filename Filename to save to (probably will end in .pdf) + + The most interesting thing that this function does is just attach + an '>' on the front of the filename. This is the syntax used to + tell the printing system to save to file. +*/ +void +CairoRendererPdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename) +{ + Inkscape::Extension::Extension * ext; + unsigned int ret; + + ext = Inkscape::Extension::db.get("org.inkscape.output.pdf.cairorenderer"); + if (ext == nullptr) + return; + + int level = 0; + try { + const gchar *new_level = mod->get_param_optiongroup("PDFversion"); + if((new_level != nullptr) && (g_ascii_strcasecmp("PDF-1.5", new_level) == 0)) { + level = 1; + } + } + catch(...) { + g_warning("Parameter might not exist"); + } + + bool new_textToPath = FALSE; + try { + new_textToPath = (strcmp(mod->get_param_optiongroup("textToPath"), "paths") == 0); + } + catch(...) { + g_warning("Parameter might not exist"); + } + + bool new_textToLaTeX = FALSE; + try { + new_textToLaTeX = (strcmp(mod->get_param_optiongroup("textToPath"), "LaTeX") == 0); + } + catch(...) { + g_warning("Parameter might not exist"); + } + + bool new_blurToBitmap = FALSE; + try { + new_blurToBitmap = mod->get_param_bool("blurToBitmap"); + } + catch(...) { + g_warning("Parameter might not exist"); + } + + int new_bitmapResolution = 72; + try { + new_bitmapResolution = mod->get_param_int("resolution"); + } + catch(...) { + g_warning("Parameter might not exist"); + } + + const gchar *new_exportId = nullptr; + try { + new_exportId = mod->get_param_string("exportId"); + } + catch(...) { + g_warning("Parameter might not exist"); + } + + bool new_exportCanvas = true; + try { + new_exportCanvas = (strcmp(ext->get_param_optiongroup("area"), "page") == 0); + } catch(...) { + g_warning("Parameter might not exist"); + } + bool new_exportDrawing = !new_exportCanvas; + + double new_bleedmargin_px = 0.; + try { + new_bleedmargin_px = Inkscape::Util::Quantity::convert(mod->get_param_float("bleed"), "mm", "px"); + } + catch(...) { + g_warning("Parameter might not exist"); + } + + // 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, new_bleedmargin_px); + g_free(final_name); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } + + // Create LaTeX file (if requested) + if (new_textToLaTeX) { + ret = latex_render_document_text_to_file(doc, filename, new_exportId, new_exportDrawing, new_exportCanvas, new_bleedmargin_px, true); + + if (!ret) + throw Inkscape::Extension::Output::save_failed(); + } +} + +#include "clear-n_.h" + +/** + \brief A function allocate a copy of this function. + + This is the definition of Cairo PDF out. This function just + calls the extension system with the memory allocated XML that + describes the data. +*/ +void +CairoRendererPdfOutput::init () +{ + // clang-format off + Inkscape::Extension::build_from_mem( + "\n" + "Portable Document Format\n" + "org.inkscape.output.pdf.cairorenderer\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "true\n" + "96\n" + "\n" + "" + "" + "" + "0\n" + "\n" + "\n" + ".pdf\n" + "application/pdf\n" + "Portable Document Format (*.pdf)\n" + "PDF File\n" + "\n" + "", new CairoRendererPdfOutput()); + // clang-format on + + return; +} + +} } } /* namespace Inkscape, Extension, Internal */ + +#endif /* HAVE_CAIRO_PDF */ -- cgit v1.2.3