external/liborcus/UnpackedTarball_liborcus.mk | 5 external/liborcus/forcepoint-83.patch.1 | 38 hwpfilter/source/hbox.cxx | 12 include/oox/export/drawingml.hxx | 13 include/svx/svdedtv.hxx | 2 instsetoo_native/inc_openoffice/unix/find-requires-x11.sh | 2 oox/qa/unit/data/tdf147978_endsubpath.odp |binary oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp |binary oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp |binary oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp |binary oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp |binary oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx |binary oox/qa/unit/export.cxx | 155 + oox/source/export/drawingml.cxx | 919 ++++++---- oox/source/export/shapes.cxx | 31 sc/qa/unit/data/xlsx/tdf147014.xlsx |binary sc/qa/unit/subsequent_filters_test2.cxx | 18 sc/qa/unit/tiledrendering/tiledrendering.cxx | 31 sc/source/filter/oox/worksheethelper.cxx | 25 sc/source/ui/view/cellsh2.cxx | 49 sc/source/ui/view/gridwin4.cxx | 4 sc/source/ui/view/tabvwsh2.cxx | 11 sd/qa/unit/data/odp/tdf147978_enhancedPath_viewBox.odp |binary sd/qa/unit/data/xml/tdf92001_0.xml | 42 sd/qa/unit/export-tests-ooxml2.cxx | 32 sd/qa/unit/export-tests-ooxml3.cxx | 16 svx/sdi/svx.sdi | 2 svx/source/svdraw/svdedtv.cxx | 7 sw/inc/deletelistener.hxx | 92 + sw/qa/extras/layout/data/forcepoint92.doc |binary sw/qa/extras/layout/layout.cxx | 6 sw/qa/extras/ooxmlexport/data/tdf147978_enhancedPath_commandABVW.odt |binary sw/qa/extras/ooxmlexport/data/tdf148111.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 44 sw/qa/extras/ooxmlexport/ooxmlexport7.cxx | 24 sw/qa/extras/uiwriter/uiwriter3.cxx | 16 sw/source/core/frmedt/tblsel.cxx | 11 sw/source/core/layout/layact.cxx | 3 sw/source/core/layout/tabfrm.cxx | 8 sw/source/core/text/itratr.cxx | 2 sw/source/core/undo/untbl.cxx | 29 sw/source/filter/ww8/docxsdrexport.cxx | 20 sw/source/filter/ww8/rtfattributeoutput.cxx | 4 sw/source/filter/ww8/ww8par2.cxx | 71 sw/uiconfig/swxform/toolbar/formdesign.xml | 2 vcl/source/helper/strhelper.cxx | 3 writerfilter/source/dmapper/SdtHelper.cxx | 3 47 files changed, 1245 insertions(+), 507 deletions(-)
New commits: commit a39eed087762afe703561782aeebdd8aa28118be Merge: 00e7d7588b8a 6b1071b6096f Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Wed Mar 30 06:50:04 2022 +0200 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Wed Mar 30 06:50:04 2022 +0200 Merge branch 'libreoffice-7-3' into distro/lhm/libreoffice-7-3+backports Change-Id: I0a6e4a1a1dd38bdad0a146aec1f839786fd78fa5 commit 6b1071b6096f65de1244f8f3994366bb9cd0ff09 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Wed Mar 23 16:49:03 2022 +0000 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Tue Mar 29 11:53:22 2022 +0200 forcepoint#83 Invalid read of size 1 Change-Id: I1576dfd8c9731d943107764aeb66bb1c2294ad5f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131996 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/external/liborcus/UnpackedTarball_liborcus.mk b/external/liborcus/UnpackedTarball_liborcus.mk index 87e462e33d91..a87da7edb88d 100644 --- a/external/liborcus/UnpackedTarball_liborcus.mk +++ b/external/liborcus/UnpackedTarball_liborcus.mk @@ -15,8 +15,8 @@ $(eval $(call gb_UnpackedTarball_set_patchlevel,liborcus,1)) $(eval $(call gb_UnpackedTarball_update_autoconf_configs,liborcus)) -# crashtesting-crash-on-passing-null-to-std-string_vie.patch.1 submitted as -# https://gitlab.com/orcus/orcus/-/merge_requests/113 +# forcepoint-83.patch.1 submitted as +# https://gitlab.com/orcus/orcus/-/merge_requests/117 $(eval $(call gb_UnpackedTarball_add_patches,liborcus,\ external/liborcus/rpath.patch.0 \ @@ -25,6 +25,7 @@ $(eval $(call gb_UnpackedTarball_add_patches,liborcus,\ external/liborcus/fix-pch.patch.0 \ external/liborcus/liborcus_newline.patch.1 \ external/liborcus/std-get-busted.patch.1 \ + external/liborcus/forcepoint-83.patch.1 \ )) ifeq ($(OS),WNT) diff --git a/external/liborcus/forcepoint-83.patch.1 b/external/liborcus/forcepoint-83.patch.1 new file mode 100644 index 000000000000..bfd3bb86fcf9 --- /dev/null +++ b/external/liborcus/forcepoint-83.patch.1 @@ -0,0 +1,38 @@ +From 283b45ba3bcb22dc28303a09a96c9b94f86d1ba2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Caol=C3=A1n=20McNamara?= <caol...@redhat.com> +Date: Wed, 23 Mar 2022 16:44:00 +0000 +Subject: [PATCH] forcepoint#83 Invalid read of size 1 + +==343916== Invalid read of size 1 +==343916== at 0x11A7B2F0: orcus::parser_base::cur_char() const (parser_base.hpp:79) +==343916== by 0x11B7B112: orcus::sax_parser<orcus::sax_ns_parser<orcus::sax_token_parser<orcus::xml_stream_handler>::handler_wrapper>::handler_wrapper, orcus::sax_parser_default_config>::element_open(long) (sax_parser.hpp:258) +==343916== by 0x11B7A2C7: orcus::sax_parser<orcus::sax_ns_parser<orcus::sax_token_parser<orcus::xml_stream_handler>::handler_wrapper>::handler_wrapper, orcus::sax_parser_default_config>::element() (sax_parser.hpp:246) +==343916== by 0x11B7A197: orcus::sax_parser<orcus::sax_ns_parser<orcus::sax_token_parser<orcus::xml_stream_handler>::handler_wrapper>::handler_wrapper, orcus::sax_parser_default_config>::body() (sax_parser.hpp:214) +==343916== by 0x11B79FD9: orcus::sax_parser<orcus::sax_ns_parser<orcus::sax_token_parser<orcus::xml_stream_handler>::handler_wrapper>::handler_wrapper, orcus::sax_parser_default_config>::parse() (sax_parser.hpp:182) +==343916== by 0x11B79F8B: orcus::sax_ns_parser<orcus::sax_token_parser<orcus::xml_stream_handler>::handler_wrapper>::parse() (sax_ns_parser.hpp:277) +==343916== by 0x11B79768: orcus::sax_token_parser<orcus::xml_stream_handler>::parse() (sax_token_parser.hpp:215) +==343916== by 0x11B79406: orcus::xml_stream_parser::parse() (xml_stream_parser.cpp:68) +==343916== by 0x11BE3805: orcus::orcus_xlsx::detect(unsigned char const*, unsigned long) (orcus_xlsx.cpp:188) +==343916== by 0x11AB2482: orcus::detect(unsigned char const*, unsigned long) (format_detection.cpp:60) +==343916== by 0x30E60945: (anonymous namespace)::OrcusFormatDetect::detect(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>&) (filterdetect.cxx:83) +==343916== by 0x30E60ABE: non-virtual thunk to (anonymous namespace)::OrcusFormatDetect::detect(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue>&) (filterdetect.cxx:0) +--- + include/orcus/sax_parser.hpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/include/orcus/sax_parser.hpp b/include/orcus/sax_parser.hpp +index 15e8d917..d0fc45b5 100644 +--- a/include/orcus/sax_parser.hpp ++++ b/include/orcus/sax_parser.hpp +@@ -255,6 +255,8 @@ void sax_parser<_Handler,_Config>::element_open(std::ptrdiff_t begin_pos) + while (true) + { + skip_space_and_control(); ++ if (!has_char()) ++ return; + char c = cur_char(); + if (c == '/') + { +-- +2.35.1 + commit e672a8351df5e9fcbdafa34857a16f2bd547ebd6 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Fri Mar 25 12:10:32 2022 +0000 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Tue Mar 29 11:38:54 2022 +0200 forcepoint#89 don't remove page with footnote continuation frame in browse/html/web mode Change-Id: Ic821dd7f2cc1f47305b5fe2ced16d5168aedc0b9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132045 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx index 33a990a15a5a..da6e7c938f6b 100644 --- a/sw/source/core/layout/layact.cxx +++ b/sw/source/core/layout/layact.cxx @@ -295,7 +295,8 @@ bool SwLayAction::RemoveEmptyBrowserPages() do { if ( (pPage->GetSortedObjs() && pPage->GetSortedObjs()->size()) || - pPage->ContainsContent() ) + pPage->ContainsContent() || + pPage->FindFootnoteCont() ) pPage = static_cast<SwPageFrame*>(pPage->GetNext()); else { commit 0d0cee5e48ca523f11540e9ae0ff022692fd9dca Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Sun Mar 27 12:03:06 2022 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Tue Mar 29 11:38:00 2022 +0200 forcepoint#92 fix crash on layout of specific doc Change-Id: Id40d25d05d10d641d071cddd2e1c84594ac777a6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132142 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caol...@redhat.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132148 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/sw/qa/extras/layout/data/forcepoint92.doc b/sw/qa/extras/layout/data/forcepoint92.doc new file mode 100644 index 000000000000..49c4a7f11dfe Binary files /dev/null and b/sw/qa/extras/layout/data/forcepoint92.doc differ diff --git a/sw/qa/extras/layout/layout.cxx b/sw/qa/extras/layout/layout.cxx index 94edf650a359..bde69da4dde3 100644 --- a/sw/qa/extras/layout/layout.cxx +++ b/sw/qa/extras/layout/layout.cxx @@ -2496,6 +2496,12 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepointFootnoteFrame) //FIXME: disabled after failing again with fixed layout //CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepoint76) { createSwDoc(DATA_DIRECTORY, "forcepoint76-1.rtf"); } +//just care it doesn't crash/assert +CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testForcepoint92) +{ + createSwDoc(DATA_DIRECTORY, "forcepoint92.doc"); +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter, testTdf118058) { SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf118058.fodt"); diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 7786f3aec4fc..b405a2124254 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -24,6 +24,7 @@ #include <viewimp.hxx> #include <fesh.hxx> #include <swtable.hxx> +#include <deletelistener.hxx> #include <dflyobj.hxx> #include <anchoreddrawobject.hxx> #include <fmtanchr.hxx> @@ -2114,13 +2115,18 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) } SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr; bool bReformat; + std::optional<SfxDeleteListener> oDeleteListener; + if (pOldBoss) + oDeleteListener.emplace(*pOldBoss); SwFrameDeleteGuard g(this); if ( MoveBwd( bReformat ) ) { + SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted"); + aRectFnSet.Refresh(this); bMovedBwd = true; aNotify.SetLowersComplete( false ); - if ( bFootnotesInDoc ) + if (bFootnotesInDoc && !oDeleteListener->WasDeleted()) MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true ); if ( bReformat || bKeep ) { commit 26872d9b408660056fedfa18ba2be25a14b771c1 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Sat Mar 26 21:50:49 2022 +0000 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Tue Mar 29 11:30:20 2022 +0200 move DeleteListener contraptions to toplevel writer includes Change-Id: Ifa1e75b62da4174f27fca52eb86559cd6a381513 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132141 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caol...@redhat.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132147 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/sw/inc/deletelistener.hxx b/sw/inc/deletelistener.hxx new file mode 100644 index 000000000000..2b212e418fef --- /dev/null +++ b/sw/inc/deletelistener.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <svl/listener.hxx> +#include <svl/lstner.hxx> +#include "calbck.hxx" + +class SwDeleteListener final : public SwClient +{ +private: + SwModify* m_pModify; + + virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override + { + if (rHint.GetId() != SfxHintId::SwLegacyModify) + return; + auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); + if (pLegacy->GetWhich() == RES_OBJECTDYING) + { + m_pModify->Remove(this); + m_pModify = nullptr; + } + } + +public: + SwDeleteListener(SwModify& rModify) + : m_pModify(&rModify) + { + m_pModify->Add(this); + } + + bool WasDeleted() const { return !m_pModify; } + + virtual ~SwDeleteListener() override + { + if (!m_pModify) + return; + m_pModify->Remove(this); + } +}; + +class SvtDeleteListener final : public SvtListener +{ +private: + bool bObjectDeleted; + +public: + explicit SvtDeleteListener(SvtBroadcaster& rNotifier) + : bObjectDeleted(false) + { + StartListening(rNotifier); + } + + virtual void Notify(const SfxHint& rHint) override + { + if (rHint.GetId() == SfxHintId::Dying) + bObjectDeleted = true; + } + + bool WasDeleted() const { return bObjectDeleted; } +}; + +class SfxDeleteListener final : public SfxListener +{ +private: + bool bObjectDeleted; + +public: + explicit SfxDeleteListener(SfxBroadcaster& rNotifier) + : bObjectDeleted(false) + { + StartListening(rNotifier); + } + + virtual void Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint) override + { + if (rHint.GetId() == SfxHintId::Dying) + bObjectDeleted = true; + } + + bool WasDeleted() const { return bObjectDeleted; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx index 3f3c3adfb74f..540b25018743 100644 --- a/sw/source/filter/ww8/ww8par2.cxx +++ b/sw/source/filter/ww8/ww8par2.cxx @@ -39,6 +39,7 @@ #include <editeng/pgrditem.hxx> #include <msfilter.hxx> #include <pam.hxx> +#include <deletelistener.hxx> #include <doc.hxx> #include <IDocumentStylePoolAccess.hxx> #include <docary.hxx> @@ -167,32 +168,6 @@ sal_uInt32 wwSectionManager::GetWWPageTopMargin() const return !maSegments.empty() ? maSegments.back().maSep.dyaTop : 0; } -namespace -{ - class DeleteListener final : public SvtListener - { - private: - bool bObjectDeleted; - public: - explicit DeleteListener(SvtBroadcaster& rNotifier) - : bObjectDeleted(false) - { - StartListening(rNotifier); - } - - virtual void Notify(const SfxHint& rHint) override - { - if (rHint.GetId() == SfxHintId::Dying) - bObjectDeleted = true; - } - - bool WasDeleted() const - { - return bObjectDeleted; - } - }; -} - sal_uInt16 SwWW8ImplReader::End_Footnote() { /* @@ -252,7 +227,7 @@ sal_uInt16 SwWW8ImplReader::End_Footnote() SwFormatFootnote& rFormatFootnote = static_cast<SwFormatFootnote&>(pFN->GetAttr()); - DeleteListener aDeleteListener(rFormatFootnote.GetNotifier()); + SvtDeleteListener aDeleteListener(rFormatFootnote.GetNotifier()); // read content of Ft-/End-Note Read_HdFtFootnoteText( pSttIdx, rDesc.mnStartCp, rDesc.mnLen, rDesc.meType); @@ -2783,46 +2758,6 @@ void WW8TabDesc::MoveOutsideTable() *m_pIo->m_pPaM->GetPoint() = *m_xTmpPos->GetPoint(); } -namespace -{ - class SwTableNodeListener final : public SwClient - { - private: - SwModify* m_pModify; - - virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override - { - if (rHint.GetId() != SfxHintId::SwLegacyModify) - return; - auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint); - if (pLegacy->GetWhich() == RES_OBJECTDYING) - { - m_pModify->Remove(this); - m_pModify = nullptr; - } - } - - public: - SwTableNodeListener(SwModify* pModify) - : m_pModify(pModify) - { - m_pModify->Add(this); - } - - bool WasDeleted() const - { - return !m_pModify; - } - - virtual ~SwTableNodeListener() override - { - if (!m_pModify) - return; - m_pModify->Remove(this); - } - }; -} - void WW8TabDesc::FinishSwTable() { m_pIo->m_xRedlineStack->closeall(*m_pIo->m_pPaM->GetPoint()); @@ -2833,7 +2768,7 @@ void WW8TabDesc::FinishSwTable() m_pIo->m_pLastAnchorPos.reset(); SwTableNode* pTableNode = m_pTable->GetTableNode(); - SwTableNodeListener aListener(pTableNode); + SwDeleteListener aListener(*pTableNode); m_pIo->m_xRedlineStack = std::move(mxOldRedlineStack); if (xLastAnchorCursor) commit 2bbd6998aefe4a83c93a1810430f1a0da5364284 Author: Aron Budea <aron.bu...@collabora.com> AuthorDate: Thu Mar 24 22:02:17 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Tue Mar 29 10:32:34 2022 +0200 sc: fix crash in SdrPageView::GetPageWindow() From online crashreport: SIG Fatal signal received: SIGSEGV SdrPageView::GetPageWindow(unsigned int) const svx/source/svdraw/svdpagv.cxx:84 (anonymous namespace)::ScLOKProxyObjectContact::calculateGridOffsetForViewOjectContact(basegfx::B2DVector&, sdr::contact::ViewObjectContact const&) const sc/source/ui/view/gridwin4.cxx:1397 sdr::contact::ViewObjectContact::getGridOffset() const svx/source/sdr/contact/viewobjectcontact.cxx:463 sdr::contact::ViewObjectContact::getPrimitive2DSequence(sdr::contact::DisplayInfo const&) const include/basegfx/tuple/b2dtuple.hxx:81 sdr::contact::ViewObjectContact::getObjectRange() const svx/source/sdr/contact/viewobjectcontact.cxx:198 ScLOKProxyObjectContact::calculateGridOffsetForViewOjectContact() didn't check if PageWindowCount() was non-zero. Change-Id: I4a00b5b13a277d0805af3076150a952306908e53 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132251 Tested-by: Jenkins Reviewed-by: Aron Budea <aron.bu...@collabora.com> (cherry picked from commit 5d0965f3f12fedb2228ba28f261a90245e343c09) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132181 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx index 5da4a54508d2..d1aa33f4c847 100644 --- a/sc/source/ui/view/gridwin4.cxx +++ b/sc/source/ui/view/gridwin4.cxx @@ -1355,7 +1355,9 @@ namespace if (!pPageView) return; - SdrPageWindow* pSdrPageWindow = pPageView->GetPageWindow(0); + SdrPageWindow* pSdrPageWindow = nullptr; + if (pPageView->PageWindowCount() > 0) + pSdrPageWindow = pPageView->GetPageWindow(0); if (!pSdrPageWindow) return; commit e42219cb3664b3727a6b399153d7891108c01f0c Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Mon Mar 28 11:05:34 2022 +0200 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Tue Mar 29 06:16:16 2022 +0200 tdf#148235 Restore toolbar item to switch XForm to design mode commit 45b6f096e7ae86d0692ecdfd5b7069622d8a6efa Date: Sun Oct 28 17:02:34 2018 +0100 sw toolbars sync context toolbars between different apps had replaced the "Form Design" toolbar item to switch a complete XForm to design mode with an entry that switches the form controls to design mode instead. (The former one is labelled "Design Mode On/Off", the latter one just "Design Mode" in the English UI.) This meant that the XForm could no longer be switched to design mode from there, unless the toolbar was manually customized to add the entry back. This brings the previous toolbar item back. It also removes the one to switch the form controls to design mode again, since that one is already contained in the "Form Controls" toolbar, where it fits better. (And having two different toolbar items with almost identical labels but different functionality would probably be rather confusing.) Change-Id: Ia4c98dfa6ad8372eba08a9f08920153133a7f88d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132207 Reviewed-by: Andreas Kainz <kain...@gmail.com> Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> Tested-by: Jenkins (cherry picked from commit 4f9bf4201bb706cd19142f0805cfc4c859186cd4) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132177 Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/sw/uiconfig/swxform/toolbar/formdesign.xml b/sw/uiconfig/swxform/toolbar/formdesign.xml index 067ddff0675f..f748bcf68e2e 100644 --- a/sw/uiconfig/swxform/toolbar/formdesign.xml +++ b/sw/uiconfig/swxform/toolbar/formdesign.xml @@ -44,7 +44,7 @@ <toolbar:toolbaritem xlink:href=".uno:LeaveGroup" toolbar:visible="false"/> <toolbar:toolbarseparator/> <toolbar:toolbaritem xlink:href=".uno:SelectObject"/> - <toolbar:toolbaritem xlink:href=".uno:SwitchControlDesignMode"/> + <toolbar:toolbaritem xlink:href=".uno:SwitchXFormsDesignMode"/> <toolbar:toolbarseparator/> <toolbar:toolbaritem xlink:href=".uno:ControlProperties"/> <toolbar:toolbaritem xlink:href=".uno:FormProperties"/> commit 0c1894acda828c02c76c97c25149187cb04ae91f Author: Mark Hung <mark...@gmail.com> AuthorDate: Sat Mar 19 21:18:54 2022 +0800 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 20:53:46 2022 +0200 tdf#141671 fix destroyed pargraph style in exported RTF in MSO. Do not call MoveCharacterProperties() in RtfAttributeOutput ::EndParagraphProperties(). RtfAttributeOutput::ParagraphStyle() has emited run associated properties defined in paragraph style. Calling MoveCharacterProperties() again overwrites them. As this bug is only visible in MS Word, no unit test case is added. Change-Id: I6e5bfd12e8afa7dc286ca54448c1ff022aade31d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131848 Tested-by: Jenkins Reviewed-by: Mark Hung <mark...@gmail.com> (cherry picked from commit 882045b934a3416cc48da2c4e30648892a419577) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132175 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx index cc7d61c80d75..ca43274da2c7 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -384,7 +384,9 @@ void RtfAttributeOutput::EndParagraphProperties( const SwRedlineData* /*pRedlineParagraphMarkerDeleted*/, const SwRedlineData* /*pRedlineParagraphMarkerInserted*/) { - const OString aProperties = MoveCharacterProperties(true); + // Do not call MoveCharacterProperties(), + // Otherwise associate properties in the paragraph style are ruined. + const OString aProperties = m_aStyles.makeStringAndClear(); m_rExport.Strm().WriteOString(aProperties); } commit 8baae1fe3de398f1e6a01d1941f78e4088c0a697 Author: László Németh <nem...@numbertext.org> AuthorDate: Mon Mar 28 11:35:35 2022 +0200 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 20:53:15 2022 +0200 tdf#148228 sw: fix Undo of tracked table deletion in Hide Changes mode In Hide Changes mode, tables didn't reappear during Undo of their tracked deletion, only by saving and reloading the document. Follow-up to commit 0c6221e1545e7b96d9df23cdc24302c28ae935b8 "tdf#148227 sw: fix Undo of tracked row deletion in Hide Changes mode". Change-Id: Ifdc25ab4ae0be25a0c7559ee05b6af2e4f1aa8cf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132169 Tested-by: László Németh <nem...@numbertext.org> Reviewed-by: László Németh <nem...@numbertext.org> (cherry picked from commit eda1a7aeff42c08e02295e5a8353a6d86a61a118) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132178 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index 57f5e6649b70..559d47641a99 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -1988,7 +1988,7 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf146962) // only a single row is visible again assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 1); - // check Undo + // tdf#148227 check Undo of tracked table row deletion dispatchCommand(mxComponent, ".uno:Undo", {}); discardDumpedLayout(); @@ -2040,6 +2040,14 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf147347) pXmlDoc = parseLayoutDump(); // no visible row again assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 0); + + // tdf#148228 check Undo of tracked table deletion + + dispatchCommand(mxComponent, ".uno:Undo", {}); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // This was 0 + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf135014) diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index d5536b01e624..d5c15a0fbc4a 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -952,14 +952,20 @@ void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox ) pFormat->InvalidateInSwCache(RES_ATTRSET_CHG); + // table without table frame + bool bHiddenTable = true; + // for safety, invalidate all TableFrames SwIterator<SwTabFrame,SwFormat> aIter( *pFormat ); for( SwTabFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() ) + { if( pLast->GetTable() == &rTable ) { pLast->InvalidateAll(); pLast->SetCompletePaint(); + bHiddenTable = false; } + } // fill FrameFormats with defaults (0) pFormat = nullptr; @@ -986,7 +992,19 @@ void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox ) m_bModifyBox = false; if ( bHideChanges ) - aTmpBox.MakeFrames( rTable ); + { + if ( bHiddenTable ) + { + SwTableNode* pTableNode = rTable.GetTableNode(); + pTableNode->DelFrames(); + SwNodeIndex aTableIdx( *pTableNode->EndOfSectionNode(), 1 ); + pTableNode->MakeOwnFrames(&aTableIdx); + } + else + { + aTmpBox.MakeFrames( rTable ); + } + } } void SaveTable::SaveContentAttrs( SwDoc* pDoc ) commit e07ea7a6f5f7059663e99daa97cbda5e63b141dd Author: László Németh <nem...@numbertext.org> AuthorDate: Fri Mar 25 15:27:21 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 20:52:58 2022 +0200 tdf#148227 sw: fix Undo of tracked row deletion in Hide Changes mode In Hide Changes mode, table rows didn't reappear during Undo of tracked deletion of table rows, only by saving and reloading the document. Follow-up to commit a74c51025fa4519caaf461492e4ed8e68bd34885 "tdf#146962 sw: hide deleted row at deletion in Hide Changes" and commit 794fd10af7361d5a64a0f8bfbe5c8b5f308617a5 "tdf#147347 sw: hide deleted table at deletion in Hide Changes". Change-Id: I7ffe8a3687d1d385a549f7d438f7058d829ffd8c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132123 Tested-by: László Németh <nem...@numbertext.org> Reviewed-by: László Németh <nem...@numbertext.org> (cherry picked from commit 0c6221e1545e7b96d9df23cdc24302c28ae935b8) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132049 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index b8cb857043b2..57f5e6649b70 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -1987,6 +1987,14 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf146962) pXmlDoc = parseLayoutDump(); // only a single row is visible again assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 1); + + // check Undo + + dispatchCommand(mxComponent, ".uno:Undo", {}); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // This was 1 + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); } CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf147347) diff --git a/sw/source/core/frmedt/tblsel.cxx b/sw/source/core/frmedt/tblsel.cxx index 80b797384859..b203281506cd 100644 --- a/sw/source/core/frmedt/tblsel.cxx +++ b/sw/source/core/frmedt/tblsel.cxx @@ -2336,6 +2336,10 @@ void FndBox_::MakeFrames( SwTable &rTable ) // And this for all instances of a table (for example in header/footer). sal_uInt16 nStPos = 0; sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1; + SwRootFrame* pLayout = + rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(); + bool bHideChanges = pLayout && pLayout->IsHideRedlines(); + if ( m_pLineBefore ) { nStPos = rTable.GetTabLines().GetPos( @@ -2389,9 +2393,14 @@ void FndBox_::MakeFrames( SwTable &rTable ) // ???? or is this the last Follow of the table ???? pUpperFrame = pTable; + SwRedlineTable::size_type nRedlinePos = 0; for ( sal_uInt16 j = nStPos; j <= nEndPos; ++j ) - ::lcl_InsertRow( *rTable.GetTabLines()[j], + { + SwTableLine * pLine = rTable.GetTabLines()[j]; + if ( !bHideChanges || !pLine->IsDeleted(nRedlinePos) ) + ::lcl_InsertRow( *pLine, static_cast<SwLayoutFrame*>(pUpperFrame), pSibling ); + } if ( pUpperFrame->IsTabFrame() ) static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers(); } diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx index d3294b5446a3..d5536b01e624 100644 --- a/sw/source/core/undo/untbl.cxx +++ b/sw/source/core/undo/untbl.cxx @@ -34,6 +34,8 @@ #include <IDocumentRedlineAccess.hxx> #include <IDocumentFieldsAccess.hxx> #include <IDocumentStylePoolAccess.hxx> +#include <IDocumentLayoutAccess.hxx> +#include <rootfrm.hxx> #include <editsh.hxx> #include <docary.hxx> #include <ndtxt.hxx> @@ -936,6 +938,12 @@ void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox ) { m_bModifyBox = bMdfyBox; + FndBox_ aTmpBox( nullptr, nullptr ); + bool bHideChanges = rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines(); + // TODO delete/make frames only at changing line attribute TextChangesOnly (RES_PRINT) to true again + if ( bHideChanges ) + aTmpBox.DelFrames( rTable ); + // first, get back attributes of TableFrameFormat SwFrameFormat* pFormat = rTable.GetFrameFormat(); SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet())); @@ -976,6 +984,9 @@ void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox ) m_aFrameFormats.clear(); m_bModifyBox = false; + + if ( bHideChanges ) + aTmpBox.MakeFrames( rTable ); } void SaveTable::SaveContentAttrs( SwDoc* pDoc ) commit 4a614ad8c32ad09f4085980e49dce251358a0a46 Author: Aron Budea <aron.bu...@collabora.com> AuthorDate: Sun Feb 13 06:57:16 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 14:04:11 2022 +0200 tdf#147014 Image missing due to integer overflow 32-bit awt::Point/Size/Rectangle cannot fit size of 1M rows with larger (eg. 5x the usual) height, and could overflow. This causes problems in 64-bit Linux builds and, since the following commit, in 64-bit Windows builds: 3d90997fb6f232d8008df4d166d7b97b869c200f For now, clamp possibly overflowing values to 32-bit. Change-Id: Ifda7265703388abdfb47f523da4f0c5822358404 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129876 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lu...@collabora.com> Reviewed-by: Aron Budea <aron.bu...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132168 diff --git a/sc/qa/unit/data/xlsx/tdf147014.xlsx b/sc/qa/unit/data/xlsx/tdf147014.xlsx new file mode 100644 index 000000000000..df4428795d9d Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf147014.xlsx differ diff --git a/sc/qa/unit/subsequent_filters_test2.cxx b/sc/qa/unit/subsequent_filters_test2.cxx index c40f26dfc78d..ac0b8d8d82e9 100644 --- a/sc/qa/unit/subsequent_filters_test2.cxx +++ b/sc/qa/unit/subsequent_filters_test2.cxx @@ -204,6 +204,7 @@ public: void testTdf129940(); void testTdf139612(); void testTdf144740(); + void testTdf147014(); void testTdf139763ShapeAnchor(); void testAutofilterNamedRangesXLSX(); void testInvalidBareBiff5(); @@ -312,6 +313,7 @@ public: CPPUNIT_TEST(testTdf129940); CPPUNIT_TEST(testTdf139612); CPPUNIT_TEST(testTdf144740); + CPPUNIT_TEST(testTdf147014); CPPUNIT_TEST(testTdf139763ShapeAnchor); CPPUNIT_TEST(testAutofilterNamedRangesXLSX); CPPUNIT_TEST(testInvalidBareBiff5); @@ -2873,6 +2875,22 @@ void ScFiltersTest2::testTdf144740() xDocSh->DoClose(); } +void ScFiltersTest2::testTdf147014() +{ + ScDocShellRef xDocSh = loadDoc(u"tdf147014.", FORMAT_XLSX); + CPPUNIT_ASSERT_MESSAGE("Failed to load tdf147014.xlsx", xDocSh.is()); + uno::Reference<frame::XModel> xModel = xDocSh->GetModel(); + uno::Reference<sheet::XSpreadsheetDocument> xDoc(xModel, uno::UNO_QUERY_THROW); + uno::Reference<container::XIndexAccess> xIA(xDoc->getSheets(), uno::UNO_QUERY_THROW); + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xIA->getByIndex(0), + uno::UNO_QUERY_THROW); + xIA.set(xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW); + // The sheet has a single shape, without the fix it was not imported, except in 32-bit builds + CPPUNIT_ASSERT_EQUAL_MESSAGE("Shape not imported", static_cast<sal_Int32>(1), xIA->getCount()); + + xDocSh->DoClose(); +} + void ScFiltersTest2::testTdf139763ShapeAnchor() { ScDocShellRef xDocSh = loadDoc(u"tdf139763ShapeAnchor.", FORMAT_XLSX); diff --git a/sc/source/filter/oox/worksheethelper.cxx b/sc/source/filter/oox/worksheethelper.cxx index 40a99cdb444b..95625751d4a0 100644 --- a/sc/source/filter/oox/worksheethelper.cxx +++ b/sc/source/filter/oox/worksheethelper.cxx @@ -75,7 +75,7 @@ #include <editeng/eeitem.hxx> #include <editeng/editobj.hxx> #include <editeng/flditem.hxx> -#include <tools/UnitConversion.hxx> +#include <tools/gen.hxx> namespace oox::xls { @@ -96,6 +96,18 @@ void lclUpdateProgressBar( const ISegmentProgressBarRef& rxProgressBar, double f rxProgressBar->setPosition( fPosition ); } +// TODO Needed because input might be >32-bit (in 64-bit builds), +// or a negative, already overflown value (in 32-bit builds) +sal_Int32 lclClampToNonNegativeInt32( tools::Long aVal ) +{ + if ( aVal > SAL_MAX_INT32 || aVal < 0 ) + { + SAL_WARN( "sc.filter", "Overflow detected, " << aVal << " does not fit into sal_Int32, or is negative." ); + return SAL_MAX_INT32; + } + return static_cast<sal_Int32>( aVal ); +} + } // namespace ColumnModel::ColumnModel() : @@ -538,9 +550,9 @@ const awt::Size& WorksheetGlobals::getDrawPageSize() const awt::Point WorksheetGlobals::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const { - awt::Point aPoint; - PropertySet aCellProp( getCell( ScAddress( nCol, nRow, getSheetIndex() ) ) ); - aCellProp.getProperty( aPoint, PROP_Position ); + const tools::Rectangle aMMRect( getScDocument().GetMMRect( nCol, nRow, nCol, nRow, getSheetIndex() ) ); + awt::Point aPoint( lclClampToNonNegativeInt32( aMMRect.Left() ), + lclClampToNonNegativeInt32( aMMRect.Top() ) ); return aPoint; } @@ -1360,8 +1372,9 @@ void WorksheetGlobals::groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLa void WorksheetGlobals::finalizeDrawings() { // calculate the current drawing page size (after rows/columns are imported) - PropertySet aRangeProp( getCellRange( ScRange( 0, 0, getSheetIndex(), mrMaxApiPos.Col(), mrMaxApiPos.Row(), getSheetIndex() ) ) ); - aRangeProp.getProperty( maDrawPageSize, PROP_Size ); + const Size aPageSize( getScDocument().GetMMRect( 0, 0, mrMaxApiPos.Col(), mrMaxApiPos.Row(), getSheetIndex() ).GetSize() ); + maDrawPageSize.Width = lclClampToNonNegativeInt32( aPageSize.Width() ); + maDrawPageSize.Height = lclClampToNonNegativeInt32( aPageSize.Height() ); // import DML and VML if( !maDrawingPath.isEmpty() ) commit a743d338c24407b4dec62f12dfff49621102eeac Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Wed Mar 9 16:41:36 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 13:07:01 2022 +0200 svx: don't remove object right after insertion This is regression from: commit 2d95b3846eac367d2baadc194ab258dc31e7bd33 Author: Tomaz Vajngerl <tomaz.vajng...@collabora.co.uk> Date: Thu Oct 7 16:48:46 2021 +0200 svx: Don't end text edit mode for all views It was visible with "direct insertion" where user doesn't need to draw anything but textbox is inserted in the center of a screen (eg. used in LOK case) Object was inserted into a view and right after that was removed when EndTextEditCurrentView was called Change-Id: I9943d46746aabadf96d76d6e74770b56d648b79d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131263 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Mert Tumer <mert.tu...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131537 Tested-by: Jenkins Reviewed-by: Szymon Kłos <szymon.k...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131596 Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/include/svx/svdedtv.hxx b/include/svx/svdedtv.hxx index 6f80c3aca0fa..d2c9b3816f6a 100644 --- a/include/svx/svdedtv.hxx +++ b/include/svx/svdedtv.hxx @@ -188,7 +188,7 @@ public: * Checks if this or other views have an active text edit, if true, end them. */ void EndTextEditAllViews() const; - void EndTextEditCurrentView(); + void EndTextEditCurrentView(bool bDontDeleteReally = false); std::vector< std::unique_ptr<SdrUndoAction> > CreateConnectorUndo( const SdrObject& rO ); void AddUndoActions( std::vector< std::unique_ptr<SdrUndoAction> > ); diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx index c4e0da7b9f87..6c6cd5990c3c 100644 --- a/sc/qa/unit/tiledrendering/tiledrendering.cxx +++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx @@ -121,6 +121,7 @@ public: void testEditCursorBounds(); void testTextSelectionBounds(); void testSheetViewDataCrash(); + void testTextBoxInsert(); CPPUNIT_TEST_SUITE(ScTiledRenderingTest); CPPUNIT_TEST(testRowColumnHeaders); @@ -175,6 +176,7 @@ public: CPPUNIT_TEST(testEditCursorBounds); CPPUNIT_TEST(testTextSelectionBounds); CPPUNIT_TEST(testSheetViewDataCrash); + CPPUNIT_TEST(testTextBoxInsert); CPPUNIT_TEST_SUITE_END(); private: @@ -458,7 +460,12 @@ struct EditCursorMessage final { std::stringstream aStream(pMessage); boost::property_tree::ptree aTree; boost::property_tree::read_json(aStream, aTree); - std::string aVal = aTree.get_child("refpoint").get_value<std::string>(); + std::string aVal; + boost::property_tree::ptree::const_assoc_iterator it = aTree.find("refpoint"); + if (it != aTree.not_found()) + aVal = aTree.get_child("refpoint").get_value<std::string>(); + else + return; // happens in testTextBoxInsert test uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::createFromAscii(aVal.c_str())); CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aSeq.getLength()); @@ -2868,6 +2875,28 @@ void ScTiledRenderingTest::testSheetViewDataCrash() Scheduler::ProcessEventsToIdle(); } +void ScTiledRenderingTest::testTextBoxInsert() +{ + comphelper::LibreOfficeKit::setActive(); + + createDoc("empty.ods"); + ViewCallback aView1; + + // insert textbox + uno::Sequence<beans::PropertyValue> aArgs( + comphelper::InitPropertySequence({ + { "CreateDirectly", uno::Any(true) } + })); + comphelper::dispatchCommand(".uno:DrawText", aArgs); + Scheduler::ProcessEventsToIdle(); + + // check if we have textbox selected + CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty()); + CPPUNIT_ASSERT(aView1.m_ShapeSelection != "EMPTY"); + + Scheduler::ProcessEventsToIdle(); +} + } CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest); diff --git a/svx/source/svdraw/svdedtv.cxx b/svx/source/svdraw/svdedtv.cxx index 47f5c971e543..420f024f2064 100644 --- a/svx/source/svdraw/svdedtv.cxx +++ b/svx/source/svdraw/svdedtv.cxx @@ -1000,7 +1000,8 @@ bool SdrEditView::InsertObjectAtView(SdrObject* pObj, SdrPageView& rPV, SdrInser } if( IsUndoEnabled()) { - EndTextEditCurrentView(); + bool bDontDeleteReally = true; + EndTextEditCurrentView(bDontDeleteReally); AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pObj)); } @@ -1080,13 +1081,13 @@ void SdrEditView::EndTextEditAllViews() const } } -void SdrEditView::EndTextEditCurrentView() +void SdrEditView::EndTextEditCurrentView(bool bDontDeleteReally) { if (IsTextEdit()) { SdrView* pSdrView = dynamic_cast<SdrView*>(this); if (pSdrView) - pSdrView->SdrEndTextEdit(); + pSdrView->SdrEndTextEdit(bDontDeleteReally); } } commit 10ee1f9d52f28db0aff61fb6f4e08b3fedfd9d19 Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Wed Mar 9 21:32:35 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 13:06:47 2022 +0200 lok: insert textbox directly in Calc Change-Id: I3ae00b255dfbaa34ab8d973356d12dfd0f71d345 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131267 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Mert Tumer <mert.tu...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131536 Tested-by: Jenkins Reviewed-by: Szymon Kłos <szymon.k...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132101 diff --git a/sc/source/ui/view/tabvwsh2.cxx b/sc/source/ui/view/tabvwsh2.cxx index a096a766ea7d..9060fa98e65c 100644 --- a/sc/source/ui/view/tabvwsh2.cxx +++ b/sc/source/ui/view/tabvwsh2.cxx @@ -265,6 +265,7 @@ void ScTabViewShell::ExecDraw(SfxRequest& rReq) case SID_DRAW_TEXT_MARQUEE: case SID_DRAW_NOTEEDIT: pTabView->SetDrawFuncPtr(new FuText(*this, pWin, pView, pDoc, aNewReq)); + bCreateDirectly = comphelper::LibreOfficeKit::isActive(); break; case SID_FM_CREATE_CONTROL: @@ -335,7 +336,6 @@ void ScTabViewShell::ExecDraw(SfxRequest& rReq) } else { - GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); ScViewData& rViewData = GetViewData(); aInsertPos = rViewData.getLOKVisibleArea().Center(); if (comphelper::LibreOfficeKit::isCompatFlagSet( @@ -370,13 +370,20 @@ void ScTabViewShell::ExecDraw(SfxRequest& rReq) // insert into page pView->InsertObjectAtView(pObj.release(), *pPageView); - if ( nNewId == SID_DRAW_CAPTION || nNewId == SID_DRAW_CAPTION_VERTICAL ) + switch ( nNewId ) { + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + case SID_DRAW_TEXT: + case SID_DRAW_TEXT_VERTICAL: // use KeyInput to start edit mode (FuText is created). // For FuText objects, edit mode is handled within CreateDefaultObject. // KEY_F2 is handled in FuDraw::KeyInput. pFuActual->KeyInput( KeyEvent( 0, vcl::KeyCode( KEY_F2 ) ) ); + break; + default: + break; } } diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi index 1e08145d3644..135efb8a845a 100644 --- a/svx/sdi/svx.sdi +++ b/svx/sdi/svx.sdi @@ -8520,7 +8520,7 @@ SfxBoolItem Text SID_ATTR_CHAR SfxBoolItem DrawText SID_DRAW_TEXT - +(SfxBoolItem CreateDirectly FN_PARAM_1) [ AutoUpdate = TRUE, FastCall = FALSE, commit db61167cdfdde69d115338be7d50447e81b43616 Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Mon Mar 14 21:55:59 2022 +0100 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 12:00:06 2022 +0200 tdf#147761 record sort command in macros fixes regression introduced in: commit 70b81e74d2a14308e1897d840c681404225d328a author Szymon Kłos <szymon.k...@collabora.com> Tue Jul 21 11:40:22 2020 +0200 SfxRequest's Done method records the action. It has to be called. Change-Id: I32529fa11febca3d3829b4afd2cd4e6dd359bf74 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131484 Tested-by: Jenkins Reviewed-by: Szymon Kłos <szymon.k...@collabora.com> (cherry picked from commit eca978841816f833616b4c2334e65aa976a9fb61) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131587 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sc/source/ui/view/cellsh2.cxx b/sc/source/ui/view/cellsh2.cxx index 0bed3b16141f..8ff829326597 100644 --- a/sc/source/ui/view/cellsh2.cxx +++ b/sc/source/ui/view/cellsh2.cxx @@ -526,6 +526,55 @@ void ScCellShell::ExecuteDB( SfxRequest& rReq ) // subtotal when needed new pTabViewShell->UISort( rOutParam ); + + SfxViewFrame* pViewFrm = pTabViewShell->GetViewFrame(); + if (pViewFrm) + { + SfxRequest aRequest(pViewFrm, SID_SORT); + + if ( rOutParam.bInplace ) + { + aRequest.AppendItem( SfxBoolItem( SID_SORT_BYROW, + rOutParam.bByRow ) ); + aRequest.AppendItem( SfxBoolItem( SID_SORT_HASHEADER, + rOutParam.bHasHeader ) ); + aRequest.AppendItem( SfxBoolItem( SID_SORT_CASESENS, + rOutParam.bCaseSens ) ); + aRequest.AppendItem( SfxBoolItem( SID_SORT_NATURALSORT, + rOutParam.bNaturalSort ) ); + aRequest.AppendItem( SfxBoolItem( SID_SORT_INCCOMMENTS, + rOutParam.aDataAreaExtras.mbCellNotes ) ); + aRequest.AppendItem( SfxBoolItem( SID_SORT_INCIMAGES, + rOutParam.aDataAreaExtras.mbCellDrawObjects ) ); + aRequest.AppendItem( SfxBoolItem( SID_SORT_ATTRIBS, + rOutParam.aDataAreaExtras.mbCellFormats ) ); + sal_uInt16 nUser = rOutParam.bUserDef ? ( rOutParam.nUserIndex + 1 ) : 0; + aRequest.AppendItem( SfxUInt16Item( SID_SORT_USERDEF, nUser ) ); + if ( rOutParam.maKeyState[0].bDoSort ) + { + aRequest.AppendItem( SfxInt32Item( FN_PARAM_1, + rOutParam.maKeyState[0].nField + 1 ) ); + aRequest.AppendItem( SfxBoolItem( FN_PARAM_2, + rOutParam.maKeyState[0].bAscending ) ); + } + if ( rOutParam.maKeyState[1].bDoSort ) + { + aRequest.AppendItem( SfxInt32Item( FN_PARAM_3, + rOutParam.maKeyState[1].nField + 1 ) ); + aRequest.AppendItem( SfxBoolItem( FN_PARAM_4, + rOutParam.maKeyState[1].bAscending ) ); + } + if ( rOutParam.maKeyState[2].bDoSort ) + { + aRequest.AppendItem( SfxInt32Item( FN_PARAM_5, + rOutParam.maKeyState[2].nField + 1 ) ); + aRequest.AppendItem( SfxBoolItem( FN_PARAM_6, + rOutParam.maKeyState[2].bAscending ) ); + } + } + + aRequest.Done(); + } } else { commit dc2e0b96be481b888d172f41cd2e94b7e1509685 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Thu Mar 24 09:33:44 2022 +0000 Commit: Xisco Fauli <xiscofa...@libreoffice.org> CommitDate: Mon Mar 28 11:58:18 2022 +0200 forcepoint#85 Conditional jump or move depends on uninitialised value ==398461== by 0xCDC7960: (anonymous namespace)::CopyUntil(char16_t*&, char16_t const*&, char16_t, bool) (strhelper.cxx:88) ==398461== by 0xCDC839F: psp::WhitespaceToSpace(rtl::OUString const&, bool) (strhelper.cxx:294) ==398461== by 0xCB54980: vcl::PDFWriterImpl::setOutlineItemText(int, rtl::OUString const&) (pdfwriter_impl.cxx:9875) ==398461== by 0xCB547FF: vcl::PDFWriterImpl::createOutlineItem(int, rtl::OUString const&, int) (pdfwriter_impl.cxx:9851) ==398461== by 0xCAF39B0: vcl::PDFWriter::CreateOutlineItem(int, rtl::OUString const&, int) (pdfwriter.cxx:383) ==398461== by 0xCABD7C8: vcl::GlobalSyncData::PlayGlobalActions(vcl::PDFWriter&) (pdfextoutdevdata.cxx:250) ==398461== by 0xCAC0628: vcl::PDFExtOutDevData::PlayGlobalActions(vcl::PDFWriter&) (pdfextoutdevdata.cxx:616) ==398461== by 0x3D06EA0F: PDFExport::Export(rtl::OUString const&, com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) (pdfexport.cxx:1004) Change-Id: I6bc086997851ee06531a4a9ae263e2b26edfba84 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132035 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/vcl/source/helper/strhelper.cxx b/vcl/source/helper/strhelper.cxx index 96e10b4865e8..782c9ce123e1 100644 --- a/vcl/source/helper/strhelper.cxx +++ b/vcl/source/helper/strhelper.cxx @@ -83,7 +83,8 @@ void CopyUntil( sal_Unicode*& pTo, const sal_Unicode*& pFrom, sal_Unicode cUntil *pTo = *pFrom; pTo++; } - pFrom++; + if( *pFrom ) + pFrom++; } while( *pFrom && *pFrom != cUntil ); // copy the terminating character unless zero or protector if( ! isProtect( *pFrom ) || bIncludeUntil ) commit 6bd85136efe3d3668b59a596d692f65bf0a4982c Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Fri Mar 18 18:31:05 2022 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Mon Mar 28 11:38:26 2022 +0200 tdf#147978 export subpaths individually in custGeom ...and implement export of all missing commands, use existing viewBox if suitable, use one EnhancedCustomShape2d move WriteCustomGeometryPoint to protected, make GetCustomGeometryPointValue local The fix solves tdf#100390 too. Without the fix the entire enhanced-path was exported as one element <a:path>. The command F was applied to the whole drawing but should affect only the subpath. The implementation is changed so that each subpath gets its own element <a:path> and command F acts only on its subpath. Support for export of handles and equations is still a long way off. Thus there is no reason to tread shapes with and without handles different. Shapes with handles had used method WritePolyPolygon, but that is not able to handle subpaths. So have desided to use method WriteCustomGeometry for all cases. To get shapes exported regardless of path commands I have added the export for the missing commands. I have removed the no longer used method WritePolyPolygon. The special treatment of shapes "moon" and "mso-spt89" (right up arrow) in export is no longer needed. Related code parts are removed. The unit test testFlipAndRotateCustomShape is adapted. In case the method WriteCustomGeometry fails, the enhanced-path is invalid. In that case an minimal custGeom is written in case of docx. Shapes whose drawing does not touch all edges of the snap rectangle were exported with wrong position and size of the drawing inside the snap rectangle. That is fixed by using an existing ViewBox for the OOXML path size. The old way of creating a path size from point coordinates is only used if the shape has no suitable ViewBox. The point values in unit test SdOOXMLExportTest2::testTdf111798 are adapted to path size 21600 x 21600 and traverse direction of the points is corrected. The resulting shape outline is still the same as before. The expected xml is updated for file tdf92001.odp in SdImportTest::testDocumentLayout. The resulting shape outline is the same, because the shape touches the edges of the snap rectangle. The case, that the shape outline does not touch a edge of the snap rectangle is tested in SdOOXMLExportTest3::testEnhancedPathViewBox. Still missing is the case, that ViewBox has other left,top than 0,0. In that case all coordinates would have to be shifted because the path size in OOXML has only width and height but not left,top. That will not be included in this patch. Change-Id: Ib1736d6a08371f4d98411d2769275f0580cd0030 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131837 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> (cherry picked from commit 2029b2f6dd0109c5892e5ac5640022b31fe42fd2) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132048 Reviewed-by: Bartosz Kosiorek <gan...@poczta.onet.pl> diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index 63a32225172b..7fbb015b4ce4 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -43,6 +43,7 @@ #include <vcl/checksum.hxx> #include <tools/gen.hxx> #include <vcl/mapmod.hxx> +#include <svx/EnhancedCustomShape2d.hxx> class Graphic; class SdrObjCustomShape; @@ -198,7 +199,8 @@ protected: void WriteGlowEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet); void WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet); - bool HasEnhancedCustomShapeSegmentCommand(const css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 nCommand); + void WriteCustomGeometryPoint(const css::drawing::EnhancedCustomShapeParameterPair& rParamPair, + const EnhancedCustomShape2d& rCustomShape2d); public: DrawingML( ::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* pFB, DocumentType eDocumentType = DOCUMENT_PPTX, DMLTextExport* pTextExport = nullptr ) @@ -306,14 +308,7 @@ public: bool WriteCustomGeometry( const css::uno::Reference<css::drawing::XShape>& rXShape, const SdrObjCustomShape& rSdrObjCustomShape); - void WriteCustomGeometryPoint( - const css::drawing::EnhancedCustomShapeParameterPair& rParamPair, - const SdrObjCustomShape& rSdrObjCustomShape); - static sal_Int32 GetCustomGeometryPointValue( - const css::drawing::EnhancedCustomShapeParameter& rParam, - const SdrObjCustomShape& rSdrObjCustomShape); - void WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape, - const tools::PolyPolygon& rPolyPolygon, const bool bClosed); + void WriteEmptyCustomGeometry(); void WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape, const bool bClosed); void WriteFill( const css::uno::Reference< css::beans::XPropertySet >& xPropSet ); diff --git a/oox/qa/unit/data/tdf147978_endsubpath.odp b/oox/qa/unit/data/tdf147978_endsubpath.odp new file mode 100644 index 000000000000..2dfd55de1be3 Binary files /dev/null and b/oox/qa/unit/data/tdf147978_endsubpath.odp differ diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp new file mode 100644 index 000000000000..99ddda7c132e Binary files /dev/null and b/oox/qa/unit/data/tdf147978_enhancedPath_commandA.odp differ diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp new file mode 100644 index 000000000000..49e01bc0933a Binary files /dev/null and b/oox/qa/unit/data/tdf147978_enhancedPath_commandHIJK.odp differ diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp new file mode 100644 index 000000000000..3dcd0d567545 Binary files /dev/null and b/oox/qa/unit/data/tdf147978_enhancedPath_commandT.odp differ diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp new file mode 100644 index 000000000000..6112251783e1 Binary files /dev/null and b/oox/qa/unit/data/tdf147978_enhancedPath_commandXY.odp differ diff --git a/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx new file mode 100644 index 000000000000..bbedc7ab98f5 Binary files /dev/null and b/oox/qa/unit/data/tdf147978_enhancedPath_subpath.pptx differ diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx index dd722cd04e79..e104b4effdd7 100644 --- a/oox/qa/unit/export.cxx +++ b/oox/qa/unit/export.cxx @@ -374,6 +374,161 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf146690_endParagraphRunPropertiesNewLinesTextSi assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[2]/a:endParaRPr", "sz", "500"); assertXPath(pXmlDoc, "//p:sp[1]/p:txBody/a:p[3]/a:endParaRPr", "sz", "500"); } + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978_endsubpath) +{ + // Given an odp file that contains a non-primitive custom shape with command N + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_endsubpath.odp"; + + // When saving that document: + loadAndSave(aURL, "Impress Office Open XML"); + + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // Then make sure the pathLst has two child elements, + // Without the accompanying fix in place, only one element a:path was exported. + assertXPathChildren(pXmlDoc, "//a:pathLst", 2); + // and make sure first path with no stroke, second with no fill + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "stroke", "0"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "none"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandA) +{ + // Given an odp file that contains a non-primitive custom shape with command N + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandA.odp"; + + // When saving that document: + loadAndSave(aURL, "Impress Office Open XML"); + + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // Then make sure the path has a child element arcTo. Prior to the fix that part of the curve was + // not exported at all. In odp it is a command A. Such does not exist in OOXML and is therefore + // exported as a:lnTo followed by a:arcTo + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo", 2); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", 1); + // And assert its attribute values + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "wR", "7200"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "hR", "5400"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "stAng", "7719588"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "swAng", "-5799266"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandT) +{ + // The odp file contains a non-primitive custom shape with commands MTZ + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandT.odp"; + + // Export to pptx had only exported the command M and has used a wrong path size + loadAndSave(aURL, "Impress Office Open XML"); + + // Verify the markup: + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // File has draw:viewBox="0 0 216 216" + assertXPath(pXmlDoc, "//a:pathLst/a:path", "w", "216"); + assertXPath(pXmlDoc, "//a:pathLst/a:path", "h", "216"); + // Command T is exported as lnTo followed by arcTo. + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo", 1); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo", 1); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", 1); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:close", 1); + // And assert its values + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo/a:pt", "x", "108"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:moveTo/a:pt", "y", "162"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo/a:pt", "x", "138"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:lnTo/a:pt", "y", "110"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "wR", "108"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "hR", "54"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "stAng", "18000000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path/a:arcTo", "swAng", "18000000"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandXY) +{ + // The odp file contains a non-primitive custom shapes with commands XY + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandXY.odp"; + + // Export to pptx had dropped commands X and Y. + loadAndSave(aURL, "Impress Office Open XML"); + + // Verify the markup: + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // File has draw:viewBox="0 0 10 10" + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "10"); + // Shape has M 0 5 Y 5 0 10 5 5 10 F Y 0 5 N M 10 10 X 0 0 + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:moveTo/a:pt", "x", "0"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:moveTo/a:pt", "y", "5"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "wR", "5"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "hR", "5"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "stAng", "10800000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[1]", "swAng", "5400000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[2]", "stAng", "16200000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[2]", "swAng", "5400000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[3]", "stAng", "0"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[3]", "swAng", "5400000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[4]", "stAng", "0"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]/a:arcTo[4]", "swAng", "-5400000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:moveTo/a:pt", "x", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:moveTo/a:pt", "y", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "wR", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "hR", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "stAng", "5400000"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]/a:arcTo", "swAng", "5400000"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978_commandHIJK) +{ + // The odp file contains a non-primitive custom shapes with commands H,I,J,K + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_commandHIJK.odp"; + + // Export to pptx had dropped commands X and Y. + loadAndSave(aURL, "Impress Office Open XML"); + + // Verify the markup: + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // File has draw:viewBox="0 0 80 80" + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "80"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "80"); + // File uses from back to front J (lighten), I (lightenLess), normal fill, K (darkenLess), + // H (darken). New feature, old versions did not export these at all. + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "fill", "lighten"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "lightenLess"); + assertXPathNoAttribute(pXmlDoc, "//a:pathLst/a:path[3]", "fill"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "fill", "darkenLess"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[5]", "fill", "darken"); +} + +CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath) +{ + // The odp file contains a non-primitive custom shapes with commands H,I,J,K + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf147978_enhancedPath_subpath.pptx"; + + // Export to pptx had dropped the subpaths. + loadAndSave(aURL, "Impress Office Open XML"); + + // Verify the markup: + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // File should have four subpaths with increasing path size + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "w", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "h", "10"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "w", "20"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "h", "20"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "w", "40"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "h", "40"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "w", "80"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80"); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index c457e65ac0c8..708aea6fb29a 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -62,6 +62,7 @@ #include <com/sun/star/drawing/ColorMode.hpp> #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp> #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp> #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp> #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp> #include <com/sun/star/drawing/Hatch.hpp> @@ -137,6 +138,7 @@ using namespace ::css::style; using namespace ::css::text; using namespace ::css::uno; using namespace ::css::container; +using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand; using ::css::io::XOutputStream; using ::sax_fastparser::FSHelperPtr; @@ -3658,6 +3660,86 @@ void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, bool b mpFS->endElementNS( XML_a, XML_prstGeom ); } +namespace // helpers for DrawingML::WriteCustomGeometry +{ +sal_Int32 +FindNextCommandEndSubpath(const sal_Int32 nStart, + const uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments) +{ + sal_Int32 i = nStart < 0 ? 0 : nStart; + while (i < rSegments.getLength() && rSegments[i].Command != ENDSUBPATH) + i++; + return i; +} + +bool HasCommandInSubPath(const sal_Int16 nCommand, const sal_Int32 nFirst, const sal_Int32 nLast, + const uno::Sequence<drawing::EnhancedCustomShapeSegment>& rSegments) +{ + for (sal_Int32 i = nFirst < 0 ? 0 : nFirst; i <= nLast && i < rSegments.getLength(); i++) + { + if (rSegments[i].Command == nCommand) + return true; + } + return false; +} + +// Ellipse is given by radii fwR and fhR and center (fCx|fCy). The ray from center through point RayP +// intersects the ellipse in point S and this point S has angle fAngleDeg in degrees. +void getEllipsePointAndAngleFromRayPoint(double& rfAngleDeg, double& rfSx, double& rfSy, + const double fWR, const double fHR, const double fCx, + const double fCy, const double fRayPx, const double fRayPy) +{ + if (basegfx::fTools::equalZero(fWR) || basegfx::fTools::equalZero(fHR)) + { + rfSx = fCx; // needed for getting new 'current point' + rfSy = fCy; + } + else + { + // center ellipse at origin, stretch in y-direction to circle, flip to Math orientation + // and get angle + double fCircleMathAngle = atan2(-fWR / fHR * (fRayPy - fCy), fRayPx - fCx); + // use angle for intersection point on circle and stretch back to ellipse + double fPointMathEllipse_x = fWR * cos(fCircleMathAngle); + double fPointMathEllipse_y = fHR * sin(fCircleMathAngle); + // get angle of intersection point on ellipse + double fEllipseMathAngle = atan2(fPointMathEllipse_y, fPointMathEllipse_x); + // convert from Math to View orientation and shift ellipse back from origin + rfAngleDeg = -basegfx::rad2deg(fEllipseMathAngle); + rfSx = fPointMathEllipse_x + fCx; + rfSy = -fPointMathEllipse_y + fCy; + } +} + +void getEllipsePointFromViewAngle(double& rfSx, double& rfSy, const double fWR, const double fHR, + const double fCx, const double fCy, const double fViewAngleDeg) +{ + if (basegfx::fTools::equalZero(fWR) || basegfx::fTools::equalZero(fHR)) + { + rfSx = fCx; // needed for getting new 'current point' + rfSy = fCy; + } + else + { + double fX = cos(basegfx::deg2rad(fViewAngleDeg)) / fWR; + double fY = sin(basegfx::deg2rad(fViewAngleDeg)) / fHR; + double fRadius = 1.0 / std::hypot(fX, fY); + rfSx = fCx + fRadius * cos(basegfx::deg2rad(fViewAngleDeg)); + rfSy = fCy + fRadius * sin(basegfx::deg2rad(fViewAngleDeg)); + } +} + +sal_Int32 GetCustomGeometryPointValue(const css::drawing::EnhancedCustomShapeParameter& rParam, + const EnhancedCustomShape2d& rCustomShape2d) +{ + double fValue = 0.0; + rCustomShape2d.GetParameter(fValue, rParam, false, false); + sal_Int32 nValue(std::lround(fValue)); + + return nValue; +} +} + bool DrawingML::WriteCustomGeometry( const Reference< XShape >& rXShape, const SdrObjCustomShape& rSdrObjCustomShape) @@ -3679,349 +3761,574 @@ bool DrawingML::WriteCustomGeometry( return false; } - auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny); + if (!pGeometrySeq) + return false; + + auto pPathProp = std::find_if(std::cbegin(*pGeometrySeq), std::cend(*pGeometrySeq), + [](const PropertyValue& rProp) { return rProp.Name == "Path"; }); + if (pPathProp == std::cend(*pGeometrySeq)) + return false; - if ( pGeometrySeq ) + uno::Sequence<beans::PropertyValue> aPathProp; + pPathProp->Value >>= aPathProp; + + uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs; + uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments; + uno::Sequence<awt::Size> aPathSize; + for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp)) { - for( const beans::PropertyValue& rProp : *pGeometrySeq ) - { - if ( rProp.Name == "Path" ) - { - uno::Sequence<beans::PropertyValue> aPathProp; - rProp.Value >>= aPathProp; + if (rPathProp.Name == "Coordinates") + rPathProp.Value >>= aPairs; + else if (rPathProp.Name == "Segments") + rPathProp.Value >>= aSegments; + else if (rPathProp.Name == "SubViewSize") + rPathProp.Value >>= aPathSize; + } - uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs; - uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments; - uno::Sequence<awt::Size> aPathSize; - for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp)) - { - if (rPathProp.Name == "Coordinates") - rPathProp.Value >>= aPairs; - else if (rPathProp.Name == "Segments") - rPathProp.Value >>= aSegments; - else if (rPathProp.Name == "SubViewSize") - rPathProp.Value >>= aPathSize; - } + if ( !aPairs.hasElements() ) + return false; - if ( !aPairs.hasElements() ) - return false; + if ( !aSegments.hasElements() ) + { + aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment> + { + { MOVETO, 1 }, + { LINETO, + static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) )) }, + { CLOSESUBPATH, 0 }, + { ENDSUBPATH, 0 } + }; + }; - if ( !aSegments.hasElements() ) - { - aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment> - { - { drawing::EnhancedCustomShapeSegmentCommand::MOVETO, 1 }, - { drawing::EnhancedCustomShapeSegmentCommand::LINETO, - static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) )) }, - { drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH, 0 }, - { drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH, 0 } - }; - }; + int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0, + [](const int nSum, const drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; }); - int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0, - [](const int nSum, const drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; }); + if ( nExpectedPairCount > aPairs.getLength() ) + { + SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs."); + return false; + } - if ( nExpectedPairCount > aPairs.getLength() ) - { - SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs."); - return false; - } + // A EnhancedCustomShape2d caches the equation results. Therefor we use only one of it for the + // entire method. + const EnhancedCustomShape2d aCustomShape2d(const_cast<SdrObjCustomShape&>(rSdrObjCustomShape)); - mpFS->startElementNS(XML_a, XML_custGeom); - mpFS->singleElementNS(XML_a, XML_avLst); - mpFS->singleElementNS(XML_a, XML_gdLst); - mpFS->singleElementNS(XML_a, XML_ahLst); - mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t", - XML_r, "r", XML_b, "b"); - mpFS->startElementNS(XML_a, XML_pathLst); + mpFS->startElementNS(XML_a, XML_custGeom); + mpFS->singleElementNS(XML_a, XML_avLst); + mpFS->singleElementNS(XML_a, XML_gdLst); + mpFS->singleElementNS(XML_a, XML_ahLst); + // ToDO: use draw:TextAreas for <a:rect> + mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t", XML_r, "r", XML_b, "b"); + mpFS->startElementNS(XML_a, XML_pathLst); - std::optional<OString> sFill; - if (HasEnhancedCustomShapeSegmentCommand(rXShape, css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL)) - sFill = "none"; // for possible values see ST_PathFillMode in OOXML standard + // Prepare width and height for <a:path> + bool bUseGlobalViewBox(false); + + // nViewBoxWidth must be integer otherwise ReplaceGeoWidth in aCustomShape2d.GetParameter() is not + // triggered; same for height. + sal_Int32 nViewBoxWidth(0); + sal_Int32 nViewBoxHeight(0); + if (!aPathSize.hasElements()) + { + bUseGlobalViewBox = true; + // If draw:viewBox is missing in draw:enhancedGeometry, then import sets + // viewBox="0 0 21600 21600". Missing ViewBox can only occur, if user has manipulated + // current file via macro. Author of macro has to fix it. + auto pProp = std::find_if( + std::cbegin(*pGeometrySeq), std::cend(*pGeometrySeq), + [](const beans::PropertyValue& rGeomProp) { return rGeomProp.Name == "ViewBox"; }); + if (pProp != std::cend(*pGeometrySeq)) + { + css::awt::Rectangle aViewBox; + if (pProp->Value >>= aViewBox) + { + nViewBoxWidth = aViewBox.Width; + nViewBoxHeight = aViewBox.Height; + css::drawing::EnhancedCustomShapeParameter aECSP; + aECSP.Type = css::drawing::EnhancedCustomShapeParameterType::NORMAL; + aECSP.Value <<= nViewBoxWidth; + double fRetValue; + aCustomShape2d.GetParameter(fRetValue, aECSP, true, false); + nViewBoxWidth = basegfx::fround(fRetValue); + aECSP.Value <<= nViewBoxHeight; + aCustomShape2d.GetParameter(fRetValue, aECSP, false, true); + nViewBoxHeight = basegfx::fround(fRetValue); + } + } + // Import from oox or documents, which are imported from oox and saved to strict ODF, might + // have no subViewSize but viewBox="0 0 0 0". We need to generate width and height in those + // cases. Even if that is fixed, we need the substitute for old documents. + if ((nViewBoxWidth == 0 && nViewBoxHeight == 0) || pProp == std::cend(*pGeometrySeq)) + { + // Generate a substitute based on point coordinates + sal_Int32 nXMin(0); + aPairs[0].First.Value >>= nXMin; + sal_Int32 nXMax = nXMin; + sal_Int32 nYMin(0); + aPairs[0].Second.Value >>= nYMin; + sal_Int32 nYMax = nYMin; - if ( aPathSize.hasElements() ) - { - mpFS->startElementNS( XML_a, XML_path, - XML_fill, sFill, - XML_w, OString::number(aPathSize[0].Width), - XML_h, OString::number(aPathSize[0].Height) ); - } - else + for (const auto& rPair : std::as_const(aPairs)) + { + sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, aCustomShape2d); + sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, aCustomShape2d); + if (nX < nXMin) + nXMin = nX; + if (nY < nYMin) + nYMin = nY; + if (nX > nXMax) + nXMax = nX; + if (nY > nYMax) + nYMax = nY; + } + nViewBoxWidth = std::max(nXMax, nXMax - nXMin); + nViewBoxHeight = std::max(nYMax, nYMax - nYMin); + } + // ToDo: Other values of left,top than 0,0 are not considered yet. Such would require a + // shift of the resulting path coordinates. + } + + // Iterate over subpaths + sal_Int32 nPairIndex = 0; // index over "Coordinates" + sal_Int32 nPathSizeIndex = 0; // index over "SubViewSize" + sal_Int32 nSubpathStartIndex(0); // index over "Segments" + do + { + bool bOK(true); // catch faulty paths were commands do not correspond to points + // get index of next command ENDSUBPATH; if such doesn't exist use index behind last segment + sal_Int32 nNextNcommandIndex = FindNextCommandEndSubpath(nSubpathStartIndex, aSegments); + + // Prepare attributes for a:path start element + // NOFILL or one of the LIGHTEN commands + std::optional<OString> sFill; + if (HasCommandInSubPath(NOFILL, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments)) + sFill = "none"; + else if (HasCommandInSubPath(DARKEN, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments)) + sFill = "darken"; + else if (HasCommandInSubPath(DARKENLESS, nSubpathStartIndex, nNextNcommandIndex - 1, + aSegments)) + sFill = "darkenLess"; + else if (HasCommandInSubPath(LIGHTEN, nSubpathStartIndex, nNextNcommandIndex - 1, + aSegments)) + sFill = "lighten"; + else if (HasCommandInSubPath(LIGHTENLESS, nSubpathStartIndex, nNextNcommandIndex - 1, + aSegments)) + sFill = "lightenLess"; + // NOSTROKE + std::optional<OString> sStroke; + if (HasCommandInSubPath(NOSTROKE, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments)) + sStroke = "0"; + + // Write a:path start element + mpFS->startElementNS( + XML_a, XML_path, XML_fill, sFill, XML_stroke, sStroke, XML_w, + OString::number(bUseGlobalViewBox ? nViewBoxWidth : aPathSize[nPathSizeIndex].Width), + XML_h, + OString::number(bUseGlobalViewBox ? nViewBoxHeight : aPathSize[nPathSizeIndex].Height)); + + // Arcs drawn by commands ELLIPTICALQUADRANTX and ELLIPTICALQUADRANTY depend on the position + // of the target point in regard to the current point. Therefore we need to track the + // current point. A current point is not defined in the beginning. + double fCurrentX(0.0); + double fCurrentY(0.0); + bool bCurrentValid(false); + // Actually write the subpath + for (sal_Int32 nSegmentIndex = nSubpathStartIndex; nSegmentIndex < nNextNcommandIndex; + ++nSegmentIndex) + { + const auto& rSegment(aSegments[nSegmentIndex]); + if (rSegment.Command == CLOSESUBPATH) + { + mpFS->singleElementNS(XML_a, XML_close); // command Z has no parameter + // ODF 1.4 specifies, that the start of the subpath becomes the current point. + // But that is not implemented yet. Currently LO keeps the last current point. + } + for (sal_Int32 k = 0; k < rSegment.Count && bOK; ++k) + { + switch (rSegment.Command) { - sal_Int32 nXMin(0); - aPairs[0].First.Value >>= nXMin; - sal_Int32 nXMax = nXMin; - sal_Int32 nYMin(0); - aPairs[0].Second.Value >>= nYMin; - sal_Int32 nYMax = nYMin; - - for ( const auto& rPair : std::as_const(aPairs) ) + case MOVETO: { - sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, rSdrObjCustomShape); - sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, rSdrObjCustomShape); - if (nX < nXMin) - nXMin = nX; - if (nY < nYMin) - nYMin = nY; - if (nX > nXMax) - nXMax = nX; - if (nY > nYMax) - nYMax = nY; + if (nPairIndex >= aPairs.getLength()) + bOK = false; + else + { + mpFS->startElementNS(XML_a, XML_moveTo); + WriteCustomGeometryPoint(aPairs[nPairIndex], aCustomShape2d); + mpFS->endElementNS(XML_a, XML_moveTo); + aCustomShape2d.GetParameter(fCurrentX, aPairs[nPairIndex].First, false, + false); + aCustomShape2d.GetParameter(fCurrentY, aPairs[nPairIndex].Second, false, + false); + bCurrentValid = true; + nPairIndex++; + } + break; } - mpFS->startElementNS( XML_a, XML_path, - XML_fill, sFill, - XML_w, OString::number(nXMax - nXMin), - XML_h, OString::number(nYMax - nYMin) ); - } - - - int nPairIndex = 0; - bool bOK = true; - for (const auto& rSegment : std::as_const(aSegments)) - { - if ( rSegment.Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH ) + case LINETO: { - mpFS->singleElementNS(XML_a, XML_close); + if (nPairIndex >= aPairs.getLength()) + bOK = false; + else + { + mpFS->startElementNS(XML_a, XML_lnTo); + WriteCustomGeometryPoint(aPairs[nPairIndex], aCustomShape2d); + mpFS->endElementNS(XML_a, XML_lnTo); + aCustomShape2d.GetParameter(fCurrentX, aPairs[nPairIndex].First, false, + false); + aCustomShape2d.GetParameter(fCurrentY, aPairs[nPairIndex].Second, false, + false); + bCurrentValid = true; + nPairIndex++; + } + break; } - for (int k = 0; k < rSegment.Count && bOK; ++k) + case CURVETO: { - switch( rSegment.Command ) + if (nPairIndex + 2 >= aPairs.getLength()) + bOK = false; + else { - case drawing::EnhancedCustomShapeSegmentCommand::MOVETO : + mpFS->startElementNS(XML_a, XML_cubicBezTo); + for (sal_uInt8 l = 0; l <= 2; ++l) { - if (nPairIndex >= aPairs.getLength()) - bOK = false; - else - { - mpFS->startElementNS(XML_a, XML_moveTo); - WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape); - mpFS->endElementNS( XML_a, XML_moveTo ); - nPairIndex++; - } - break; + WriteCustomGeometryPoint(aPairs[nPairIndex + l], aCustomShape2d); } - case drawing::EnhancedCustomShapeSegmentCommand::LINETO : + mpFS->endElementNS(XML_a, XML_cubicBezTo); + aCustomShape2d.GetParameter(fCurrentX, aPairs[nPairIndex + 2].First, + false, false); + aCustomShape2d.GetParameter(fCurrentY, aPairs[nPairIndex + 2].Second, + false, false); + bCurrentValid = true; + nPairIndex += 3; + } + break; + } + case ANGLEELLIPSETO: + case ANGLEELLIPSE: + { + if (nPairIndex + 2 >= aPairs.getLength()) + bOK = false; + else + { + // Read parameters + double fCx = 0.0; + aCustomShape2d.GetParameter(fCx, aPairs[nPairIndex].First, false, + false); + double fCy = 0.0; + aCustomShape2d.GetParameter(fCy, aPairs[nPairIndex].Second, false, + false); + double fWR = 0.0; + aCustomShape2d.GetParameter(fWR, aPairs[nPairIndex + 1].First, false, + false); + double fHR = 0.0; + aCustomShape2d.GetParameter(fHR, aPairs[nPairIndex + 1].Second, false, + false); + double fStartAngle = 0.0; + aCustomShape2d.GetParameter(fStartAngle, aPairs[nPairIndex + 2].First, + false, false); + double fEndAngle = 0.0; + aCustomShape2d.GetParameter(fEndAngle, aPairs[nPairIndex + 2].Second, + false, false); + + // Prepare start and swing angle + sal_Int32 nStartAng(std::lround(fStartAngle * 60000)); + sal_Int32 nSwingAng = 0; + if (basegfx::fTools::equalZero(fStartAngle) + && basegfx::fTools::equalZero(fEndAngle - 360.0)) + nSwingAng = 360 * 60000; // special case full circle + else { - if (nPairIndex >= aPairs.getLength()) - bOK = false; - else - { - mpFS->startElementNS(XML_a, XML_lnTo); - WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape); - mpFS->endElementNS( XML_a, XML_lnTo ); - nPairIndex++; - } - break; + nSwingAng = std::lround((fEndAngle - fStartAngle) * 60000); + if (nSwingAng < 0) + nSwingAng += 360 * 60000; } - case drawing::EnhancedCustomShapeSegmentCommand::CURVETO : + + // calculate start point on ellipse + double fSx = 0.0; + double fSy = 0.0; + getEllipsePointFromViewAngle(fSx, fSy, fWR, fHR, fCx, fCy, fStartAngle); + + // write markup for going to start point + if (rSegment.Command == ANGLEELLIPSETO) { - if (nPairIndex + 2 >= aPairs.getLength()) - bOK = false; - else - { - mpFS->startElementNS(XML_a, XML_cubicBezTo); - for( sal_uInt8 l = 0; l <= 2; ++l ) - { - WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape); - } - mpFS->endElementNS( XML_a, XML_cubicBezTo ); - nPairIndex += 3; - } - break; + mpFS->startElementNS(XML_a, XML_lnTo); + mpFS->singleElementNS(XML_a, XML_pt, XML_x, + OString::number(std::lround(fSx)), XML_y, + OString::number(std::lround(fSy))); + mpFS->endElementNS(XML_a, XML_lnTo); } - case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO : - case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE : + else { - nPairIndex += 3; - break; + mpFS->startElementNS(XML_a, XML_moveTo); + mpFS->singleElementNS(XML_a, XML_pt, XML_x, + OString::number(std::lround(fSx)), XML_y, + OString::number(std::lround(fSy))); + mpFS->endElementNS(XML_a, XML_moveTo); } - case drawing::EnhancedCustomShapeSegmentCommand::ARCTO : - case drawing::EnhancedCustomShapeSegmentCommand::ARC : - case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO : - case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC : + // write markup for arcTo + if (!basegfx::fTools::equalZero(fWR) + && !basegfx::fTools::equalZero(fHR)) + mpFS->singleElement(FSNS(XML_a, XML_arcTo), XML_wR, + OString::number(std::lround(fWR)), XML_hR, + OString::number(std::lround(fHR)), XML_stAng, + OString::number(nStartAng), XML_swAng, + OString::number(nSwingAng)); + + getEllipsePointFromViewAngle(fCurrentX, fCurrentY, fWR, fHR, fCx, fCy, + fEndAngle); + bCurrentValid = true; + nPairIndex += 3; + } + break; + } + case ARCTO: + case ARC: + case CLOCKWISEARCTO: + case CLOCKWISEARC: + { + if (nPairIndex + 3 >= aPairs.getLength()) + bOK = false; + else + { + // read parameters + double fX1 = 0.0; + aCustomShape2d.GetParameter(fX1, aPairs[nPairIndex].First, false, + false); + double fY1 = 0.0; + aCustomShape2d.GetParameter(fY1, aPairs[nPairIndex].Second, false, + false); + double fX2 = 0.0; + aCustomShape2d.GetParameter(fX2, aPairs[nPairIndex + 1].First, false, + false); + double fY2 = 0.0; + aCustomShape2d.GetParameter(fY2, aPairs[nPairIndex + 1].Second, false, + false); + double fX3 = 0.0; + aCustomShape2d.GetParameter(fX3, aPairs[nPairIndex + 2].First, false, + false); + double fY3 = 0.0; + aCustomShape2d.GetParameter(fY3, aPairs[nPairIndex + 2].Second, false, + false); + double fX4 = 0.0; + aCustomShape2d.GetParameter(fX4, aPairs[nPairIndex + 3].First, false, + false); + double fY4 = 0.0; + aCustomShape2d.GetParameter(fY4, aPairs[nPairIndex + 3].Second, false, + false); + // calculate ellipse parameter + const double fWR = (fX2 - fX1) / 2.0; + const double fHR = (fY2 - fY1) / 2.0; + const double fCx = (fX1 + fX2) / 2.0; + const double fCy = (fY1 + fY2) / 2.0; + // calculate start angle + double fStartAngle = 0.0; + double fPx = 0.0; + double fPy = 0.0; + getEllipsePointAndAngleFromRayPoint(fStartAngle, fPx, fPy, fWR, fHR, + fCx, fCy, fX3, fY3); + // markup for going to start point + if (rSegment.Command == ARCTO || rSegment.Command == CLOCKWISEARCTO) { - nPairIndex += 4; - break; + mpFS->startElementNS(XML_a, XML_lnTo); + mpFS->singleElementNS(XML_a, XML_pt, XML_x, + OString::number(std::lround(fPx)), XML_y, + OString::number(std::lround(fPy))); + mpFS->endElementNS(XML_a, XML_lnTo); } - case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX : - case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY : + else { - nPairIndex++; - break; + mpFS->startElementNS(XML_a, XML_moveTo); + mpFS->singleElementNS(XML_a, XML_pt, XML_x, + OString::number(std::lround(fPx)), XML_y, + OString::number(std::lround(fPy))); + mpFS->endElementNS(XML_a, XML_moveTo); ... etc. - the rest is truncated