Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kpkpass for openSUSE:Factory checked 
in at 2026-08-24 12:03:45
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kpkpass (Old)
 and      /work/SRC/openSUSE:Factory/.kpkpass.new.1258 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kpkpass"

Mon Aug 24 12:03:45 2026 rev:96 rq:1372457 version:26.08.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/kpkpass/kpkpass.changes  2026-07-02 
22:07:33.840955360 +0200
+++ /work/SRC/openSUSE:Factory/.kpkpass.new.1258/kpkpass.changes        
2026-08-24 12:06:11.042327307 +0200
@@ -1,0 +2,40 @@
+Sun Aug 16 07:27:36 UTC 2026 - Christophe Marin <[email protected]>
+
+- Update to 26.08.0
+  * New feature release
+  * For more details please see:
+  * https://kde.org/announcements/gear/26.08.0/
+- No code change since 26.07.90
+
+-------------------------------------------------------------------
+Sat Aug  1 11:39:17 UTC 2026 - Christophe Marin <[email protected]>
+
+- Update to 26.07.90
+  * New feature release
+- No code change since 26.07.80
+
+-------------------------------------------------------------------
+Sat Jul 18 11:11:59 UTC 2026 - Christophe Marin <[email protected]>
+
+- Update to 26.07.80
+  * New feature release
+- Changes since 26.04.3:
+  * Add additional API for semantic tag seat information
+  * Add basic support for semantic tag seat information
+  * Also print ZIP decoding error when we fail to open a pkpass
+  * Expose semantic tag API to QML as well
+  * Expose translation catalog lookup in public API
+  * Add method for checking whether a field value contains rich text markup
+  * Support date-only/time-only field formats
+  * Fix organization and description fields not being translated
+  * Install Qt metatypes file
+  * Support multi-row auxiliary fields
+  * Add properties for checking whether a specific color is set
+  * Support rgba() color values
+  * Find image assets only existing in a higher pixel ratio variant as well
+  * Add support for semantic tags and new pass styles
+  * CMAKE_CXX_SCAN_FOR_MODULES is already defined in ecm
+  * Move CMakePresets.json.license to REUSE.toml
+  * Use prek support
+
+-------------------------------------------------------------------

Old:
----
  kpkpass-26.04.3.tar.xz
  kpkpass-26.04.3.tar.xz.sig

New:
----
  kpkpass-26.08.0.tar.xz
  kpkpass-26.08.0.tar.xz.sig

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

Other differences:
------------------
++++++ kpkpass.spec ++++++
--- /var/tmp/diff_new_pack.AgKKAP/_old  2026-08-24 12:06:12.195368366 +0200
+++ /var/tmp/diff_new_pack.AgKKAP/_new  2026-08-24 12:06:12.197368438 +0200
@@ -16,12 +16,12 @@
 #
 
 
-%define kf6_version 6.19.0
+%define kf6_version 6.27.0
 %define qt6_version 6.9.0
 
 %bcond_without released
 Name:           kpkpass
-Version:        26.04.3
+Version:        26.08.0
 Release:        0
 Summary:        Library to parse Passbook files
 License:        LGPL-2.1-or-later
@@ -105,4 +105,5 @@
 %{_includedir}/KPim6/KPkPass/
 %{_kf6_cmakedir}/KPim6PkPass/
 %{_kf6_libdir}/libKPim6PkPass.so
+%{_qt6_metatypesdir}/qt6kpim6pkpass_metatypes.json
 


++++++ kpkpass-26.04.3.tar.xz -> kpkpass-26.08.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/.pre-commit-config.yaml 
new/kpkpass-26.08.0/.pre-commit-config.yaml
--- old/kpkpass-26.04.3/.pre-commit-config.yaml 2026-06-05 07:05:26.000000000 
+0200
+++ new/kpkpass-26.08.0/.pre-commit-config.yaml 2026-08-02 07:45:33.000000000 
+0200
@@ -22,7 +22,7 @@
     - id: check-executables-have-shebangs
     - id: check-toml
  - repo: https://github.com/pre-commit/mirrors-clang-format
-   rev: v21.1.8
+   rev: v22.1.5
    hooks:
     - id: clang-format
     # force using this project's configuration rather
@@ -30,14 +30,14 @@
       args: ["--style=file:.clang-format-pre-commit"]
  - repo: https://github.com/astral-sh/ruff-pre-commit
   # Ruff version.
-   rev: v0.15.0
+   rev: v0.15.12
    hooks:
     # Run the linter.
     - id: ruff
     # Run the formatter.
     - id: ruff-format
  - repo: https://github.com/codespell-project/codespell
-   rev: v2.4.1
+   rev: v2.4.2
    hooks:
     - id: codespell
       exclude: 
(textemoticons/core/data/emoji.json|textgrammarcheck/grammalecte/autotests/data/result1.json|texttranslator/translator/plugins/bergamot/autotests/data/modellanguages/models.json|textgrammarcheck/languagetool/autotests/data/test2.json|textautocorrection/core/autotests/data/custom-fr-ref.xml|textgrammarcheck/common/autotests/grammarresultutiltest.cpp|textautocorrection/core/autotests/data/custom-fr.xml)
@@ -46,11 +46,11 @@
    hooks:
     - id: gersemi
  - repo: https://github.com/DavidAnson/markdownlint-cli2
-   rev: v0.20.0
+   rev: v0.22.1
    hooks:
     - id: markdownlint-cli2
       files: \.(md|mdown|markdown)$
  - repo: https://github.com/scop/pre-commit-shfmt
-   rev: v3.12.0-2
+   rev: v3.13.1-1
    hooks:
     - id: shfmt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/CMakeLists.txt 
new/kpkpass-26.08.0/CMakeLists.txt
--- old/kpkpass-26.04.3/CMakeLists.txt  2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/CMakeLists.txt  2026-08-02 07:45:33.000000000 +0200
@@ -2,15 +2,14 @@
 # SPDX-FileCopyrightText: 2018-2026 Laurent Montel <[email protected]>
 # SPDX-License-Identifier: BSD-3-Clause
 
-cmake_minimum_required(VERSION 3.27)
-set(PIM_VERSION "6.7.3")
+cmake_minimum_required(VERSION 3.29)
+set(PIM_VERSION "6.8.0")
 project(KPkPass VERSION ${PIM_VERSION})
 
 set(KF_MIN_VERSION "6.13.0")
 set(QT_REQUIRED_VERSION "6.9.0")
 find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
 set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
-set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
 
 include(KDECompilerSettings NO_POLICY_SCOPE)
 include(KDEInstallDirs)
@@ -35,6 +34,9 @@
 
 if(GIT_SOURCE_TARBALL)
     set(PRE_COMMIT_INSTALLED FALSE)
+    include(KDEGitCommitHooks)
+    include(KDEClangFormat)
+    kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
     if(${CMAKE_SOURCE_DIR} STREQUAL ${PROJECT_SOURCE_DIR})
         if(EXISTS "${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit")
             file(READ "${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit" 
FILE_CONTENTS)
@@ -49,35 +51,38 @@
         endif()
     endif()
     if(NOT ${PRE_COMMIT_INSTALLED})
-        find_program(PRE_COMMIT_EXECUTABLE pre-commit)
-        if(PRE_COMMIT_EXECUTABLE)
+        function(install_hook executable hook_name)
             message(
                 STATUS
-                "This module has pre-commit support. pre-commit binary was 
found. Installing pre-commit hook"
+                "This module has ${hook_name} support. ${hook_name} binary was 
found. Installing ${hook_name} hook"
             )
             execute_process(
                 COMMAND
-                    "${PRE_COMMIT_EXECUTABLE}" install -f
+                    "${executable}" install -f
                 ERROR_QUIET
                 RESULT_VARIABLE _precommitresult
                 WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
             )
             if(_precommitresult EQUAL 0)
