Source: librsvg Version: 2.40.20-2 Severity: wishlist Tags: patch User: reproducible-bui...@lists.alioth.debian.org Usertags: timestamps toolchain X-Debbugs-Cc: reproducible-b...@lists.alioth.debian.org
Hi, 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. Patch attached. (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.) [0] https://reproducible-builds.org/ Regards, -- ,''`. : :' : Chris Lamb `. `'` la...@debian.org / chris-lamb.co.uk `-
diff --git a/rsvg-convert.c b/rsvg-convert.c index 34b48db..b6da76d 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[23]; + char *endptr; + char *source_date_epoch; + time_t now; + struct tm *build_time; + unsigned long long epoch; + #ifdef G_OS_WIN32 HANDLE handle; #endif @@ -333,9 +342,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); + 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")){