Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kitinerary for openSUSE:Factory 
checked in at 2022-10-15 16:34:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kitinerary (Old)
 and      /work/SRC/openSUSE:Factory/.kitinerary.new.2275 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kitinerary"

Sat Oct 15 16:34:03 2022 rev:54 rq:1010755 version:22.08.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes    2022-09-09 
18:23:46.848309761 +0200
+++ /work/SRC/openSUSE:Factory/.kitinerary.new.2275/kitinerary.changes  
2022-10-15 16:34:39.837750638 +0200
@@ -1,0 +2,8 @@
+Tue Oct 11 14:32:46 UTC 2022 - Christophe Giboudeaux <christo...@krop.fr>
+
+- Update to 22.08.2
+  * New bugfix release
+  * For more details please see:
+  * https://kde.org/announcements/gear/22.08.2/
+
+-------------------------------------------------------------------

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

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

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

Other differences:
------------------
++++++ kitinerary.spec ++++++
--- /var/tmp/diff_new_pack.XXXWjj/_old  2022-10-15 16:34:40.357751888 +0200
+++ /var/tmp/diff_new_pack.XXXWjj/_new  2022-10-15 16:34:40.361751898 +0200
@@ -18,7 +18,7 @@
 
 %bcond_without released
 Name:           kitinerary
-Version:        22.08.1
+Version:        22.08.2
 Release:        0
 Summary:        Data model and extraction system for travel reservations
 License:        LGPL-2.1-or-later


++++++ kitinerary-22.08.1.tar.xz -> kitinerary-22.08.2.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/CMakeLists.txt 
new/kitinerary-22.08.2/CMakeLists.txt
--- old/kitinerary-22.08.1/CMakeLists.txt       2022-09-03 00:41:00.000000000 
+0200
+++ new/kitinerary-22.08.2/CMakeLists.txt       2022-10-10 22:29:22.000000000 
+0200
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: BSD-3-Clause
 
 cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-set(PIM_VERSION "5.21.1")
+set(PIM_VERSION "5.21.2")
 project(KItinerary VERSION ${PIM_VERSION})
 
 set(KF5_MIN_VERSION "5.91.0")
@@ -41,8 +41,8 @@
     find_package(SharedMimeInfo 1.3 REQUIRED)
 endif()
 
-set(KMIME_VERSION "5.21.1")
-set(PIM_PKPASS "5.21.1")
+set(KMIME_VERSION "5.21.2")
+set(PIM_PKPASS "5.21.2")
 
 find_package(KF5Mime ${KMIME_VERSION} CONFIG REQUIRED)
 find_package(KPimPkPass ${PIM_PKPASS} CONFIG REQUIRED)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/autotests/locationutiltest.cpp 
new/kitinerary-22.08.2/autotests/locationutiltest.cpp
--- old/kitinerary-22.08.1/autotests/locationutiltest.cpp       2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/autotests/locationutiltest.cpp       2022-10-10 
22:29:22.000000000 +0200
@@ -42,6 +42,7 @@
         Airport txlAddress;
         txlAddress.setAddress(addr);
         Airport sxfAddress;
+        addr.setAddressLocality(_("BERLIN"));
         sxfAddress.setAddress(addr);
 
         QVERIFY(LocationUtil::isSameLocation(txlAddress, sxfAddress, 
LocationUtil::CityLevel));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/autotests/mergeutiltest.cpp 
new/kitinerary-22.08.2/autotests/mergeutiltest.cpp
--- old/kitinerary-22.08.1/autotests/mergeutiltest.cpp  2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/autotests/mergeutiltest.cpp  2022-10-10 
22:29:22.000000000 +0200
@@ -143,6 +143,11 @@
             {_("DANIEL VRATIL"), {}, {} },
             {_("DANIEL VRATIL"), _("DANIEL"), _("VRATIL") }
         };
+
+        QTest::newRow("transliteration") << QVector<QStringList> {
+            {_("Bj??rn Lastname"), {}, {} },
+            {_("BJAERN LASTNAME"), _("BJAERN"), _("LASTNAME") },
+        };
     }
 
     void testIsSamePerson()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-22.08.1/autotests/scriptenginedata/iata-bcbp-demo.pdf-no-zxing.json
 
new/kitinerary-22.08.2/autotests/scriptenginedata/iata-bcbp-demo.pdf-no-zxing.json
--- 
old/kitinerary-22.08.1/autotests/scriptenginedata/iata-bcbp-demo.pdf-no-zxing.json
  2022-09-03 00:41:00.000000000 +0200
+++ 
new/kitinerary-22.08.2/autotests/scriptenginedata/iata-bcbp-demo.pdf-no-zxing.json
  2022-10-10 22:29:22.000000000 +0200
@@ -20,6 +20,7 @@
                     "text": "              Akademy Airways\n              
Boarding Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, 
Terminal 1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
                 }
             ],
+            "producer": "cairo 1.16.0 (https://cairographics.org)",
             "text": "              Akademy Airways\n              Boarding 
Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, Terminal 
1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
         }
     },
@@ -67,6 +68,7 @@
                     "text": "              Akademy Airways\n              
Boarding Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, 
Terminal 1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
                 }
             ],
+            "producer": "cairo 1.16.0 (https://cairographics.org)",
             "text": "              Akademy Airways\n              Boarding 
Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, Terminal 
1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
         },
         "contextDateTime": "Mon Aug 19 20:23:28 2019 GMT+0200",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-22.08.1/autotests/scriptenginedata/iata-bcbp-demo.pdf.json 
new/kitinerary-22.08.2/autotests/scriptenginedata/iata-bcbp-demo.pdf.json
--- old/kitinerary-22.08.1/autotests/scriptenginedata/iata-bcbp-demo.pdf.json   
2022-09-03 00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/autotests/scriptenginedata/iata-bcbp-demo.pdf.json   
2022-10-10 22:29:22.000000000 +0200
@@ -20,6 +20,7 @@
                     "text": "              Akademy Airways\n              
Boarding Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, 
Terminal 1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
                 }
             ],
+            "producer": "cairo 1.16.0 (https://cairographics.org)",
             "text": "              Akademy Airways\n              Boarding 
Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, Terminal 
1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
         }
     },
@@ -79,6 +80,7 @@
                     "text": "              Akademy Airways\n              
Boarding Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, 
Terminal 1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
                 }
             ],
+            "producer": "cairo 1.16.0 (https://cairographics.org)",
             "text": "              Akademy Airways\n              Boarding 
Pass\nFrom: Vienna International, Terminal 2\nTo: Milano Malpensa, Terminal 
1\nFlight: AK 1996\nGate: A36\nBoarding Time: 15:20\nDeparture Time: 
15:45\nArrival Time: 17:20\nPassenger: Dragon, Dr. Konqi\n"
         },
         "contextDateTime": "Mon Aug 19 20:23:28 2019 GMT+0200",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/po/zh_CN/kitinerary.po 
new/kitinerary-22.08.2/po/zh_CN/kitinerary.po
--- old/kitinerary-22.08.1/po/zh_CN/kitinerary.po       2022-09-06 
02:16:52.000000000 +0200
+++ new/kitinerary-22.08.2/po/zh_CN/kitinerary.po       2022-10-11 
07:04:57.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-03 00:47+0000\n"
-"PO-Revision-Date: 2022-08-20 14:20\n"
+"PO-Revision-Date: 2022-10-02 15:52\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-22.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml 
new/kitinerary-22.08.2/src/cli/org.kde.kitinerary-extractor.appdata.xml
--- old/kitinerary-22.08.1/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2022-09-03 00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2022-10-10 22:29:22.000000000 +0200
@@ -110,9 +110,9 @@
     <binary>kitinerary-extractor</binary>
   </provides>
   <releases>