-                message(STATUS "Pre-commit installed.")
+                message(STATUS "${hook_name} installed.")
             else()
                 message(
                     WARNING
-                    "Error during installing pre-commit hook: 
${PRE_COMMIT_EXECUTABLE} project source dir ${PROJECT_SOURCE_DIR}"
+                    "Error during installing ${hook_name} hook: ${executable} 
project source dir ${PROJECT_SOURCE_DIR}"
                 )
-                include(KDEGitCommitHooks)
-                include(KDEClangFormat)
-                kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
             endif()
+        endfunction()
+
+        find_program(PREK_EXECUTABLE prek)
+        if(PREK_EXECUTABLE)
+            install_hook(${PREK_EXECUTABLE} "Prek")
         else()
-            message(WARNING "This module has pre-commit support. pre-commit 
binary was not found.")
-            include(KDEGitCommitHooks)
-            include(KDEClangFormat)
-            kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
+            find_program(PRE_COMMIT_EXECUTABLE pre-commit)
+            if(PRE_COMMIT_EXECUTABLE)
+                install_hook(${PRE_COMMIT_EXECUTABLE} "pre-commit")
+            else()
+                message(WARNING "This module has pre-commit support. 
pre-commit binary was not found.")
+            endif()
         endif()
     endif()
 endif()
@@ -119,7 +124,7 @@
     # see e.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56556 or 
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79328
     string(APPEND CMAKE_CXX_FLAGS " -Wshadow")
 endif()
-ecm_set_disabled_deprecation_versions(QT 6.11.0 KF 6.23.0)
+ecm_set_disabled_deprecation_versions(QT 6.11.0 KF 6.28.0)
 
 option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile 
time)" OFF)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/CMakePresets.json.license 
new/kpkpass-26.08.0/CMakePresets.json.license
--- old/kpkpass-26.04.3/CMakePresets.json.license       2026-06-05 
07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/CMakePresets.json.license       1970-01-01 
01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-# SPDX-FileCopyrightText: 2021-2026 Laurent Montel <[email protected]>
-# SPDX-License-Identifier: BSD-3-Clause
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/REUSE.toml 
new/kpkpass-26.08.0/REUSE.toml
--- old/kpkpass-26.04.3/REUSE.toml      2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/REUSE.toml      2026-08-02 07:45:33.000000000 +0200
@@ -8,3 +8,9 @@
 precedence = "aggregate"
 SPDX-FileCopyrightText = "none"
 SPDX-License-Identifier = "CC0-1.0"
+
+[[annotations]]
+path = ["CMakePresets.json"]
+precedence = "aggregate"
+SPDX-FileCopyrightText = "2021-2026 Laurent Montel <[email protected]>"
+SPDX-License-Identifier = "BSD-3-Clause"
Binary files 
old/kpkpass-26.04.3/autotests/data/apple-store-UA-sample-unsigned-scrubbed.pkpass
 and 
new/kpkpass-26.08.0/autotests/data/apple-store-UA-sample-unsigned-scrubbed.pkpass
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kpkpass-26.04.3/autotests/data/apple-store-UA-sample-unsigned-scrubbed.pkpass.license
 
new/kpkpass-26.08.0/autotests/data/apple-store-UA-sample-unsigned-scrubbed.pkpass.license
--- 
old/kpkpass-26.04.3/autotests/data/apple-store-UA-sample-unsigned-scrubbed.pkpass.license
   1970-01-01 01:00:00.000000000 +0100
+++ 
new/kpkpass-26.08.0/autotests/data/apple-store-UA-sample-unsigned-scrubbed.pkpass.license
   2026-08-02 07:45:33.000000000 +0200
@@ -0,0 +1,2 @@
+SPDX-FileCopyrightText: none
+SPDX-License-Identifier: CC0-1.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/autotests/fieldtest.cpp 
new/kpkpass-26.08.0/autotests/fieldtest.cpp
--- old/kpkpass-26.04.3/autotests/fieldtest.cpp 2026-06-05 07:05:26.000000000 
+0200
+++ new/kpkpass-26.08.0/autotests/fieldtest.cpp 2026-08-02 07:45:33.000000000 
+0200
@@ -45,26 +45,77 @@
         KPkPass::Field f(obj, pass.get());
         QCOMPARE(f.value().userType(), QMetaType::QDateTime);
         QCOMPARE(f.value(), QDateTime({2021, 6, 27}, {14, 30}, 
QTimeZone::fromSecondsAheadOfUtc(7200)));
-        QCOMPARE(f.valueDisplayString(), QLatin1StringView("27/06/2021 
14:30"));
-
-        obj = 
QJsonDocument::fromJson(R"({"key":"valid-date","label":"Datum","dateStyle":"PKDateStyleShort","value":"2021-06-27T00:00:00+02:00"})").object();
-        f = KPkPass::Field(obj, pass.get());
-        QCOMPARE(f.value().userType(), QMetaType::QDateTime);
-        QCOMPARE(f.value().toDateTime().date(), QDate({2021, 6, 27}));
-        QCOMPARE(f.valueDisplayString(), QLatin1StringView("27/06/2021"));
 
         obj = 
QJsonDocument::fromJson(R"({"key":"valid-locations","label":"Ort","value":"Freibad
 Killesberg\n"})").object();
         f = KPkPass::Field(obj, pass.get());
-
         QCOMPARE(f.value().userType(), QMetaType::QString);
         QCOMPARE(f.value(), QLatin1StringView("Freibad Killesberg\n"));
         QCOMPARE(f.valueDisplayString(), QLatin1StringView("Freibad 
Killesberg"));
+        QCOMPARE(f.row(), 0);
 
         obj = 
QJsonDocument::fromJson(R"({"key":"booking-number","label":"Buchungsnummer","value":1234567894})").object();
         f = KPkPass::Field(obj, pass.get());
         QCOMPARE(f.value().userType(), QMetaType::Double);
         QCOMPARE(f.valueDisplayString(), "1234567894"_L1);
     }
+
+    static void testDateTimeFormat_data()
+    {
+        QTest::addColumn<QByteArray>("field");
+        QTest::addColumn<QString>("output");
+
+        QTest::newRow("implicit-short-date-only")
+            << 
R"({"key":"valid-date","label":"Datum","dateStyle":"PKDateStyleShort","value":"2021-06-27T00:00:00+02:00"})"_ba
 << u"27/06/2021"_s;
+        QTest::newRow("explicit-short-date-only")
+            << 
R"({"key":"valid-date","label":"Datum","dateStyle":"PKDateStyleShort","value":"2021-06-27T12:35:00+02:00","timeStyle":"PKDateStyleNone"})"_ba
+            << u"27/06/2021"_s;
+        QTest::newRow("implicit-time-only") << 
R"({"key":"valid-date","label":"Datum","timeStyle":"PKDateStyleShort","value":"2021-06-27T12:34:00+02:00"})"_ba
+                                            << u"12:34"_s;
+        QTest::newRow("explicit-time-only")
+            << 
R"({"key":"valid-date","label":"Datum","dateStyle":"PKDateStyleNone","timeStyle":"PKDateStyleShort","value":"2021-06-27T12:34:00+02:00"})"_ba
+            << u"12:34"_s;
+        QTest::newRow("implicit-all") << 
R"({"key":"valid-date","label":"Datum","value":"2021-06-27T12:34:00+02:00"})"_ba
 << u"27/06/2021 12:34"_s;
+        QTest::newRow("explicit-long-all")
+            << 
R"({"key":"valid-date","label":"Datum","value":"2021-06-27T12:34:00+02:00","dateStyle":"PKDateStyleFull","timeStyle":"PKDateStyleLong"})"_ba
+            << u"dimanche 27 juin 2021 12:34:00 UTC+02:00"_s;
+    }
+
+    static void testDateTimeFormat()
+    {
+        QFETCH(QByteArray, field);
+        QFETCH(QString, output);
+
+        std::unique_ptr<KPkPass::Pass> 
pass(KPkPass::Pass::fromFile(QStringLiteral(SOURCE_DIR 
"/data/boardingpass-v1.pkpass")));
+        QVERIFY(pass);
+        const auto obj = QJsonDocument::fromJson(field).object();
+        const auto f = KPkPass::Field(obj, pass.get());
+        QCOMPARE(f.value().userType(), QMetaType::QDateTime);
+        QCOMPARE(f.valueDisplayString(), output);
+    }
+
+    static void testIsRichText_data()
+    {
+        QTest::addColumn<QString>("value");
+        QTest::addColumn<bool>("output");
+
+        QTest::newRow("empty") << QString() << false;
+        QTest::newRow("plain") << u"Frankfurt (Main) Hbf"_s << false;
+        QTest::newRow("link") << u"<a 
href=\"https://maps.apple.com/?q=Frankfurt+%28Main%29+Hbf&ll=50.107149%2C8.663785\";>Frankfurt
 (Main) Hbf</a>"_s << true;
+        QTest::newRow("format") << u"bla <b>bla</b> bla"_s << true;
+        QTest::newRow("entity") << u"Via: 
&lt;1080&gt;(L*EF*FD/SDL*WOB*H*GOE*KS)"_s << true;
+    }
+
+    static void testIsRichText()
+    {
+        QFETCH(QString, value);
+        QFETCH(bool, output);
+
+        std::unique_ptr<KPkPass::Pass> 
pass(KPkPass::Pass::fromFile(QStringLiteral(SOURCE_DIR 
"/data/boardingpass-v1.pkpass")));
+        QVERIFY(pass);
+        const auto obj = QJsonObject{{"value"_L1, value}};
+        const auto f = KPkPass::Field(obj, pass.get());
+        QCOMPARE(f.isRichText(), output);
+    }
 };
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/autotests/pkpasstest.cpp 
new/kpkpass-26.08.0/autotests/pkpasstest.cpp
--- old/kpkpass-26.04.3/autotests/pkpasstest.cpp        2026-06-05 
07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/autotests/pkpasstest.cpp        2026-08-02 
07:45:33.000000000 +0200
@@ -7,13 +7,17 @@
 #include "barcode.h"
 #include "boardingpass.h"
 #include "location.h"
+#include "seat.h"
 
+#include <QJsonObject>
 #include <QLocale>
 #include <QTest>
 #include <QTimeZone>
 
 #include <cmath>
 
+using namespace Qt::Literals;
+
 class PkPassTest : public QObject
 {
     Q_OBJECT
@@ -30,11 +34,15 @@
 
         QCOMPARE(pass->type(), KPkPass::Pass::BoardingPass);
         QCOMPARE(pass->serialNumber(), QLatin1StringView("1234"));
-        QCOMPARE(pass->description(), QLatin1StringView("description"));
+        QCOMPARE(pass->description(), "KDE Bordkarte"_L1);
+        QCOMPARE(pass->lookupMessage(u"description"_s), "KDE Bordkarte"_L1);
         QCOMPARE(pass->organizationName(), QLatin1StringView("KDE"));
 
         QCOMPARE(pass->logoText(), QLatin1StringView("Boarding Pass"));
+        QVERIFY(pass->hasBackgroundColor());
         QCOMPARE(pass->backgroundColor(), QColor(61, 174, 233));
+        QVERIFY(pass->hasForegroundColor());
+        QVERIFY(pass->hasLabelColor());
         QCOMPARE(pass->relevantDate(), QDateTime(QDate(2017, 9, 17), QTime(0, 
4, 0), QTimeZone::UTC));
         QCOMPARE(pass->expirationDate(), QDateTime(QDate(2017, 9, 18), 
QTime(0, 0, 36), QTimeZone::UTC));
         QCOMPARE(pass->isVoided(), false);
@@ -55,10 +63,16 @@
         QCOMPARE(field.value().toString(), QLatin1StringView("20:25"));
         QCOMPARE(field.textAlignment(), Qt::AlignLeft);
 
+        QCOMPARE(pass->auxiliaryFieldsRowCount(), 1);
+        QCOMPARE(pass->auxiliaryFields().size(), 4);
+        QCOMPARE(pass->auxiliaryFieldsInRow(0).size(), 4);
+        QCOMPARE(pass->auxiliaryFieldsInRow(1).size(), 0);
+
         auto boardingPass = dynamic_cast<KPkPass::BoardingPass *>(pass.get());
         QVERIFY(boardingPass);
         QCOMPARE(boardingPass->transitType(), KPkPass::BoardingPass::Air);
 
+        QVERIFY(pass->hasBarcode());
         auto barcodes = pass->barcodes();
         QCOMPARE(barcodes.size(), 1);
         auto bc = barcodes.at(0);
@@ -88,10 +102,41 @@
         img = pass->image(QStringLiteral("I don't exist"));
         QVERIFY(img.isNull());
 
+        QVERIFY(pass->preferredStyleSchemes().isEmpty());
+        QCOMPARE(pass->seats().size(), 0);
+
         auto sourceFile = QFile(QStringLiteral(SOURCE_DIR 
"/data/boardingpass-v1.pkpass"));
         QVERIFY(sourceFile.open(QFile::ReadOnly));
         QCOMPARE(pass->rawData(), sourceFile.readAll());
     }
+
+    static void testSemanticTags()
+    {
+        std::unique_ptr<KPkPass::Pass> pass(KPkPass::Pass::fromFile(u"" 
SOURCE_DIR "/data/apple-store-UA-sample-unsigned-scrubbed.pkpass"_s));
+        QVERIFY(pass);
+        const auto semanticTags = pass->semanticTags();
+        QVERIFY(!semanticTags.isEmpty());
+        QCOMPARE(semanticTags.value("departureAirportName"_L1).toString(), 
"O'Hare International Airport"_L1);
+
+        QCOMPARE(pass->rawValue("changeSeatURL"_L1).toString(), 
"https://www.united.com/en/us/fly/travel/trip-planning/seat-options-and-upgrades.html"_L1);
+
+        QVERIFY(pass->hasBarcode());
+        QCOMPARE(pass->barcodes().size(), 1);
+        QCOMPARE(pass->barcodes().front().format(), KPkPass::Barcode::Aztec);
+
+        const auto styles = pass->preferredStyleSchemes();
+        QCOMPARE(styles.size(), 2);
+        QCOMPARE(styles.front(), "semanticBoardingPass"_L1);
+
+        const auto seats = pass->seats();
+        QCOMPARE(seats.size(), 1);
+        const auto &seat = seats[0];
+        QCOMPARE(seat.seatRow(), "52"_L1);
+        QCOMPARE(seat.hasSeatNumber(), true);
+        QCOMPARE(seat.seatNumber(), "C"_L1);
+        QCOMPARE(seat.hasSeatSection(), false);
+        QCOMPARE(seat.asAirplaneSeat(), "52C"_L1);
+    }
 };
 
 QTEST_GUILESS_MAIN(PkPassTest)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/CMakeLists.txt 
new/kpkpass-26.08.0/src/CMakeLists.txt
--- old/kpkpass-26.04.3/src/CMakeLists.txt      2026-06-05 07:05:26.000000000 
+0200
+++ new/kpkpass-26.08.0/src/CMakeLists.txt      2026-08-02 07:45:33.000000000 
+0200
@@ -6,7 +6,9 @@
 add_library(KPim6PkPass)
 add_library(KPim6::PkPass ALIAS KPim6PkPass)
 
-qt_extract_metatypes(KPim6PkPass)
+qt_extract_metatypes(KPim6PkPass OUTPUT_FILES METATYPES_FILE)
+get_filename_component(METATYPES_FILE_NAME ${METATYPES_FILE} NAME)
+target_sources(KPim6PkPass INTERFACE 
$<INSTALL_INTERFACE:${KDE_INSTALL_QTMETATYPESDIR}/${METATYPES_FILE_NAME}>)
 
 target_sources(
     KPim6PkPass
@@ -19,6 +21,7 @@
         pass.h
         pass_p.h
         passes.cpp
+        seat.cpp
         location.h
         field.h
         boardingpass.h
@@ -69,6 +72,7 @@
         Location
         Pass
         Passes
+        Seat
     REQUIRED_HEADERS KPkPass_HEADERS
 )
 
@@ -80,6 +84,9 @@
         ${CMAKE_BINARY_DIR}/src/kpkpass_version.h
     DESTINATION ${KDE_INSTALL_INCLUDEDIR_PIM}/KPkPass
 )
+if(KDE_INSTALL_QTMETATYPESDIR) # since 6.27
+    install(FILES ${METATYPES_FILE} DESTINATION ${KDE_INSTALL_QTMETATYPESDIR})
+endif()
 
 if(NOT ANDROID)
     if(SharedMimeInfo_VERSION_STRING VERSION_LESS 2.2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/barcode.cpp 
new/kpkpass-26.08.0/src/barcode.cpp
--- old/kpkpass-26.04.3/src/barcode.cpp 2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/barcode.cpp 2026-08-02 07:45:33.000000000 +0200
@@ -10,6 +10,7 @@
 
 #include <QJsonObject>
 
+using namespace Qt::Literals;
 using namespace KPkPass;
 
 namespace KPkPass
@@ -44,19 +45,27 @@
     return {};
 }
 
+struct {
+    const char *name;
+    KPkPass::Barcode::Format format;
+} static constexpr const barcode_formats[] = {
+    {"PKBarcodeFormatQR", Barcode::QR},
+    {"PKBarcodeFormatPDF417", Barcode::PDF417},
+    {"PKBarcodeFormatAztec", Barcode::Aztec},
+    {"PKBarcodeFormatCode128", Barcode::Code128},
+    {"PKBarcodeFormatCode39", Barcode::Code39},
+    {"PKBarcodeFormatCodabar", Barcode::Codabar},
+    {"PKBarcodeFormatEAN13", Barcode::EAN13},
+    {"PKBarcodeFormatI2of5", Barcode::I2of5},
+};
+
 Barcode::Format KPkPass::Barcode::format() const
 {
-    const auto format = d->obj.value(QLatin1StringView("format")).toString();
-    if (format == QLatin1StringView("PKBarcodeFormatQR")) {
-        return QR;
-    } else if (format == QLatin1StringView("PKBarcodeFormatPDF417")) {
-        return PDF417;
-    } else if (format == QLatin1StringView("PKBarcodeFormatAztec")) {
-        return Aztec;
-    } else if (format == QLatin1StringView("PKBarcodeFormatCode128")) {
-        return Code128;
-    }
-    return Invalid;
+    const auto format = d->obj.value("format"_L1);
+    const auto it = std::ranges::find_if(barcode_formats, [format](const auto 
&f) {
+        return QLatin1StringView(f.name) == format;
+    });
+    return it != std::end(barcode_formats) ? (*it).format : Invalid;
 }
 
 QString Barcode::message() const
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/barcode.h 
new/kpkpass-26.08.0/src/barcode.h
--- old/kpkpass-26.04.3/src/barcode.h   2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/barcode.h   2026-08-02 07:45:33.000000000 +0200
@@ -41,7 +41,11 @@
         QR,
         PDF417,
         Aztec,
-        Code128
+        Code128,
+        Code39,
+        Codabar,
+        EAN13,
+        I2of5,
     };
     Q_ENUM(Format)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/field.cpp 
