diff --git a/plugins/messageviewer/bodypartformatter/itinerary/itineraryrenderer.cpp b/plugins/messageviewer/bodypartformatter/itinerary/itineraryrenderer.cpp index 268c255d..89ea1d41 100644 --- a/plugins/messageviewer/bodypartformatter/itinerary/itineraryrenderer.cpp +++ b/plugins/messageviewer/bodypartformatter/itinerary/itineraryrenderer.cpp @@ -1,251 +1,250 @@ /* Copyright (c) 2017 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "itineraryrenderer.h" #include "itinerarymemento.h" #include "itineraryurlhandler.h" #include "itinerarykdeconnecthandler.h" #include "itinerary_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KItinerary; // Grantlee has no Q_GADGET support yet #define GRANTLEE_MAKE_GADGET(Class) \ GRANTLEE_BEGIN_LOOKUP(Class) \ const auto idx = Class::staticMetaObject.indexOfProperty(property.toUtf8().constData()); \ if (idx < 0) { \ return {};} \ const auto mp = Class::staticMetaObject.property(idx); \ return mp.readOnGadget(&object); \ GRANTLEE_END_LOOKUP GRANTLEE_MAKE_GADGET(Airport) GRANTLEE_MAKE_GADGET(Airline) GRANTLEE_MAKE_GADGET(Event) GRANTLEE_MAKE_GADGET(EventReservation) GRANTLEE_MAKE_GADGET(Flight) GRANTLEE_MAKE_GADGET(FlightReservation) GRANTLEE_MAKE_GADGET(GeoCoordinates) GRANTLEE_MAKE_GADGET(LodgingBusiness) GRANTLEE_MAKE_GADGET(LodgingReservation) GRANTLEE_MAKE_GADGET(Person) GRANTLEE_MAKE_GADGET(Place) GRANTLEE_MAKE_GADGET(PostalAddress) GRANTLEE_MAKE_GADGET(Seat) GRANTLEE_MAKE_GADGET(Ticket) GRANTLEE_MAKE_GADGET(TrainStation) GRANTLEE_MAKE_GADGET(TrainTrip) GRANTLEE_MAKE_GADGET(TrainReservation) GRANTLEE_MAKE_GADGET(BusStation) GRANTLEE_MAKE_GADGET(BusTrip) GRANTLEE_MAKE_GADGET(BusReservation) GRANTLEE_MAKE_GADGET(CancelAction) GRANTLEE_MAKE_GADGET(CheckInAction) GRANTLEE_MAKE_GADGET(DownloadAction) GRANTLEE_MAKE_GADGET(UpdateAction) GRANTLEE_MAKE_GADGET(ViewAction) GRANTLEE_MAKE_GADGET(FoodEstablishment) GRANTLEE_MAKE_GADGET(FoodEstablishmentReservation) GRANTLEE_MAKE_GADGET(RentalCarReservation) GRANTLEE_MAKE_GADGET(RentalCar) GRANTLEE_MAKE_GADGET(Brand) GRANTLEE_MAKE_GADGET(Organization) ItineraryRenderer::ItineraryRenderer() { Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); Grantlee::registerMetaType(); } void ItineraryRenderer::setKDEConnectHandler(ItineraryKDEConnectHandler *kdeConnect) { m_kdeConnect = kdeConnect; } bool ItineraryRenderer::render(const MimeTreeParser::MessagePartPtr &msgPart, MessageViewer::HtmlWriter *htmlWriter, MessageViewer::RenderContext *context) const { Q_UNUSED(context); const auto mpList = msgPart.dynamicCast(); if (!msgPart->isRoot() || !mpList->hasSubParts()) { return false; } const auto node = mpList->subParts().at(0)->content(); const auto nodeHelper = msgPart->nodeHelper(); if (!nodeHelper || !node) { return false; } auto memento = dynamic_cast(nodeHelper->bodyPartMemento(node->topLevel(), ItineraryMemento::identifier())); if (!memento || !memento->hasData()) { return false; } const auto extractedData = memento->data(); if (extractedData.isEmpty()) { // hasData() will not be correct for filtered structured data on the first pass through here... return false; } const auto dir = nodeHelper->createTempDir(QStringLiteral("semantic")); auto c = MessageViewer::MessagePartRendererManager::self()->createContext(); const auto pal = qGuiApp->palette(); QVariantMap style; style.insert(QStringLiteral("expandIcon"), QString(QStringLiteral("file://") + MessageViewer::IconNameCache::instance()->iconPathFromLocal(QStringLiteral("quoteexpand.png")))); style.insert(QStringLiteral("collapseIcon"), QString(QStringLiteral("file://") + MessageViewer::IconNameCache::instance()->iconPathFromLocal(QStringLiteral("quotecollapse.png")))); style.insert(QStringLiteral("palette"), QGuiApplication::palette()); style.insert(QStringLiteral("viewScheme"), QVariant::fromValue(KColorScheme(QPalette::Normal, KColorScheme::View))); c.insert(QStringLiteral("style"), style); const bool testMode = qEnvironmentVariableIsSet("BPF_ITINERARY_TESTMODE"); // ensure deterministic results for unit tests QVariantMap actionState; actionState.insert(QStringLiteral("canShowCalendar"), memento->startDate().isValid()); actionState.insert(QStringLiteral("canAddToCalendar"), memento->canAddToCalendar()); actionState.insert(QStringLiteral("hasItineraryApp"), ItineraryUrlHandler::hasItineraryApp() || testMode); if (!testMode) { const auto devices = m_kdeConnect->devices(); actionState.insert(QStringLiteral("canSendToDevice"), !devices.isEmpty()); if (devices.size() == 1) { actionState.insert(QStringLiteral("defaultDeviceName"), devices[0].name); actionState.insert(QStringLiteral("defaultDeviceId"), devices[0].deviceId); } } c.insert(QStringLiteral("actionState"), actionState); // Grantlee can't do indexed map/array lookups, so we need to interleave this here already QVariantList elems; elems.reserve(extractedData.size()); int ticketTokenId = 0; for (int i = 0; i < extractedData.size(); ++i) { QVariantMap data; QVariantMap state; const auto d = extractedData.at(i); state.insert(QStringLiteral("expanded"), d.expanded); data.insert(QStringLiteral("state"), state); data.insert(QStringLiteral("groupId"), i); QVector reservations; for (const auto &r : d.reservations) { QVariantMap m; m.insert(QStringLiteral("reservation"), r); // generate ticket barcodes const auto ticket = JsonLd::convert(r).reservedTicket().value(); std::unique_ptr barcode; switch (ticket.ticketTokenType()) { case Ticket::AztecCode: barcode.reset(Prison::createBarcode(Prison::Aztec)); barcode->setData(ticket.ticketTokenData()); break; case Ticket::QRCode: barcode.reset(Prison::createBarcode(Prison::QRCode)); barcode->setData(ticket.ticketTokenData()); break; case Ticket::DataMatrix: barcode.reset(Prison::createBarcode(Prison::DataMatrix)); barcode->setData(ticket.ticketTokenData()); default: break; } if (barcode) { - barcode->toImage(barcode->minimumSize()); // minimumSize is only available after we rendered once... - const auto img = barcode->toImage(barcode->minimumSize()); + const auto img = barcode->toImage(barcode->preferredSize(qGuiApp->devicePixelRatio())); const QString fileName = dir + QLatin1String("/ticketToken") + QString::number(ticketTokenId++) + QLatin1String(".png"); img.save(fileName); m.insert(QStringLiteral("ticketToken"), fileName); nodeHelper->addTempFile(fileName); } reservations.push_back(m); } data.insert(QStringLiteral("reservations"), QVariant::fromValue(reservations)); elems.push_back(data); } c.insert(QStringLiteral("data"), elems); auto t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("org.kde.messageviewer/itinerary/itinerary.html")); const_cast(t->engine())->addDefaultLibrary(QStringLiteral("kitinerary_grantlee_extension")); dynamic_cast(const_cast(t->engine()))->localizer()->setApplicationDomain(QByteArrayLiteral("messageviewer_semantic_plugin")); Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); dynamic_cast(const_cast(t->engine()))->localizer()->setApplicationDomain(QByteArrayLiteral("libmessageviewer")); return false; // yes, false, we want the rest of the email rendered normally after this } diff --git a/plugins/messageviewer/bodypartformatter/pkpass/pkpass_plugin.cpp b/plugins/messageviewer/bodypartformatter/pkpass/pkpass_plugin.cpp index 3f9c3a30..02a07d4b 100644 --- a/plugins/messageviewer/bodypartformatter/pkpass/pkpass_plugin.cpp +++ b/plugins/messageviewer/bodypartformatter/pkpass/pkpass_plugin.cpp @@ -1,181 +1,181 @@ /* Copyright (c) 2017 Volker Krause This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include +#include #include #include #include static bool isPkPassContent(KMime::Content *content) { const auto ct = content->contentType(false); if (ct && ct->mimeType() == "application/vnd.apple.pkpass") { return true; } if (ct && ct->mimeType() != "application/octet-stream" && ct->mimeType() != "application/zip") { return false; } if (ct && ct->name().endsWith(QLatin1String("pkpass"))) { return true; } const auto cd = content->contentDisposition(false); return cd && cd->filename().endsWith(QLatin1String("pkpass")); } // Grantlee has no Q_GADGET support yet #define GRANTLEE_MAKE_GADGET(Class) \ GRANTLEE_BEGIN_LOOKUP(Class) \ const auto idx = Class::staticMetaObject.indexOfProperty(property.toUtf8().constData()); \ if (idx < 0) { \ return {}; \ } \ const auto mp = Class::staticMetaObject.property(idx); \ return mp.readOnGadget(&object); \ GRANTLEE_END_LOOKUP GRANTLEE_MAKE_GADGET(KPkPass::Barcode) GRANTLEE_MAKE_GADGET(KPkPass::Field) namespace { class Formatter : public MessageViewer::MessagePartRendererBase { public: bool render(const MimeTreeParser::MessagePartPtr &msgPart, MessageViewer::HtmlWriter *htmlWriter, MessageViewer::RenderContext *context) const override { Q_UNUSED(context); auto mp = msgPart.dynamicCast(); if (!mp || context->isHiddenHint(msgPart) || !msgPart->content() || !isPkPassContent(msgPart->content())) { return false; } std::unique_ptr pass(KPkPass::Pass::fromData(msgPart->content()->decodedContent())); const auto dir = mp->nodeHelper()->createTempDir(QStringLiteral("pkpass")); const auto logo = pass->logo(); if (!logo.isNull()) { const QString fileName = dir + QStringLiteral("/logo.png"); logo.save(fileName); pass->setProperty("logoUrl", QUrl::fromLocalFile(fileName)); mp->nodeHelper()->addTempFile(fileName); } const auto strip = pass->strip(); if (!strip.isNull()) { const QString fileName = dir + QStringLiteral("/strip.png"); strip.save(fileName); pass->setProperty("stripUrl", QUrl::fromLocalFile(fileName)); mp->nodeHelper()->addTempFile(fileName); } const auto background = pass->background(); if (!background.isNull()) { const QString fileName = dir + QStringLiteral("/background.png"); background.save(fileName); pass->setProperty("backgroundUrl", QUrl::fromLocalFile(fileName)); mp->nodeHelper()->addTempFile(fileName); } const auto footer = pass->footer(); if (!footer.isNull()) { const QString fileName = dir + QStringLiteral("/footer.png"); footer.save(fileName); pass->setProperty("footerUrl", QUrl::fromLocalFile(fileName)); mp->nodeHelper()->addTempFile(fileName); } const auto barcodes = pass->barcodes(); if (!barcodes.isEmpty()) { const auto barcode = barcodes.at(0); std::unique_ptr code; switch (barcode.format()) { case KPkPass::Barcode::QR: code.reset(Prison::createBarcode(Prison::QRCode)); break; case KPkPass::Barcode::Aztec: code.reset(Prison::createBarcode(Prison::Aztec)); break; default: break; } if (code) { code->setData(barcode.message()); - code->toImage(code->minimumSize()); // minimumSize is only available after we rendered once... const QString fileName = dir + QStringLiteral("/barcode.png"); - code->toImage(code->minimumSize()).save(fileName); + code->toImage(code->preferredSize(qGuiApp->devicePixelRatio())).save(fileName); pass->setProperty("barcodeUrl", QUrl::fromLocalFile(fileName)); mp->nodeHelper()->addTempFile(fileName); } } // Grantlee can't handle QColor... pass->setProperty("foregroundColorName", pass->foregroundColor().name()); pass->setProperty("backgroundColorName", pass->backgroundColor().name()); pass->setProperty("labelColorName", pass->labelColor().name()); auto c = MessageViewer::MessagePartRendererManager::self()->createContext(); c.insert(QStringLiteral("block"), mp.data()); c.insert(QStringLiteral("pass"), pass.get()); Grantlee::Template t; if (qobject_cast(pass.get())) { t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("org.kde.messageviewer/pkpass/boardingpass.html")); } else if (pass->type() == KPkPass::Pass::EventTicket) { t = MessageViewer::MessagePartRendererManager::self()->loadByName(QStringLiteral("org.kde.messageviewer/pkpass/eventticket.html")); } Grantlee::OutputStream s(htmlWriter->stream()); t->render(&s, &c); return true; } }; class Plugin : public QObject, public MessageViewer::MessagePartRenderPlugin { Q_OBJECT Q_INTERFACES(MessageViewer::MessagePartRenderPlugin) Q_PLUGIN_METADATA(IID "com.kde.messageviewer.bodypartformatter" FILE "pkpass_plugin.json") public: explicit Plugin(QObject *parent = nullptr) : QObject(parent) { Grantlee::registerMetaType(); Grantlee::registerMetaType(); } MessageViewer::MessagePartRendererBase *renderer(int index) override { return index == 0 ? new Formatter() : nullptr; } }; } #include "pkpass_plugin.moc"