+    <release version="5.21.2" date="2022-10-13"/>
     <release version="5.21.1" date="2022-09-08"/>
     <release version="5.21.0" date="2022-08-18"/>
     <release version="5.20.3" date="2022-07-07"/>
-    <release version="5.20.2" date="2022-06-09"/>
   </releases>
 </component>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/jsapi/jsonld.cpp 
new/kitinerary-22.08.2/src/lib/jsapi/jsonld.cpp
--- old/kitinerary-22.08.1/src/lib/jsapi/jsonld.cpp     2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/jsapi/jsonld.cpp     2022-10-10 
22:29:22.000000000 +0200
@@ -99,7 +99,10 @@
     const auto dep = newPlace(QStringLiteral("BusStation"));
     const auto arr = newPlace(QStringLiteral("BusStation"));
     const auto person = newObject(QStringLiteral("Person"));
-    const auto ticket = newObject(QStringLiteral("Ticket"));
+    const auto seat = newObject(QStringLiteral("Seat"));
+
+    auto ticket = newObject(QStringLiteral("Ticket"));
+    ticket.setProperty(QStringLiteral("ticketedSeat"), seat);
 
     auto resFor = newObject(QStringLiteral("BusTrip"));
     resFor.setProperty(QStringLiteral("departureBusStop"), dep);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/locationutil.cpp 
new/kitinerary-22.08.2/src/lib/locationutil.cpp
--- old/kitinerary-22.08.1/src/lib/locationutil.cpp     2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/locationutil.cpp     2022-10-10 
22:29:22.000000000 +0200
@@ -6,6 +6,7 @@
 
 #include "locationutil.h"
 #include "locationutil_p.h"
+#include "stringutil.h"
 
 #include <KItinerary/BoatTrip>
 #include <KItinerary/BusTrip>
@@ -189,41 +190,6 @@
     return res;
 }
 