new/kpkpass-26.08.0/src/field.cpp
--- old/kpkpass-26.04.3/src/field.cpp   2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/field.cpp   2026-08-02 07:45:33.000000000 +0200
@@ -80,6 +80,12 @@
     return {};
 }
 
+constexpr inline const auto PKDateStyleNone = "PKDateStyleNone"_L1;
+constexpr inline const auto PKDateStyleShort = "PKDateStyleShort"_L1;
+constexpr inline const auto PKDateStyleMedium = "PKDateStyleMedium"_L1;
+constexpr inline const auto PKDateStyleLong = "PKDateStyleLong"_L1;
+constexpr inline const auto PKDateStyleFull = "PKDateStyleFull"_L1;
+
 QString Field::valueDisplayString() const
 {
     const auto v = value();
@@ -88,15 +94,21 @@
     // however, real-world data doesn't strictly follow that, so we have to 
guess a bit here...
     if (v.typeId() == QMetaType::QDateTime) {
         const auto dt = v.toDateTime();
+        const auto dateStyle = d->obj.value("dateStyle"_L1);
+        const auto timeStyle = d->obj.value("timeStyle"_L1);
+
         auto fmt = QLocale::ShortFormat;
-        const auto dtStyle = 
d->obj.value(QLatin1StringView("dateStyle")).toString();
-        if (dtStyle == QLatin1StringView("PKDateStyleLong") || dtStyle == 
QLatin1StringView("PKDateStyleFull")) {
+        if (dateStyle == PKDateStyleLong || dateStyle == PKDateStyleFull) {
             fmt = QLocale::LongFormat;
         }
-        const auto timeStyle = 
d->obj.value(QLatin1StringView("timeStyle")).toString();
-        if (timeStyle == QLatin1StringView("PKDateStyleNone") || 
(timeStyle.isEmpty() && !dtStyle.isEmpty() && dt.time() == QTime(0, 0))) {
+        // time only
+        if (timeStyle == PKDateStyleNone || (timeStyle.isUndefined() && 
!dateStyle.isUndefined())) {
             return QLocale().toString(dt.date(), fmt);
         }
+        // date only
+        if (dateStyle == PKDateStyleNone || (dateStyle.isUndefined() && 
!timeStyle.isUndefined())) {
+            return QLocale().toString(dt.time(), QLocale::ShortFormat);
+        }
 
         return QLocale().toString(dt, fmt);
     }
@@ -144,4 +156,29 @@
     return d->obj.value("currencyCode"_L1).toString();
 }
 
+int Field::row() const
+{
+    return d->obj.value("row"_L1).toInt(0);
+}
+
+bool Field::isRichText() const
+{
+    const auto v = value();
+    if (v.typeId() != QMetaType::QString) {
+        return false;
+    }
+    const auto text = v.toString();
+    auto idx = text.indexOf('<'_L1);
+    if (idx >= 0 && idx < text.size() - 2) {
+        return text[idx + 1].isLetter() || text[idx + 1] == '/'_L1;
+    }
+    idx = text.indexOf('&'_L1);
+    for (auto i = idx; idx >= 0 && idx - i < 5; ++i) {
+        if (text[i] == ';'_L1) {
+            return true;
+        }
+    }
+    return false;
+}
+
 #include "moc_field.cpp"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/field.h 
new/kpkpass-26.08.0/src/field.h
--- old/kpkpass-26.04.3/src/field.h     2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/field.h     2026-08-02 07:45:33.000000000 +0200
@@ -39,6 +39,8 @@
     Q_PROPERTY(QString changeMessage READ changeMessage CONSTANT)
     Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment CONSTANT)
     Q_PROPERTY(QString currencyCode READ currencyCode CONSTANT)
+    Q_PROPERTY(int row READ row CONSTANT)
+    Q_PROPERTY(bool isRichText READ isRichText CONSTANT)
 
 public:
     Field();
@@ -71,6 +73,14 @@
     /*! Currency code. */
     [[nodiscard]] QString currencyCode() const;
 
+    /*! Row index of an (auxiliary) field. */
+    [[nodiscard]] int row() const;
+
+    /*! Returns whether the field value contains rich text markup.
+     *  \since 26.08
+     */
+    [[nodiscard]] bool isRichText() const;
+
 private:
     friend class PassPrivate;
     friend class FieldTest;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/pass.cpp 
new/kpkpass-26.08.0/src/pass.cpp
--- old/kpkpass-26.04.3/src/pass.cpp    2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/pass.cpp    2026-08-02 07:45:33.000000000 +0200
@@ -10,6 +10,7 @@
 #include "location.h"
 #include "logging.h"
 #include "pass_p.h"
+#include "seat.h"
 
 #include <KZip>
 
@@ -26,6 +27,7 @@
 
 #include <cctype>
 
+using namespace Qt::Literals;
 using namespace KPkPass;
 
 static const char *const passTypes[] = {"boardingPass", "coupon", 
"eventTicket", "generic", "storeCard"};
@@ -165,13 +167,17 @@
     return !messages.isEmpty();
 }
 
