sfx2/source/control/thumbnailview.cxx | 149 +++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 55 deletions(-)
New commits: commit bf2b5b64b2ab3d4082c766fc64b3cf8f27125ff4 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Mon Apr 28 11:19:04 2025 -0400 Commit: Justin Luth <justin.l...@collabora.com> CommitDate: Thu May 1 17:42:40 2025 +0200 Refactor getting thumbnail a bit This backport includes 7cad6f88cc6fdef91e7a71b9b4694559517c2e28 tdf#166035 thumbnailview: recognize some MS-format thumbnails Use OOXML package relations (and XRelationshipAccess) to access OOXML's thumbnails. Unify obtaining the bitmap. This change also fixes a possible nullptr dereference when trying to read the older Thumbnail/thumbnail.png - access to xDocStorage wasn't checked before. Change-Id: Iff9d85cb461960d2b11acaa008ba9ae69af475a9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184750 Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Tested-by: Jenkins (cherry picked from commit b4965d0a7875384d1fedc7d43762c94b30e5e90f) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184767 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184845 Reviewed-by: Justin Luth <jl...@mail.com> diff --git a/sfx2/source/control/thumbnailview.cxx b/sfx2/source/control/thumbnailview.cxx index 1ae47016935e..e87080fe2085 100644 --- a/sfx2/source/control/thumbnailview.cxx +++ b/sfx2/source/control/thumbnailview.cxx @@ -18,6 +18,7 @@ #include <basegfx/color/bcolortools.hxx> #include <comphelper/processfactory.hxx> +#include <comphelper/propertyvalue.hxx> #include <drawinglayer/attribute/fontattribute.hxx> #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> #include <drawinglayer/primitive2d/Primitive2DContainer.hxx> @@ -34,12 +35,17 @@ #include <vcl/settings.hxx> #include <vcl/event.hxx> #include <vcl/filter/PngImageReader.hxx> +#include <vcl/graphicfilter.hxx> #include <vcl/weldutils.hxx> #include <com/sun/star/accessibility/AccessibleEventId.hpp> #include <com/sun/star/embed/ElementModes.hpp> #include <com/sun/star/embed/StorageFactory.hpp> +#include <com/sun/star/embed/StorageFormats.hpp> +#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp> +#include <com/sun/star/embed/XRelationshipAccess.hpp> #include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/packages/zip/ZipFileAccess.hpp> #include <memory> #if !ENABLE_WASM_STRIP_RECENT @@ -59,72 +65,90 @@ bool ThumbnailView::renameItem(ThumbnailViewItem*, const OUString&) return false; } -BitmapEx ThumbnailView::readThumbnail(const OUString &msURL) +static css::uno::Reference<css::embed::XHierarchicalStorageAccess> +getStorageAccess(const OUString& URL, sal_Int32 format) { - using namespace ::com::sun::star; - using namespace ::com::sun::star::uno; + auto xFactory = css::embed::StorageFactory::create(comphelper::getProcessComponentContext()); + css::uno::Sequence descriptor{ comphelper::makePropertyValue(u"StorageFormat"_ustr, format) }; + css::uno::Sequence args{ css::uno::Any(URL), css::uno::Any(css::embed::ElementModes::READ), + css::uno::Any(descriptor) }; + return xFactory->createInstanceWithArguments(args) + .queryThrow<css::embed::XHierarchicalStorageAccess>(); +} - // Load the thumbnail from a template document. - uno::Reference<io::XInputStream> xIStream; +static css::uno::Reference<css::io::XInputStream> +getHierarchicalStream(const css::uno::Reference<css::embed::XHierarchicalStorageAccess>& xStorage, + const OUString& name) +{ + auto xStream + = xStorage->openStreamElementByHierarchicalName(name, css::embed::ElementModes::READ); + return xStream->getInputStream(); +} - uno::Reference< uno::XComponentContext > xContext(::comphelper::getProcessComponentContext()); - try +static css::uno::Reference<css::io::XInputStream> +getFirstHierarchicalStream(const OUString& URL, sal_Int32 format, + std::initializer_list<OUString> names) +{ + auto xStorage(getStorageAccess(URL, format)); + for (const auto& name : names) { - uno::Reference<lang::XSingleServiceFactory> xStorageFactory = embed::StorageFactory::create(xContext); - - uno::Sequence<uno::Any> aArgs{ uno::Any(msURL), uno::Any(embed::ElementModes::READ) }; - uno::Reference<embed::XStorage> xDocStorage ( - xStorageFactory->createInstanceWithArguments(aArgs), - uno::UNO_QUERY); - try { - if (xDocStorage.is()) - { - uno::Reference<embed::XStorage> xStorage ( - xDocStorage->openStorageElement( - "Thumbnails", - embed::ElementModes::READ)); - if (xStorage.is()) - { - uno::Reference<io::XStream> xThumbnailCopy ( - xStorage->cloneStreamElement("thumbnail.png")); - if (xThumbnailCopy.is()) - xIStream = xThumbnailCopy->getInputStream(); - } - } + return getHierarchicalStream(xStorage, name); } - catch (const uno::Exception&) + catch (const css::uno::Exception&) { - TOOLS_WARN_EXCEPTION("sfx", - "caught exception while trying to access Thumbnail/thumbnail.png of " << msURL); + TOOLS_WARN_EXCEPTION("sfx", "caught exception while trying to access " << name << " of " + << URL); } + } + return {}; +} - try +static css::uno::Reference<css::io::XInputStream> +getFirstStreamByRelType(const OUString& URL, std::initializer_list<OUString> types) +{ + auto xStorage(getStorageAccess(URL, css::embed::StorageFormats::OFOPXML)); + if (auto xRelationshipAccess = xStorage.query<css::embed::XRelationshipAccess>()) + { + for (const auto& type : types) { - // An (older) implementation had a bug - The storage - // name was "Thumbnail" instead of "Thumbnails". The - // old name is still used as fallback but this code can - // be removed soon. - if ( ! xIStream.is()) + auto rels = xRelationshipAccess->getRelationshipsByType(type); + if (rels.hasElements()) { - uno::Reference<embed::XStorage> xStorage ( - xDocStorage->openStorageElement( "Thumbnail", - embed::ElementModes::READ)); - if (xStorage.is()) + // ISO/IEC 29500-1:2016(E) 15.2.16 Thumbnail Part: "Packages shall not contain + // more than one thumbnail relationship associated with the package as a whole" + for (const auto& [tag, value] : rels[0]) { - uno::Reference<io::XStream> xThumbnailCopy ( - xStorage->cloneStreamElement("thumbnail.png")); - if (xThumbnailCopy.is()) - xIStream = xThumbnailCopy->getInputStream(); + if (tag == "Id") + { + return getHierarchicalStream(xStorage, + xRelationshipAccess->getTargetByID(value)); + } } } } - catch (const uno::Exception&) - { - TOOLS_WARN_EXCEPTION("sfx", - "caught exception while trying to access Thumbnails/thumbnail.png of " << msURL); - } + } + return {}; +} + +BitmapEx ThumbnailView::readThumbnail(const OUString &msURL) +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + + // Load the thumbnail from a template document. + uno::Reference<io::XInputStream> xIStream; + + try + { + // An (older) implementation had a bug - The storage + // name was "Thumbnail" instead of "Thumbnails". The + // old name is still used as fallback but this code can + // be removed soon. + xIStream = getFirstHierarchicalStream( + msURL, embed::StorageFormats::PACKAGE, + { u"Thumbnails/thumbnail.png"_ustr, u"Thumbnail/thumbnail.png"_ustr }); } catch (const uno::Exception&) { @@ -133,14 +157,29 @@ BitmapEx ThumbnailView::readThumbnail(const OUString &msURL) << msURL); } + if (!xIStream.is()) + { + // OOXML? + try + { + // Check both Transitional and Strict relationships + xIStream = getFirstStreamByRelType( + msURL, + { u"http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"_ustr, + u"http://purl.oclc.org/ooxml/officeDocument/relationships/metadata/thumbnail"_ustr }); + } + catch (const uno::Exception&) + { + // Not an OOXML; fine + } + } + // Extract the image from the stream. BitmapEx aThumbnail; - if (xIStream.is()) + if (auto pStream = utl::UcbStreamHelper::CreateStream(xIStream, /*CloseStream=*/true)) { - std::unique_ptr<SvStream> pStream ( - ::utl::UcbStreamHelper::CreateStream (xIStream)); - vcl::PngImageReader aReader (*pStream); - aThumbnail = aReader.read (); + Graphic aGraphic = GraphicFilter::GetGraphicFilter().ImportUnloadedGraphic(*pStream); + aThumbnail = aGraphic.GetBitmapEx(); } // Note that the preview is returned without scaling it to the desired