sd/qa/unit/data/odp/tdf168109.fodp | 65 +++++++++++++++++++++++++++++++++++++ sd/qa/unit/import-tests2.cxx | 31 +++++++++++++++++ vcl/win/gdi/salprn.cxx | 17 +++++---- xmloff/source/draw/ximpbody.cxx | 5 +- xmloff/source/draw/ximpnote.cxx | 17 +++++++++ xmloff/source/draw/ximpnote.hxx | 11 +++++- xmloff/source/draw/ximpshap.cxx | 2 - xmloff/source/draw/ximpshap.hxx | 5 ++ 8 files changed, 140 insertions(+), 13 deletions(-)
New commits: commit 5a0d14eb62dcccfd8f6185b9f493d6e1ff3f4bf1 Author: Mike Kaganski <[email protected]> AuthorDate: Thu Feb 12 19:11:29 2026 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Thu Feb 12 18:45:46 2026 +0100 tdf#168109: Ignore draw:page-number attribute for notes' thumbnails ODF 1.4 Part 3 sect. 19.203 draw:page-number tells: > For thumbnails on notes pages, the value of this attribute is fixed > to the drawing page of a notes page. As further clarified in OASIS ODF TC call from 2025-11-24 for issue OFFICE-4178, that wording shall be improved to unambiguously point to exactly the drawing page of the respective notes page. This means, that for thumbnail of DrawingPage's notes page, the actual value of the attribute is unimportant. As the bugdoc shows, the attribute may be missing. It may also possibly have wrong number. In any case, we should not break the default correct reference established between the thumbnail object and respective DrawingPage, when thumbnail was created. This change implements just that. For SdXMLPageShapeContext, it adds a new method ignorePageNumber, which sets mnPageNumber to -1. Given that this value corresponds to draw:page-number, having XML type positiveInteger, this special value is safe and cannot coincide with any valid value there. Its meaning is "do not set the shape's page number", which will keep the mentioned original reference. ignorePageNumber is only called in the context of DrawingPage/notes page: the "ignore" flag is passed through SdXMLNotesContext from SdXMLDrawPageContext::createFastChildContext. For notes of master page, handled in SdXMLMasterPageContext::createFastChildContext, no change is done. Change-Id: I39ef16d4e0ceda89b38ee994c908157e93c119a2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199283 Reviewed-by: Mike Kaganski <[email protected]> Tested-by: Jenkins diff --git a/sd/qa/unit/data/odp/tdf168109.fodp b/sd/qa/unit/data/odp/tdf168109.fodp new file mode 100644 index 000000000000..9a38821214a3 --- /dev/null +++ b/sd/qa/unit/data/odp/tdf168109.fodp @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.presentation"> + <office:styles> + <style:style style:name="standard" style:family="graphic"> + <style:graphic-properties draw:fill="none"/> + </style:style> + </office:styles> + <office:body> + <office:presentation> + <draw:page> + <draw:frame svg:width="20cm" svg:height="3cm" svg:x="5mm" svg:y="5mm" presentation:class="title"> + <draw:text-box> + <text:p>Slide One</text:p> + </draw:text-box> + </draw:frame> + <draw:frame svg:width="20cm" svg:height="9cm" svg:x="5mm" svg:y="4cm" presentation:class="outline"> + <draw:text-box> + <text:list> + <text:list-item> + <text:p>One</text:p> + </text:list-item> + <text:list-item> + <text:p>Two</text:p> + </text:list-item> + </text:list> + </draw:text-box> + </draw:frame> + <presentation:notes> + <!-- First case: the 'draw:page-number' attribute is missing --> + <draw:page-thumbnail svg:width="20cm" svg:height="11cm" svg:x="5mm" svg:y="5mm" presentation:class="page"/> + <draw:frame svg:width="20cm" svg:height="13cm" svg:x="5mm" svg:y="12cm"> + <draw:text-box/> + </draw:frame> + </presentation:notes> + </draw:page> + <draw:page> + <draw:frame svg:width="20cm" svg:height="3cm" svg:x="5mm" svg:y="5mm" presentation:class="title"> + <draw:text-box> + <text:p>Slide Two</text:p> + </draw:text-box> + </draw:frame> + <draw:frame svg:width="20cm" svg:height="9cm" svg:x="5mm" svg:y="4cm" presentation:class="outline"> + <draw:text-box> + <text:list> + <text:list-item> + <text:p>Foo</text:p> + </text:list-item> + <text:list-item> + <text:p>Bar</text:p> + </text:list-item> + </text:list> + </draw:text-box> + </draw:frame> + <presentation:notes> + <!-- Second case: the 'draw:page-number' attribute is wrong --> + <draw:page-thumbnail svg:width="20cm" svg:height="11cm" svg:x="5mm" svg:y="5mm" draw:page-number="1" presentation:class="page"/> + <draw:frame svg:width="20cm" svg:height="13cm" svg:x="5mm" svg:y="12cm"> + <draw:text-box/> + </draw:frame> + </presentation:notes> + </draw:page> + </office:presentation> + </office:body> +</office:document> \ No newline at end of file diff --git a/sd/qa/unit/import-tests2.cxx b/sd/qa/unit/import-tests2.cxx index 570e2b9690cb..6fe0cf439d65 100644 --- a/sd/qa/unit/import-tests2.cxx +++ b/sd/qa/unit/import-tests2.cxx @@ -2314,6 +2314,37 @@ CPPUNIT_TEST_FIXTURE(SdImportTest2, testTdf169524) CPPUNIT_ASSERT_EQUAL(sal_Int32(0), nLeftMargin); } +CPPUNIT_TEST_FIXTURE(SdImportTest2, testTdf168109) +{ + createSdImpressDoc("odp/tdf168109.fodp"); + + // Slide 1 + { + auto xPage = getPage(0).queryThrow<presentation::XPresentationPage>(); + auto xNotesPage = xPage->getNotesPage(); + auto xThumbnail = getShape(0, xNotesPage); + auto xDescriptor = xThumbnail.queryThrow<drawing::XShapeDescriptor>(); + + CPPUNIT_ASSERT_EQUAL(u"com.sun.star.presentation.PageShape"_ustr, + xDescriptor->getShapeType()); + CPPUNIT_ASSERT_EQUAL(uno::Any(sal_Int32(1)), + xThumbnail->getPropertyValue(u"PageNumber"_ustr)); + } + + // Slide 2 + { + auto xPage = getPage(1).queryThrow<presentation::XPresentationPage>(); + auto xNotesPage = xPage->getNotesPage(); + auto xThumbnail = getShape(0, xNotesPage); + auto xDescriptor = xThumbnail.queryThrow<drawing::XShapeDescriptor>(); + + CPPUNIT_ASSERT_EQUAL(u"com.sun.star.presentation.PageShape"_ustr, + xDescriptor->getShapeType()); + CPPUNIT_ASSERT_EQUAL(uno::Any(sal_Int32(2)), + xThumbnail->getPropertyValue(u"PageNumber"_ustr)); + } +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/draw/ximpbody.cxx b/xmloff/source/draw/ximpbody.cxx index f1f19a156ffa..591832735414 100644 --- a/xmloff/source/draw/ximpbody.cxx +++ b/xmloff/source/draw/ximpbody.cxx @@ -247,8 +247,9 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLDrawPageContext: uno::Reference< drawing::XDrawPage > xNotesDrawPage = xPresPage->getNotesPage(); if(xNotesDrawPage.is()) { - // presentation:notes inside draw:page context - return new SdXMLNotesContext( GetSdImport(), xAttrList, xNotesDrawPage); + // presentation:notes inside draw:page context. Inside notes, thumbnails + // are fixed to this page, and actual page number attribute must be ignored + return new SdXMLNotesContext(GetSdImport(), xAttrList, xNotesDrawPage, true); } } } diff --git a/xmloff/source/draw/ximpnote.cxx b/xmloff/source/draw/ximpnote.cxx index d855e85bf4ad..9035648cb9ed 100644 --- a/xmloff/source/draw/ximpnote.cxx +++ b/xmloff/source/draw/ximpnote.cxx @@ -18,6 +18,7 @@ */ #include "ximpnote.hxx" +#include "ximpshap.hxx" #include <xmloff/xmlnamespace.hxx> using namespace ::com::sun::star; @@ -25,8 +26,9 @@ using namespace ::xmloff::token; SdXMLNotesContext::SdXMLNotesContext( SdXMLImport& rImport, const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList, - uno::Reference<drawing::XShapes> const& rShapes) + uno::Reference<drawing::XShapes> const& rShapes, bool ignorePageNumberInThumbnail) : SdXMLGenericPageContext(rImport, xAttrList, rShapes) + , mbIgnorePageNumberInThumbnail(ignorePageNumberInThumbnail) { OUString sStyleName, sPageMasterName; @@ -89,4 +91,17 @@ SdXMLNotesContext::SdXMLNotesContext( SdXMLNotesContext::~SdXMLNotesContext() {} +uno::Reference<xml::sax::XFastContextHandler> SdXMLNotesContext::createFastChildContext( + sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) +{ + auto pContext = SdXMLGenericPageContext::createFastChildContext(nElement, xAttrList); + if (nElement == XML_ELEMENT(DRAW, XML_PAGE_THUMBNAIL) && mbIgnorePageNumberInThumbnail) + { + assert(dynamic_cast<SdXMLPageShapeContext*>(pContext.get())); + auto pPageShapeContext = static_cast<SdXMLPageShapeContext*>(pContext.get()); + pPageShapeContext->ignorePageNumber(); + } + return pContext; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/draw/ximpnote.hxx b/xmloff/source/draw/ximpnote.hxx index d7fe276b84eb..2048fc36ea62 100644 --- a/xmloff/source/draw/ximpnote.hxx +++ b/xmloff/source/draw/ximpnote.hxx @@ -29,8 +29,17 @@ class SdXMLNotesContext : public SdXMLGenericPageContext public: SdXMLNotesContext( SdXMLImport& rImport, const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList, - css::uno::Reference< css::drawing::XShapes > const & rShapes); + css::uno::Reference< css::drawing::XShapes > const & rShapes, + bool ignorePageNumberInThumbnail = false); virtual ~SdXMLNotesContext() override; + + // XFastContextHandler + virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override; + +private: + bool mbIgnorePageNumberInThumbnail; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/draw/ximpshap.cxx b/xmloff/source/draw/ximpshap.cxx index 3c840b6d4bc7..5caef558f8a7 100644 --- a/xmloff/source/draw/ximpshap.cxx +++ b/xmloff/source/draw/ximpshap.cxx @@ -2244,7 +2244,7 @@ void SdXMLPageShapeContext::startFastElement (sal_Int32 nElement, SetTransformation(); uno::Reference< beans::XPropertySet > xPropSet(mxShape, uno::UNO_QUERY); - if(xPropSet.is()) + if (xPropSet.is() && mnPageNumber >= 0) // mnPageNumber < 0 means "ignore" { uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); static constexpr OUString aPageNumberStr(u"PageNumber"_ustr); diff --git a/xmloff/source/draw/ximpshap.hxx b/xmloff/source/draw/ximpshap.hxx index 6a00cb252e5f..640cf26fdb5b 100644 --- a/xmloff/source/draw/ximpshap.hxx +++ b/xmloff/source/draw/ximpshap.hxx @@ -340,7 +340,7 @@ public: virtual bool processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & ) override; }; -// draw:page context +// draw:thumbnail context class SdXMLPageShapeContext : public SdXMLShapeContext { @@ -359,6 +359,9 @@ public: // this is called from the parent group for each unparsed attribute in the attribute list virtual bool processAttribute( const sax_fastparser::FastAttributeList::FastAttributeIter & ) override; + + // Do not try to set page number to the thumbnail (keep the default association) + void ignorePageNumber() { mnPageNumber = -1; } }; // draw:caption context commit 390eebddedd955ee7ce4b258fa9aaabbf9374a62 Author: Mike Kaganski <[email protected]> AuthorDate: Wed Feb 11 17:43:53 2026 +0500 Commit: Mike Kaganski <[email protected]> CommitDate: Thu Feb 12 18:45:39 2026 +0100 rtl_allocateZeroMemory / std::free mismatch; use std::calloc Indeed, rtl_allocateZeroMemory is implemented itself using calloc; but to not rely on implementation details, we either should use rtl_FreeMemory, or one of std allocation functions. Even if it's not a problem here, rtl_allocateZeroMemory has signature that takes a single size for the whole block. If allocating an array, multiplying a size, that doesn't match fundamental alignment, by the number of elements might give wrong size of array. Change-Id: Icc45772113f4058155c1fccc7c4096958c46e3c4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199282 Reviewed-by: Mike Kaganski <[email protected]> Tested-by: Jenkins diff --git a/vcl/win/gdi/salprn.cxx b/vcl/win/gdi/salprn.cxx index 8fa2a2adb16f..9520e33ba133 100644 --- a/vcl/win/gdi/salprn.cxx +++ b/vcl/win/gdi/salprn.cxx @@ -441,7 +441,8 @@ static void ImplDevModeToJobSetup( WinSalInfoPrinter const * pPrinter, ImplJobSe if ( nCount && (nCount != GDI_ERROR) ) { - WORD* pBins = static_cast<WORD*>(rtl_allocateZeroMemory( nCount*sizeof(WORD) )); + WORD* pBins = static_cast<WORD*>(std::calloc(nCount, sizeof(WORD))); + assert(pBins); ImplDeviceCaps( pPrinter, DC_BINS, pBins, pSetupData ); pSetupData->SetPaperBin( 0 ); @@ -475,12 +476,12 @@ static void ImplDevModeToJobSetup( WinSalInfoPrinter const * pPrinter, ImplJobSe POINT* pPaperSizes = nullptr; if ( nPaperCount && (nPaperCount != GDI_ERROR) ) { - pPapers = static_cast<WORD*>(rtl_allocateZeroMemory(nPaperCount*sizeof(WORD))); + pPapers = static_cast<WORD*>(std::calloc(nPaperCount, sizeof(WORD))); ImplDeviceCaps( pPrinter, DC_PAPERS, pPapers, pSetupData ); } if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) ) { - pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT))); + pPaperSizes = static_cast<POINT*>(std::calloc(nPaperSizeCount, sizeof(POINT))); ImplDeviceCaps( pPrinter, DC_PAPERSIZE, pPaperSizes, pSetupData ); } if( nPaperSizeCount == nPaperCount && pPaperSizes && pPapers ) @@ -710,7 +711,8 @@ static void ImplJobSetupToDevMode( WinSalInfoPrinter const * pPrinter, const Imp if ( nCount && (nCount != GDI_ERROR) ) { - WORD* pBins = static_cast<WORD*>(rtl_allocateZeroMemory(nCount*sizeof(WORD))); + WORD* pBins = static_cast<WORD*>(std::calloc(nCount, sizeof(WORD))); + assert(pBins); ImplDeviceCaps( pPrinter, DC_BINS, pBins, pSetupData ); pDevModeW->dmFields |= DM_DEFAULTSOURCE; pDevModeW->dmDefaultSource = pBins[ pSetupData->GetPaperBin() ]; @@ -888,12 +890,12 @@ static void ImplJobSetupToDevMode( WinSalInfoPrinter const * pPrinter, const Imp DWORD nLandscapeAngle = ImplDeviceCaps( pPrinter, DC_ORIENTATION, nullptr, pSetupData ); if ( nPaperCount && (nPaperCount != GDI_ERROR) ) { - pPapers = static_cast<WORD*>(rtl_allocateZeroMemory(nPaperCount*sizeof(WORD))); + pPapers = static_cast<WORD*>(std::calloc(nPaperCount, sizeof(WORD))); ImplDeviceCaps( pPrinter, DC_PAPERS, pPapers, pSetupData ); } if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) ) { - pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT))); + pPaperSizes = static_cast<POINT*>(std::calloc(nPaperSizeCount, sizeof(POINT))); ImplDeviceCaps( pPrinter, DC_PAPERSIZE, pPaperSizes, pSetupData ); } if ( (nPaperSizeCount == nPaperCount) && pPapers && pPaperSizes ) @@ -1095,7 +1097,8 @@ void WinSalInfoPrinter::InitPaperFormats( const ImplJobSetup* pSetupData ) if( nCount ) { - POINT* pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nCount*sizeof(POINT))); + POINT* pPaperSizes = static_cast<POINT*>(std::calloc(nCount, sizeof(POINT))); + assert(pPaperSizes); ImplDeviceCaps( this, DC_PAPERSIZE, pPaperSizes, pSetupData ); SAL_INFO("vcl.print", "DC_PAPERSIZE sizes (mm) from printer: " << DC_PAPERSIZE_array_to_string(pPaperSizes, nCount));