-QList<Field> PassPrivate::fields(QLatin1StringView fieldType, const Pass *q) 
const
+QList<Field> PassPrivate::fields(QLatin1StringView fieldType, const Pass *q, 
int row) const
 {
     const auto a = passData().value(fieldType).toArray();
     QList<Field> f;
     f.reserve(a.size());
     for (const auto &v : a) {
-        f.push_back(Field{v.toObject(), q});
+        Field fld{v.toObject(), q};
+        if (row >= 0 && fld.row() != row) {
+            continue;
+        }
+        f.push_back(std::move(fld));
     }
     return f;
 }
@@ -180,12 +186,14 @@
 {
     std::unique_ptr<KZip> zip(new KZip(device.get()));
     if (!zip->open(QIODevice::ReadOnly)) {
+        qCWarning(Log) << "Failed to open ZIP file" << zip->errorString();
         return nullptr;
     }
 
     // extract pass.json
     auto file = zip->directory()->file(QStringLiteral("pass.json"));
     if (!file) {
+        qCWarning(Log) << "Cannot find pass.json file";
         return nullptr;
     }
     std::unique_ptr<QIODevice> dev(file->createDevice());
@@ -256,12 +264,12 @@
 
 QString Pass::description() const
 {
-    return d->passObj.value(QLatin1StringView("description")).toString();
+    return d->message(d->passObj.value("description"_L1).toString());
 }
 
 QString Pass::organizationName() const
 {
-    return d->passObj.value(QLatin1StringView("organizationName")).toString();
+    return d->message(d->passObj.value("organizationName"_L1).toString());
 }
 
 QString Pass::passTypeIdentifier() const
@@ -306,25 +314,48 @@
     return 
QDateTime::fromString(d->passObj.value(QLatin1StringView("relevantDate")).toString(),
 Qt::ISODate);
 }
 
