[NTG-pdftex] Patch CreationDate

Heiko Oberdiek oberdiek@uni-freiburg.de
Wed, 30 Jul 2003 22:08:11 +0200


--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hello,

Patch CreationDate

If /CreationDate is not given by \pdfinfo, then a default is
used by computing the string by the current setting of
\year, \month, and \time.

Problem A:
The time zone is not set. See the discussion in
  de.comp.text.tex, "[pdftex] Zeitzone von CreationDate"
Valentin Schwamberger observed that his AR/Windows shows then
the time as GMT.

Problem B:
It is easy to fool pdfTeX:
* Setting \year to a number less or greater than 4 digits.
* Setting \year, \month, \date, \time to invalid numbers,
  eg.: \month=13
The result are invalid dates.

The following patch tries to solve these problems:
* Time zone detection by glibc's strftime("%z").
* The current time is used, not \year, \month, ...

Patch files based on 2003/07/30 v1.11a:
  utils.c.diff        for TeX/texk/web2c/pdftexdir/utils.c
  pdftex.ch.diff      for TeX/texk/web2c/pdftexdir/pdftex.ch
  pdftex.defines.diff for TeX/texk/web2c/pdftexdir/pdftex.defines

Yours sincerely
  Heiko <oberdiek@uni-freiburg.de>
-- 
--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="utils.c.diff"

*** utils.c.org	Wed Jul 30 21:43:08 2003
--- utils.c	Wed Jul 30 21:42:46 2003
***************
*** 473,475 ****
--- 473,644 ----
      convertStringToHexString ((char*)digest, id);
      pdf_printf("/ID [%s %s]\n", id, id);
  }
