From 1c62d521676fec5f2d2cced0203328946761d9ab Mon Sep 17 00:00:00 2001 From: Chris Lamb Date: Tue, 13 Feb 2018 09:27:15 +0000 Subject: [PATCH] Please make the PDF output reproducible 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 --- configure.ac | 2 +- rsvg-convert.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 0d8528cce..6ddb1c700 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.16.0 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 c4130ea0c..6c0f02ee6 100644 --- a/rsvg-convert.c +++ b/rsvg-convert.c @@ -30,9 +30,11 @@ #include "config.h" +#include #include #include #include +#include #include #include #include @@ -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, 23, "%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")){ -- GitLab