-static QColor parseColor(const QString &s)
+static QColor parseColor(QStringView s)
 {
-    if (s.startsWith(QLatin1StringView("rgb("), Qt::CaseInsensitive)) {
-        const auto l = QStringView(s).mid(4, s.length() - 
5).split(QLatin1Char(','));
-        if (l.size() != 3)
+    if (s.startsWith("rgb("_L1, Qt::CaseInsensitive)) {
+        const auto l = s.mid(4, s.length() - 5).split(','_L1);
+        if (l.size() != 3) {
             return {};
+        }
         return QColor(l[0].trimmed().toInt(), l[1].trimmed().toInt(), 
l[2].trimmed().toInt());
     }
+    if (s.startsWith("rgba("_L1, Qt::CaseInsensitive)) {
+        const auto l = s.mid(5, s.length() - 6).split(','_L1);
+        if (l.size() != 4) {
+            return {};
+        }
+        return QColor(l[0].trimmed().toInt(), l[1].trimmed().toInt(), 
l[2].trimmed().toInt(), l[3].trimmed().toDouble() * 255.0);
+    }
     return QColor(s);
 }
 
+bool Pass::hasBackgroundColor() const
+{
+    return d->passObj.value("backgroundColor"_L1).isString();
+}
+
+bool Pass::hasForegroundColor() const
+{
+    return d->passObj.value("foregroundColor"_L1).isString();
+}
+
+bool Pass::hasLabelColor() const
+{
+    return d->passObj.value("labelColor"_L1).isString() || 
hasForegroundColor();
+}
+
 QColor Pass::backgroundColor() const
 {
-    return 
parseColor(d->passObj.value(QLatin1StringView("backgroundColor")).toString());
+    return parseColor(d->passObj.value("backgroundColor"_L1).toString());
 }
 
 QColor Pass::foregroundColor() const
 {
-    return 
parseColor(d->passObj.value(QLatin1StringView("foregroundColor")).toString());
+    return parseColor(d->passObj.value("foregroundColor"_L1).toString());
 }
 
 QString Pass::groupingIdentifier() const
@@ -334,7 +365,7 @@
 
 QColor Pass::labelColor() const
 {
-    const auto c = 
parseColor(d->passObj.value(QLatin1StringView("labelColor")).toString());
+    const auto c = parseColor(d->passObj.value("labelColor"_L1).toString());
     if (c.isValid()) {
         return c;
     }
@@ -351,8 +382,8 @@
     const auto entries = d->zip->directory()->entries();
     for (const auto &entry : entries) {
         if (entry.startsWith(baseName)
-            && 
(QStringView(entry).mid(baseName.size()).startsWith(QLatin1Char('@')) || 
QStringView(entry).mid(baseName.size()).startsWith(QLatin1Char('.')))
-            && entry.endsWith(QLatin1StringView(".png"))) {
+            && (QStringView(entry).mid(baseName.size()).startsWith('@'_L1) || 
QStringView(entry).mid(baseName.size()).startsWith('.'_L1))
+            && entry.endsWith(".png"_L1)) {
             return true;
         }
     }
@@ -369,6 +400,16 @@
     return hasImage(QStringLiteral("logo"));
 }
 
+bool Pass::hasPrimaryLogo() const
+{
+    return hasImage(u"primaryLogo"_s);
+}
+
+bool Pass::hasSecondaryLogo() const
+{
+    return hasImage(u"secondaryLogo"_s);
+}
+
 bool Pass::hasStrip() const
 {
     return hasImage(QStringLiteral("strip"));
@@ -379,6 +420,11 @@
     return hasImage(QStringLiteral("background"));
 }
 
+bool Pass::hasArtwork() const
+{
+    return hasImage(u"artwork"_s);
+}
+
 bool Pass::hasFooter() const
 {
     return hasImage(QStringLiteral("footer"));
@@ -402,17 +448,32 @@
             break;
         }
         if (dpr > 1) {
-            file = d->zip->directory()->file(baseName + QLatin1Char('@') + 
QString::number(dpr) + QLatin1StringView("x.png"));
+            file = d->zip->directory()->file(baseName + '@'_L1 + 
QString::number(dpr) + "x.png"_L1);
         } else {
-            file = d->zip->directory()->file(baseName + 
QLatin1StringView(".png"));
+            file = d->zip->directory()->file(baseName + ".png"_L1);
         }
-        if (file)
+        if (file) {
             break;
+        }
     }
     if (!img.isNull()) { // cache hit
         return img;
     }
 
+    // no hit, check if there is any variant at all (happens in passes only 
containing eg. a 3x variant)
+    // (matches what hasImage does)
+    if (!file) {
+        const auto entries = d->zip->directory()->entries();
+        for (const auto &entry : entries) {
+            if (entry.startsWith(baseName)
+                && (QStringView(entry).mid(baseName.size()).startsWith('@'_L1) 
|| QStringView(entry).mid(baseName.size()).startsWith('.'_L1))
+                && entry.endsWith(".png"_L1)) {
+                file = d->zip->directory()->file(entry);
+                break;
+            }
+        }
+    }
+
     if (!file) {
         return {};
     }
