Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kitinerary for openSUSE:Factory 
checked in at 2023-10-12 23:40:06
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kitinerary (Old)
 and      /work/SRC/openSUSE:Factory/.kitinerary.new.1807 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kitinerary"

Thu Oct 12 23:40:06 2023 rev:66 rq:1117312 version:23.08.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes    2023-09-15 
22:03:00.823854852 +0200
+++ /work/SRC/openSUSE:Factory/.kitinerary.new.1807/kitinerary.changes  
2023-10-12 23:41:02.642584285 +0200
@@ -1,0 +2,32 @@
+Tue Oct 10 18:27:34 UTC 2023 - Christophe Marin <[email protected]>
+
+- Update to 23.08.2
+  * New bugfix release
+  * For more details please see:
+  * https://kde.org/announcements/gear/23.08.2/
+- Changes since 23.08.1:
+  * Fix several copy/paste errors in SortUtil::hasStart/EndTime()
+  * Add bookingkit PDF extractor script
+  * Handle departure/arrival/duration triples in generic extraction as well
+  * Update clang-tidy settings to something more reasonable
+  * Make the Amadeus PDF timestamp workaround a bit less specific
+  * Correctly compare times with and without timezones when merging
+  * Fix parsing newer UK railway PDF tickets
+  * Deal with an alternative way to mark non-reservation RSP-6 tickets
+  * Fix parsing GWR iCal attachments for multi-leg train trips
+  * Manually merge international Renfe results
+  * Unify reservation/ticket number extraction for Renfe/Ouigo ES barcodes
+  * Move Renfe barcode documentation to the wiki
+  * Add a workaround for fixing broken UIC 918.3 payloads in Renfe tickets
+  * Sanity-check the RCT2 traveler name
+  * Handle one more data format in RCT2 tickets
+  * Try barcode decoding both on transformed and untranformed source images
+  * Remove some excessive debug output
+  * Extract un-styled Pretix PDF tickets
+  * Extract multi-leg Renfe tickets correctly
+  * Make train number matching slightly less strict
+  * Merge the two ÖBB UIC 918.3 extractor scripts
+  * Extract multi-leg ÖBB PDF tickets
+  * Fix online import of unidirectional SNCF bookings (kde#474197)
+
+-------------------------------------------------------------------

Old:
----
  kitinerary-23.08.1.tar.xz
  kitinerary-23.08.1.tar.xz.sig

New:
----
  kitinerary-23.08.2.tar.xz
  kitinerary-23.08.2.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kitinerary.spec ++++++
--- /var/tmp/diff_new_pack.cVuX6H/_old  2023-10-12 23:41:03.894629607 +0200
+++ /var/tmp/diff_new_pack.cVuX6H/_new  2023-10-12 23:41:03.898629752 +0200
@@ -19,7 +19,7 @@
 %define libname libKPimItinerary5
 %bcond_without released
 Name:           kitinerary
-Version:        23.08.1
+Version:        23.08.2
 Release:        0
 Summary:        Data model and extraction system for travel reservations
 License:        LGPL-2.1-or-later


++++++ kitinerary-23.08.1.tar.xz -> kitinerary-23.08.2.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/.clang-tidy 
new/kitinerary-23.08.2/.clang-tidy
--- old/kitinerary-23.08.1/.clang-tidy  2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/.clang-tidy  2023-10-09 17:36:11.000000000 +0200
@@ -2,11 +2,39 @@
 # SPDX-FileCopyrightText: none
 
 ---
-Checks:          
'clang-diagnostic-*,-clang-diagnostic-gnu-zero-variadic-macro-arguments,-clang-diagnostic-unknown-warning-option,clang-analyzer-*,bugprone-*,misc-*,modernize-*,performance-*,readability-*,-readability-implicit-bool-conversion,-modernize-use-trailing-return-type'
+Checks: >
+    clang-diagnostic-*,
+    -clang-diagnostic-gnu-zero-variadic-macro-arguments,
+    -clang-diagnostic-unknown-warning-option,
+    clang-analyzer-*,
+    bugprone-*,
+    -bugprone-easily-swappable-parameters,
+    -bugprone-suspicious-include,
+    misc-*,
+    -misc-use-anonymous-namespace,
+    modernize-*,
+    -modernize-avoid-c-arrays,
+    -modernize-concat-nested-namespaces,
+    -modernize-use-trailing-return-type,
+    performance-*,
+    readability-*,
+    -readability-function-cognitive-complexity,
+    -readability-identifier-length,
+    -readability-implicit-bool-conversion,
+    -readability-isolate-declaration,
+    -readability-magic-numbers,
+    -readability-named-parameter,
+    -readability-qualified-auto,
+    -readability-uppercase-literal-suffix
+
 FormatStyle:     none
-CheckOptions:    
+CheckOptions:
   - key:             
readability-implicit-bool-conversion.AllowIntegerConditions
     value:           '1'
   - key:             
readability-implicit-bool-conversion.AllowPointerConditions
     value:           '1'
+  - key:             
misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
+    value:           '1'
+  - key:             modernize-use-override.IgnoreDestructors
+    value:           '1'
 ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/CMakeLists.txt 
new/kitinerary-23.08.2/CMakeLists.txt
--- old/kitinerary-23.08.1/CMakeLists.txt       2023-09-11 17:49:23.000000000 
+0200
+++ new/kitinerary-23.08.2/CMakeLists.txt       2023-10-09 17:36:11.000000000 
+0200
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 
 cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-set(PIM_VERSION "5.24.1")
+set(PIM_VERSION "5.24.2")
 project(KItinerary VERSION ${PIM_VERSION})
 
 set(KF_MIN_VERSION "5.91.0")
@@ -48,8 +48,8 @@
     find_package(SharedMimeInfo 1.3 REQUIRED)
 endif()
 
-set(KMIME_VERSION "5.24.1")
-set(PIM_PKPASS "5.24.1")
+set(KMIME_VERSION "5.24.2")
+set(PIM_PKPASS "5.24.2")
 
 find_package(KPim${KF_MAJOR_VERSION}Mime ${KMIME_VERSION} CONFIG REQUIRED)
 find_package(KPimPkPass ${PIM_PKPASS} CONFIG REQUIRED)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/autotests/extractortest.cpp 
new/kitinerary-23.08.2/autotests/extractortest.cpp
--- old/kitinerary-23.08.1/autotests/extractortest.cpp  2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/autotests/extractortest.cpp  2023-10-09 
17:36:11.000000000 +0200
@@ -172,7 +172,6 @@
                 continue;
             }
             const auto tokenData = ticket.ticketTokenData();
-            qDebug() << tokenData << ticket.ticketToken();
             if (tokenData.userType() == QMetaType::QString) {
                 QVERIFY(tokenData.toString() != ticket.ticketToken());
             }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/po/zh_CN/kitinerary.po 
