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(""", "\""); + 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); } }