@@ -437,6 +498,16 @@
     return image(QStringLiteral("logo"), devicePixelRatio);
 }
 
+QImage Pass::primaryLogo(unsigned int devicePixelRatio) const
+{
+    return image(u"primaryLogo"_s, devicePixelRatio);
+}
+
+QImage Pass::secondaryLogo(unsigned int devicePixelRatio) const
+{
+    return image(u"secondaryLogo"_s, devicePixelRatio);
+}
+
 QImage Pass::strip(unsigned int devicePixelRatio) const
 {
     return image(QStringLiteral("strip"), devicePixelRatio);
@@ -447,6 +518,11 @@
     return image(QStringLiteral("background"), devicePixelRatio);
 }
 
+QImage Pass::artwork(unsigned int devicePixelRatio) const
+{
+    return image(u"artwork"_s, devicePixelRatio);
+}
+
 QImage Pass::footer(unsigned int devicePixelRatio) const
 {
     return image(QStringLiteral("footer"), devicePixelRatio);
@@ -462,6 +538,17 @@
     return 
d->passObj.value(QLatin1StringView("authenticationToken")).toString();
 }
 
+QStringList Pass::preferredStyleSchemes() const
+{
+    const auto styleArray = 
d->passObj.value("preferredStyleSchemes"_L1).toArray();
+    QStringList l;
+    l.reserve(styleArray.size());
+    std::ranges::transform(styleArray, std::back_inserter(l), [](const auto 
&v) {
+        return v.toString();
+    });
+    return l;
+}
+
 QUrl Pass::webServiceUrl() const
 {
     return 
QUrl(d->passObj.value(QLatin1StringView("webServiceURL")).toString());
@@ -477,21 +564,28 @@
     return url;
 }
 
