summaryrefslogtreecommitdiffstats
path: root/src/extension/internal/cairo-renderer-pdf-out.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /src/extension/internal/cairo-renderer-pdf-out.cpp
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/extension/internal/cairo-renderer-pdf-out.cpp')
-rw-r--r--src/extension/internal/cairo-renderer-pdf-out.cpp335
1 files changed, 335 insertions, 0 deletions
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 <ted@gould.cx>
+ * Ulf Erikson <ulferikson@users.sf.net>
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2004-2010 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cairo.h>
+#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 <print.h>
+#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<SPItem *>(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 <PDFversion> might not exist");
+ }
+
+ bool new_textToPath = FALSE;
+ try {
+ new_textToPath = (strcmp(mod->get_param_optiongroup("textToPath"), "paths") == 0);
+ }
+ catch(...) {
+ g_warning("Parameter <textToPath> might not exist");
+ }
+
+ bool new_textToLaTeX = FALSE;
+ try {
+ new_textToLaTeX = (strcmp(mod->get_param_optiongroup("textToPath"), "LaTeX") == 0);
+ }
+ catch(...) {
+ g_warning("Parameter <textToLaTeX> might not exist");
+ }
+
+ bool new_blurToBitmap = FALSE;
+ try {
+ new_blurToBitmap = mod->get_param_bool("blurToBitmap");
+ }
+ catch(...) {
+ g_warning("Parameter <blurToBitmap> might not exist");
+ }
+
+ int new_bitmapResolution = 72;
+ try {
+ new_bitmapResolution = mod->get_param_int("resolution");
+ }
+ catch(...) {
+ g_warning("Parameter <resolution> might not exist");
+ }
+
+ const gchar *new_exportId = nullptr;
+ try {
+ new_exportId = mod->get_param_string("exportId");
+ }
+ catch(...) {
+ g_warning("Parameter <exportId> might not exist");
+ }
+
+ bool new_exportCanvas = true;
+ try {
+ new_exportCanvas = (strcmp(ext->get_param_optiongroup("area"), "page") == 0);
+ } catch(...) {
+ g_warning("Parameter <area> 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 <bleed> 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(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>Portable Document Format</name>\n"
+ "<id>org.inkscape.output.pdf.cairorenderer</id>\n"
+ "<param name=\"PDFversion\" gui-text=\"" N_("Restrict to PDF version:") "\" type=\"optiongroup\" appearance=\"combo\" >\n"
+ "<option value='PDF-1.5'>" N_("PDF 1.5") "</option>\n"
+ "<option value='PDF-1.4'>" N_("PDF 1.4") "</option>\n"
+ "</param>\n"
+ "<param name=\"textToPath\" gui-text=\"" N_("Text output options:") "\" type=\"optiongroup\" appearance=\"radio\">\n"
+ "<option value=\"embed\">" N_("Embed fonts") "</option>\n"
+ "<option value=\"paths\">" N_("Convert text to paths") "</option>\n"
+ "<option value=\"LaTeX\">" N_("Omit text in PDF and create LaTeX file") "</option>\n"
+ "</param>\n"
+ "<param name=\"blurToBitmap\" gui-text=\"" N_("Rasterize filter effects") "\" type=\"bool\">true</param>\n"
+ "<param name=\"resolution\" gui-text=\"" N_("Resolution for rasterization (dpi):") "\" type=\"int\" min=\"1\" max=\"10000\">96</param>\n"
+ "<param name=\"area\" gui-text=\"" N_("Output page size:") "\" type=\"optiongroup\" appearance=\"radio\" >\n"
+ "<option value=\"page\">" N_("Use document's page size") "</option>"
+ "<option value=\"drawing\">" N_("Use exported object's size") "</option>"
+ "</param>"
+ "<param name=\"bleed\" gui-text=\"" N_("Bleed/margin (mm):") "\" type=\"float\" min=\"-10000\" max=\"10000\">0</param>\n"
+ "<param name=\"exportId\" gui-text=\"" N_("Limit export to the object with ID:") "\" type=\"string\"></param>\n"
+ "<output is_exported='true'>\n"
+ "<extension>.pdf</extension>\n"
+ "<mimetype>application/pdf</mimetype>\n"
+ "<filetypename>Portable Document Format (*.pdf)</filetypename>\n"
+ "<filetypetooltip>PDF File</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>", new CairoRendererPdfOutput());
+ // clang-format on
+
+ return;
+}
+
+} } } /* namespace Inkscape, Extension, Internal */
+
+#endif /* HAVE_CAIRO_PDF */