new/kitinerary-23.08.2/po/zh_CN/kitinerary.po
--- old/kitinerary-23.08.1/po/zh_CN/kitinerary.po       2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/po/zh_CN/kitinerary.po       2023-10-09 
17:36:11.000000000 +0200
@@ -3,7 +3,7 @@
 "Project-Id-Version: kdeorg\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n";
 "POT-Creation-Date: 2022-07-20 00:46+0000\n"
-"PO-Revision-Date: 2023-09-02 02:59\n"
+"PO-Revision-Date: 2023-09-16 10:11\n"
 "Last-Translator: \n"
 "Language-Team: Chinese Simplified\n"
 "Language: zh_CN\n"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml 
new/kitinerary-23.08.2/src/cli/org.kde.kitinerary-extractor.appdata.xml
--- old/kitinerary-23.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2023-10-09 17:36:11.000000000 +0200
@@ -119,9 +119,9 @@
     <binary>kitinerary-extractor</binary>
   </provides>
   <releases>
+    <release version="5.24.2" date="2023-10-12"/>
     <release version="5.24.1" date="2023-09-14"/>
     <release version="5.24.0" date="2023-08-24"/>
     <release version="5.23.3" date="2023-07-06"/>
-    <release version="5.23.2" date="2023-06-08"/>
   </releases>
 </component>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/lib/extractors/genericboardingpassextractor.cpp 
new/kitinerary-23.08.2/src/lib/extractors/genericboardingpassextractor.cpp
--- old/kitinerary-23.08.1/src/lib/extractors/genericboardingpassextractor.cpp  
2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/extractors/genericboardingpassextractor.cpp  
2023-10-09 17:36:11.000000000 +0200
@@ -89,6 +89,16 @@
     return fromDt < toDt && 
FlightUtil::isPlausibleDistanceForDuration(distance, flightDuration);
 }
 
+[[nodiscard]] static qint64 flightDuration(const QDateTime &fromTime, const 
QDateTime &toTime, KnowledgeDb::IataCode from, KnowledgeDb::IataCode to)
+{
+    // times are local, so convert them to the right timezone first
+    auto fromDt = fromTime;
+    fromDt.setTimeZone(KnowledgeDb::timezoneForAirport(from));
+    auto toDt = toTime;
+    toDt.setTimeZone(KnowledgeDb::timezoneForAirport(to));
+    return fromDt.secsTo(toDt) / 60;
+}
+
 static bool conflictIfSet(const QDateTime &lhs, const QDateTime &rhs)
 {
     return lhs.isValid() && rhs.isValid() && lhs != rhs;
@@ -244,8 +254,11 @@
                 } else if (isPlausibleBoardingTime(times[1], times[2]) && 
isPlausibleFlightTime(times[2], times[0].addDays(1), from, to)) {
                     applyFlightTimes(result, times[1], times[2], 
times[0].addDays(1));
                 }
-
                 // TODO handle boarding before midnight
+                // departure/arrival/duration
+                else if (isPlausibleFlightTime(times[1], times[2], from, to) 
&& flightDuration(times[1], times[2], from, to) == (times[0].time().hour() * 60 
+ times[0].time().minute())) {
+                    applyFlightTimes(result, {}, times[1], times[2]);
+                }
             }
         }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/mergeutil.cpp 
new/kitinerary-23.08.2/src/lib/mergeutil.cpp
--- old/kitinerary-23.08.1/src/lib/mergeutil.cpp        2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/mergeutil.cpp        2023-10-09 
17:36:11.000000000 +0200
@@ -54,6 +54,27 @@
 
 using namespace KItinerary;
 