+bool Pass::hasBarcode() const
+{
+    return !d->passObj.value("barcodes"_L1).toArray().isEmpty() || 
!d->passObj.value("barcode"_L1).toObject().isEmpty();
+}
+
 QList<Barcode> Pass::barcodes() const
 {
     QList<Barcode> codes;
 
     // barcodes array
-    const auto a = d->passObj.value(QLatin1StringView("barcodes")).toArray();
+    const auto a = d->passObj.value("barcodes"_L1).toArray();
     codes.reserve(a.size());
-    for (const auto &bc : a)
+    for (const auto &bc : a) {
         codes.push_back(Barcode(bc.toObject(), this));
+    }
 
     // just a single barcode
     if (codes.isEmpty()) {
-        const auto bc = 
d->passObj.value(QLatin1StringView("barcode")).toObject();
-        if (!bc.isEmpty())
+        const auto bc = d->passObj.value("barcode"_L1).toObject();
+        if (!bc.isEmpty()) {
             codes.push_back(Barcode(bc, this));
+        }
     }
 
     return codes;
@@ -500,6 +594,17 @@
 static const char *const fieldNames[] = {"auxiliaryFields", "backFields", 
"headerFields", "primaryFields", "secondaryFields"};
 static const auto fieldNameCount = sizeof(fieldNames) / sizeof(fieldNames[0]);
 
+int Pass::auxiliaryFieldsRowCount() const
+{
+    int maxRow = 0;
+    const auto auxFields = 
d->passData().value(QLatin1StringView(fieldNames[0])).toArray();
+    for (const auto &v : auxFields) {
+        const auto auxField = v.toObject();
+        maxRow = std::max(maxRow, auxField.value("row"_L1).toInt(0));
+    }
+    return maxRow + 1;
+}
+
 QList<Field> Pass::auxiliaryFields() const
 {
     return d->fields(QLatin1StringView(fieldNames[0]), this);
@@ -525,6 +630,11 @@
     return d->fields(QLatin1StringView(fieldNames[4]), this);
 }
 
+QList<Field> Pass::auxiliaryFieldsInRow(int row) const
+{
+    return d->fields(QLatin1StringView(fieldNames[0]), this, row);
+}
+
 Field Pass::field(const QString &key) const
 {
     for (unsigned int i = 0; i < fieldNameCount; ++i) {
@@ -547,6 +657,41 @@
     return fs;
 }
 
+QList<Seat> Pass::seats() const
+{
+    const auto a = semanticTags().value("seats"_L1).toArray();
+    QList<Seat> seats;
+    seats.reserve(a.size());
+    std::ranges::transform(a, std::back_inserter(seats), [this](const 
QJsonValue &v) {
+        return Seat(v.toObject(), this);
+    });
+    return seats;
+}
+
+QJsonObject Pass::semanticTags() const
+{
+    return d->passObj.value("semantics"_L1).toObject();
+}
+
+QString Pass::lookupMessage(const QString &msg) const
+{
+    return d->message(msg);
+}
+
+QJsonValue Pass::rawValue(const QString &key) const
+{
+    return d->passObj.value(key);
+}
+
+QJsonValue Pass::rawValue(QStringView key) const
+{
+    return d->passObj.value(key);
+}
+QJsonValue Pass::rawValue(QLatin1StringView key) const
+{
+    return d->passObj.value(key);
+}
+
 Pass *Pass::fromData(const QByteArray &data, QObject *parent)
 {
     std::unique_ptr<QBuffer> buffer(new QBuffer);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/pass.h 
new/kpkpass-26.08.0/src/pass.h
--- old/kpkpass-26.04.3/src/pass.h      2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/pass.h      2026-08-02 07:45:33.000000000 +0200
@@ -26,6 +26,7 @@
 class Barcode;
 class Location;
 class PassPrivate;
+class Seat;
 
 /*!
  * \sa 
https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/PassKit_PG/index.html
@@ -50,6 +51,10 @@
 
     Q_PROPERTY(QDateTime relevantDate READ relevantDate CONSTANT)
 
+    Q_PROPERTY(bool hasBackgroundColor READ hasBackgroundColor CONSTANT)
+    Q_PROPERTY(bool hasForegroundColor READ hasForegroundColor CONSTANT)
+    Q_PROPERTY(bool hasLabelColor READ hasLabelColor CONSTANT)
+
     Q_PROPERTY(QColor backgroundColor READ backgroundColor CONSTANT)
     Q_PROPERTY(QColor foregroundColor READ foregroundColor CONSTANT)
     Q_PROPERTY(QString groupingIdentifier READ groupingIdentifier CONSTANT)
@@ -58,13 +63,20 @@
 
     Q_PROPERTY(bool hasIcon READ hasIcon CONSTANT)
     Q_PROPERTY(bool hasLogo READ hasLogo CONSTANT)
+    Q_PROPERTY(bool hasPrimaryLogo READ hasPrimaryLogo CONSTANT)
+    Q_PROPERTY(bool hasSecondaryLogo READ hasSecondaryLogo CONSTANT)
     Q_PROPERTY(bool hasStrip READ hasStrip CONSTANT)
     Q_PROPERTY(bool hasBackground READ hasBackground CONSTANT)
+    Q_PROPERTY(bool hasArtwork READ hasArtwork CONSTANT)
     Q_PROPERTY(bool hasFooter READ hasFooter CONSTANT)
     Q_PROPERTY(bool hasThumbnail READ hasThumbnail CONSTANT)
 
-    // needs to be QVariantList just for QML (Grantlee would also work with 
QList<Field>
+    Q_PROPERTY(QStringList preferredStyleSchemes READ preferredStyleSchemes 
CONSTANT)
+
+    Q_PROPERTY(bool hasBarcode READ hasBarcode CONSTANT)
     Q_PROPERTY(QList<KPkPass::Barcode> barcodes READ barcodes CONSTANT)
+
+    Q_PROPERTY(int auxiliaryFieldsRowCount READ auxiliaryFieldsRowCount 
CONSTANT)
     Q_PROPERTY(QList<KPkPass::Field> auxiliaryFields READ auxiliaryFields 
CONSTANT)
     Q_PROPERTY(QList<KPkPass::Field> backFields READ backFields CONSTANT)
     Q_PROPERTY(QList<KPkPass::Field> headerFields READ headerFields CONSTANT)
@@ -73,6 +85,10 @@
     Q_PROPERTY(QList<KPkPass::Location> locations READ locations CONSTANT)
     Q_PROPERTY(QVariantMap field READ fieldsVariantMap CONSTANT)
 
+    Q_PROPERTY(QList<KPkPass::Seat> seats READ seats CONSTANT)
+
+    Q_PROPERTY(QJsonObject semanticTags READ semanticTags CONSTANT)
+
 public:
     ~Pass() override;
 
@@ -106,7 +122,13 @@
 
     // visual appearance keys
     /*! Returns all barcodes defined in the pass. */
+    [[nodiscard]] bool hasBarcode() const;
     [[nodiscard]] QList<Barcode> barcodes() const;
+
+    [[nodiscard]] bool hasBackgroundColor() const;
+    [[nodiscard]] bool hasForegroundColor() const;
+    [[nodiscard]] bool hasLabelColor() const;
+
     [[nodiscard]] QColor backgroundColor() const;
     [[nodiscard]] QColor foregroundColor() const;
     [[nodiscard]] QString groupingIdentifier() const;
@@ -117,13 +139,16 @@
      *  \a baseName The name of the asset, without the file type and high dpi 
extensions.
      *  \since 5.20.41
      */
-    bool hasImage(const QString &baseName) const;
-    bool hasIcon() const;
-    bool hasLogo() const;
-    bool hasStrip() const;
-    bool hasBackground() const;
-    bool hasFooter() const;
-    bool hasThumbnail() const;
+    [[nodiscard]] bool hasImage(const QString &baseName) const;
+    [[nodiscard]] bool hasIcon() const;
+    [[nodiscard]] bool hasLogo() const;
+    [[nodiscard]] bool hasPrimaryLogo() const;
+    [[nodiscard]] bool hasSecondaryLogo() const;
+    [[nodiscard]] bool hasStrip() const;
+    [[nodiscard]] bool hasBackground() const;
+    [[nodiscard]] bool hasArtwork() const;
+    [[nodiscard]] bool hasFooter() const;
+    [[nodiscard]] bool hasThumbnail() const;
 
     /*! Returns an image asset of this pass.
      *  \a baseName The name of the asset, without the file name extension.
@@ -134,15 +159,32 @@
     Q_INVOKABLE [[nodiscard]] QImage icon(unsigned int devicePixelRatio = 1) 
const;
     /*! Returns the pass logo. */
     Q_INVOKABLE [[nodiscard]] QImage logo(unsigned int devicePixelRatio = 1) 
const;
+    /*! Returns the pass primary logo.
+     *  \since 26.08
+     */
+    Q_INVOKABLE [[nodiscard]] QImage primaryLogo(unsigned int devicePixelRatio 
= 1) const;
+    /*! Returns the pass secondary logo.
+     *  \since 26.08
+     */
+    Q_INVOKABLE [[nodiscard]] QImage secondaryLogo(unsigned int 
devicePixelRatio = 1) const;
     /*! Returns the strip image if present. */
     Q_INVOKABLE [[nodiscard]] QImage strip(unsigned int devicePixelRatio = 1) 
const;
     /*! Returns the background image if present. */
     Q_INVOKABLE [[nodiscard]] QImage background(unsigned int devicePixelRatio 
= 1) const;
+    /*! Returns the artwork background image if present.
+     *  \since 26.08
+     */
+    Q_INVOKABLE [[nodiscard]] QImage artwork(unsigned int devicePixelRatio = 
1) const;
     /*! Returns the footer image if present. */
     Q_INVOKABLE [[nodiscard]] QImage footer(unsigned int devicePixelRatio = 1) 
const;
     /*! Returns the thumbnail image if present. */
     Q_INVOKABLE [[nodiscard]] QImage thumbnail(unsigned int devicePixelRatio = 
1) const;
 
+    /*! Preferred style schemes.
+     *  \since 26.08
+     */
+    [[nodiscard]] QStringList preferredStyleSchemes() const;
+
     // web service keys
     [[nodiscard]] QString authenticationToken() const;
     [[nodiscard]] QUrl webServiceUrl() const;
@@ -151,16 +193,51 @@
      */
     [[nodiscard]] QUrl passUpdateUrl() const;
 
-    QList<Field> auxiliaryFields() const;
-    QList<Field> backFields() const;
-    QList<Field> headerFields() const;
-    QList<Field> primaryFields() const;
-    QList<Field> secondaryFields() const;
+    /*! Number of auxiliary field rows.
+     *  \since 26.08
+     */
+    [[nodiscard]] int auxiliaryFieldsRowCount() const;
+
+    [[nodiscard]] QList<Field> auxiliaryFields() const;
+    [[nodiscard]] QList<Field> backFields() const;
+    [[nodiscard]] QList<Field> headerFields() const;
+    [[nodiscard]] QList<Field> primaryFields() const;
+    [[nodiscard]] QList<Field> secondaryFields() const;
+
+    /*! Returns only those auxiliary fields in \p row.
+     *  \since 26.08
+     */
+    Q_INVOKABLE [[nodiscard]] QList<Field> auxiliaryFieldsInRow(int row) const;
 
     /*! Returns the field with key \a key. */
-    Field field(const QString &key) const;
+    [[nodiscard]] Field field(const QString &key) const;
     /*! Returns all fields found in this pass. */
-    QList<Field> fields() const;
+    [[nodiscard]] QList<Field> fields() const;
+
+    /*! Returns seat information specified in semantic tag data.
+     *  \since 26.08
+     */
+    [[nodiscard]] QList<Seat> seats() const;
+
+    /*! Returns the semantic tags, if available.
+     *  \since 26.08
+     */
+    [[nodiscard]] QJsonObject semanticTags() const;
+
+    /*! Lookup a message in the passes translation catalog.
+     *  This is mainly necessary for semantic tags or raw values of
+     *  type localized string, other properties already do this internally.
+     *  \since 26.08
+     */
+    Q_INVOKABLE [[nodiscard]] QString lookupMessage(const QString &msg) const;
+
+    /*! Returns a raw entry from this pass' \c pass.json.
+     *  Useful e.g. for semi-standardized additional flight information values.
+     * \since 26.08
+     */
+    Q_INVOKABLE [[nodiscard]] QJsonValue rawValue(const QString &key) const;
+    [[nodiscard]] QJsonValue rawValue(QStringView key) const;
+    [[nodiscard]] QJsonValue rawValue(QLatin1StringView key) const;
 
     /*! Create a appropriate sub-class based on the pkpass file type. */
     static Pass *fromData(const QByteArray &data, QObject *parent = nullptr);
@@ -172,13 +249,14 @@
      *  all the pass data.
      *  \since 5.20.41
      */
-    QByteArray rawData() const;
+    [[nodiscard]] QByteArray rawData() const;
 
 protected:
     ///\\ond internal
     friend class Barcode;
     friend class Field;
     friend class PassPrivate;
+    friend class Seat;
     explicit Pass(Type passType, QObject *parent = nullptr);
     std::unique_ptr<PassPrivate> d;
     ///@endcond
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/pass_p.h 
new/kpkpass-26.08.0/src/pass_p.h
--- old/kpkpass-26.04.3/src/pass_p.h    2026-06-05 07:05:26.000000000 +0200
+++ new/kpkpass-26.08.0/src/pass_p.h    2026-08-02 07:45:33.000000000 +0200
@@ -6,6 +6,8 @@
 
 #pragma once
 
+#include "pass.h"
+
 #include <QHash>
 #include <QImage>
 #include <QJsonObject>
@@ -40,14 +42,14 @@
 {
 public:
     /** The pass data structure of the pass.json file. */
-    QJsonObject passData() const;
+    [[nodiscard]] QJsonObject passData() const;
     /** Localized message for the given key. */
-    QString message(const QString &key) const;
+    [[nodiscard]] QString message(const QString &key) const;
 
     void parse();
     bool parseMessages(const QString &lang);
 
-    QList<Field> fields(QLatin1StringView fieldType, const Pass *q) const;
+    [[nodiscard]] QList<Field> fields(QLatin1StringView fieldType, const Pass 
*q, int row = -1) const;
 
     static Pass *fromData(std::unique_ptr<QIODevice> device, QObject *parent);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/quick/types.h 
new/kpkpass-26.08.0/src/quick/types.h
--- old/kpkpass-26.04.3/src/quick/types.h       2026-06-05 07:05:26.000000000 
+0200
+++ new/kpkpass-26.08.0/src/quick/types.h       2026-08-02 07:45:33.000000000 
+0200
@@ -7,6 +7,7 @@
 #include <barcode.h>
 #include <boardingpass.h>
 #include <field.h>
+#include <seat.h>
 
 #define FOREIGN_ENUM_GADGET(Class)                                             
                                                                                
\
     class Class##Derived : public KPkPass::Class                               
                                                                                
\
@@ -48,3 +49,10 @@
     QML_FOREIGN(KPkPass::Barcode)
 };
 FOREIGN_ENUM_GADGET(Barcode)
+
+class SeatForeign
+{
+    Q_GADGET
+    QML_VALUE_TYPE(seat)
+    QML_FOREIGN(KPkPass::Seat)
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/seat.cpp 
new/kpkpass-26.08.0/src/seat.cpp
--- old/kpkpass-26.04.3/src/seat.cpp    1970-01-01 01:00:00.000000000 +0100
+++ new/kpkpass-26.08.0/src/seat.cpp    2026-08-02 07:45:33.000000000 +0200
@@ -0,0 +1,98 @@
+/*
+   SPDX-FileCopyrightText: 2026 Volker Krause <[email protected]>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#include "seat.h"
+#include "pass.h"
+#include "pass_p.h"
+
+#include <QJsonObject>
+
+using namespace Qt::Literals;
+using namespace KPkPass;
+
+namespace KPkPass
+{
+class SeatPrivate
+{
+public:
+    const Pass *pass = nullptr;
+    QJsonObject obj;
+};
+}
+
+Seat::Seat()
+    : d(new SeatPrivate)
+{
+}
+
+Seat::Seat(const Seat &) = default;
+Seat::Seat(Seat &&) noexcept = default;
+Seat::~Seat() = default;
+Seat &Seat::operator=(const Seat &) = default;
+Seat &Seat::operator=(Seat &&) noexcept = default;
+
+Seat::Seat(const QJsonObject &obj, const Pass *pass)
+    : d(new SeatPrivate)
+{
+    d->pass = pass;
+    d->obj = obj;
+}
+
+bool Seat::hasSeatAisle() const
+{
+    return !seatAisle().isEmpty();
+}
+
+bool Seat::hasSeatLevel() const
+{
+    return !seatLevel().isEmpty();
+}
+
+bool Seat::hasSeatNumber() const
+{
+    return !seatNumber().isEmpty();
+}
+
+bool Seat::hasSeatRow() const
+{
+    return !seatRow().isEmpty();
+}
+
+bool Seat::hasSeatSection() const
+{
+    return !seatSection().isEmpty();
+}
+
+QString Seat::seatAisle() const
+{
+    return d->pass->d->message(d->obj.value("seatAisle"_L1).toString());
+}
+
+QString Seat::seatLevel() const
+{
+    return d->pass->d->message(d->obj.value("seatLevel"_L1).toString());
+}
+
+QString Seat::seatNumber() const
+{
+    return d->pass->d->message(d->obj.value("seatNumber"_L1).toString());
+}
+
+QString Seat::seatRow() const
+{
+    return d->pass->d->message(d->obj.value("seatRow"_L1).toString());
+}
+
+QString Seat::seatSection() const
+{
+    return d->pass->d->message(d->obj.value("seatSection"_L1).toString());
+}
+
+QString Seat::asAirplaneSeat() const
+{
+    return seatRow() + seatNumber();
+}
+
+#include "moc_seat.cpp"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kpkpass-26.04.3/src/seat.h 
new/kpkpass-26.08.0/src/seat.h
--- old/kpkpass-26.04.3/src/seat.h      1970-01-01 01:00:00.000000000 +0100
+++ new/kpkpass-26.08.0/src/seat.h      2026-08-02 07:45:33.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+   SPDX-FileCopyrightText: 2026 Volker Krause <[email protected]>
+   SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+#ifndef KPKPASS_SEAT_H
+#define KPKPASS_SEAT_H
+
+#include "kpkpass_export.h"
+
+#include <qobjectdefs.h>
+
+class QJsonObject;
+
+namespace KPkPass
+{
+
+class Pass;
+class SeatPrivate;
+
+/*!
+ * \brief Seat information in semantic tag data in a pkpass.
+ *
+ * \sa 
https://developer.apple.com/documentation/walletpasses/semantictagtype/seat-data.dictionary
+ * \class KPkPass::Seat
+ * \inmodule KPkPass
+ * \inheaderfile KPkPass/Seat
+ * \since 26.08
+ */
+class KPKPASS_EXPORT Seat
+{
+    Q_GADGET
+
+    Q_PROPERTY(bool hasSeatAisle READ hasSeatAisle)
+    Q_PROPERTY(bool hasSeatLevel READ hasSeatLevel)
+    Q_PROPERTY(bool hasSeatNumber READ hasSeatNumber)
+    Q_PROPERTY(bool hasSeatRow READ hasSeatRow)
+    Q_PROPERTY(bool hasSeatSection READ hasSeatSection)
+
+    Q_PROPERTY(QString seatAisle READ seatAisle)
+    Q_PROPERTY(QString seatLevel READ seatLevel)
+    Q_PROPERTY(QString seatNumber READ seatNumber)
+    Q_PROPERTY(QString seatRow READ seatRow)
+    Q_PROPERTY(QString seatSection READ seatSection)
+
+public:
+    explicit Seat();
+    Seat(const Seat &);
+    Seat(Seat &&) noexcept;
+    ~Seat();
+    Seat &operator=(const Seat &);
+    Seat &operator=(Seat &&) noexcept;
+
+    [[nodiscard]] bool hasSeatAisle() const;
+    [[nodiscard]] bool hasSeatLevel() const;
+    [[nodiscard]] bool hasSeatNumber() const;
+    [[nodiscard]] bool hasSeatRow() const;
+    [[nodiscard]] bool hasSeatSection() const;
+
+    [[nodiscard]] QString seatAisle() const;
+    [[nodiscard]] QString seatLevel() const;
+    [[nodiscard]] QString seatNumber() const;
+    [[nodiscard]] QString seatRow() const;
+    [[nodiscard]] QString seatSection() const;
+
+    /*! Format seat information in the usual airplane seat format. */
+    Q_INVOKABLE [[nodiscard]] QString asAirplaneSeat() const;
+
+private:
+    friend class Pass;
+    explicit Seat(const QJsonObject &obj, const Pass *pass);
+
+    std::shared_ptr<SeatPrivate> d;
+};
+
+}
+
+#endif

Reply via email to