Code

Merge from trunk.
[inkscape.git] / src / helper / png-write.cpp
index 69848e9a6995350c909a1989977df0709a07e13a..d3fe2771ff8d07970153b3b1427879a0d08a0977 100644 (file)
@@ -1,11 +1,11 @@
-#define __SP_PNG_WRITE_C__
-
 /*
  * PNG file format utilities
  *
  * Authors:
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   Whoever wrote this example in libpng documentation
+ *   Jon A. Cruz <jon@joncruz.org>
+ *   Abhishek Sharma
  *
  * Copyright (C) 1999-2002 authors
  *
@@ -19,6 +19,7 @@
 #include <interface.h>
 #include <libnr/nr-pixops.h>
 #include <libnr/nr-translate-scale-ops.h>
+#include <2geom/rect.h>
 #include <glib/gmessages.h>
 #include <png.h>
 #include "png-write.h"
@@ -29,7 +30,8 @@
 #include <sp-item.h>
 #include <sp-root.h>
 #include <sp-defs.h>
-#include "prefs-utils.h"
+#include "preferences.h"
+#include "rdf.h"
 
 /* This is an example of how to use libpng to read and write PNG files.
  * The file libpng.txt is much more verbose then this.  If you have not
@@ -61,8 +63,65 @@ typedef struct SPPNGBD {
     int rowstride;
 } SPPNGBD;
 
+/**
+ * A simple wrapper to list png_text.
+ */
+class PngTextList {
+public:
+    PngTextList() : count(0), textItems(0) {}
+    ~PngTextList();
+
+    void add(gchar const* key, gchar const* text);
+    gint getCount() {return count;}
+    png_text* getPtext() {return textItems;}
+
+private:
+    gint count;
+    png_text* textItems;
+};
+
+PngTextList::~PngTextList() {
+    for (gint i = 0; i < count; i++) {
+        if (textItems[i].key) {
+            g_free(textItems[i].key);
+        }
+        if (textItems[i].text) {
+            g_free(textItems[i].text);
+        }
+    }
+}
+
+void PngTextList::add(gchar const* key, gchar const* text)
+{
+    if (count < 0) {
+        count = 0;
+        textItems = 0;
+    }
+    png_text* tmp = (count > 0) ? g_try_renew(png_text, textItems, count + 1): g_try_new(png_text, 1);
+    if (tmp) {
+        textItems = tmp;
+        count++;
+
+        png_text* item = &(textItems[count - 1]);
+        item->compression = PNG_TEXT_COMPRESSION_NONE;
+        item->key = g_strdup(key);
+        item->text = g_strdup(text);
+        item->text_length = 0;
+#ifdef PNG_iTXt_SUPPORTED
+        item->itxt_length = 0;
+        item->lang = 0;
+        item->lang_key = 0;
+#endif // PNG_iTXt_SUPPORTED
+    } else {
+        g_warning("Unable to allocate arrary for %d PNG text data.", count);
+        textItems = 0;
+        count = 0;
+    }
+}
+
 static bool
