File: 0001-PDFInfo.java-Support-SOURCE_DATE_EPOCH-environment-v.patch

package info (click to toggle)
fop 1%3A2.10%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 103,024 kB
  • sloc: java: 220,896; xml: 108,681; sh: 372; python: 316; javascript: 272; makefile: 44
file content (145 lines) | stat: -rw-r--r-- 6,235 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
Author: Vagrant Cascadian <vagrant@reproducible-builds.org>
        tony mancill <tmancill@debian.org>
Description: Use SOURCE_DATE_EPOCH environment variable when set instead of
 current system time to enable reproducible generation of PDF documents.
 If you desire the previous behavior, either unset SOURCE_DATE_EPOCH or
 overwrite it to a non-integer value.
 Also see: https://reproducible-builds.org/docs/source-date-epoch/
Forwarded: not-needed
Last-Update: 2024-02-18
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=978499

--- a/fop-core/src/main/java/org/apache/fop/pdf/PDFInfo.java
+++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFInfo.java
@@ -21,6 +21,8 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -305,9 +307,40 @@
      * @return the requested String representation
      */
     protected static String formatDateTime(final Date time) {
+        final Date sourceDateEpoch = getSourceDateEpoch();
+        if (sourceDateEpoch != null) {
+            return formatDateTime(sourceDateEpoch, TimeZone.getTimeZone("Etc/UTC"));
+        }
         return formatDateTime(time, TimeZone.getDefault());
     }
 
+    /** @return a Date initialized from SOURCE_DATE_EPOCH or null if not set */
+    public static Date getSourceDateEpoch() {
+        // https://reproducible-builds.org/docs/source-date-epoch/
+        // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=978499
+        final String sourceDateEpochString = getEnvVar("SOURCE_DATE_EPOCH", null);
+        if (sourceDateEpochString != null) {
+            try {
+                final Long sourcedate = (1000 * Long.parseLong(sourceDateEpochString));
+                return new Date(sourcedate);
+            } catch (NumberFormatException ignored) {
+                // ignored
+            }
+        }
+        return null;
+    }
+
+    private static String getEnvVar(String envVar, String defaultVal) {
+        try {
+            return AccessController.doPrivileged((PrivilegedAction<String>)
+                () -> System.getenv(envVar)
+            );
+        } catch(SecurityException ignored) {
+            // do nothing
+        }
+        return defaultVal;
+    }
+
     /**
      * Adds a custom property to this Info dictionary.
      */
--- a/fop-core/src/main/java/org/apache/fop/pdf/PDFMetadata.java
+++ b/fop-core/src/main/java/org/apache/fop/pdf/PDFMetadata.java
@@ -135,8 +135,12 @@
 
         //Set creation date if not available, yet
         if (info.getCreationDate() == null) {
-            Date d = new Date();
-            info.setCreationDate(d);
+            final Date sourceDateEpoch = PDFInfo.getSourceDateEpoch();
+            if (sourceDateEpoch != null) {
+                info.setCreationDate(sourceDateEpoch);
+            } else {
+                info.setCreationDate(new Date());
+            }
         }
 
         //Important: Acrobat 7's preflight check for PDF/A-1b wants the creation date in the Info
--- a/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
+++ b/fop-core/src/main/java/org/apache/fop/render/pdf/PDFRenderingUtil.java
@@ -261,8 +261,14 @@
         }
         fopXMP.mergeInto(docXMP, exclude);
         XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(docXMP);
-        //Metadata was changed so update metadata date
-        xmpBasic.setMetadataDate(new java.util.Date());
+        //Metadata was changed so potentially update metadata date
+        final Date sourceDateEpoch = PDFInfo.getSourceDateEpoch();
+        if (sourceDateEpoch != null) {
+            xmpBasic.setMetadataDate(sourceDateEpoch);
+        } else {
+            xmpBasic.setMetadataDate(new Date());
+        }
+
         PDFMetadata.updateInfoFromMetadata(docXMP, pdfDoc.getInfo());
 
         PDFMetadata pdfMetadata = pdfDoc.getFactory().makeMetadata(
@@ -481,7 +487,13 @@
                 augmentDictionary((PDFDictionary)currentPage.get("DPart"), extension);
             }
         } else if (type == PDFDictionaryType.PagePiece) {
-            String date = DateFormatUtil.formatPDFDate(new Date(), TimeZone.getDefault());
+            final Date sourceDateEpoch = PDFInfo.getSourceDateEpoch();
+            final String date;
+            if (sourceDateEpoch != null) {
+                date = DateFormatUtil.formatPDFDate(sourceDateEpoch, TimeZone.getTimeZone("Etc/UTC"));
+            } else {
+                date = DateFormatUtil.formatPDFDate(new Date(), TimeZone.getDefault());
+            }
             if (currentPage.get("PieceInfo") == null) {
                 currentPage.put("PieceInfo", new PDFDictionary());
                 currentPage.put("LastModified", date);
--- a/fop-core/src/main/java/org/apache/fop/pdf/FileIDGenerator.java
+++ b/fop-core/src/main/java/org/apache/fop/pdf/FileIDGenerator.java
@@ -24,8 +24,11 @@
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.TimeZone;
 import java.util.Random;
 
+import org.apache.xmlgraphics.util.DateFormatUtil;
+
 /**
  * A class to generate the File Identifier of a PDF document (the ID entry of the file
  * trailer dictionary).
@@ -85,8 +88,14 @@
         }
 
         private void generateFileID() {
-            DateFormat df = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS");
-            digest.update(PDFDocument.encode(df.format(new Date())));
+            final Date sourceDateEpoch = PDFInfo.getSourceDateEpoch();
+            if (sourceDateEpoch != null) {
+                digest.update(PDFDocument.encode(
+                    DateFormatUtil.formatPDFDate(sourceDateEpoch, TimeZone.getTimeZone("Etc/UTC"))));
+            } else {
+                DateFormat df = new SimpleDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS");
+                digest.update(PDFDocument.encode(df.format(new Date())));
+            }
             // Ignoring the filename here for simplicity even though it's recommended
             // by the PDF spec
             digest.update(PDFDocument.encode(String.valueOf(document.getCurrentFileSize())));