[librsvg: 1/3] Make PDF output reproducible if the SOURCE_DATE_EPOCH envvar is set



commit dc4a78d3761b2bd83bbfefc806a52d80229e3f1b
Author: Chris Lamb <chris chris-lamb co uk>
Date:   Tue Feb 13 09:27:15 2018 +0000

    Make PDF output reproducible if the SOURCE_DATE_EPOCH envvar is set
    
    Whilst working on the Reproducible Builds effort [0], we noticed
    that rsvg-convert does not create reproducible output.
    
    This is because it calls cairo_pdf_surface_create_for_stream
    without setting a modification time, thus leading to the current
    date always being generated.
    
    An alternative patch *could* use the creation time of the input
    file, but this would be misleading metadata when applied to the
    output - it was not created at the same time as the input.
    
    This has also been filed in Debian [1].
    
     [0] https://reproducible-builds.org/
     [1] https://bugs.debian.org/890027
    
    Signed-off-by: Chris Lamb <chris chris-lamb co uk>

 configure.ac   |  2 +-
 rsvg-convert.c | 44 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 44 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0d8528cc..57d6e510 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,7 +41,7 @@ dnl ===========================================================================
 GLIB_REQUIRED=2.52.0
 GIO_REQUIRED=2.24.0
 LIBXML_REQUIRED=2.9.0
-CAIRO_REQUIRED=1.2.0
+CAIRO_REQUIRED=1.15.4
 PANGO_REQUIRED=1.38.0
 GDK_PIXBUF_REQUIRED=2.20
 GTK3_REQUIRED=3.10.0
diff --git a/rsvg-convert.c b/rsvg-convert.c
index c4130ea0..7a5120fe 100644
--- a/rsvg-convert.c
+++ b/rsvg-convert.c
@@ -30,9 +30,11 @@
 
 #include "config.h"
 
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 #include <locale.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
@@ -143,6 +145,13 @@ main (int argc, char **argv)
     double unscaled_width, unscaled_height;
     int scaled_width, scaled_height;
 
+    char buffer[25];
+    char *endptr;
+    char *source_date_epoch;
+    time_t now;
+    struct tm *build_time;
+    unsigned long long epoch;
+
 #ifdef G_OS_WIN32
     HANDLE handle;
 #endif
@@ -349,9 +358,42 @@ main (int argc, char **argv)
                 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                                       scaled_width, scaled_height);
 #ifdef CAIRO_HAS_PDF_SURFACE
-            else if (!strcmp (format, "pdf"))
+            else if (!strcmp (format, "pdf")) {
                 surface = cairo_pdf_surface_create_for_stream (rsvg_cairo_write_func, output_file,
                                                                scaled_width, scaled_height);
+                source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+                if (source_date_epoch) {
+                    errno = 0;
+                    epoch = strtoull(source_date_epoch, &endptr, 10);
+                    if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0))
+                            || (errno != 0 && epoch == 0)) {
+                        g_printerr (_("Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n"),
+                                    strerror(errno));
+                        exit (1);
+                    }
+                    if (endptr == source_date_epoch) {
+                        g_printerr (_("Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n"),
+                                    endptr);
+                        exit (1);
+                    }
+                    if (*endptr != '\0') {
+                        g_printerr (_("Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n"),
+                                    endptr);
+                        exit (1);
+                    }
+                    if (epoch > ULONG_MAX) {
+                        g_printerr (_("Environment variable $SOURCE_DATE_EPOCH: value must be smaller than 
or equal to %lu but was found to be: %llu \n"),
+                                    ULONG_MAX, epoch);
+                        exit (1);
+                    }
+                    now = (time_t) epoch;
+                    build_time = gmtime(&now);
+                    g_assert (strftime (buffer, sizeof (buffer), "%Y-%m-%dT%H:%M:%S%z", build_time));
+                    cairo_pdf_surface_set_metadata (surface,
+                                                    CAIRO_PDF_METADATA_CREATE_DATE,
+                                                    buffer);
+                }
+            }
 #endif
 #ifdef CAIRO_HAS_PS_SURFACE
             else if (!strcmp (format, "ps") || !strcmp (format, "eps")){


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]