-// keep this ordered (see 
https://en.wikipedia.org/wiki/List_of_Unicode_characters)
-struct {
-    ushort key;
-    const char* replacement;
-} static const transliteration_map[] = {
-    { u'??', "ae" },
-    { u'??', "oe" },
-    { u'??', "oe" },
-    { u'??', "ue" }
-};
-
-static QString applyTransliterations(const QString &s)
-{
-    QString res;
-    res.reserve(s.size());
-
-    for (const auto c : s) {
-        const auto it = std::lower_bound(std::begin(transliteration_map), 
std::end(transliteration_map), c, [](const auto &lhs, const auto rhs) {
-            return QChar(lhs.key) < rhs;
-        });
-        if (it != std::end(transliteration_map) && QChar((*it).key) == c) {
-            res += QString::fromUtf8((*it).replacement);
-            continue;
-        }
-
-        if (c.decompositionTag() == QChar::Canonical) { // see above
-            res += c.decomposition().at(0);
-        } else {
-            res += c;
-        }
-    }
-
-    return res;
-}
-
 static bool compareSpaceCaseInsenstive(const QString &lhs, const QString &rhs)
 {
     auto lit = lhs.begin();
@@ -262,8 +228,8 @@
     // check if any of the Unicode normalization approaches helps
     const auto lhsNormalized = stripDiacritics(lhs);
     const auto rhsNormalized = stripDiacritics(rhs);
-    const auto lhsTransliterated = applyTransliterations(lhs);
-    const auto rhsTransliterated = applyTransliterations(rhs);
+    const auto lhsTransliterated = StringUtil::transliterate(lhs);
+    const auto rhsTransliterated = StringUtil::transliterate(rhs);
     if (compareSpaceCaseInsenstive(lhsNormalized, rhsNormalized) || 
compareSpaceCaseInsenstive(lhsNormalized, rhsTransliterated)
         || compareSpaceCaseInsenstive(lhsTransliterated, rhsNormalized) || 
compareSpaceCaseInsenstive(lhsTransliterated, rhsTransliterated)) {
         return true;
@@ -325,7 +291,7 @@
             break;
         case CityLevel:
             if (!lhsAddr.addressLocality().isEmpty()) {
-                return lhsAddr.addressLocality() == rhsAddr.addressLocality();
+                return isSameLocationName(lhsAddr.addressLocality(), 
rhsAddr.addressLocality(), LocationUtil::Exact);
             }
             break;
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/mergeutil.cpp 
new/kitinerary-22.08.2/src/lib/mergeutil.cpp
--- old/kitinerary-22.08.1/src/lib/mergeutil.cpp        2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/mergeutil.cpp        2022-10-10 
22:29:22.000000000 +0200
@@ -470,8 +470,21 @@
 
 bool MergeUtil::isSamePerson(const Person& lhs, const Person& rhs)
 {
-    return isNameEqualish(lhs.name(), rhs.name()) ||
-        (isNameEqualish(lhs.givenName(), rhs.givenName()) && 
isNameEqualish(lhs.familyName(), rhs.familyName()));
+    if (isNameEqualish(lhs.name(), rhs.name()) ||
+        (isNameEqualish(lhs.givenName(), rhs.givenName()) && 
isNameEqualish(lhs.familyName(), rhs.familyName()))) {
+        return true;
+    }
+
+    const auto lhsNameT = StringUtil::transliterate(lhs.name());
+    const auto lhsGivenNameT = StringUtil::transliterate(lhs.givenName());
+    const auto lhsFamilyNameT = StringUtil::transliterate(lhs.familyName());
+
+    const auto rhsNameT = StringUtil::transliterate(rhs.name());
+    const auto rhsGivenNameT = StringUtil::transliterate(rhs.givenName());
+    const auto rhsFamilyNameT = StringUtil::transliterate(rhs.familyName());
+
+    return isNameEqualish(lhsNameT, rhsNameT) ||
+        (isNameEqualish(lhsGivenNameT, rhsGivenNameT) && 
isNameEqualish(lhsFamilyNameT, rhsFamilyNameT));
 }
 
 static bool isSameEvent(const Event &lhs, const Event &rhs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/pdf/pdfdocument.cpp 
new/kitinerary-22.08.2/src/lib/pdf/pdfdocument.cpp
--- old/kitinerary-22.08.1/src/lib/pdf/pdfdocument.cpp  2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/pdf/pdfdocument.cpp  2022-10-10 
22:29:22.000000000 +0200
@@ -243,6 +243,15 @@
 #endif
 }
 
+QString PdfDocument::producer() const
+{
+    std::unique_ptr<GooString> s(d->m_popplerDoc->getDocInfoProducer());
+    if (!s) {
+        return {};
+    }
+    return QString::fromUtf8(s->c_str());
+}
+
 QVariantList PdfDocument::pagesVariant() const
 {
     QVariantList l;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/pdf/pdfdocument.h 
new/kitinerary-22.08.2/src/lib/pdf/pdfdocument.h
--- old/kitinerary-22.08.1/src/lib/pdf/pdfdocument.h    2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/pdf/pdfdocument.h    2022-10-10 
22:29:22.000000000 +0200
@@ -77,6 +77,7 @@
     Q_PROPERTY(QVariantList pages READ pagesVariant CONSTANT)
     Q_PROPERTY(QDateTime creationTime READ creationTime CONSTANT)
     Q_PROPERTY(QDateTime modificationTime READ modificationTime CONSTANT)
+    Q_PROPERTY(QString producer READ producer CONSTANT)
 
 public:
     explicit PdfDocument(QObject *parent = nullptr);
@@ -99,6 +100,9 @@
     /** Modification time as specified in the PDF file. */
     QDateTime modificationTime() const;
 
+    /** The document producer. */
+    QString producer() const;
+
     /** Creates a PdfDocument from the given raw data.
      *  @returns @c nullptr if loading fails or Poppler was not found.
      */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-22.08.1/src/lib/processors/htmldocumentprocessor.cpp 
new/kitinerary-22.08.2/src/lib/processors/htmldocumentprocessor.cpp
--- old/kitinerary-22.08.1/src/lib/processors/htmldocumentprocessor.cpp 
2022-09-03 00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/processors/htmldocumentprocessor.cpp 
2022-10-10 22:29:22.000000000 +0200
@@ -111,6 +111,9 @@
         }
     }
 
+    // Airbnb applies XML entity encoding...
+    output.replace("&quot;", "\"");
+
     return output;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-22.08.1/src/lib/processors/pdfdocumentprocessor.cpp 
new/kitinerary-22.08.2/src/lib/processors/pdfdocumentprocessor.cpp
--- old/kitinerary-22.08.1/src/lib/processors/pdfdocumentprocessor.cpp  
2022-09-03 00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/processors/pdfdocumentprocessor.cpp  
2022-10-10 22:29:22.000000000 +0200
@@ -35,6 +35,11 @@
 
 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)) {
+        return;
+    }
+
     auto dt = pdf->modificationTime();
     if (!dt.isValid()) {
         dt = pdf->creationTime();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-22.08.1/src/lib/processors/pkpassdocumentprocessor.cpp 
new/kitinerary-22.08.2/src/lib/processors/pkpassdocumentprocessor.cpp
--- old/kitinerary-22.08.1/src/lib/processors/pkpassdocumentprocessor.cpp       
2022-09-03 00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/processors/pkpassdocumentprocessor.cpp       
2022-10-10 22:29:22.000000000 +0200
@@ -126,16 +126,6 @@
 
 static Flight extractBoardingPass(KPkPass::Pass *pass, Flight flight)
 {
-    // "relevantDate" is the best guess for the boarding time
-    if (pass->relevantDate().isValid() && !flight.boardingTime().isValid()) {
-        const auto tz = 
KnowledgeDb::timezoneForAirport(KnowledgeDb::IataCode{flight.departureAirport().iataCode()});
-        if (tz.isValid()) {
-            flight.setBoardingTime(pass->relevantDate().toTimeZone(tz));
-        } else {
-            flight.setBoardingTime(pass->relevantDate());
-        }
-    }
-
     // search for missing information by field key
     const auto fields = pass->fields();
     for (const auto &field : fields) {
@@ -158,7 +148,17 @@
         }
     }
 
-    // search for missing information in field content
+    // "relevantDate" is the best guess for the boarding time if we didn't 
find an explicit field for it
+    if (pass->relevantDate().isValid() && !flight.boardingTime().isValid()) {
+        const auto tz = 
KnowledgeDb::timezoneForAirport(KnowledgeDb::IataCode{flight.departureAirport().iataCode()});
+        if (tz.isValid()) {
+            flight.setBoardingTime(pass->relevantDate().toTimeZone(tz));
+        } else {
+            flight.setBoardingTime(pass->relevantDate());
+        }
+    }
+
+   // search for missing information in field content
     const auto depIata = 
KnowledgeDb::IataCode(flight.departureAirport().iataCode());
     const auto arrIata = 
KnowledgeDb::IataCode(flight.arrivalAirport().iataCode());
     const auto frontFields = frontFieldsForPass(pass);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/blablacar-bus.js 
new/kitinerary-22.08.2/src/lib/scripts/blablacar-bus.js
--- old/kitinerary-22.08.1/src/lib/scripts/blablacar-bus.js     1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-22.08.2/src/lib/scripts/blablacar-bus.js     2022-10-10 
22:29:22.000000000 +0200
@@ -0,0 +1,237 @@
+/*
+   SPDX-FileCopyrightText: 2022 Luca Weiss <l...@z3ntu.xyz>
+
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function main(pdf) {
+    for (var i = 0; i < pdf.pageCount; ++i) {
+        var page = pdf.pages[i];
+        var nextBarcode = null;
+        var images = page.images;
+        for (var j = 0; j < images.length && !nextBarcode; ++j) {
+            nextBarcode = Barcode.decodeQR(images[j]);
+            if (nextBarcode)
+                return decodeBarcode(pdf.text, nextBarcode);
+        }
+    }
+}
+
+function decodeBarcode(text, barcode) {
+    var res = JsonLd.newBusReservation();
+
+    // Time and date is only in the PDF text
+    const times = text.match(/(\d{2}\.\d{2}\.\d{4} \d{2}:\d{2})/g);
+    if (times.length !== 2) {
+        console.log("Failed to extract departure/arrival time from text: " + 
times);
+        return null;
+    }
+
+    res.reservationFor.departureTime = JsonLd.toDateTime(times[0], "dd.MM.yyyy 
hh:mm", "en");
+    res.reservationFor.arrivalTime = JsonLd.toDateTime(times[1], "dd.MM.yyyy 
hh:mm", "en");
+
+    // The rest of the info can be found in the QR code, separated by |
+    const parts = barcode.split("|");
+    if (parts.length != 17) {
+        console.log("Failed to extract info from barcode, got " + parts.length 
+ " parts.");
+        return null;
+    }
+
+    res.reservationId = parts[0];
+    res.reservationFor.busNumber = parts[2];
+
+    const departureStop = stops[parts[3]];
+    res.reservationFor.departureBusStop.identifier = parts[3];
+    res.reservationFor.departureBusStop.name = departureStop.name;
+    res.reservationFor.departureBusStop.geo.latitude = departureStop.coords[0];
+    res.reservationFor.departureBusStop.geo.longitude = 
departureStop.coords[1];
+
+    const arrivalStop = stops[parts[4]];
+    res.reservationFor.arrivalBusStop.identifier = parts[4];
+    res.reservationFor.arrivalBusStop.name = arrivalStop.name;
+    res.reservationFor.arrivalBusStop.geo.latitude = arrivalStop.coords[0];
+    res.reservationFor.arrivalBusStop.geo.longitude = arrivalStop.coords[1];
+
+    res.reservedTicket.ticketedSeat.seatNumber = parts[8];
+
+    res.totalPrice = parts[10];
+    res.underName.familyName = parts[14];
+    res.underName.givenName = parts[15];
+    res.underName.birthDate = parts[16];
+
+    res.reservedTicket.ticketToken = "qrCode:" + barcode;
+
+    return res;
+}
+
+// Converted from GTFS data: 
https://gist.github.com/z3ntu/605c459f1897f0e6ce31325df7bac3d7
+// Last updated: 2022-09-07
+var stops = {
+    "ABY": {name: "Barcelone - Airport Prat T1", coords: [41.28768, 2.0729]},
+    "ACW": {name: "Madrid - Barajas Airport", coords: [40.49233, -3.59385]},
+    "ADB": {name: "Saint-Egr??ve", coords: [45.23688, 5.66302]},
+    "ADC": {name: "Voiron  - Champfeuillet", coords: [45.34994, 5.56775]},
+    "ADU": {name: "Dusseldorf", coords: [51.22288, 6.79555]},
+    "AFR": {name: "Frankfurt - Main bus station", coords: [50.10448, 8.66254]},
+    "AGN": {name: "Agen", coords: [44.16675, 0.60577]},
+    "AKA": {name: "Karlsruhe", coords: [48.99157, 8.40034]},
+    "AMS": {name: "Amsterdam - Schipol Airport", coords: [52.30855, 4.76144]},
+    "ANB": {name: "Antibes", coords: [43.59961, 7.08636]},
+    "ANG": {name: "Angoul??me", coords: [45.65343, 0.16559]},
+    "AOS": {name: "Aosta", coords: [45.73524, 7.32441]},
+    "ARN": {name: "Nantes - Airport", coords: [47.15788, -1.60068]},
+    "ATT": {name: "Stuttgart - Airport", coords: [48.6918, 9.19669]},
+    "AUF": {name: "Auxerre", coords: [47.79662, 3.58448]},
+    "BDL": {name: "Bandol", coords: [43.13934, 5.76632]},
+    "BGM": {name: "Bergamo", coords: [45.69161, 9.6762]},
+    "BIJ": {name: "Biarritz - Airport", coords: [43.47201, -1.53241]},
+    "BLB": {name: "Bilbao", coords: [43.26131, -2.94995]},
+    "BLF": {name: "Belfort", coords: [47.62856, 6.8569]},
+    "BOL": {name: "Bologna", coords: [44.50393, 11.34688]},
+    "BRG": {name: "Bruges", coords: [51.19547, 3.21647]},
+    "BXA": {name: "Brussels - Zaventem Airport", coords: [50.89765, 4.47987]},
+    "CAB": {name: "Cabourg", coords: [49.29431, -0.10655]},
+    "CAG": {name: "Calais - City ??????Hall", coords: [50.95318, 1.85348]},
+    "CAR": {name: "Carcassonne", coords: [43.21254, 2.34552]},
+    "CAT": {name: "Castets", coords: [43.87712, -1.14344]},
+    "CBH": {name: "Capbreton - Hossegor", coords: [43.64113, -1.42571]},
+    "CDG": {name: "Paris - Roissy Charles De Gaulle Airport", coords: 
[49.01089, 2.55893]},
+    "CMX": {name: "Chamonix Sud - Bus station", coords: [45.9173, 6.8668]},
+    "COL": {name: "Colmar", coords: [48.07375, 7.34779]},
+    "CRO": {name: "Crolles - Le Rafour", coords: [45.2696, 5.89323]},
+    "CRX": {name: "Ch??teauroux", coords: [46.84914, 1.70904]},
+    "CSM": {name: "Cavalaire-sur-Mer", coords: [43.1723, 6.52948]},
+    "CVM": {name: "Charleville-M??zi??res", coords: [49.76881, 4.72488]},
+    "DLP": {name: "Marne-la-Vall??e - Chessy", coords: [48.86573, 2.78304]},
+    "DOL": {name: "Deauville", coords: [49.35911, 0.08406]},
+    "EAS": {name: "San Sebasti??n", coords: [43.31713, -1.97738]},
+    "EBU": {name: "Saint-Etienne", coords: [45.44241, 4.40297]},
+    "EPL": {name: "Epinal", coords: [48.17949, 6.44162]},
+    "ESS": {name: "Essen", coords: [51.45025, 7.01491]},
+    "FFA": {name: "Frankfurt - Airport", coords: [50.05276, 8.57753]},
+    "FRZ": {name: "Florence", coords: [43.7552, 11.17221]},
+    "GAN": {name: "Ghent", coords: [51.05317, 3.74067]},
+    "GIA": {name: "Girona", coords: [41.97896, 2.81731]},
+    "GOA": {name: "Genoa - Via Fanti d'Italia", coords: [44.41635, 8.91905]},
+    "GVA": {name: "Geneva - Airport", coords: [46.23012, 6.10928]},
+    "HYP": {name: "Hy??res - Place Louis Versin", coords: [43.11702, 6.13415]},
+    "LDE": {name: "Tarbes", coords: [43.22333, 0.04721]},
+    "LHY": {name: "The Hague", coords: [52.07986, 4.32571]},
+    "LIG": {name: "Li??ge", coords: [50.62407, 5.56857]},
+    "LON": {name: "Lorient Nord", coords: [47.77787, -3.34215]},
+    "LSN": {name: "Lausanne", coords: [46.53611, 6.62374]},
+    "LVD": {name: "Le Lavandou", coords: [43.13799, 6.36434]},
+    "LYS": {name: "Lyon - Saint-Exup??ry Airport", coords: [45.71909, 
5.07953]},
+    "MAA": {name: "Marseille - Marseille Provence Airport", coords: [43.44357, 
5.21993]},
+    "MDS": {name: "Madrid - South Station", coords: [40.39426, -3.67772]},
+    "MHM": {name: "Mannheim", coords: [49.47827, 8.47282]},
+    "MIM": {name: "Mimizan", coords: [44.20189, -1.2294]},
+    "MSY": {name: "Paris South - Massy-Palaiseau", coords: [48.72575, 
2.25682]},
+    "MUZ": {name: "Munich - Bus station", coords: [48.14245, 11.55006]},
+    "NAQ": {name: "Nancy - Centre", coords: [48.69481, 6.19049]},
+    "NCE": {name: "Nice - Airport T2", coords: [43.66023, 7.20485]},
+    "NCY": {name: "Annecy", coords: [45.90167, 6.12129]},
+    "NRB": {name: "Narbonne - Croix Sud", coords: [43.16389, 2.98864]},
+    "ORY": {name: "Paris - Orly  Airport", coords: [48.73063, 2.36406]},
+    "PCO": {name: "Paris - La D??fense (H??tel Pullman)", coords: [48.89494, 
2.23907]},
+    "PDG": {name: "Port Grimaud", coords: [43.27537, 6.57892]},
+    "PDL": {name: "Paris - Pont de Levallois", coords: [48.89774, 2.28125]},
+    "PGF": {name: "Perpignan", coords: [42.69493, 2.87924]},
+    "PGX": {name: "P??rigueux", coords: [45.14311, 0.69851]},
+    "PUF": {name: "Pau - Technop??le H??liparc", coords: [43.319, -0.36215]},
+    "QJZ": {name: "Nantes", coords: [47.24878, -1.52089]},
+    "QKU": {name: "Cologne - Airport", coords: [50.88144, 7.11708]},
+    "QRH": {name: "Rotterdam", coords: [51.92353, 4.46653]},
+    "QUE": {name: "Quimper Est (Le Rouillen)", coords: [47.99919, -4.05204]},
+    "QXB": {name: "Aix-en-Provence", coords: [43.511, 5.44747]},
+    "QXG": {name: "Angers", coords: [47.46476, -0.55773]},
+    "RCF": {name: "Rochefort", coords: [45.96508, -0.95944]},
+    "ROZ": {name: "Rouen - Zenith", coords: [49.39207, 1.05879]},
+    "RYN": {name: "Royan", coords: [45.6261, -1.01637]},
+    "SAR": {name: "Saarbr??cken", coords: [49.24178, 7.00013]},
+    "SET": {name: "S??te", coords: [43.41273, 3.69728]},
+    "SLG": {name: "Sallanches", coords: [45.9355, 6.63627]},
+    "SMX": {name: "Sainte-Maxime", coords: [43.31521, 6.63321]},
+    "STE": {name: "Saintes", coords: [45.75557, -0.65213]},
+    "STG": {name: "Saint-Gaudens", coords: [43.10508, 0.72897]},
+    "TGR": {name: "Torino - Bus station", coords: [45.07017, 7.65781]},
+    "TLN": {name: "Toulon", coords: [43.12779, 5.93075]},
+    "TRR": {name: "Tarragona", coords: [41.11823, 1.24422]},
+    "ULM": {name: "Ulm", coords: [48.42586, 10.01041]},
+    "URT": {name: "Utrecht", coords: [52.0901, 5.10517]},
+    "VNM": {name: "Venice - Mestre", coords: [45.48241, 12.23359]},
+    "VNT": {name: "Venice - Tronchetto", coords: [45.44148, 12.30497]},
+    "XAC": {name: "Arcachon", coords: [44.63695, -1.14297]},
+    "XAG": {name: "Le Cap d'Agde", coords: [43.31692, 3.46623]},
+    "XAM": {name: "Amsterdam City Center - Sloterdijk", coords: [52.38972, 
4.83844]},
+    "XQR": {name: "Quimper", coords: [47.99446, -4.09355]},
+    "XAN": {name: "Antwerp", coords: [51.21446, 4.41559]},
+    "XAS": {name: "Amiens", coords: [49.89709, 2.31149]},
+    "XAV": {name: "Avranches", coords: [48.69015, -1.36967]},
+    "XAX": {name: "Parc Ast??rix", coords: [49.13689, 2.5704]},
+    "XBA": {name: "Bayonne", coords: [43.4976, -1.47902]},
+    "XBC": {name: "Barcelona Nord - Bus Station", coords: [41.39496, 2.18327]},
+    "XBE": {name: "Brive-la-Gaillarde", coords: [45.14591, 1.48273]},
+    "XBI": {name: "Biscarrosse", coords: [44.39366, -1.16464]},
+    "XBN": {name: "Besan??on", coords: [47.2215, 5.97861]},
+    "XBP": {name: "Bordeaux Pessac", coords: [44.80436, -0.63259]},
+    "XBS": {name: "Bourges", coords: [47.06382, 2.36815]},
+    "XBT": {name: "Brest - Train station", coords: [48.38749, -4.48214]},
+    "XCA": {name: "Caen", coords: [49.17643, -0.34764]},
+    "XCD": {name: "Chalon-sur-Sa??ne", coords: [46.78205, 4.84426]},
+    "XCF": {name: "Clermont-Ferrand", coords: [45.77078, 3.0823]},
+    "XCN": {name: "Cannes", coords: [43.57089, 7.01458]},
+    "XCY": {name: "Chamb??ry", coords: [45.56995, 5.91727]},
+    "XDB": {name: "Lille", coords: [50.63863, 3.07649]},
+    "XDE": {name: "Dunkirk", coords: [51.03171, 2.36846]},
+    "XDI": {name: "Dijon", coords: [47.32405, 5.02779]},
+    "XDU": {name: "Marennes-Ol??ron", coords: [45.821, -1.10857]},
+    "XDX": {name: "Dax", coords: [43.72026, -1.04965]},
+    "XER": {name: "Strasbourg", coords: [48.57424, 7.75426]},
+    "XFJ": {name: "Fr??jus - Saint-Rapha??l", coords: [43.43558, 6.73739]},
+    "XGB": {name: "Grenoble - Oxford", coords: [45.20456, 5.70107]},
+    "XGC": {name: "Geneva - Bus station", coords: [46.20838, 6.14674]},
+    "XGE": {name: "Grenoble - Bus Station", coords: [45.19283, 5.71429]},
+    "XGP": {name: "Guingamp", coords: [48.55584, -3.14254]},
+    "XHD": {name: "Hendaye", coords: [43.3511, -1.78328]},
+    "XHF": {name: "Honfleur", coords: [49.41921, 0.23715]},
+    "XIZ": {name: "Reims", coords: [49.21475, 3.99456]},
+    "XLH": {name: "Le Havre", coords: [49.4921, 0.12534]},
+    "XLM": {name: "Milan", coords: [45.48976, 9.12759]},
+    "XLR": {name: "La Rochelle", coords: [46.16214, -1.15389]},
+    "XLS": {name: "Limoges", coords: [45.83615, 1.26848]},
+    "XLT": {name: "Lorient", coords: [47.7554, -3.36413]},
+    "XLU": {name: "Lourdes", coords: [43.10018, -0.04155]},
+    "XLV": {name: "Laval", coords: [48.07594, -0.76056]},
+    "XLZ": {name: "Lannemezan", coords: [43.11434, 0.38781]},
+    "XMK": {name: "Mont??limar", coords: [44.55966, 4.74527]},
+    "XMS": {name: "Le Mans", coords: [48.01741, 0.14943]},
+    "XMT": {name: "Montpellier", coords: [43.58423, 3.86]},
+    "XMW": {name: "Montauban Sud", coords: [43.98177, 1.33166]},
+    "XMX": {name: "Morlaix", coords: [48.57752, -3.83294]},
+    "XMZ": {name: "Metz", coords: [49.11063, 6.18331]},
+    "XNS": {name: "N??mes", coords: [43.8173, 4.36177]},
+    "XNT": {name: "Niort", coords: [46.30755, -0.48562]},
+    "XOP": {name: "Poitiers", coords: [46.58295, 0.33455]},
+    "XOS": {name: "Orl??ans", coords: [47.89613, 1.85401]},
+    "XPB": {name: "Paris City Centre - Bercy Seine", coords: [48.83568, 
2.38016]},
+    "XPD": {name: "Paris - La D??fense (Terminal Jules Verne)", coords: 
[48.89132, 2.24233]},
+    "XRF": {name: "Marseille - St-Charles Bus station", coords: [43.30417, 
5.37986]},
+    "XRN": {name: "Rouen - Rive gauche", coords: [49.43409, 1.09247]},
+    "XSB": {name: "Saint-Brieuc", coords: [48.50682, -2.76644]},
+    "XSD": {name: "Paris Nord - Saint-Denis Universit??", coords: [48.94645, 
2.36457]},
+    "XSJ": {name: "Saint-Jean-de-Luz", coords: [43.38616, -1.66076]},
+    "XTO": {name: "Tours", coords: [47.38349, 0.70217]},
+    "XTS": {name: "Toulouse", coords: [43.61329, 1.45222]},
+    "XVG": {name: "Valence - Centre", coords: [44.92684, 4.89264]},
+    "XVS": {name: "Vannes", coords: [47.66492, -2.75143]},
+    "XXB": {name: "Aix-les-Bains", coords: [45.68834, 5.90957]},
+    "XYL": {name: "Lyon - Perrache Bus Station", coords: [45.74971, 4.82678]},
+    "XZN": {name: "Avignon - Le Pontet", coords: [43.96062, 4.85593]},
+    "XZR": {name: "B??ziers", coords: [43.33657, 3.22058]},
+    "ZDH": {name: "Mulhouse", coords: [47.74218, 7.34187]},
+    "ZEP": {name: "London - Victoria Coach Station", coords: [51.49251, 
-0.14834]},
+    "ZFJ": {name: "Rennes", coords: [48.1041, -1.6699]},
+    "ZFQ": {name: "Bordeaux Saint-Jean - Terres de Borde", coords: [44.82303, 
-0.55452]},
+    "ZYR": {name: "Brussels City Center - Midi Train station", coords: 
[50.83496, 4.33306]},
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-22.08.1/src/lib/scripts/blablacar-bus.json 
new/kitinerary-22.08.2/src/lib/scripts/blablacar-bus.json
--- old/kitinerary-22.08.1/src/lib/scripts/blablacar-bus.json   1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-22.08.2/src/lib/scripts/blablacar-bus.json   2022-10-10 
22:29:22.000000000 +0200
@@ -0,0 +1,18 @@
+{
+    "filter": [
+        {
+            "field": "From",
+            "match": "notificat...@blablacar.com",
+            "mimeType": "message/rfc822",
+            "scope": "Ancestors"
+        },
+        {
+            "match": "^(?:[^\\|]*\\|){16}[^\\|]*$",
+            "mimeType": "text/plain",
+            "scope": "Descendants"
+        }
+    ],
+    "function": "main",
+    "mimeType": "application/pdf",
+    "script": "blablacar-bus.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/eventim.js 
new/kitinerary-22.08.2/src/lib/scripts/eventim.js
--- old/kitinerary-22.08.1/src/lib/scripts/eventim.js   1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-22.08.2/src/lib/scripts/eventim.js   2022-10-10 
22:29:22.000000000 +0200
@@ -0,0 +1,20 @@
+/*
+   SPDX-FileCopyrightText: 2022 Volker Krause <vkra...@kde.org>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parsePkPass(pass, node) {
+    let res = node.result[0];
+    console.log(pass);
+    res.reservationFor.name = pass.logoText;
+    const loc = pass.field['KEY_LOCATION'].value.split('\n');
+    res.reservationFor.location = JsonLd.newObject('Place');
+    res.reservationFor.location.name = loc[0];
+    res.reservedTicket.ticketedSeat = JsonLd.newObject('Seat');
+    res.reservationFor.location.address = JsonLd.newObject('PostalAddress');
+    const addr = loc[1].match(/(.*), (.*?)$/);
+    res.reservationFor.location.address.streetAddress = addr[1];
+    res.reservationFor.location.address.addressLocality = addr[2];
+    res.reservedTicket.ticketedSeat.seatNumber = 
pass.field['KEY_BACK_SEATLINE'].value;
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/eventim.json 
new/kitinerary-22.08.2/src/lib/scripts/eventim.json
--- old/kitinerary-22.08.1/src/lib/scripts/eventim.json 1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-22.08.2/src/lib/scripts/eventim.json 2022-10-10 
22:29:22.000000000 +0200
@@ -0,0 +1,13 @@
+{
+    "filter": [
+        {
+            "field": "passTypeIdentifier",
+            "match": "^pass\\.de\\.eventim\\.",
+            "mimeType": "application/vnd.apple.pkpass",
+            "scope": "Current"
+        }
+    ],
+    "function": "parsePkPass",
+    "mimeType": "application/vnd.apple.pkpass",
+    "script": "eventim.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/extractors.qrc 
new/kitinerary-22.08.2/src/lib/scripts/extractors.qrc
--- old/kitinerary-22.08.1/src/lib/scripts/extractors.qrc       2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/extractors.qrc       2022-10-10 
22:29:22.000000000 +0200
@@ -28,6 +28,8 @@
         <file>availpro.js</file>
         <file>baeder-suite.json</file>
         <file>baeder-suite.js</file>
+        <file>blablacar-bus.json</file>
+        <file>blablacar-bus.js</file>
         <file>booking.json</file>
         <file>booking.js</file>
         <file>bremer-baeder.json</file>
@@ -60,6 +62,8 @@
         <file>eurowings-pkpass.js</file>
         <file>eventbrite.json</file>
         <file>eventbrite.js</file>
+        <file>eventim.json</file>
+        <file>eventim.js</file>
         <file>fcmtravel.json</file>
         <file>fcmtravel.js</file>
         <file>feratel-card.json</file>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/ryanair.js 
new/kitinerary-22.08.2/src/lib/scripts/ryanair.js
--- old/kitinerary-22.08.1/src/lib/scripts/ryanair.js   2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/ryanair.js   2022-10-10 
22:29:22.000000000 +0200
@@ -7,7 +7,7 @@
     var res = triggerNode.result[0];
     const page = pdf.pages[triggerNode.location];
     const timesText = page.textInRect(0.5, 0.5, 1, 1);
-    const times = 
timesText.match(/\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\d\d:\d\d\./);
+    const times = 
timesText.match(/\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)[\s\S]*?\n(\d\d:\d\d)/);
     res.reservationFor.boardingTime = JsonLd.toDateTime(times[2], "hh:mm", 
"en");
     res.reservationFor.departureTime = JsonLd.toDateTime(times[3], "hh:mm", 
"en");
     res.reservationFor.arrivalTime = JsonLd.toDateTime(times[4], "hh:mm", 
"en");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/sncf.js 
new/kitinerary-22.08.2/src/lib/scripts/sncf.js
--- old/kitinerary-22.08.1/src/lib/scripts/sncf.js      2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/sncf.js      2022-10-10 
22:29:22.000000000 +0200
@@ -23,11 +23,11 @@
 
 function parseSncfPdfText(text) {
     var reservations = new Array();
-    var bookingRef = text.match(/(?:DOSSIER VOYAGE|BOOKING FILE 
REFERENCE|REFERENCE NUMBER) ?: +([A-Z0-9]{6})/);
+    var bookingRef = text.match(/(?:DOSSIER VOYAGE|BOOKING FILE 
REFERENCE|REFERENCE NUMBER|REISEREFERENZ) ?: +([A-Z0-9]{6})/);
 
     var pos = 0;
     while (true) {
-        var header = text.substr(pos).match(/ +(?:D??part \/ 
Arriv??e|Departure \/ Arrival).*\n/);
+        var header = text.substr(pos).match(/ +(?:D??part \/ 
Arriv??e|Departure \/ Arrival|Abfahrt \/ Ankunft).*\n/);
         if (!header)
             break;
         var index = header.index + header[0].length;
@@ -35,28 +35,28 @@
         var res = JsonLd.newTrainReservation();
         res.reservationNumber = bookingRef[1];
 
-        var depLine = text.substr(pos + index).match(/\n {2,3}([\w -]+?)  
+(\d{2}\/\d{2}) (?:??|at) (\d{2}[h:]\d{2})/);
+        var depLine = text.substr(pos + index).match(/\n {2,3}([\w -]+?)  
+(\d{2}[\/\.]\d{2}) (?:??|at|um) (\d{2}[h:]\d{2})/);
         if (!depLine)
             break;
         index += depLine.index + depLine[0].length;
         res.reservationFor.departureStation.name = depLine[1];
-        res.reservationFor.departureTime = JsonLd.toDateTime(depLine[2] + " " 
+ depLine[3], ["dd/MM hh'h'mm", "dd/MM hh:mm"], "fr");
+        res.reservationFor.departureTime = JsonLd.toDateTime(depLine[2] + " " 
+ depLine[3], ["dd/MM hh'h'mm", "dd/MM hh:mm", "dd.MM hh:mm"], "fr");
 
-        var arrLine = text.substr(pos + index).match(/\n {2,3}([\w -]+?)  
+(\d{2}\/\d{2}) (?:??|at) (\d{2}[h:]\d{2})/);
+        var arrLine = text.substr(pos + index).match(/\n {2,3}([\w -]+?)  
+(\d{2}[\/\.]\d{2}) (?:??|at|um) (\d{2}[h:]\d{2})/);
         if (!arrLine)
             break;
         index += arrLine.index + arrLine[0].length;
         res.reservationFor.arrivalStation.name = arrLine[1];
-        res.reservationFor.arrivalTime = JsonLd.toDateTime(arrLine[2] + " " + 
arrLine[3], ["dd/MM hh'h'mm", "dd/MM hh:mm"], "fr");
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(arrLine[2] + " " + 
arrLine[3], ["dd/MM hh'h'mm", "dd/MM hh:mm", "dd.MM hh:mm"], "fr");
 
         // parse seat, train number, etc from the text for one leg
         // since the stations are vertically centered, the stuff we are 
looking for might be at different
         // positions relative to them
         var legText = text.substring(pos + header.index + header[0].length, 
pos + index);
-        var trainNumber = legText.match(/TRAIN (?:N??|NUMBER) ?(\d{3,5})/);
+        var trainNumber = legText.match(/(?:TRAIN N??|TRAIN NUMBER|ZUGNUMMER) 
?(\d{3,5})/);
         if (trainNumber)
             res.reservationFor.trainNumber = trainNumber[1];
-        var seatRes = legText.match(/(?:VOITURE|COACH) (\d+) - (?:PLACE|SEAT) 
(\d+)/);
+        var seatRes = legText.match(/(?:VOITURE|COACH|WAGEN) (\d+) - 
(?:PLACE|SEAT|PLATZ) (\d+)/);
         if (seatRes) {
             res.reservedTicket.ticketedSeat.seatSection = seatRes[1];
             res.reservedTicket.ticketedSeat.seatNumber = seatRes[2];
@@ -76,7 +76,7 @@
     var reservations = new Array();
     var text = page.textInRect(0.0, 0.0, 0.5, 1.0);
 
-    var date = text.match(/(\d+ [^ ]+ \d{4})\n/)
+    var date = text.match(/(\d+\.? [^ ]+ \d{4})\n/)
     if (!date)
         return reservations;
     var pos = date.index + date[0].length;
@@ -87,20 +87,20 @@
         pos += dep.index + dep[0].length;
 
         var res = JsonLd.newTrainReservation();
-        res.reservationFor.departureTime = JsonLd.toDateTime(date[1] + dep[1], 
["d MMMM yyyyhh'h'mm", "dd MMMM yyyyhh:mm"], ["fr", "en"]);
+        res.reservationFor.departureTime = JsonLd.toDateTime(date[1] + dep[1], 
["d MMMM yyyyhh'h'mm", "dd MMMM yyyyhh:mm", "dd. MMMM yyyyhh:mm"], ["fr", "en", 
"de"]);
         res.reservationFor.departureStation.name = dep[2];
 
         var arr = text.substr(pos).match(/(\d{2}[h:]\d{2}) +(.*)\n/);
         if (!arr)
             break;
         var endPos = arr.index + arr[0].length;
-        res.reservationFor.arrivalTime = JsonLd.toDateTime(date[1] + arr[1], 
["d MMMM yyyyhh'h'mm", "dd MMMM yyyyhh:mm"], ["fr", "en"]);
+        res.reservationFor.arrivalTime = JsonLd.toDateTime(date[1] + arr[1], 
["d MMMM yyyyhh'h'mm", "dd MMMM yyyyhh:mm", "dd. MMMM yyyyhh:mm"], ["fr", "en", 
"de"]);
         res.reservationFor.arrivalStation.name = arr[2];
 
         var detailsText = text.substr(pos, endPos - arr[0].length);
         var train = detailsText.match(/^ *(.*?) *-/);
         res.reservationFor.trainNumber = train[1];
-        var seat = detailsText.match(/(?:Voiture|Coach) *(\d+) *(?:Place|Seat) 
*(\d+)/);
+        var seat = detailsText.match(/(?:Voiture|Coach|Wagen) *(\d+) 
*(?:Place|Seat|Platz) *(\d+)/);
         if (seat) {
             res.reservedTicket.ticketedSeat.seatSection = seat[1];
             res.reservedTicket.ticketedSeat.seatNumber = seat[2];
@@ -230,7 +230,7 @@
     var reservations = new Array();
     var pos = 0;
     while (true) {
-        var data = 
text.substr(pos).match(/(\d+h\d+)\n(.*)\n(.*)\n(\d+h\d+)\n(.*)\n/);
+        var data = text.substr(pos).match(/ *(\d+h\d+)\n *(.*)\n *(.*)\n(?: 
*Voiture (\d+) - Place (\d+)\n.*\n.*\n)? *(\d+h\d+)\n(.*)\n/);
         if (!data)
             break;
         pos += data.index + data[0].length;
@@ -239,12 +239,14 @@
         leg.reservationFor.departureStation.name = data[2];
         leg.reservationFor.departureDay = res.reservationFor.departureDay;
         leg.reservationFor.departureTime = JsonLd.toDateTime(data[1], 
"hh'h'mm", "fr");
-        leg.reservationFor.arrivalStation.name = data[5];
-        leg.reservationFor.arrivalTime = JsonLd.toDateTime(data[4], "hh'h'mm", 
"fr");
+        leg.reservationFor.arrivalStation.name = data[7];
+        leg.reservationFor.arrivalTime = JsonLd.toDateTime(data[6], "hh'h'mm", 
"fr");
         leg.reservationFor.trainNumber = data[3];
         leg.underName = res.underName;
         leg.reservationNumber = res.reservationNumber;
         leg.reservedTicket = res.reservedTicket;
+        leg.reservedTicket.ticketedSeat.seatSection = data[4];
+        leg.reservedTicket.ticketedSeat.seatNumber = data[5];
         leg.programMembershipUsed = res.programMembershipUsed;
 
         reservations.push(leg);
@@ -513,3 +515,24 @@
     carte.validTo = JsonLd.toDateTime(validity[2] + ' 23:59:59', 'dd/MM/yyyy 
hh:mm:ss', 'fr');
     return carte;
 }
+
+// see 
https://community.kde.org/KDE_PIM/KItinerary/SNCF_Barcodes#SNCF_Normandie_Tickets
+// PDF layout matches that of the "secutix" v2
+function parseSncfNormandie(pdf, node, triggerNode) {
+    let res = JsonLd.newTrainReservation();
+    res.reservedTicket.ticketToken = "aztecbin:" + 
ByteArray.toBase64(triggerNode.content);
+
+    const page = pdf.pages[triggerNode.location];
+    const textRight = page.textInRect(0.5, 0.0, 1.0, 1.0);
+    const pnr = textRight.match(/(.*)\n(.*)\n\d{2}\/\d{2}\/\d{4} 
+(?:PAO|REF)\s*:\s*([A-Z0-9]{6,8})\n/);
+    res.reservationNumber = pnr[3];
+    res.underName.givenName = pnr[2];
+    res.underName.familyName = pnr[1];
+    res.reservedTicket.ticketedSeat.seatingType = textRight.match(/Classe 
(.*)\n/)[1];
+
+    const textLeft = pdf.pages[triggerNode.location].textInRect(0.0, 0.0, 0.5, 
1.0);
+    const date = textLeft.match(/(\d{1,2} \S+ \d{4})/)[1];
+    res.reservationFor.departureDay = JsonLd.toDateTime(date, 'd MMMM yyyy', 
'fr');
+    let reservations = parseSecutixPdfItineraryV2(textLeft, res);
+    return reservations;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/sncf.json 
new/kitinerary-22.08.2/src/lib/scripts/sncf.json
--- old/kitinerary-22.08.1/src/lib/scripts/sncf.json    2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/sncf.json    2022-10-10 
22:29:22.000000000 +0200
@@ -141,5 +141,17 @@
         "function": "parseSncfCartePdf",
         "mimeType": "application/pdf",
         "script": "sncf.js"
+    },
+    {
+        "filter": [
+            {
+                "match": "^\\x01UcP",
+                "mimeType": "application/octet-stream",
+                "scope": "Descendants"
+            }
+        ],
+        "function": "parseSncfNormandie",
+        "mimeType": "application/pdf",
+        "script": "sncf.js"
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/thalys.js 
new/kitinerary-22.08.2/src/lib/scripts/thalys.js
--- old/kitinerary-22.08.1/src/lib/scripts/thalys.js    2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/thalys.js    2022-10-10 
22:29:22.000000000 +0200
@@ -60,15 +60,19 @@
     const page = pdf.pages[triggerNode.location];
     var res = triggerNode.result[0];
 
-    const dep = page.textInRect(0.0, 0.15, 0.35, 
0.3).match(/([\s\S]+)\nD??PART ??\n(\d\d:\d\d)/);
+    // determine departure day - this is in the ERA SSB code, but seems to 
occasionally be off by one day?
+    const depDay = page.text.match(/\d\d\/\d{2}\/\d{4} 
+(\d{2})\/(\d{2})\/(\d{4})/);
+    res.reservationFor.departureDay = depDay[3] + '-' + depDay[2] + '-' + 
depDay[1] + 'T00:00:00';
+
+    const dep = page.textInRect(0.0, 0.15, 0.35, 
0.3).match(/([\s\S]+)\n(?:D??PART ??|ABFAHRT)\n(\d\d:\d\d)/);
     res.reservationFor.departureStation.name = dep[1];
     res.reservationFor.departureTime = 
res.reservationFor.departureDay.substr(0, 11) + dep[2];
 
-    const arr = page.textInRect(0.35, 0.15, 0.65, 
0.3).match(/([\s\S]+)\nARRIV??E ??\n(\d\d:\d\d)/);
+    const arr = page.textInRect(0.35, 0.15, 0.65, 
0.3).match(/([\s\S]+)\n(?:ARRIV??E ??|ANKUNFT)\n(\d\d:\d\d)/);
     res.reservationFor.arrivalStation.name = arr[1];
     res.reservationFor.arrivalTime = res.reservationFor.departureDay.substr(0, 
11) + arr[2];
 
-    const passenger = page.text.match(/PASSAGER\n(.*)\n/);
+    const passenger = page.text.match(/(?:PASSAGER|FAHRGAST)\n(.*)\n/);
     res.underName.name = passenger[1];
 
     return res;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/vueling.js 
new/kitinerary-22.08.2/src/lib/scripts/vueling.js
--- old/kitinerary-22.08.1/src/lib/scripts/vueling.js   2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/vueling.js   2022-10-10 
22:29:22.000000000 +0200
@@ -41,3 +41,13 @@
 
     return reservations;
 }
+
+function parsePdfBoardingPass(pdf, node, triggerNode) {
+    let res = triggerNode.result[0];
+    const page = pdf.pages[triggerNode.location];
+    const topRight = page.textInRect(0.5, 0.0, 1.0, 0.5);
+    const times = topRight.match(/(\d{2}:\d{2}) (?:H|Uhr) +(\d{2}:\d{2}) 
(?:H|Uhr)/);
+    res.reservationFor.departureTime = JsonLd.toDateTime(times[1], "hh:mm", 
"en");
+    res.reservationFor.arrivalTime = JsonLd.toDateTime(times[2], "hh:mm", 
"en");
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/scripts/vueling.json 
new/kitinerary-22.08.2/src/lib/scripts/vueling.json
--- old/kitinerary-22.08.1/src/lib/scripts/vueling.json 2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/scripts/vueling.json 2022-10-10 
22:29:22.000000000 +0200
@@ -1,8 +1,28 @@
 [
     {
+        "filter": [
+            {
+                "field": "From",
+                "match": "@vueling.com",
+                "mimeType": "message/rfc822",
+                "scope": "Ancestors"
+            }
+        ],
+        "function": "parseHtmlBooking",
         "mimeType": "text/html",
-        "filter": [ { "field": "From", "match": "@vueling.com", "mimeType": 
"message/rfc822", "scope": "Ancestors" } ],
-        "script": "vueling.js",
-        "function": "parseHtmlBooking"
+        "script": "vueling.js"
+    },
+    {
+        "filter": [
+            {
+                "field": "operatingCarrierDesignator",
+                "match": "VY",
+                "mimeType": "internal/iata-bcbp",
+                "scope": "Descendants"
+            }
+        ],
+        "function": "parsePdfBoardingPass",
+        "mimeType": "application/pdf",
+        "script": "vueling.js"
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/stringutil.cpp 
new/kitinerary-22.08.2/src/lib/stringutil.cpp
--- old/kitinerary-22.08.1/src/lib/stringutil.cpp       2022-09-03 
00:41:00.000000000 +0200
+++ new/kitinerary-22.08.2/src/lib/stringutil.cpp       2022-10-10 
22:29:22.000000000 +0200
@@ -118,3 +118,38 @@
 {
     return KCharsets::resolveEntities(s).simplified();
 }
+
+// keep this ordered (see 
https://en.wikipedia.org/wiki/List_of_Unicode_characters)
+struct {
+    ushort key;
+    const char* replacement;
+} static const transliteration_map[] = {
+    { u'??', "ae" },
+    { u'??', "oe" },
+    { u'??', "oe" },
+    { u'??', "ue" }
+};
+
+QString StringUtil::transliterate(QStringView s)
+{
+    QString res;
+    res.reserve(s.size());
+
+    for (const auto c : s) {
+        const auto it = std::lower_bound(std::begin(transliteration_map), 
std::end(transliteration_map), c, [](const auto &lhs, const auto rhs) {
+            return QChar(lhs.key) < rhs;
+        });
+        if (it != std::end(transliteration_map) && QChar((*it).key) == c) {
+            res += QString::fromUtf8((*it).replacement);
+            continue;
+        }
+
+        if (c.decompositionTag() == QChar::Canonical) { // see above
+            res += c.decomposition().at(0);
+        } else {
+            res += c;
+        }
+    }
+
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-22.08.1/src/lib/stringutil.h 
new/kitinerary-22.08.2/src/lib/stringutil.h
--- old/kitinerary-22.08.1/src/lib/stringutil.h 2022-09-03 00:41:00.000000000 
+0200
+++ new/kitinerary-22.08.2/src/lib/stringutil.h 2022-10-10 22:29:22.000000000 
+0200
@@ -30,6 +30,9 @@
 
     /** Cleans up extra white spaces and XML entities from @p s. */
     QString clean(const QString &s);
+
+    /** Transliterate diacritics or other special characters. */
+    QString transliterate(QStringView s);
 }
 
 }

Reply via email to