+ 
+ /* Print the /CreationDate entry.
+ 
+   PDF Reference, third edition says about the expected date format:
+   <blockquote>
+     3.8.2 Dates
+ 
+       PDF defines a standard date format, which closely follows that of
+       the international standard ASN.1 (Abstract Syntax Notation One),
+       defined in ISO/IEC 8824 (see the Bibliography). A date is a string
+       of the form
+ 
+         (D:YYYYMMDDHHmmSSOHH'mm')
+ 
+       where
+ 
+         YYYY is the year
+         MM is the month
+         DD is the day (01-31)
+         HH is the hour (00-23)
+         mm is the minute (00-59)
+         SS is the second (00-59)
+         O is the relationship of local time to Universal Time (UT),
+           denoted by one of the characters +, -, or Z (see below)
+         HH followed by ' is the absolute value of the offset from UT
+           in hours (00-23)
+         mm followed by ' is the absolute value of the offset from UT
+           in minutes (00-59)
+ 
+       The apostrophe character (') after HH and mm is part of the syntax.
+       All elds after the year are optional. (The prefix D:, although also
+       optional, is strongly recommended.) The default values for MM and DD
+       are both 01; all other numerical elds default to zero values.  A plus
+       sign (+) as the value of the O field signifies that local time is
+       later than UT, a minus sign (-) that local time is earlier than UT,
+       and the letter Z that local time is equal to UT. If no UT information
+       is specified, the relationship of the specified time to UT is
+       considered to be unknown. Whether or not the time zone is known, the
+       rest of the date should be specified in local time.
+ 
+       For example, December 23, 1998, at 7:52 PM, U.S. Pacific Standard
+       Time, is represented by the string
+ 
+         D:199812231952-08'00'
+   </blockquote>
+ 
+   The main difficulty is get the time zone data. Function strftime()
+   of glibc knows "%z", the manual page says:
+   <blockquote>
+        %z     The time-zone as hour offset from GMT.  Required to
+               emit RFC822-conformant dates (using "%a, %d  %b  %Y
+               %H:%M:%S %z"). (GNU)
+   </blockquote>
+   Does this mean that the output of "%z" can have any RFC822-conformant
+   format?
+ 
+   RFC 822 specifies:
+   <blockquote>
+      5.  DATE AND TIME SPECIFICATION
+ 
+      5.1.  SYNTAX
+ 
+      [...]
+ 
+      zone        =  "UT"  / "GMT"                ; Universal Time
+                                                  ; North American : UT
+                  /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
+                  /  "CST" / "CDT"                ;  Central:  - 6/ - 5
+                  /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
+                  /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
+                  /  1ALPHA                       ; Military: Z = UT;
+                                                  ;  A:-1; (J not used)
+                                                  ;  M:-12; N:+1; Y:+12
+                  / ( ("+" / "-") 4DIGIT )        ; Local differential
+                                                  ;  hours+min. (HHMM)
+   </blockquote>
+ */
+ void printcreationdate()
+ {
+     time_t t;
+     struct tm *tt;
+     size_t size;
+     char time_str[40];
+     char *zone_str;
+ 
+     /* get the time */
+     t = time(NULL);
+     tt = localtime(&t);
+     size = strftime(time_str, sizeof(time_str), "%Y%m%d%H%M%S%z", tt);
+     /* expected format: "YYYYmmddHHMMSSzzzzz" */
+ 
+     /* correction for seconds: %S can be in range 00..61,
+        the PDF reference expects 00..59,
+        therefore we map "60" and "61" to "59" */
+     if (zone_str[12] == '6' && zone_str[13] != 0) {
+         zone_str[12] == '5';
+         zone_str[13] == '9';
+     }
+ 
+     /* correction for time zone */
+     zone_str = &time_str[14];
+     if ((zone_str[0] == '+' || zone_str[0] == '-') &&
+         isdigit(zone_str[1]) &&
+         isdigit(zone_str[2]) &&
+         isdigit(zone_str[3]) &&
+         isdigit(zone_str[4]) &&
+         zone_str[5] == 0) {
+         /* test for GMT */
+         if (strcmp(&zone_str[1], "0000") == 0) {
+             /* case "+0000" or "-0000" */
+             zone_str[0] = 'Z';
+             zone_str[1] = 0;
+         }
+         else {
+             /* convert "sdddd" to "sdd'dd'" (s = sign, d = digit) */
+             zone_str[7] = 0;
+             zone_str[6] = '\'';
+             zone_str[5] = zone_str[4];
+             zone_str[4] = zone_str[3];
+             zone_str[3] = '\'';
+         }
+     }
+     else if (strcmp(zone_str, "Z") == 0) {
+         /* case "Z", nothing to do */
+     }
+     else if (strcmp(zone_str, "GMT") == 0 ||
+              strcmp(zone_str, "UT") == 0) {
+         /* case Universal Time */
+         zone_str[0] = 'Z';
+         zone_str[1] = 0;
+     }
+     else if (zone_str[1] == 0 &&
+              zone_str[0] >= 'A' &&
+              zone_str[0] <= 'M') {
+         /* Military: A:-1 .. M:-12 */
+         sprintf(zone_str, "-%02d'00'", zone_str[0] - 'A' + 1);
+     }
+     else if (zone_str[1] == 0 &&
+              zone_str[0] >= 'N' &&
+              zone_str[0] <= 'Y') {
+         /* Military: N:+1 .. Y:+12 */
+         sprintf(zone_str, "+%02d'00'", zone_str[0] - 'N' + 1);
+     }
+     else if (strcmp(zone_str, "EDT") == 0) {
+         strcpy(zone_str, "-04'00'");
+     }
+     else if (strcmp(zone_str, "EST") == 0 ||
+              strcmp(zone_str, "CDT") == 0) {
+         strcpy(zone_str, "-05'00'");
+     }
+     else if (strcmp(zone_str, "CST") == 0 ||
+              strcmp(zone_str, "MDT") == 0) {
+         strcpy(zone_str, "-06'00'");
+     }
+     else if (strcmp(zone_str, "MST") == 0 ||
+              strcmp(zone_str, "PDT") == 0) {
+         strcpy(zone_str, "-07'00'");
+     }
+     else if (strcmp(zone_str, "PST") == 0) {
+         strcpy(zone_str, "-08'00'");
+     }
+     else {
+         /* unknown zone format or "%z" is not supported by strftime() */
+         zone_str[0] = 0;
+     }
+ 
+     /* print result */
+     pdf_printf("/CreationDate (D:%s)\n", time_str);
+ }

--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="pdftex.ch.diff"

*** pdftex.ch.org	Wed Jul 30 20:47:21 2003
--- pdftex.ch	Wed Jul 30 20:58:40 2003
***************
*** 4585,4597 ****
  pdf_print_ln(")")
  
  @ @<Print the CreationDate key@>=
! pdf_print("/CreationDate (D:");
! pdf_print_int(year);
! pdf_print_two(month);
! pdf_print_two(day);
! pdf_print_two(time div 60);
! pdf_print_two(time mod 60);
! pdf_print_ln("00)")
  
  @ @<Glob...@>=
  @!pdftex_banner: str_number;   {the complete banner}
--- 4585,4591 ----
  pdf_print_ln(")")
  
  @ @<Print the CreationDate key@>=
! print_creation_date;
  
  @ @<Glob...@>=
  @!pdftex_banner: str_number;   {the complete banner}

--X1bOJ3K7DJ5YkBrT
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="pdftex.defines.diff"

*** pdftex.defines.org	Wed Jul 30 20:46:06 2003
--- pdftex.defines	Wed Jul 30 21:00:34 2003
***************
*** 50,55 ****
--- 50,56 ----
  @define procedure libpdffinish;
  @define procedure writestreamlength();
  @define procedure printID();
+ @define procedure printcreationdate;
  
  { functions from vfpacket.c }
  @define function newvfpacket();

--X1bOJ3K7DJ5YkBrT--