+/* Compare times without assuming times without a timezone are in the current 
time zone
+ * (they might be local to the destination instead).
+ */
+static bool dateTimeCompare(const QDateTime &lhs, const QDateTime &rhs)
+{
+    if (lhs == rhs) {
+        return true;
+    }
+    if (lhs.timeSpec() == Qt::LocalTime && rhs.timeSpec() != Qt::LocalTime) {
+        QDateTime dt(rhs);
+        dt.setTimeSpec(Qt::LocalTime);
+        return lhs == dt;
+    }
+    if (lhs.timeSpec() != Qt::LocalTime && rhs.timeSpec() == Qt::LocalTime) {
+        QDateTime dt(lhs);
+        dt.setTimeSpec(Qt::LocalTime);
+        return dt == rhs;
+    }
+    return false;
+}
+
 /* Checks that @p lhs and @p rhs are non-empty and equal. */
 static bool equalAndPresent(QStringView lhs, QStringView rhs, 
Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive)
 {
@@ -64,17 +85,25 @@
 {
     return lhs.isValid() && lhs == rhs;
 }
+static bool equalAndPresent(const QDateTime &lhs, const QDateTime &rhs)
+{
+    return lhs.isValid() && dateTimeCompare(lhs, rhs);
+}
 
 /* Checks that @p lhs and @p rhs are not non-equal if both values are set. */
-static bool conflictIfPresent(const QString &lhs, const QString &rhs, 
Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive)
+static bool conflictIfPresent(QStringView lhs, QStringView rhs, 
Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive)
 {
     return !lhs.isEmpty() && !rhs.isEmpty() && lhs.compare(rhs, caseSensitive) 
!= 0;
 }
 template <typename T>
-static bool conflictIfPresent(const T &lhs, const T &rhs)
+static typename std::enable_if<!std::is_same_v<T, QString>, bool>::type 
conflictIfPresent(const T &lhs, const T &rhs)
 {
     return lhs.isValid() && rhs.isValid() && lhs != rhs;
 }
+static bool conflictIfPresent(const QDateTime &lhs, const QDateTime &rhs)
+{
+    return lhs.isValid() && rhs.isValid() && !dateTimeCompare(lhs, rhs);
+}
 static bool conflictIfPresent(const Person &lhs, const Person &rhs)
 {
     return !lhs.name().isEmpty() && !rhs.name().isEmpty() && 
!MergeUtil::isSamePerson(lhs, rhs);
@@ -404,6 +433,14 @@
     return (lIt != lEnd && (*lIt).isSpace()) || (rIt != rEnd && 
(*rIt).isSpace());
 }
 
+static bool isSameTrainName(QStringView lhs, QStringView rhs)
+{
+    if (lhs.isEmpty() || rhs.isEmpty()) {
+        return false;
+    }
+    return lhs.startsWith(rhs) || rhs.startsWith(lhs);
+}
+
 static bool isSameLineName(const QString &lhs, const QString &rhs)
 {
     if (isSameLineName(lhs.begin(), lhs.end(), rhs.begin(), rhs.end())
@@ -419,8 +456,8 @@
     if (!lhsMatch.hasMatch() || !rhsMatch.hasMatch()) {
         return false;
     }
-    return equalAndPresent(lhsMatch.capturedView(u"type"), 
rhsMatch.capturedView(u"type")) && (
-           (equalAndPresent(lhsMatch.capturedView(u"line"), 
rhsMatch.capturedView(u"line")) && 
equalAndPresent(lhsMatch.capturedView(u"route"), 
rhsMatch.capturedView(u"route")))
+    return isSameTrainName(lhsMatch.capturedView(u"type"), 
rhsMatch.capturedView(u"type")) && (
+           (equalAndPresent(lhsMatch.capturedView(u"line"), 
rhsMatch.capturedView(u"line")) && 
!conflictIfPresent(lhsMatch.capturedView(u"route"), 
rhsMatch.capturedView(u"route")))
         || (equalAndPresent(lhsMatch.capturedView(u"line"), 
rhsMatch.capturedView(u"route")) && lhsMatch.capturedView(u"route").isEmpty())
         || (equalAndPresent(lhsMatch.capturedView(u"route"), 
rhsMatch.capturedView(u"line")) && rhsMatch.capturedView(u"route").isEmpty())
     );
@@ -440,6 +477,7 @@
             && LocationUtil::isSameLocation(lhs.arrivalStation(), 
rhs.arrivalStation(), LocationUtil::Exact);
     } else if (lhs.departureTime().isValid() && rhs.departureTime().isValid()) 
{
         // if we have both departure times, they have to match
+        qCDebug(CompareLog) << "departure time" << lhs.departureTime() << 
rhs.departureTime() <<equalAndPresent(lhs.departureTime(), rhs.departureTime());
         if (!equalAndPresent(lhs.departureTime(), rhs.departureTime())) {
             return false;
         }
@@ -454,6 +492,7 @@
     // arrival times (when present) should either match exactly, or be almost 
the same at a matching arrival location
     // (tickets even for the same connection booked on the same day sometimes 
have slight variation in the arrival time...)
     if (conflictIfPresent(lhs.arrivalTime(), rhs.arrivalTime())) {
+        qCDebug(CompareLog) << "arrival time" << lhs.arrivalTime() << 
rhs.arrivalTime();
         if (std::abs(lhs.arrivalTime().secsTo(rhs.arrivalTime())) > 180) {
             return false;
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/pdf/pdfimage.cpp 
new/kitinerary-23.08.2/src/lib/pdf/pdfimage.cpp
--- old/kitinerary-23.08.1/src/lib/pdf/pdfimage.cpp     2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/pdf/pdfimage.cpp     2023-10-09 
17:36:11.000000000 +0200
@@ -231,12 +231,7 @@
     if (d->m_format == QImage::Format_Invalid) {
         return d->m_vectorPicture.renderToImage();
     }
-
-    const auto img = d->load();
-    if (!img.isNull() && (d->m_width != d->m_sourceWidth || d->m_height != 
d->m_sourceHeight)) {
-        return img.scaled(d->m_width, d->m_height);
-    }
-    return img;
+    return d->load();
 }
 
 bool PdfImage::hasObjectId() const
@@ -258,3 +253,13 @@
 {
     return d->m_vectorPicture.pathElementsCount();
 }
+
+bool PdfImage::hasAspectRatioTransform() const
+{
+    return d->m_format != QImage::Format_Invalid && (d->m_width != 
d->m_sourceWidth || d->m_height != d->m_sourceHeight);
+}
+
+QImage PdfImage::applyAspectRatioTransform(const QImage &image) const
+{
+    return image.scaled(d->m_width, d->m_height);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/pdf/pdfimage.h 
new/kitinerary-23.08.2/src/lib/pdf/pdfimage.h
--- old/kitinerary-23.08.1/src/lib/pdf/pdfimage.h       2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/pdf/pdfimage.h       2023-10-09 
17:36:11.000000000 +0200
@@ -106,7 +106,7 @@
     /** Sets image loading hints. */
     void setLoadingHints(LoadingHints hints);
 
-    /** The source image with display transformations applied. */
+    /** The source image without display transformations applied. */
     QImage image() const;
 
     /** Returns whether this image has an object id.
@@ -128,6 +128,17 @@
      */
     int pathElementsCount() const;
 
+    /** Returns @c true if this image has an aspect-ratio changing transform.
+     *  That might need to be applied before doing barcode decoding for 
example.
+     */
+    [[nodiscard]] bool hasAspectRatioTransform() const;
+
+    /** Applies the aspect ratio changing part of the transform
+     *  to the given image (which typically should be the one returned
+     *  by image()).
+     */
+    [[nodiscard]] QImage applyAspectRatioTransform(const QImage &image) const;
+
 private:
     friend class PdfExtractorOutputDevice;
     friend class PdfPagePrivate;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/lib/processors/barcodedocumentprocessorhelper.cpp 
new/kitinerary-23.08.2/src/lib/processors/barcodedocumentprocessorhelper.cpp
--- 
old/kitinerary-23.08.1/src/lib/processors/barcodedocumentprocessorhelper.cpp    
    2023-09-11 17:49:23.000000000 +0200
+++ 
new/kitinerary-23.08.2/src/lib/processors/barcodedocumentprocessorhelper.cpp    
    2023-10-09 17:36:11.000000000 +0200
@@ -11,7 +11,7 @@
 
 using namespace KItinerary;
 
-void BarcodeDocumentProcessorHelper::expandNode(const QImage &img, 
BarcodeDecoder::BarcodeTypes barcodeHints, ExtractorDocumentNode &parent, const 
ExtractorEngine* engine)
+bool BarcodeDocumentProcessorHelper::expandNode(const QImage &img, 
BarcodeDecoder::BarcodeTypes barcodeHints, ExtractorDocumentNode &parent, const 
ExtractorEngine* engine)
 {
     // in case the barcode raw data (string or bytearray) gets detected as a 
type we handle,
     // we nevertheless inject a raw data node in between. This is useful in 
cases where the
@@ -22,12 +22,12 @@
         auto c = engine->documentNodeFactory()->createNode(b);
         if (c.isA<QByteArray>() || c.isA<QString>()) {
             parent.appendChild(c);
-            return;
+            return true;
         }
         auto rawNode = 
engine->documentNodeFactory()->createNode(QVariant::fromValue(b), 
u"application/octet-stream");
         rawNode.appendChild(c);
         parent.appendChild(rawNode);
-        return;
+        return true;
     }
 
     const auto s = engine->barcodeDecoder()->decodeString(img, barcodeHints);
@@ -35,11 +35,13 @@
         auto c = engine->documentNodeFactory()->createNode(s.toUtf8());
         if (c.isA<QByteArray>() || c.isA<QString>()) {
             parent.appendChild(c);
-            return;
+            return true;
         }
         auto rawNode = 
engine->documentNodeFactory()->createNode(QVariant::fromValue(s), 
u"text/plain");
         rawNode.appendChild(c);
         parent.appendChild(rawNode);
-        return;
+        return true;
     }
+
+    return false;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/lib/processors/barcodedocumentprocessorhelper.h 
new/kitinerary-23.08.2/src/lib/processors/barcodedocumentprocessorhelper.h
--- old/kitinerary-23.08.1/src/lib/processors/barcodedocumentprocessorhelper.h  
2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/processors/barcodedocumentprocessorhelper.h  
2023-10-09 17:36:11.000000000 +0200
@@ -16,7 +16,7 @@
 /** Barcode-related document processor implementation details. */
 namespace BarcodeDocumentProcessorHelper
 {
-void expandNode(const QImage &img, BarcodeDecoder::BarcodeTypes barcodeHints, 
ExtractorDocumentNode &parent, const ExtractorEngine *engine);
+bool expandNode(const QImage &img, BarcodeDecoder::BarcodeTypes barcodeHints, 
ExtractorDocumentNode &parent, const ExtractorEngine *engine);
 }
 
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/lib/processors/pdfdocumentprocessor.cpp 
new/kitinerary-23.08.2/src/lib/processors/pdfdocumentprocessor.cpp
--- old/kitinerary-23.08.1/src/lib/processors/pdfdocumentprocessor.cpp  
2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/processors/pdfdocumentprocessor.cpp  
2023-10-09 17:36:11.000000000 +0200
@@ -39,7 +39,7 @@
 static void applyContextDateTime(PdfDocument *pdf, ExtractorDocumentNode &node)
 {
     // ignore broken PDF times for Amadeus documents
-    if (pdf->producer() == QLatin1String("Amadeus") && pdf->creationTime() == 
pdf->modificationTime() && pdf->creationTime().date() == QDate(2011, 5, 10)) {
+    if (pdf->producer() == QLatin1String("Amadeus") && pdf->creationTime() == 
pdf->modificationTime() && pdf->creationTime().date().year() <= 2013) {
         return;
     }
 
@@ -117,7 +117,14 @@
 
             // technically not our job to do this here rather than letting the 
image node processor handle this
             // but we have the output aspect ratio of the barcode only here, 
which gives better decoding hints
-            BarcodeDocumentProcessorHelper::expandNode(imgData, barcodeHints, 
childNode, engine);
+            if (BarcodeDocumentProcessorHelper::expandNode(imgData, 
barcodeHints, childNode, engine)) {
+                continue;
+            }
+
+            // if this failed, check if the image as a aspect-ratio distorting 
scale and try again with that
+            if (img.hasAspectRatioTransform()) {
+                
BarcodeDocumentProcessorHelper::expandNode(img.applyAspectRatioTransform(imgData),
 barcodeHints, childNode, engine);
+            }
         }
 
         // handle full page raster images
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/bookingkit.js 
new/kitinerary-23.08.2/src/lib/scripts/bookingkit.js
--- old/kitinerary-23.08.1/src/lib/scripts/bookingkit.js        2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/bookingkit.js        2023-10-09 
17:36:11.000000000 +0200
@@ -27,3 +27,30 @@
 
     return res;
 }
+
+function parsePdf(pdf, node, barcode) {
+    const page = pdf.pages[barcode.location];
+    const text = page.textInRect(0.0, 0.0, 0.9, 0.3);
+    console.log(text);
+    let res = JsonLd.newEventReservation();
+    res.reservationFor.name = text.match(/^ *(\S.*)\n/)[1];
+    res.reservedTicket.ticketToken = 'qrCode:' + barcode.content;
+    res.reservationNumber = barcode.content;
+
+    const dts = text.match(/(\d\d\.\d\d\.\d{4}).*\n *(\d\d\.\d\d\.\d{4})\n.*\n 
*(\d\d:\d\d).*(\d\d:\d\d)/);
+    if (dts) {
+        res.reservationFor.startDate = JsonLd.toDateTime(dts[1] + dts[3], 
'dd.MM.yyyyhh:mm', 'en');
+        res.reservationFor.endDate = JsonLd.toDateTime(dts[2] + dts[4], 
'dd.MM.yyyyhh:mm', 'en');
+    } else {
+        const dt = text.match(/(\d\d\.\d\d\.\d{4}).*\n *(\d\d:\d\d)/);
+        res.reservationFor.startDate = JsonLd.toDateTime(dt[1] + dt[2], 
'dd.MM.yyyyhh:mm', 'en');
+    }
+
+    const loc = text.match(/(\S.*), (.*), (.*)\n *(.*)\n *(.*)\n.*##/);
+    res.reservationFor.location.address.streetAddress = loc[1];
+    res.reservationFor.location.address.addressLocality = loc[2];
+    res.reservationFor.location.address.addressCountry = loc[3];
+    res.reservedTicket.name = loc[4];
+    res.underName.name = loc[5];
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/bookingkit.json 
new/kitinerary-23.08.2/src/lib/scripts/bookingkit.json
--- old/kitinerary-23.08.1/src/lib/scripts/bookingkit.json      2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/bookingkit.json      2023-10-09 
17:36:11.000000000 +0200
@@ -7,4 +7,14 @@
         "scope": "Current"
     } ],
     "script": "bookingkit.js"
+},
+{
+    "mimeType": "application/pdf",
+    "filter": [ {
+        "match": "^[A-Z0-9]{3}-[A-z0-9]{6}-[A-Z0-9]{3}$",
+        "mimeType": "text/plain",
+        "scope": "Descendants"
+    } ],
+    "script": "bookingkit.js",
+    "function": "parsePdf"
 }]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/lib/scripts/chaos-communication-congress.js 
new/kitinerary-23.08.2/src/lib/scripts/chaos-communication-congress.js
--- old/kitinerary-23.08.1/src/lib/scripts/chaos-communication-congress.js      
2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/chaos-communication-congress.js      
2023-10-09 17:36:11.000000000 +0200
@@ -12,7 +12,7 @@
 
     var ptTicket = triggerNode.content
     var res = JsonLd.newEventReservation();
-    res.reservationFor.name = ptTicket.person.name;
+    res.reservationFor.name = ptTicket.ticketLayout.text(0, 52, 19, 1).trim();
     res.reservationFor.location.name = "Congress Center Leipzig";
     res.reservationFor.location.address.streetAddress = "Messeallee";
     res.reservationFor.location.address.postalCode = "04356";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/elron.js 
new/kitinerary-23.08.2/src/lib/scripts/elron.js
--- old/kitinerary-23.08.1/src/lib/scripts/elron.js     2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/elron.js     2023-10-09 
17:36:11.000000000 +0200
@@ -5,7 +5,6 @@
 
 function parsePdfTicket(pdf, node, triggerNode) {
     const text = pdf.pages[triggerNode.location].text;
-    console.log(text);
     let res = JsonLd.newTrainReservation();
     const trip = text.match(/(\d\d:\d\d) (\S.*) - (\d\d:\d\d) (\S.*)  
+(\d\d\.\d\d\.\d{4})\n *(\S.*?) /);
     res.reservationFor.departureTime = JsonLd.toDateTime(trip[5] + ' ' + 
trip[1], 'dd.MM.yyyy hh:mm', 'ee');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/fjordline.js 
new/kitinerary-23.08.2/src/lib/scripts/fjordline.js
--- old/kitinerary-23.08.1/src/lib/scripts/fjordline.js 2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/fjordline.js 2023-10-09 
17:36:11.000000000 +0200
@@ -7,7 +7,6 @@
     const text = pdf.pages[triggerNode.location].text;
     if (!text.match(/Fjord Line/))
         return;
-    console.log(text);
     let res = JsonLd.newBoatReservation();
     const trip = text.match(/(\d{2}.\d{2}.\d{4}) +(.*?) +(\d{2}\.\d{2}) +(.*) 
+(\d{2}\.\d{2}) +(.*?)  +(.*)/);
     res.reservationFor.departureBoatTerminal.name = trip[2];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/gwr.js 
new/kitinerary-23.08.2/src/lib/scripts/gwr.js
--- old/kitinerary-23.08.1/src/lib/scripts/gwr.js       2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/gwr.js       2023-10-09 
17:36:11.000000000 +0200
@@ -6,7 +6,6 @@
 function parseEvent(event) {
     let reservations = [];
     let idx = 0;
-    const dtStart = JsonLd.readQDateTime(event, 'dtStart');
     while (true) {
         const trip = event.description.substr(idx).match(/Journey Details: 
(.*) \(([A-Z]{3})\) to (.*) \(([A-Z]{3})\), *dep *(\d\d):(\d\d), *arr 
*(\d\d):(\d\d)/);
         if (!trip) {
@@ -15,11 +14,11 @@
         idx += trip.index + trip[0].length;
 
         let res = JsonLd.newTrainReservation();
-        let depDt = new Date(dtStart);
+        let depDt = new Date(event.dtStart);
         depDt.setHours(trip[5]);
         depDt.setMinutes(trip[6]);
         res.reservationFor.departureTime = depDt;
-        let arrDt = new Date(dtStart);
+        let arrDt = new Date(event.dtStart);
         arrDt.setHours(trip[7]);
         arrDt.setMinutes(trip[8]);
         res.reservationFor.arrivalTime = arrDt;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/nationalrail.js 
new/kitinerary-23.08.2/src/lib/scripts/nationalrail.js
--- old/kitinerary-23.08.1/src/lib/scripts/nationalrail.js      2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/nationalrail.js      2023-10-09 
17:36:11.000000000 +0200
@@ -82,7 +82,7 @@
         }
         const seatLetter = readRSP6String(rsp6, 422 + offset, 1);
         const seatNum = rsp6.readNumberMSB(428 + offset, 7);
-        if (seatLetter || seatNum) {
+        if ((seatLetter && seatLetter != '*') || seatNum) {
             res.reservedTicket.ticketedSeat.seatNumber = (seatLetter ? 
seatLetter : "") + (seatNum ? seatNum : "");
         }
         offset += 45;
@@ -99,7 +99,7 @@
 function parseTicket(pdf, node, triggerNode) {
     const text = pdf.pages[triggerNode.location].text;
     var res = triggerNode.result[0];
-    const header = text.match(/= +(\d{2}[ -][A-Z][a-z]{2}[ -]\d{4}) +(?:Out: 
|Ret: )?([A-Z]{3}) ?- ?([A-Z]{3})\n(.*)  +(.*)/);
+    const header = text.match(/ +(\d{2}[ -][A-Z][a-z]{2}[ -]\d{4}) +(?:Out: 
|Ret: )?([A-Z]{3}) ?- ?([A-Z]{3})\n(.*)  +(.*)/);
     const date = header[1].replace(/-/g, ' ');
     const itinerary = text.match(/Itinerary.*\n +(.*)\n +(\d{2}:\d{2})\n 
+([\S\s]*?)\n +(\d{2}:\d{2})\n +(.*)/);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/oebb.js 
new/kitinerary-23.08.2/src/lib/scripts/oebb.js
--- old/kitinerary-23.08.1/src/lib/scripts/oebb.js      2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/oebb.js      2023-10-09 
17:36:11.000000000 +0200
@@ -3,14 +3,24 @@
    SPDX-License-Identifier: LGPL-2.0-or-later
 */
 
-function parseTicket(ticket, node) {
-    if (node.result.length > 1) // not sure this can happen
-        return;
-
-    var res = node.result[0];
+function parseTicket(code, node) {
+    // VorteilsCard
+    if (code.ticketLayout && code.ticketLayout.type == "RCT2" && 
code.ticketLayout.text(0, 0, 50, 1).match(/VORTEILSCARD/i)) {
+        let card = JsonLd.newObject("ProgramMembership");
+        card.programName = code.ticketLayout.text(0, 0, 50, 1);
+        card.membershipNumber = code.ticketLayout.text(1, 1, 16, 1);
+        card.member = JsonLd.newObject("Person");
+        card.member.givenName = code.ticketLayout.text(1, 52, 19 ,1);
+        card.member.familyName = code.ticketLayout.text(2, 52, 19 ,1);
+        card.token = 'aztecbin:' + ByteArray.toBase64(code.rawData);
+        card.validFrom = JsonLd.readQDateTime(code, 'validFrom');
+        card.validUntil = JsonLd.readQDateTime(code, 'validUntil');
+        return card.programName != undefined ? card : undefined;
+    }
 
     // decode 118199 vendor block
-    const block = ticket.block("118199");
+    let res = node.result[0];
+    const block = code.block("118199");
     const json = JSON.parse(block.contentText);
     if (!res.reservationFor.trainNumber)
         res.reservationFor.trainNumber = json["Z"];
@@ -19,15 +29,33 @@
 }
 
 function parsePage(pdf, node, triggerNode) {
-    if (triggerNode.parent.result.length > 2) // not sure this can happen
-        return;
+    const details = pdf.pages[triggerNode.location].textInRect(0, 0.35, 0.65, 
1);
+    let reservations = [];
+    let idx = 0;
+    while (true) {
+        const leg = details.substr(idx).match(/(\S.*\S)  +(\d\d\.\d\d\.\d{4}) 
+(\d\d:\d\d) +(.*)\n *(\S.*\S)  +(\d\d\.\d\d\.\d{4}) +(\d\d:\d\d)/);
+        if (!leg)
+            break;
+        idx += leg.index + leg[0].length;
+
+        let res = JsonLd.newTrainReservation();
+        res.reservationFor.departureStation.name = leg[1];
+        res.reservationFor.departureTime = JsonLd.toDateTime(leg[2] + ' ' + 
leg[3], 'dd.MM.yyyy hh:mm', 'de');
+        res.reservationFor.trainNumber = leg[4];
+        res.reservationFor.arrivalStation.name = leg[5];
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(leg[6] + ' ' + 
leg[7], 'dd.MM.yyyy hh:mm', 'de');
+        res = JsonLd.apply(triggerNode.result[0], res);
+        reservations.push(res);
+    }
+
+    if (reservations.length > 0)
+        return reservations;
 
     // look for non-elided station names
     const text = pdf.pages[triggerNode.location].text;
     const legs = text.match(/VON +-> NACH.*\n.*? ([A-Z].*) +-> (.*?)  .*\n.*? 
([\w\*].*) +-> (.*?)  /);
     if (!legs) { return triggerNode.result; }
 
-    var reservations = [];
     var res = triggerNode.parent.result[0];
     res.reservationFor.departureStation.name = legs[1];
     res.reservationFor.arrivalStation.name = legs[2];
@@ -41,19 +69,3 @@
 
     return reservations;
 }
-
-function parseUic9183(code, node) {
-    // VorteilsCard
-    if (code.ticketLayout && code.ticketLayout.type == "RCT2" && 
code.ticketLayout.text(0, 0, 50, 1).match(/VORTEILSCARD/i)) {
-        var card = JsonLd.newObject("ProgramMembership");
-        card.programName = code.ticketLayout.text(0, 0, 50, 1);
-        card.membershipNumber = code.ticketLayout.text(1, 1, 16, 1);
-        card.member = JsonLd.newObject("Person");
-        card.member.givenName = code.ticketLayout.text(1, 52, 19 ,1);
-        card.member.familyName = code.ticketLayout.text(2, 52, 19 ,1);
-        card.token = 'aztecbin:' + ByteArray.toBase64(code.rawData);
-        card.validFrom = JsonLd.readQDateTime(code, 'validFrom');
-        card.validUntil = JsonLd.readQDateTime(code, 'validUntil');
-        return card.programName != undefined ? card : undefined;
-    }
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/oebb.json 
new/kitinerary-23.08.2/src/lib/scripts/oebb.json
--- old/kitinerary-23.08.1/src/lib/scripts/oebb.json    2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/oebb.json    2023-10-09 
17:36:11.000000000 +0200
@@ -24,18 +24,5 @@
         "function": "parsePage",
         "mimeType": "application/pdf",
         "script": "oebb.js"
-    },
-    {
-        "filter": [
-            {
-                "field": "carrierId",
-                "match": "1181",
-                "mimeType": "internal/uic9183",
-                "scope": "Current"
-            }
-        ],
-        "function": "parseUic9183",
-        "mimeType": "internal/uic9183",
-        "script": "oebb.js"
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/ouigo-es.js 
new/kitinerary-23.08.2/src/lib/scripts/ouigo-es.js
--- old/kitinerary-23.08.1/src/lib/scripts/ouigo-es.js  2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/ouigo-es.js  2023-10-09 
17:36:11.000000000 +0200
@@ -3,20 +3,12 @@
    SPDX-License-Identifier: LGPL-2.0-or-later
 */
 
-// barcode format
-// 13x ticket number
-// 5x ?
-// 5x train number
-// 15x departure time (dd/MM/yyyyhh:mm)
-// 7x departure UIC station code
-// 7x arrival UIC station code
-// 3x coach number
-// 3x seat number
-// more stuff - signature?
+// see 
https://community.kde.org/KDE_PIM/KItinerary/Renfe_Barcodes#Common_Spanish_Ticket_Barcode
 function parseBarcode(content) {
     let res = JsonLd.newTrainReservation();
     res.reservedTicket.ticketToken = 'azteccode:' + content;
     res.reservedTicket.ticketNumber = content.substr(0, 13);
+    res.reservationFor.provider.identifier = 'uic:' + content.substr(13, 5);
     res.reservationFor.trainNumber = content.substr(18, 5);
     res.reservationFor.departureTime = JsonLd.toDateTime(content.substr(23, 
15), 'dd/MM/yyyyhh:mm', 'es');
     res.reservationFor.departureStation.identifier = 'uic:71' + 
content.substr(40, 5);
@@ -25,6 +17,10 @@
     res.reservationFor.arrivalStation.name = content.substr(45, 7);
     res.reservedTicket.ticketedSeat.seatSection = content.substr(52, 3);
     res.reservedTicket.ticketedSeat.seatNumber = content.substr(55, 3);
+
+    if (content.substr(13, 5) == "01071") {
+        res.reservationNumber = content.substr(143, 6);
+    }
     return res;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/pretix.js 
new/kitinerary-23.08.2/src/lib/scripts/pretix.js
--- old/kitinerary-23.08.1/src/lib/scripts/pretix.js    2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/pretix.js    2023-10-09 
17:36:11.000000000 +0200
@@ -15,3 +15,17 @@
     res.reservedTicket.name = content.field['ticket'].value;
     return res;
 }
+
+// only works for unstyled PDFs common for smaller events
+function parsePdf(pdf, node, barcode) {
+    let res = JsonLd.newEventReservation();
+    const text = pdf.pages[barcode.location].textInRect(0.0, 0.0, 1.0, 0.4);
+    const data = text.trim().split(/\n/);
+    res.reservationFor.location.name = data[data.length - 2];
+    res.reservationNumber = data[data.length - 1].match(/(\S+) /)[1];
+    res.reservationFor.startDate = JsonLd.toDateTime(data[data.length - 3], 
['dd.MM.yyyy hh:mm', 'yyyy-MM-dd hh:mm'], 'en');
+    res.underName.name = data[data.length - 4];
+    res.reservationFor.name = data.slice(0, data.length - 5).join(' ');
+    res.reservedTicket.ticketToken = 'qrcode:' + barcode.content;
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/pretix.json 
new/kitinerary-23.08.2/src/lib/scripts/pretix.json
--- old/kitinerary-23.08.1/src/lib/scripts/pretix.json  2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/pretix.json  2023-10-09 
17:36:11.000000000 +0200
@@ -1,13 +1,27 @@
-{
-    "filter": [
-        {
-            "field": "passTypeIdentifier",
-            "match": "pass.eu.pretix.ticket",
-            "mimeType": "application/vnd.apple.pkpass",
-            "scope": "Current"
-        }
-    ],
-    "function": "parsePass",
-    "mimeType": "application/vnd.apple.pkpass",
-    "script": "pretix.js"
-}
+[
+    {
+        "filter": [
+            {
+                "field": "passTypeIdentifier",
+                "match": "pass.eu.pretix.ticket",
+                "mimeType": "application/vnd.apple.pkpass",
+                "scope": "Current"
+            }
+        ],
+        "function": "parsePass",
+        "mimeType": "application/vnd.apple.pkpass",
+        "script": "pretix.js"
+    },
+    {
+        "filter": [
+            {
+                "match": "^[0-9a-z]{32}$",
+                "mimeType": "text/plain",
+                "scope": "Descendants"
+            }
+        ],
+        "function": "parsePdf",
+        "mimeType": "application/pdf",
+        "script": "pretix.js"
+    }
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/qatar-airways.js 
new/kitinerary-23.08.2/src/lib/scripts/qatar-airways.js
--- old/kitinerary-23.08.1/src/lib/scripts/qatar-airways.js     2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/qatar-airways.js     2023-10-09 
17:36:11.000000000 +0200
@@ -9,7 +9,6 @@
     let reservations = [];
     for(let idx = 0;;) {
         const leg = leftCol.substr(idx).match(/([A-Z0-9]{2})(\d{1,4})  +... 
\(([A-Z]{3})\), (.*)\n(.*?)  +\S*, (.*)\n.*\n *... \(([A-Z]{3})\), (.*)\n.*, 
(.*)\n/);
-        console.log(leg);
         if (!leg)
             break;
         idx += leg.index + leg[0].length;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/renfe.js 
new/kitinerary-23.08.2/src/lib/scripts/renfe.js
--- old/kitinerary-23.08.1/src/lib/scripts/renfe.js     2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/renfe.js     2023-10-09 
17:36:11.000000000 +0200
@@ -4,22 +4,9 @@
    SPDX-License-Identifier: LGPL-2.0-or-later
 */
 
+// see 
https://community.kde.org/KDE_PIM/KItinerary/Renfe_Barcodes#Renfe_Barcode
 function parseBarcode(barcode)
 {
-    // barcode content:
-    // 13x ticket number
-    // 5x Renfe departure station id
-    // 5x Renfe arrival station id
-    // 6x departure(?) date: ddMMyy
-    // 5x train number
-    // 3x coach number
-    // 3x seat number
-    // 2x unknown number
-    // 1x unknown number
-    // 6x "localizador" ~ PNR?
-    // ".." (optional)
-    // 5x "CombinadoCercanias" (optional)
-
     if (barcode.trim().length > 56)
         return null;
 
@@ -28,7 +15,8 @@
         barcode = barcode.substr(0, 49);
 
     var res = JsonLd.newTrainReservation();
-    res.reservedTicket.ticketToken = "qrCode:" + barcode;
+    res.reservedTicket.ticketToken = "qrCode:" + barcode.trim();
+    res.reservedTicket.ticketNumber = barcode.substr(0, 13);
     res.reservationFor.trainNumber = barcode.substr(29, 5);
     res.reservedTicket.ticketedSeat.seatSection = barcode.substr(34, 3);
     res.reservedTicket.ticketedSeat.seatNumber = barcode.substr(37, 3);
@@ -42,25 +30,50 @@
     return res;
 }
 
-function parsePdf(pdf, node, triggerNode)
+function parseLeg(text, baseRes)
 {
-    if (!triggerNode.content)
-        return;
+    let res = JsonLd.newTrainReservation();
 
-    var res = parseBarcode(triggerNode.content.trim());
-    var text = pdf.pages[triggerNode.location].text;
-    var dep = text.match(/(?:Salida|Origen:) +(.*?) {2,}([\d\/]+) 
+(\d\d:\d\d)/);
+    const dep = text.match(/(?:Salida|Origen:) +(.*?) {2,}([\d\/]+) 
+(\d\d[:\.]\d\d)/);
     res.reservationFor.departureStation.name = dep[1];
-    res.reservationFor.departureTime = JsonLd.toDateTime(dep[2] + dep[3], 
"dd/MM/yyyyhh:mm", "es");
+    res.reservationFor.departureTime = JsonLd.toDateTime(dep[2] + dep[3], 
["dd/MM/yyyyhh:mm", "dd/MM/yyyyhh.mm"], "es");
 
-    var arr = text.match(/(?:Llegada|Destino:)\s+(.*?) {2,}([\d\/]+) 
+(\d\d:\d\d)\n(?:.* )?([A-Z]+) +(\d+)/);
+    const arr = text.match(/(?:Llegada|Destino:)\s+(.*?) {2,}([\d\/]+) 
+(\d\d[:\.]\d\d)\n(?:.* )?([A-Z]+) +(\d+)/);
     res.reservationFor.arrivalStation.name = arr[1];
-    res.reservationFor.arrivalTime = JsonLd.toDateTime(arr[2] + arr[3], 
"dd/MM/yyyyhh:mm", "es");
+    res.reservationFor.arrivalTime = JsonLd.toDateTime(arr[2] + arr[3], 
["dd/MM/yyyyhh:mm", "dd/MM/yyyyhh.mm"], "es");
     res.reservationFor.trainName = arr[4];
 
+    const train = text.match(/Coche:\s+(\S+)\s+Plaza:\s+(\S+)\s+(\S.*) 
+(\d{1,5})/);
+    if (train) {
+        res.reservedTicket.ticketedSeat.seatSection = train[1];
+        res.reservedTicket.ticketedSeat.seatNumber = train[2];
+        res.reservationFor.trainName = train[3];
+        res.reservationFor.trainNumber = train[4];
+    }
+
+    res.reservationNumber = baseRes.reservationNumber;
+    res.reservationFor.provider = baseRes.reservationFor.provider;
+    res.reservedTicket.ticketToken = baseRes.reservedTicket.ticketToken;
     return res;
 }
 
+function parsePdf(pdf, node, triggerNode)
+{
+    const res = triggerNode.result[0];
+    const page = pdf.pages[triggerNode.location]
+    const text = page.text;
+    if (!text.match(/TRAYECTO/)) {
+        return JsonLd.apply(res, parseLeg(text, res));
+    }
+
+    // multi-leg ticket
+    let res1 = parseLeg(page.textInRect(0.0, 0.0, 0.5, 1.0), res);
+    let res2 = parseLeg(page.textInRect(0.5, 0.0, 1.0, 1.0), res);
+    res1.reservationFor.departureStation = 
JsonLd.apply(res.reservationFor.departureStation, 
res1.reservationFor.departureStation);
+    res2.reservationFor.arrivalStation = 
JsonLd.apply(res.reservationFor.arrivalStation, 
res2.reservationFor.arrivalStation);
+    return [res1, res2];
+}
+
 function parsePkPass(content)
 {
     var res = parseBarcode(content.barcodes[0].message);
@@ -84,3 +97,17 @@
 
     return res;
 }
+
+function parseInternationalPdf(pdf, node, uicNode)
+{
+    const renfeNode = node.findChildNodes({ scope: "Descendants", mimeType: 
"text/plain", match: "^\\d{25}/\\d{2}/\\d{6}:\\d{16}" }).find(n => n.location 
=== uicNode.location);
+
+    let res = JsonLd.apply(renfeNode.result[0], uicNode.result[0]);
+    res.reservationFor.departureStation.identifier = undefined;
+    res.reservationFor.arrivalStation.identifier = undefined;
+    res.reservationNumber = renfeNode.result[0].reservationNumber;
+    let renfeRes = JsonLd.clone(res);
+    renfeRes.reservedTicket.ticketToken = 
renfeNode.result[0].reservedTicket.ticketToken;
+    renfeRes.reservedTicket.name = renfeNode.result[0].reservedTicket.name;
+    return [renfeRes, res];
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/scripts/renfe.json 
new/kitinerary-23.08.2/src/lib/scripts/renfe.json
--- old/kitinerary-23.08.1/src/lib/scripts/renfe.json   2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/renfe.json   2023-10-09 
17:36:11.000000000 +0200
@@ -33,4 +33,17 @@
     "function": "parseBarcode",
     "script": "renfe.js",
     "mimeType": "text/plain"
+},
+{
+    "filter": [
+        {
+            "field": "carrierId",
+            "match": "1071",
+            "mimeType": "internal/uic9183",
+            "scope": "Descendants"
+        }
+    ],
+    "function": "parseInternationalPdf",
+    "script": "renfe.js",
+    "mimeType": "application/pdf"
 }]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-23.08.1/src/lib/scripts/sncf-online-ticket.js 
new/kitinerary-23.08.2/src/lib/scripts/sncf-online-ticket.js
--- old/kitinerary-23.08.1/src/lib/scripts/sncf-online-ticket.js        
2023-09-11 17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/scripts/sncf-online-ticket.js        
2023-10-09 17:36:11.000000000 +0200
@@ -10,8 +10,11 @@
     // TODO mark cancelled trips as such
     for (const trips of [response.cancelledTrips, response.passedTrips, 
response.trips, response.preReservedTrips]) {
         for (const trip of trips) {
-            for (const jny of [trip.trip.tripDetails.inwardJourney, 
trip.trip.tripDetails.outwardJourney]) {
+            for (const jny of [trip.trip.tripDetails.inwardJourney, 
trip.trip.tripDetails.outwardJourney, trip.trip.tripDetails]) {
                 let jnyResult = [];
+                if (!jny || !jny.timeline) {
+                    continue;
+                }
                 for (const step of jny.timeline.steps) {
                     if (!step['train']) {
                         continue;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/sortutil.cpp 
new/kitinerary-23.08.2/src/lib/sortutil.cpp
--- old/kitinerary-23.08.1/src/lib/sortutil.cpp 2023-09-11 17:49:23.000000000 
+0200
+++ new/kitinerary-23.08.2/src/lib/sortutil.cpp 2023-10-09 17:36:11.000000000 
+0200
@@ -186,7 +186,7 @@
         return elem.value<TrainTrip>().departureTime().isValid();
     }
     if (JsonLd::isA<Flight>(elem)) {
-        return elem.value<TrainTrip>().departureTime().isValid();
+        return elem.value<Flight>().departureTime().isValid();
     }
 
     return SortUtil::startDateTime(elem).isValid();
@@ -195,14 +195,14 @@
 bool SortUtil::hasEndTime(const QVariant &elem)
 {
     if (JsonLd::canConvert<Reservation>(elem)) {
-        return 
hasStartTime(JsonLd::convert<Reservation>(elem).reservationFor());
+        return hasEndTime(JsonLd::convert<Reservation>(elem).reservationFor());
     }
     if (JsonLd::isA<TrainTrip>(elem)) {
         return elem.value<TrainTrip>().arrivalTime().isValid();
     }
     if (JsonLd::isA<Flight>(elem)) {
-        return elem.value<TrainTrip>().arrivalTime().isValid();
+        return elem.value<Flight>().arrivalTime().isValid();
     }
 
-    return SortUtil::startDateTime(elem).isValid();
+    return SortUtil::endDateTime(elem).isValid();
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/uic9183/rct2ticket.cpp 
new/kitinerary-23.08.2/src/lib/uic9183/rct2ticket.cpp
--- old/kitinerary-23.08.1/src/lib/uic9183/rct2ticket.cpp       2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/uic9183/rct2ticket.cpp       2023-10-09 
17:36:11.000000000 +0200
@@ -60,6 +60,9 @@
     if (!d.isValid()) {
         d = QDate::fromString(dateStr, QStringLiteral("dd/MM"));
     }
+    if (!d.isValid()) {
+        d = QDate::fromString(dateStr, QStringLiteral("dd-MM"));
+    }
     auto t = QTime::fromString(timeStr, QStringLiteral("hh:mm"));
     if (!t.isValid()) {
         t = QTime::fromString(timeStr, QStringLiteral("hh.mm"));
@@ -203,7 +206,9 @@
 
 QString Rct2Ticket::passengerName() const
 {
-    return d->layout.text(0, 52, 19, 1).trimmed();
+    const auto name = d->layout.text(0, 52, 19, 1).trimmed();
+    // sanity-check if this is a plausible name, e.g. Renfe has random other 
stuff here
+    return std::any_of(name.begin(), name.end(), [](QChar c) { return 
c.isDigit(); }) ? QString() : name;
 }
 
 QDateTime Rct2Ticket::outboundDepartureTime() const
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-23.08.1/src/lib/uic9183/uic9183parser.cpp 
new/kitinerary-23.08.2/src/lib/uic9183/uic9183parser.cpp
--- old/kitinerary-23.08.1/src/lib/uic9183/uic9183parser.cpp    2023-09-11 
17:49:23.000000000 +0200
+++ new/kitinerary-23.08.2/src/lib/uic9183/uic9183parser.cpp    2023-10-09 
17:36:11.000000000 +0200
@@ -126,6 +126,19 @@
     inflateEnd(&stream);
     d->m_payload.truncate(d->m_payload.size() - stream.avail_out);
     //qCDebug(Log) << res <<  d->m_payload << stream.avail_out;
+
+    // workaround for Renfe (1071) having various errors...
+    if (d->m_payload.size() > 600 && 
d->m_payload.startsWith("U_HEAD0100531071") && d->m_payload[54] == 'U' && 
d->m_payload[36] == ' ') {
+        qCWarning(Log) << "Applying Renfe workaround for broken UIC 913.8 
content...";
+        d->m_payload.remove(36, 1); // off by one in U_HEAD
+        const auto idx = d->m_payload.indexOf("U_TLAY00");
+        if (idx < d->m_payload.size() + 400 && 
std::strncmp(d->m_payload.constData() + idx + 12, "RCT2", 4) != 0) {
+            d->m_payload.insert(idx + 7, "1"); // wrong U_TLAY version
+            d->m_payload.replace(idx + 12, 4, "RCT2"); // wrong layout type
+            d->m_payload.remove(idx + 20, 1); // garbage trailing the layout 
type?
+            qCDebug(Log) << d->m_payload;
+        }
+    }
 }
 
 bool Uic9183Parser::isValid() const

Reply via email to