Hi Dominik, I rechecked the algorithm, D:20120530235959Z00'00' does not fail anymore, i did not parse correctly Z.

D:2012010
D:20120

are still failing... the solution is a bit more convoluted:

Instead of returning a simple, boolean, ParseFixLenNumber should return one of possible 3 states, something like an enum {OK, Missing, Error}.


I think something like

----
enum EParseFixLenNumberResult {OK, Missing, Error};
EParseFixLenNumberResult PdfDate::ParseFixLenNumber(const char *&in, unsigned int length, int min, int max, int &ret_)
{
    if ( in == NULL || *in == '\0') return Missing;
    int ret = 0;
    for(unsigned int i=0;i<length;i++)
    {
        if ( in == NULL || !isdigit(*in)) return Error;
        ret = ret*10+ (*in-'0');
        in++;
    }
    if ( ret < min || ret > max ) return Error;
    ret_ = ret;
    return Ok;
}
----

should do the work, but first one should decide if those optional fields should really be treated as errors...

Federico
Index: src/podofo/base/PdfDate.cpp
===================================================================
--- src/podofo/base/PdfDate.cpp	(revision 2016)
+++ src/podofo/base/PdfDate.cpp	(working copy)
@@ -47,17 +47,14 @@
 }
 
 PdfDate::PdfDate( const time_t & t )
-    : m_bValid( false )
+    : m_time( t ), m_bValid( false )
 {
-    m_time = t;
     CreateStringRepresentation();
 }
 
 PdfDate::PdfDate( const PdfString & sDate )
-    : m_bValid( false )
+    : m_time( -1 ), m_bValid( false )
 {
-    m_time = -1;
-
     if ( !sDate.IsValid() ) 
     {
         m_szDate[0] = 0;
@@ -66,11 +63,8 @@
 
     strncpy(m_szDate,sDate.GetString(),PDF_DATE_BUFFER_SIZE);
 
-    struct tm _tm;
-    memset( &_tm, 0, sizeof(_tm) );
-    int nZoneShift = 0;
-    int nZoneHour = 0;
-    int nZoneMin = 0;
+    struct tm _tm{};
+    _tm.tm_mday = 1;
 
     const char * pszDate = sDate.GetString();
     if ( pszDate == NULL ) return;
@@ -79,53 +73,57 @@
         if ( *pszDate++ != ':' ) return;
     }
 
-    if ( ParseFixLenNumber(pszDate,4,0,9999,_tm.tm_year) == false ) 
+    // year is not optional
+    if ( !ParseFixLenNumber(pszDate,4,0,9999,_tm.tm_year) )
         return;
-
     _tm.tm_year -= 1900;
-    if ( *pszDate != '\0' ) {
-        if ( ParseFixLenNumber(pszDate,2,1,12,_tm.tm_mon) == false ) 
-            return;
 
+    // all other values are optional, if not set they are 0-init (except mday)
+    if ( ParseFixLenNumber(pszDate,2,1,12,_tm.tm_mon) )
+    {
         _tm.tm_mon--;
-        if ( *pszDate != '\0' ) {
-            if ( ParseFixLenNumber(pszDate,2,1,31,_tm.tm_mday) == false ) return;
-            if ( *pszDate != '\0' ) {
-                if ( ParseFixLenNumber(pszDate,2,0,23,_tm.tm_hour) == false ) return;
-                if ( *pszDate != '\0' ) {
-                    if ( ParseFixLenNumber(pszDate,2,0,59,_tm.tm_min) == false ) return;
-                    if ( *pszDate != '\0' ) {
-                        if ( ParseFixLenNumber(pszDate,2,0,59,_tm.tm_sec) == false ) return;
-                        if ( *pszDate != '\0' ) {
-                            switch(*pszDate++) {
-                            case '+':
-                                nZoneShift = -1;
-                                break;
-                            case '-':
-                                nZoneShift = 1;
-                                break;
-                            case 'Z':
-                                nZoneShift = 0;
-                                break;
-                            default:
-                                return;
-                            }
-                            if ( ParseFixLenNumber(pszDate,2,0,59,nZoneHour) == false ) return;
-                            if ( *pszDate == '\'' ) {
-                                pszDate++;
-                                if ( ParseFixLenNumber(pszDate,2,0,59,nZoneMin) == false ) return;
-                                if ( *pszDate != '\'' ) return;
-                                pszDate++;
-                            }
-                        }
-                    }
-                }
+        if ( ParseFixLenNumber(pszDate,2,1,31,_tm.tm_mday) )
+        {
+            if ( ParseFixLenNumber(pszDate,2,0,23,_tm.tm_hour) )
+            {
+                if ( ParseFixLenNumber(pszDate,2,0,59,_tm.tm_min) )
+                    ParseFixLenNumber(pszDate,2,0,59,_tm.tm_sec);
             }
         }
     }
 
+    // zone is optional
+    int nZoneShift = 0;
+    int nZoneHour = 0;
+    int nZoneMin = 0;
+
     if ( *pszDate != '\0' ) 
     {
+        switch (*pszDate) {
+        case '+':
+            nZoneShift = -1;
+            break;
+        case '-':
+            nZoneShift = 1;
+            break;
+        case 'Z':
+            nZoneShift = 0;
+            break;
+        default:
+            return;
+        }
+        pszDate++;
+        if ( !ParseFixLenNumber(pszDate,2,0,59,nZoneHour) ) return;
+        if (*pszDate == '\'') {
+            pszDate++;
+            if ( !ParseFixLenNumber(pszDate,2,0,59,nZoneMin) ) return;
+            if (*pszDate != '\'')
+                return;
+            pszDate++;
+        }
+    }
+    if ( *pszDate != '\0' ) 
+    {
         return;
     }
 
@@ -136,7 +134,7 @@
         return;
     }
 
-    m_time += nZoneShift*(nZoneHour*3600 + nZoneMin*60);
+    m_time += nZoneShift*(nZoneHour*3600 + nZoneMin*60) ;//- timezone;
     m_bValid = true;
 }
 
@@ -206,9 +204,9 @@
 }
 
 
-bool PdfDate::ParseFixLenNumber(const char *&in, unsigned int length, int min, int max, int &ret)
+bool PdfDate::ParseFixLenNumber(const char *&in, unsigned int length, int min, int max, int &ret_)
 {
-    ret = 0;
+    int ret = 0;
     for(unsigned int i=0;i<length;i++)
     {
         if ( in == NULL || !isdigit(*in)) return false;
@@ -216,6 +214,7 @@
         in++;
     }
     if ( ret < min || ret > max ) return false;
+    ret_ = ret;
     return true;
 }
 
Index: src/podofo/base/PdfDate.h
===================================================================
--- src/podofo/base/PdfDate.h	(revision 2016)
+++ src/podofo/base/PdfDate.h	(working copy)
@@ -121,10 +121,10 @@
 
     /** Parse fixed length number from string
      *  \param in string to read number from
-     *  \param length of number to read 
+     *  \param length exact number characters to read 
      *  \param min minimal value of number
      *  \param max maximal value of number
-     *  \param ret parsed number
+     *  \param ret parsed number (updated only on success)
      */
     bool ParseFixLenNumber(const char *&in, unsigned int length, int min, int max, int &ret);
 
Index: test/unit/DateTest.cpp
===================================================================
--- test/unit/DateTest.cpp	(revision 2016)
+++ test/unit/DateTest.cpp	(working copy)
@@ -30,8 +30,8 @@
 {
 }
 
-void DateTest::tearDown()
-{
+void DateTest::tearDown(){
+
 }
 
 void checkExpected(const char *pszDate, bool bExpected)
@@ -38,7 +38,14 @@
 {
     PdfString tmp(pszDate);
     PdfDate date(tmp);
-    CPPUNIT_ASSERT_EQUAL(bExpected,date.IsValid());
+    if( pszDate != NULL )
+    {
+      CPPUNIT_ASSERT_EQUAL_MESSAGE(pszDate,bExpected,date.IsValid());
+    }
+    else
+    {
+      CPPUNIT_ASSERT_EQUAL_MESSAGE("NULL",bExpected,date.IsValid());
+    }
 }
 
 void DateTest::testCreateDateFromString()
@@ -45,7 +52,7 @@
 {
     checkExpected(NULL,false);
     checkExpected("D:2012",true);
-    checkExpected("D:20120",false);
+    checkExpected("D:20120",false); //
     checkExpected("D:201201",true);
     checkExpected("D:2012010",false);
     checkExpected("D:20120101",true);
@@ -66,8 +73,10 @@
 
 void DateTest::testDateValue()
 {
-    PdfDate date(PdfString("D:20120530235959Z00'00'"));
-    CPPUNIT_ASSERT_EQUAL(true,date.IsValid());
+    const char* pszDate = "D:20120530235959Z00'00'";
+    PdfString tmp(pszDate);
+    PdfDate date(tmp);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE(std::string(pszDate),true,date.IsValid());
     const time_t &time = date.GetTime();
     struct tm  _tm;
     memset (&_tm, 0, sizeof(struct tm));
@@ -81,4 +90,27 @@
     CPPUNIT_ASSERT_EQUAL(true,time==time2);
 }
 
+void DateTest::testAdditional()
+{
+  struct name_date {
+    std::string name;
+    std::string date;
+  };
 
+  const name_date data[] = {
+			    {"sample from pdf_reference_1_7.pdf", "D:199812231952-08'00'"}, 
+			    // UTC 1998-12-24 03:52:00
+			    {"all fields set", "D:20201223195200-08'00'"},   // UTC 2020-12-03:52:00
+			    {"set year", "D:2020"},   // UTC 2020-01-01 00:00:00
+			    {"set year, month", "D:202001"},   // UTC 2020-01-01 00:00:00
+			    {"set year, month, day", "D:20200101"},   // UTC 202001-01 00:00:00
+			    {"only year and timezone set", "D:2020-08'00'"},   // UTC 2020-01-01 08:00:00
+			    {"berlin", "D:20200315120820+01'00'"},   // UTC 2020-03-15 11:08:20
+  };
+
+  for (const auto& d : data) {
+    std::cout << "Parse " << d.name << "\n";
+    assert(PoDoFo::PdfDate(d.date).IsValid());
+  }
+}
+
Index: test/unit/DateTest.h
===================================================================
--- test/unit/DateTest.h	(revision 2016)
+++ test/unit/DateTest.h	(working copy)
@@ -29,6 +29,7 @@
     CPPUNIT_TEST_SUITE( DateTest );
     CPPUNIT_TEST( testCreateDateFromString );
     CPPUNIT_TEST( testDateValue );
+    CPPUNIT_TEST( testAdditional );
     CPPUNIT_TEST_SUITE_END();
 public:
     void setUp();
@@ -36,6 +37,8 @@
 
     void testCreateDateFromString();
     void testDateValue();
+    void testAdditional();
+  
 };
 
 #endif
_______________________________________________
Podofo-users mailing list
Podofo-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/podofo-users

Reply via email to