-sp_png_write_rgba_striped(gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi,
+sp_png_write_rgba_striped(SPDocument *doc,
+                          gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi,
                           int (* get_rows)(guchar const **rows, int row, int num_rows, void *data),
                           void *data)
 {
@@ -71,10 +130,10 @@ sp_png_write_rgba_striped(gchar const *filename, unsigned long int width, unsign
     png_structp png_ptr;
     png_infop info_ptr;
     png_color_8 sig_bit;
-    png_text text_ptr[3];
     png_uint_32 r;
 
     g_return_val_if_fail(filename != NULL, false);
+    g_return_val_if_fail(data != NULL, false);
 
     /* open the file */
 
@@ -141,11 +200,49 @@ sp_png_write_rgba_striped(gchar const *filename, unsigned long int width, unsign
     sig_bit.alpha = 8;
     png_set_sBIT(png_ptr, info_ptr, &sig_bit);
 
-    /* Made by Inkscape comment */
-    text_ptr[0].key = "Software";
-    text_ptr[0].text = "www.inkscape.org";
-    text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
-    png_set_text(png_ptr, info_ptr, text_ptr, 1);
+    PngTextList textList;
+
+    textList.add("Software", "www.inkscape.org"); // Made by Inkscape comment
+    {
+        const gchar* pngToDc[] = {"Title", "title",
+                               "Author", "creator",
+                               "Description", "description",
+                               //"Copyright", "",
+                               "Creation Time", "date",
+                               //"Disclaimer", "",
+                               //"Warning", "",
+                               "Source", "source"
+                               //"Comment", ""
+        };
+        for (size_t i = 0; i < G_N_ELEMENTS(pngToDc); i += 2) {
+            struct rdf_work_entity_t * entity = rdf_find_entity ( pngToDc[i + 1] );
+            if (entity) {
+                gchar const* data = rdf_get_work_entity(doc, entity);
+                if (data && *data) {
+                    textList.add(pngToDc[i], data);
+                }
+            } else {
+                g_warning("Unable to find entity [%s]", pngToDc[i + 1]);
+            }
+        }
+
+
+        struct rdf_license_t *license =  rdf_get_license(doc);
+        if (license) {
+            if (license->name && license->uri) {
+                gchar* tmp = g_strdup_printf("%s %s", license->name, license->uri);
+                textList.add("Copyright", tmp);
+                g_free(tmp);
+            } else if (license->name) {
+                textList.add("Copyright", license->name);
+            } else if (license->uri) {
+                textList.add("Copyright", license->uri);
+            }
+        }
+    }
+    if (textList.getCount() > 0) {
+        png_set_text(png_ptr, info_ptr, textList.getPtext(), textList.getCount());
+    }
 
     /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */
     /* note that if sRGB is present the cHRM chunk must be ignored
@@ -230,7 +327,7 @@ sp_export_get_rows(guchar const **rows, int row, int num_rows, void *data)
     bbox.y1 = row + num_rows;
     /* Update to renderable state */
     NRGC gc(NULL);
-    gc.transform.set_identity();
+    gc.transform.setIdentity();
 
     nr_arena_item_invoke_update(ebp->root, &bbox, &gc,
            NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
@@ -265,8 +362,7 @@ sp_export_get_rows(guchar const **rows, int row, int num_rows, void *data)
 /**
  * Hide all items that are not listed in list, recursively, skipping groups and defs.
  */
-static void
-hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
+static void hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
 {
     if ( SP_IS_ITEM(o)
          && !SP_IS_DEFS(o)
@@ -274,12 +370,12 @@ hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
          && !SP_IS_GROUP(o)
          && !g_slist_find(list, o) )
     {
-        sp_item_invoke_hide(SP_ITEM(o), dkey);
+        SP_ITEM(o)->invoke_hide(dkey);
     }
 
     // recurse
     if (!g_slist_find(list, o)) {
-        for (SPObject *child = sp_object_first_child(o) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+        for ( SPObject *child = o->firstChild() ; child; child = child->getNext() ) {
             hide_other_items_recursively(child, list, dkey);
         }
     }
@@ -291,9 +387,20 @@ hide_other_items_recursively(SPObject *o, GSList *list, unsigned dkey)
  *
  * \return true if succeeded (or if no action was taken), false if an error occurred.
  */
+bool sp_export_png_file (SPDocument *doc, gchar const *filename,
+                   double x0, double y0, double x1, double y1,
+                   unsigned long int width, unsigned long int height, double xdpi, double ydpi,
+                   unsigned long bgcolor,
+                   unsigned int (*status) (float, void *),
+                   void *data, bool force_overwrite,
+                   GSList *items_only)
+{
+    return sp_export_png_file(doc, filename, Geom::Rect(Geom::Point(x0,y0),Geom::Point(x1,y1)),
+                              width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only);
+}
 bool
 sp_export_png_file(SPDocument *doc, gchar const *filename,
-                   double x0, double y0, double x1, double y1,
+                   Geom::Rect const &area,
                    unsigned long width, unsigned long height, double xdpi, double ydpi,
                    unsigned long bgcolor,
                    unsigned (*status)(float, void *),
@@ -304,6 +411,7 @@ sp_export_png_file(SPDocument *doc, gchar const *filename,
     g_return_val_if_fail(filename != NULL, false);
     g_return_val_if_fail(width >= 1, false);
     g_return_val_if_fail(height >= 1, false);
+    g_return_val_if_fail(!area.hasZeroArea(), false);
 
     if (!force_overwrite && !sp_ui_overwrite_file(filename)) {
         /* Remark: We return true so as not to invoke an error dialog in case export is cancelled
@@ -313,20 +421,12 @@ sp_export_png_file(SPDocument *doc, gchar const *filename,
         return true;
     }
 
-    // export with maximum blur rendering quality
-    int saved_quality = prefs_get_int_attribute("options.blurquality", "value", 0);
-    prefs_set_int_attribute("options.blurquality", "value", 2);
+    doc->ensureUpToDate();
 
-    sp_document_ensure_up_to_date(doc);
+    /* Calculate translation by transforming to document coordinates (flipping Y)*/
+    Geom::Point translation = Geom::Point(-area[Geom::X][0], area[Geom::Y][1] - doc->getHeight());
 
-    /* Go to document coordinates */
-    {
-        gdouble const t = y0;
-        y0 = sp_document_height(doc) - y1;
-        y1 = sp_document_height(doc) - t;
-    }
-
-    /*
+    /*  This calculation is only valid when assumed that (x0,y0)= area.corner(0) and (x1,y1) = area.corner(2)
      * 1) a[0] * x0 + a[2] * y1 + a[4] = 0.0
      * 2) a[1] * x0 + a[3] * y1 + a[5] = 0.0
      * 3) a[0] * x1 + a[2] * y1 + a[4] = width
@@ -342,9 +442,9 @@ sp_export_png_file(SPDocument *doc, gchar const *filename,
      * (2) a[5] = -a[3] * y1
      */
 
-    NR::Matrix const affine(NR::translate(-x0, -y0)
-                            * NR::scale(width / (x1 - x0),
-                                        height / (y1 - y0)));
+    Geom::Matrix const affine(Geom::Translate(translation)
+                            * Geom::Scale(width / area.width(),
+                                        height / area.height()));
 
     //SP_PRINT_MATRIX("SVG2PNG", &affine);
 
@@ -358,16 +458,18 @@ sp_export_png_file(SPDocument *doc, gchar const *filename,
 
     /* Create new arena */
     NRArena *const arena = NRArena::create();
-    unsigned const dkey = sp_item_display_key_new(1);
+    // export with maximum blur rendering quality
+    nr_arena_set_renderoffscreen(arena);
+    unsigned const dkey = SPItem::display_key_new(1);
 
     /* Create ArenaItems and set transform */
-    ebp.root = sp_item_invoke_show(SP_ITEM(sp_document_root(doc)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
+    ebp.root = SP_ITEM(doc->getRoot())->invoke_show(arena, dkey, SP_ITEM_SHOW_DISPLAY);
     nr_arena_item_set_transform(NR_ARENA_ITEM(ebp.root), affine);
 
     // We show all and then hide all items we don't want, instead of showing only requested items,
     // because that would not work if the shown item references something in defs
     if (items_only) {
-        hide_other_items_recursively(sp_document_root(doc), items_only, dkey);
+        hide_other_items_recursively(doc->getRoot(), items_only, dkey);
     }
 
     ebp.status = status;
@@ -377,25 +479,21 @@ sp_export_png_file(SPDocument *doc, gchar const *filename,
     if ((width < 256) || ((width * height) < 32768)) {
         ebp.px = nr_pixelstore_64K_new(FALSE, 0);
         ebp.sheight = 65536 / (4 * width);
-        write_status = sp_png_write_rgba_striped(filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
+        write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
         nr_pixelstore_64K_free(ebp.px);
     } else {
-        ebp.px = g_new(guchar, 4 * 64 * width);
         ebp.sheight = 64;
-        write_status = sp_png_write_rgba_striped(filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
+        ebp.px = g_try_new(guchar, 4 * ebp.sheight * width);
+        write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
         g_free(ebp.px);
     }
 
-    // Hide items
-    sp_item_invoke_hide(SP_ITEM(sp_document_root(doc)), dkey);
+    // Hide items, this releases arenaitem
+    SP_ITEM(doc->getRoot())->invoke_hide(dkey);
 
-    /* Free Arena and ArenaItem */
-    nr_arena_item_unref(ebp.root);
+    /* Free arena */
     nr_object_unref((NRObject *) arena);
 
-    // restore saved blur quality
-    prefs_set_int_attribute("options.blurquality", "value", saved_quality);
-
     return write_status;
 }
 
@@ -409,4 +507,4 @@ sp_export_png_file(SPDocument *doc, gchar const *filename,
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :