cui/source/tabpages/numpages.cxx | 8 hwpfilter/source/hwpreader.cxx | 12 include/oox/export/drawingml.hxx | 5 include/svx/EnhancedCustomShape2d.hxx | 2 oox/qa/unit/data/FaultyPathStart.odp |binary oox/qa/unit/data/tdf109169_OctagonBevel.odt |binary oox/qa/unit/export.cxx | 41 + oox/source/drawingml/fillproperties.cxx | 6 oox/source/drawingml/graphicshapecontext.cxx | 32 - oox/source/drawingml/textparagraphproperties.cxx | 3 oox/source/export/drawingml.cxx | 678 ++++++++++------------ sd/qa/unit/data/pptx/tdf147586.pptx |binary sd/qa/unit/export-tests-ooxml2.cxx | 19 sd/qa/unit/export-tests-ooxml3.cxx | 22 sd/source/ui/slideshow/slideshowimpl.cxx | 2 svtools/source/control/ctrlbox.cxx | 28 svx/qa/unit/customshapes.cxx | 58 + svx/qa/unit/data/tdf148501_OctagonBevel.odp |binary svx/source/customshapes/EnhancedCustomShape2d.cxx | 18 sw/CppunitTest_sw_core_text.mk | 1 sw/qa/core/text/text.cxx | 46 + sw/qa/extras/ooxmlexport/data/tdf139948.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport17.cxx | 26 sw/qa/extras/rtfexport/data/tdf139948.rtf | 8 sw/qa/extras/rtfexport/data/tdf148515.rtf | 14 sw/qa/extras/rtfexport/rtfexport4.cxx | 51 + sw/qa/extras/uiwriter/uiwriter.cxx | 51 + sw/qa/extras/uiwriter/uiwriter3.cxx | 204 ++++++ sw/source/core/frmedt/fecopy.cxx | 14 sw/source/core/text/itrcrsr.cxx | 4 sw/source/core/txtnode/ndtxt.cxx | 5 sw/source/uibase/dochdl/swdtflvr.cxx | 26 sw/source/uibase/shells/tabsh.cxx | 46 + vcl/source/outdev/textline.cxx | 2 writerfilter/source/dmapper/DomainMapper.cxx | 11 writerfilter/source/dmapper/DomainMapper_Impl.cxx | 25 writerfilter/source/dmapper/DomainMapper_Impl.hxx | 3 writerfilter/source/dmapper/PropertyIds.cxx | 1 writerfilter/source/dmapper/PropertyIds.hxx | 1 writerfilter/source/rtftok/rtfdispatchflag.cxx | 4 writerfilter/source/rtftok/rtfdocumentimpl.cxx | 5 writerfilter/source/rtftok/rtfsprm.cxx | 2 42 files changed, 1084 insertions(+), 400 deletions(-)
New commits: commit b1dce5778af2343d5029034a6690b55993d7cfe5 Author: László Németh <nem...@numbertext.org> AuthorDate: Tue Apr 26 12:29:29 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:02 2022 +0200 tdf#148791 tdf#64902 tdf#127759 sw: fix row insert/paste Table insertion mode and Paste as Rows Above inserted only a single row above, and overwrite the next ones in the following cases (instead of inserting/pasting rows above according to the clipboard table content): The table stored in the clipboard – has centered, right or not paragraph starting alignment; – or its resource table has a table header with 2 or more repeating rows, and the clipboard content contains the same amount or less rows (it doesn't matter, that the copied rows weren't header rows originally). The reason was that parsing of the HTML clipboard content which is used for counting the row count of the clipboard content hadn't handle the following cases: – different table aligment results <tr> elements with greater indentation level in the HTML extract, according to the new root element <center>, <div> or <dl>; – copying rows from a table with row header always starts with <thead> in the HTML extract, i.e. detecting <tbody> can fail, because there could be only <thead> in the HTML extract (see Case 2 above). Follow-up to commit 0c8b1efbad48fa9697c0b1afbe4753bbbc3c4c5c "tdf#127759 Writer: add table row/column insert mode" and commit 7efae60f3625a58f8a617c80f2a55a695fbaef36 "tdf#64902 Writer table: Paste Special->Rows Above". Change-Id: I466cea20705bc5dd5455e22842da7dfa6631ba81 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133445 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> (cherry picked from commit 2fbf0f418ccb25010add33449d4e42b8b3f7fd0b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133411 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index 26ca4e8db96f..3c453ab313da 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -2267,6 +2267,119 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf141391) assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt/Text", "Portion", "hello"); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf148791) +{ + // test Paste as Rows Above with centered table alignment + + // load a 2-row table + SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf116789.fodt"); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // select and copy the table, and Paste As Rows Above + + dispatchCommand(mxComponent, ".uno:SelectTable", {}); + dispatchCommand(mxComponent, ".uno:Copy", {}); + // remove the selection and positionate the cursor at beginning of A2 + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + pWrtShell->Up(/*bSelect=*/false); + dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {}); + Scheduler::ProcessEventsToIdle(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // Paste as Rows Above results 4-row table with default table aligment + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 4); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[1]/cell[1]/txt/Text", "Portion", "hello"); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[3]/cell[1]/txt/Text", "Portion", "hello"); + + // set table alignment to center, select and copy the table again + uno::Reference<text::XTextTablesSupplier> xTextTablesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xIndexAccess(xTextTablesSupplier->getTextTables(), + uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount()); + + uno::Reference<text::XTextTable> xTextTable(xIndexAccess->getByIndex(0), uno::UNO_QUERY); + + // Default table alignment + CPPUNIT_ASSERT_EQUAL(text::HoriOrientation::FULL, + getProperty<sal_Int16>(xTextTable, "HoriOrient")); + + //CPPUNIT_ASSERT_EQUAL(OUString(""), getProperty<OUString>(xTextTable, "TableTemplateName")); + uno::Reference<beans::XPropertySet> xTableProps(xTextTable, uno::UNO_QUERY_THROW); + + xTableProps->setPropertyValue("HoriOrient", uno::makeAny(text::HoriOrientation::CENTER)); + + CPPUNIT_ASSERT_EQUAL(text::HoriOrientation::CENTER, + getProperty<sal_Int16>(xTextTable, "HoriOrient")); + + dispatchCommand(mxComponent, ".uno:SelectTable", {}); + dispatchCommand(mxComponent, ".uno:Copy", {}); + // remove the selection and positionate the cursor at beginning of A2 + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + pWrtShell->Up(/*bSelect=*/false); + pWrtShell->Up(/*bSelect=*/false); + pWrtShell->Up(/*bSelect=*/false); + dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {}); + Scheduler::ProcessEventsToIdle(); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // This was 5 (inserting only a single row for the 4-row clipboard content, and + // overwriting 3 existing rows) + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 8); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[1]/cell[1]/txt/Text", "Portion", "hello"); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[3]/cell[1]/txt/Text", "Portion", "hello"); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[5]/cell[1]/txt/Text", "Portion", "hello"); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[7]/cell[1]/txt/Text", "Portion", "hello"); + + // tdf#64902 add a test case for nested tables + + // insert a nested table, and copy as paste as rows above the whole table with it + dispatchCommand(mxComponent, ".uno:PasteNestedTable", {}); + dispatchCommand(mxComponent, ".uno:SelectTable", {}); + dispatchCommand(mxComponent, ".uno:Copy", {}); + // remove the selection and positionate the cursor at beginning of A2 + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + // skip 7 table rows plus 4 rows of the nested table + for (int i = 0; i < 7 + 4; ++i) + pWrtShell->Up(/*bSelect=*/false); + dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {}); + Scheduler::ProcessEventsToIdle(); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // rows of the nested table doesn't effect row number of the main table + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 16); + // there are two nested tables after the paste + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell/tab", 2); + + // tdf#64902 add a test case for repeated table headings + + xTableProps->setPropertyValue("RepeatHeadline", uno::makeAny(true)); + CPPUNIT_ASSERT(getProperty<bool>(xTextTable, "RepeatHeadline")); + + xTableProps->setPropertyValue("HeaderRowCount", uno::makeAny(sal_Int32(3))); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getProperty<sal_Int32>(xTextTable, "HeaderRowCount")); + + dispatchCommand(mxComponent, ".uno:SelectTable", {}); + dispatchCommand(mxComponent, ".uno:Copy", {}); + // remove the selection and positionate the cursor at beginning of A2 + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + // skip 15 table rows plus 4 * 2 rows of the nested tables + for (int i = 0; i < 15 + 4 * 2; ++i) + pWrtShell->Up(/*bSelect=*/false); + dispatchCommand(mxComponent, ".uno:PasteRowsBefore", {}); + Scheduler::ProcessEventsToIdle(); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // repeating table header (and its thead/tbody indentation) doesn't effect row number + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 32); + // there are two nested tables after the paste + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row/cell/tab", 4); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf135014) { createSwDoc(); diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index 0ba19a1ec537..45b74a55773b 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -1454,6 +1454,18 @@ void SwTransferable::SelectPasteFormat(TransferableDataHelper& rData, sal_uInt8& nFormat = SotClipboardFormatId::EMBED_SOURCE; } +// get HTML indentation level by counting tabulator characters before the index +// (also index value -1 returns with 0) +static sal_Int32 lcl_getLevel(OUString& sText, sal_Int32 nIdx) +{ + sal_Int32 nRet = 0; + while ( nIdx-- > 0 && sText[nIdx] == '\t' ) + { + nRet++; + } + return nRet; +} + bool SwTransferable::Paste(SwWrtShell& rSh, TransferableDataHelper& rData, RndStdIds nAnchorType, bool bIgnoreComments, PasteTableType ePasteTable) { SwPasteContext aPasteContext(rSh); @@ -1568,16 +1580,22 @@ bool SwTransferable::Paste(SwWrtShell& rSh, TransferableDataHelper& rData, RndSt bool bRowMode = rSh.GetTableInsertMode() == SwTable::SEARCH_ROW || ePasteTable == PasteTableType::PASTE_ROW; if( rData.GetString( SotClipboardFormatId::HTML, aExpand ) && (nIdx = aExpand.indexOf("<table")) > -1 ) { - // table rows with span use also tbody - bool bShifted = aExpand.indexOf("<tbody>") > -1; + // calculate table row/column count by analysing indentation of the HTML table extract + + // calculate indentation level of <table>, which is the base of the next calculations + // (tdf#148791 table alignment can enlarge it using first level <center>, <div> or <dl>) + sal_Int32 nTableLevel = lcl_getLevel(aExpand, nIdx); + // table rows repeated heading use extra indentation, too: + // <thead> is always used here, and the first table with <thead> is not nested, + // if its indentation level is greater only by 1, than intentation level of the table + bool bShifted = lcl_getLevel(aExpand, aExpand.indexOf("<thead")) == nTableLevel + 1; // calculate count of selected rows or columns sal_Int32 nSelectedRowsOrCols = 0; const OUString sSearchRowOrCol = bRowMode ? OUString("</tr>") : OUString("<col "); while((nIdx = aExpand.indexOf(sSearchRowOrCol, nIdx)) > -1) { // skip rows/columns of nested tables, based on HTML indentation - if ( nIdx > 3 && (aExpand[nIdx-1] != '\t' || aExpand[nIdx-2] != '\t' || - ( bShifted && aExpand[nIdx-3] != '\t') ) && + if ( lcl_getLevel(aExpand, nIdx) == nTableLevel + (bShifted ? 2 : 1) && // skip also strange hidden empty rows <tr></tr> !aExpand.match("<tr></tr>", nIdx - 4) ) { commit 272a1403164cdd236e33af8e72fbecae23907d91 Author: Tünde Tóth <toth.tu...@nisz.hu> AuthorDate: Wed Apr 6 16:18:29 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:02 2022 +0200 tdf#53970 PPTX: fix import of linked media files Linked media files were imported as images in documents created with Impress after PPTX export. Change-Id: If4920c2e40f45fff73eca4a5fa987d524177597e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132635 Tested-by: Jenkins Tested-by: László Németh <nem...@numbertext.org> Reviewed-by: László Németh <nem...@numbertext.org> (cherry picked from commit 9564747d2fd5d2c859a359dd7fa6242c6859c0d7) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133410 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx index 2b682bcd1b99..e3830618522b 100644 --- a/oox/source/drawingml/fillproperties.cxx +++ b/oox/source/drawingml/fillproperties.cxx @@ -954,11 +954,11 @@ void GraphicProperties::pushToPropMap( PropertyMap& rPropMap, const GraphicHelpe rPropMap.setProperty(PROP_AdjustContrast, nContrast); // Media content - assert(m_xMediaStream.is() != m_sMediaPackageURL.isEmpty()); - if (m_xMediaStream.is() && !m_sMediaPackageURL.isEmpty()) + if (!m_sMediaPackageURL.isEmpty()) { - rPropMap.setProperty(PROP_PrivateStream, m_xMediaStream); rPropMap.setProperty(PROP_MediaURL, m_sMediaPackageURL); + if (m_xMediaStream.is()) + rPropMap.setProperty(PROP_PrivateStream, m_xMediaStream); } } diff --git a/oox/source/drawingml/graphicshapecontext.cxx b/oox/source/drawingml/graphicshapecontext.cxx index 3ed00edfd28c..d90980a87824 100644 --- a/oox/source/drawingml/graphicshapecontext.cxx +++ b/oox/source/drawingml/graphicshapecontext.cxx @@ -84,10 +84,12 @@ ContextHandlerRef GraphicShapeContext::onCreateContext( sal_Int32 aElementToken, case XML_wavAudioFile: { OUString const path(getEmbeddedWAVAudioFile(getRelations(), rAttribs)); - mpShapePtr->getGraphicProperties().m_xMediaStream = - lcl_GetMediaStream(path, getFilter()); - mpShapePtr->getGraphicProperties().m_sMediaPackageURL = - lcl_GetMediaReference(path); + Reference<XInputStream> xMediaStream = lcl_GetMediaStream(path, getFilter()); + if (xMediaStream.is()) + { + mpShapePtr->getGraphicProperties().m_xMediaStream = xMediaStream; + mpShapePtr->getGraphicProperties().m_sMediaPackageURL = lcl_GetMediaReference(path); + } } break; case XML_audioFile: @@ -95,10 +97,24 @@ ContextHandlerRef GraphicShapeContext::onCreateContext( sal_Int32 aElementToken, { OUString rPath = getRelations().getFragmentPathFromRelId( rAttribs.getString(R_TOKEN(link)).get() ); - mpShapePtr->getGraphicProperties().m_xMediaStream = - lcl_GetMediaStream(rPath, getFilter()); - mpShapePtr->getGraphicProperties().m_sMediaPackageURL = - lcl_GetMediaReference(rPath); + if (!rPath.isEmpty()) + { + Reference<XInputStream> xMediaStream = lcl_GetMediaStream(rPath, getFilter()); + if (xMediaStream.is()) // embedded media file + { + mpShapePtr->getGraphicProperties().m_xMediaStream = xMediaStream; + mpShapePtr->getGraphicProperties().m_sMediaPackageURL + = lcl_GetMediaReference(rPath); + } + } + else + { + rPath = getRelations().getExternalTargetFromRelId( + rAttribs.getString(R_TOKEN(link)).get()); + if (!rPath.isEmpty()) // linked media file + mpShapePtr->getGraphicProperties().m_sMediaPackageURL + = getFilter().getAbsoluteUrl(rPath); + } } break; } diff --git a/sd/qa/unit/export-tests-ooxml2.cxx b/sd/qa/unit/export-tests-ooxml2.cxx index dc6498ec614d..0b244bc76233 100644 --- a/sd/qa/unit/export-tests-ooxml2.cxx +++ b/sd/qa/unit/export-tests-ooxml2.cxx @@ -1832,11 +1832,28 @@ void SdOOXMLExportTest2::testTdf53970() m_directories.getURLFromSrc(u"/sd/qa/unit/data/odp/tdf53970_linked.odp"), ODP); utl::TempFile tempFile; xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile); - xDocShRef->DoClose(); xmlDocUniquePtr pXmlRels = parseExport(tempFile, "ppt/slides/_rels/slide1.xml.rels"); CPPUNIT_ASSERT(pXmlRels); assertXPath(pXmlRels, "/rels:Relationships/rels:Relationship[@TargetMode='External']", 2); + + uno::Reference<beans::XPropertySet> xShape(getShape(0, getPage(0, xDocShRef))); + CPPUNIT_ASSERT(xShape.is()); + OUString sVideoURL; + + // Without fix in place, the media shape was imported as an image after export. + try + { + CPPUNIT_ASSERT_MESSAGE("MediaURL property is not set", + xShape->getPropertyValue("MediaURL") >>= sVideoURL); + } + catch (const beans::UnknownPropertyException&) + { + CPPUNIT_FAIL("Error: MediaURL is unknown property"); + } + CPPUNIT_ASSERT_MESSAGE("MediaURL is empty", !sVideoURL.isEmpty()); + + xDocShRef->DoClose(); } } commit 124de76ff63dfd0393ff3fa038ad0bc594ad3ff8 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Tue Apr 26 16:51:01 2022 +0100 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:02 2022 +0200 ofz#47042 previous use of static variable affecting later runs Change-Id: I64fb184e43fb025798781c85c9a0a8e0354b21b0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133453 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/hwpfilter/source/hwpreader.cxx b/hwpfilter/source/hwpreader.cxx index 1bd12242b436..d5a73be7e623 100644 --- a/hwpfilter/source/hwpreader.cxx +++ b/hwpfilter/source/hwpreader.cxx @@ -71,7 +71,6 @@ constexpr OUStringLiteral sXML_CDATA = u"CDATA"; rendEl("text:span"); \ tstart = false -static hchar *field = nullptr; static char buf[1024]; namespace @@ -97,12 +96,13 @@ struct HwpReaderPrivate bInHeader = false; nPnPos = 0; pPn = nullptr; - + pField = nullptr; } bool bFirstPara; bool bInBody; bool bInHeader; ShowPageNum *pPn; + hchar *pField; int nPnPos; }; @@ -2933,7 +2933,7 @@ void HwpReader::make_text_p3(HWPPara * para,bool bParaStart) firstspace = 1; if( hbox->type[0] == 4 && hbox->type[1] == 0 ) { - field = hbox->str3.get(); + d->pField = hbox->str3.get(); } else{ makeFieldCode(str, hbox); @@ -2946,7 +2946,7 @@ void HwpReader::make_text_p3(HWPPara * para,bool bParaStart) if( hbox->type[0] == 4 && hbox->type[1] == 0 ) { makeFieldCode(str, hbox); - field = nullptr; + d->pField = nullptr; } infield = false; str.clear(); @@ -3114,8 +3114,8 @@ void HwpReader::makeFieldCode(hchar_string const & rStr, FieldCode const *hbox) if( hbox->type[0] == 4 && hbox->type[1] == 0 ) { padd("text:placeholder-type", sXML_CDATA, "text"); - if( field ) - padd("text:description", sXML_CDATA, fromHcharStringToOUString(hstr2ucsstr(field))); + if (d->pField) + padd("text:description", sXML_CDATA, fromHcharStringToOUString(hstr2ucsstr(d->pField))); rstartEl( "text:placeholder", mxList); mxList->clear(); rchars( fromHcharStringToOUString(rStr) ); commit 16793d3f597fff2d62f55c13931a7d7104a2059f Author: Vasily Melenchuk <vasily.melenc...@cib.de> AuthorDate: Thu Apr 21 10:04:50 2022 +0300 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:02 2022 +0200 tdf#148515: RTF filter: do not use char prop defaults for tables During deduplication of table row (when nStyleType == 0) we should not deduplicate character properties against default style: this leads to invalid default settings for table rows/cells. Attempts to do so are already made during \pard processing when default style is checked if it is paragraph style. But this is not enough: style definition can contain paragraph and character properties as well. Change-Id: If520c5a248897728b7de08a017136ca1a01a5f13 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132943 Tested-by: Jenkins Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de> (cherry picked from commit 3a05acb8f0d94728ea6cbfd7a69dac6ffa7ffc68) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133477 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/extras/rtfexport/data/tdf148515.rtf b/sw/qa/extras/rtfexport/data/tdf148515.rtf new file mode 100644 index 000000000000..f8e27e577425 --- /dev/null +++ b/sw/qa/extras/rtfexport/data/tdf148515.rtf @@ -0,0 +1,14 @@ +{\rtf1\ansi +{\fonttbl +{\f1 Impact;} +} + +{\stylesheet +{\fs20\f1\af1 Normal;} +} + +\trowd\cellx5000\cellx10000 +\pard\intbl\f1\fs10 XXXXXX\cell +\pard\intbl\cell +\row +} diff --git a/sw/qa/extras/rtfexport/rtfexport4.cxx b/sw/qa/extras/rtfexport/rtfexport4.cxx index 98bf1042b212..3c898337f92d 100644 --- a/sw/qa/extras/rtfexport/rtfexport4.cxx +++ b/sw/qa/extras/rtfexport/rtfexport4.cxx @@ -635,6 +635,25 @@ DECLARE_RTFEXPORT_TEST(testTdf139948, "tdf139948.rtf") sal_uInt32(0), getProperty<table::BorderLine2>(getParagraph(5), "BottomBorder").LineWidth); } +DECLARE_RTFEXPORT_TEST(testTdf148515, "tdf148515.rtf") +{ + uno::Reference<text::XTextTable> xTable(getParagraphOrTable(1), uno::UNO_QUERY); + + uno::Reference<text::XTextRange> xCell1(xTable->getCellByName("A1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("XXXXXX"), xCell1->getString()); + CPPUNIT_ASSERT_EQUAL( + 5.0f, + getProperty<float>(getRun(getParagraphOfText(1, xCell1->getText()), 1), "CharHeight")); + + uno::Reference<text::XTextRange> xCell2(xTable->getCellByName("B1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString(""), xCell2->getString()); + CPPUNIT_ASSERT_EQUAL( + 5.0f, + getProperty<float>(getRun(getParagraphOfText(1, xCell2->getText()), 1), "CharHeight")); + + CPPUNIT_ASSERT_EQUAL(10.f, getProperty<float>(getRun(getParagraph(2), 1), "CharHeight")); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfsprm.cxx b/writerfilter/source/rtftok/rtfsprm.cxx index 04df49bc0287..2edfec829edf 100644 --- a/writerfilter/source/rtftok/rtfsprm.cxx +++ b/writerfilter/source/rtftok/rtfsprm.cxx @@ -159,7 +159,7 @@ void RTFSprms::eraseLast(Id nKeyword) static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType) { - if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character) + if (nStyleType == NS_ooxml::LN_Value_ST_StyleType_character) { switch (id) { commit ee9d26032f8f6dd43f3914d43d608eb6748406c4 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Apr 26 15:40:44 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:02 2022 +0200 sw: fix double-click opening frame dialog, not graphic dialog on images The user-visible problem was that once a user clicks on an image a lot (e.g. 5 times), then the slot ID dispatched on double-click in SwEditWin::MouseButtonDown() is no longer FN_FORMAT_GRAFIC_DLG, but it's FN_FORMAT_FRAME_DLG. This is already inconsistent, but it's especially problematic in case an UNO client intercepts only the first UNO command, but not the second one. The other inconsistency is that in practice this only happens for as-char images, at-char anchored images work fine. The reason for this seems to be how we get the doc model position for a twips view point. At-char anchored images are handled at lcl_GetModelPositionForViewPoint_Objects(), and there we return the SwGrfNode in case the view point is inside the frame of the matching fly frame. SwTextCursor::GetModelPositionForViewPoint() restricted the same to as-char fly frames which have text or layout frame children. Fix the problem by allowing non-text frame children for as-char images. Change-Id: If08e7dd2a72f46ebcfb8c6ddf110703eaeb7df6d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133443 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133462 diff --git a/sw/CppunitTest_sw_core_text.mk b/sw/CppunitTest_sw_core_text.mk index 6f19ff690f5a..19d8cd422edc 100644 --- a/sw/CppunitTest_sw_core_text.mk +++ b/sw/CppunitTest_sw_core_text.mk @@ -23,6 +23,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sw_core_text, \ cppuhelper \ sal \ sfx \ + svl \ sw \ swqahelper \ test \ diff --git a/sw/qa/core/text/text.cxx b/sw/qa/core/text/text.cxx index 3d2c91f2b131..0cd732c4a4af 100644 --- a/sw/qa/core/text/text.cxx +++ b/sw/qa/core/text/text.cxx @@ -28,6 +28,9 @@ #include <porlay.hxx> #include <pormulti.hxx> #include <formatlinebreak.hxx> +#include <sortedobjs.hxx> +#include <anchoredobject.hxx> +#include <fmtcntnt.hxx> constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/core/text/data/"; @@ -361,6 +364,49 @@ CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testClearingLineBreakHeader) assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout[1]", "height", "276"); } +CPPUNIT_TEST_FIXTURE(SwCoreTextTest, testAsCharImageDocModelFromViewPoint) +{ + // Given a document with an as-char image: + SwDoc* pDoc = createSwDoc(); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xTextGraphic( + xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); + // Only set the anchor type, the actual bitmap content is not interesting. + xTextGraphic->setPropertyValue("AnchorType", + uno::makeAny(text::TextContentAnchorType_AS_CHARACTER)); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xBodyText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor()); + uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY); + xBodyText->insertTextContent(xCursor, xTextContent, false); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + SwRootFrame* pRootFrame = pWrtShell->GetLayout(); + SwFrame* pPageFrame = pRootFrame->GetLower(); + SwFrame* pBodyFrame = pPageFrame->GetLower(); + SwFrame* pTextFrame = pBodyFrame->GetLower(); + const SwSortedObjs& rSortedObjs = *pTextFrame->GetDrawObjs(); + const SwAnchoredObject* pAnchoredObject = rSortedObjs[0]; + // The content points to the start node, the next node is the graphic node. + SwNodeIndex aGraphicNode = *pAnchoredObject->GetFrameFormat().GetContent().GetContentIdx(); + ++aGraphicNode; + tools::Rectangle aFlyFrame = pAnchoredObject->GetDrawObj()->GetLastBoundRect(); + Point aDocPos = aFlyFrame.Center(); + + // When translating the view point to the model position: + pWrtShell->SttCursorMove(); + pWrtShell->CallSetCursor(&aDocPos, /*bOnlyText=*/false); + pWrtShell->EndCursorMove(); + + // Then make sure that we find the graphic node, and not its anchor: + SwShellCursor* pShellCursor = pWrtShell->getShellCursor(/*bBlock=*/false); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: SwNodeIndex (node 6) + // - Actual : SwNodeIndex (node 12) + // i.e. the cursor position was the text node hosting the as-char image, not the graphic node of + // the image. + CPPUNIT_ASSERT_EQUAL(aGraphicNode, pShellCursor->GetMark()->nNode); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx index d9d87aaa713c..f6c8ed380107 100644 --- a/sw/source/core/text/itrcrsr.cxx +++ b/sw/source/core/text/itrcrsr.cxx @@ -1761,8 +1761,10 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con // (BugId: 9692 + Change in feshview) SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame(); SwFrame* pLower = pTmp->GetLower(); + // Allow non-text-frames to get SwGrfNode for as-char anchored images into pPos + // instead of the closest SwTextNode, to be consistent with at-char behavior. bool bChgNodeInner = pLower - && (pLower->IsTextFrame() || pLower->IsLayoutFrame()); + && (pLower->IsTextFrame() || pLower->IsLayoutFrame() || pLower->IsNoTextFrame()); Point aTmpPoint( rPoint ); if ( m_pFrame->IsRightToLeft() ) commit cf19ed587de53a3d4c737c01d9bb1ad570843a38 Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Wed Apr 20 18:16:50 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:02 2022 +0200 tdf#109169 export Type encoded shading to OOXML A shape might have the shading information not in commands in the enhanced-path, but generated in ctor of EnhancedCustomShape2d from the Type value of the shape. This shading information is a 32 bit value with first the number of the shadings and then the shadings. A shading is encoded with 1,2,3,4,5,6,7 for lighten 10 to 70 and 8,9,a,b,c,d,e,f for darken -80 to -10. To get this information from EnhanceCustomShape2d I have made its method GetLuminanceChange() public. Because OOXML only has darken, darkenLess, lighten and lightenLess our values are mapped: -10, -20, -30 to darkenLess -40, -50, -60, -70, -80 to darken 10, 20, 30 to lightenLess 40, 50, 60, 70 to lighten The bupreport mentions only 'Octagon Bevel' and 'Diamond Bevel'. But the patch fixes missing shading for shapes of Types 'ActionButton*' as well. Such shapes come in from MS binary import. Change-Id: I03f19496b915f3ced6346222e8806832b4ee2827 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133220 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.hensc...@t-online.de> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133366 diff --git a/include/svx/EnhancedCustomShape2d.hxx b/include/svx/EnhancedCustomShape2d.hxx index 18ce21437bcc..abe9319407ff 100644 --- a/include/svx/EnhancedCustomShape2d.hxx +++ b/include/svx/EnhancedCustomShape2d.hxx @@ -127,7 +127,6 @@ class SVXCORE_DLLPUBLIC EnhancedCustomShape2d final : public SfxItemSet Degree100 nRotateAngle; SAL_DLLPRIVATE bool SetAdjustValueAsDouble( const double& rValue, const sal_Int32 nIndex ); - SAL_DLLPRIVATE sal_Int32 GetLuminanceChange( sal_uInt32 nIndex ) const; SAL_DLLPRIVATE Color GetColorData( const Color& rFillColor, sal_uInt32 nIndex, double dBrightness ) const; SAL_DLLPRIVATE void AdaptObjColor( SdrPathObj& rObj, @@ -183,6 +182,7 @@ class SVXCORE_DLLPUBLIC EnhancedCustomShape2d final : public SfxItemSet } }; + sal_Int32 GetLuminanceChange( sal_uInt32 nIndex ) const; SAL_DLLPRIVATE bool IsFlipVert() const { return bFlipV; }; SAL_DLLPRIVATE bool IsFlipHorz() const { return bFlipH; }; SAL_DLLPRIVATE Degree100 GetRotateAngle() const { return nRotateAngle; }; diff --git a/oox/qa/unit/data/tdf109169_OctagonBevel.odt b/oox/qa/unit/data/tdf109169_OctagonBevel.odt new file mode 100644 index 000000000000..2ba39b102e1e Binary files /dev/null and b/oox/qa/unit/data/tdf109169_OctagonBevel.odt differ diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx index 26d1a0e973bb..62988f7fd3bf 100644 --- a/oox/qa/unit/export.cxx +++ b/oox/qa/unit/export.cxx @@ -530,6 +530,27 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath) assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80"); } +CPPUNIT_TEST_FIXTURE(Test, testTdf109169_OctagonBevel) +{ + // The odp file contains an "Octagon Bevel" shape. Such has shading not in commands H,I,J,K + // but shading is generated in ctor of EnhancedCustomShape2d from the Type value. + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf109169_OctagonBevel.odt"; + + // Export to docx had not written a:fill or a:stroke attributes at all. + loadAndSave(aURL, "Office Open XML Text"); + + // Verify the markup: + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "word/document.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // File should have six subpaths, one with stroke and five with fill + assertXPath(pXmlDoc, "//a:pathLst/a:path[1]", "stroke", "0"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[2]", "fill", "darkenLess"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[3]", "fill", "darken"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "fill", "darken"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[5]", "fill", "lightenLess"); + assertXPath(pXmlDoc, "//a:pathLst/a:path[6]", "fill", "lighten"); +} + CPPUNIT_TEST_FIXTURE(Test, testFaultyPathCommandsAWT) { // The odp file contains shapes whose path starts with command A, W, T or L. That is a faulty diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index c2bdf39042a4..2a11fa507959 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -3898,6 +3898,7 @@ bool DrawingML::WriteCustomGeometry( sal_Int32 nPairIndex = 0; // index over "Coordinates" sal_Int32 nPathSizeIndex = 0; // index over "SubViewSize" sal_Int32 nSubpathStartIndex(0); // index over "Segments" + sal_Int32 nSubPathIndex(0); // serial number of current subpath do { bool bOK(true); // catch faulty paths were commands do not correspond to points @@ -3920,6 +3921,19 @@ bool DrawingML::WriteCustomGeometry( else if (HasCommandInSubPath(LIGHTENLESS, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments)) sFill = "lightenLess"; + else + { + // shading info might be in object type, e.g. "Octagon Bevel". + sal_Int32 nLuminanceChange(aCustomShape2d.GetLuminanceChange(nSubPathIndex)); + if (nLuminanceChange <= -40) + sFill = "darken"; + else if (nLuminanceChange <= -10) + sFill = "darkenLess"; + else if (nLuminanceChange >= 40) + sFill = "lighten"; + else if (nLuminanceChange >= 10) + sFill = "lightenLess"; + } // NOSTROKE std::optional<OString> sStroke; if (HasCommandInSubPath(NOSTROKE, nSubpathStartIndex, nNextNcommandIndex - 1, aSegments)) @@ -3964,6 +3978,7 @@ bool DrawingML::WriteCustomGeometry( // step forward to next subpath nSubpathStartIndex = nNextNcommandIndex + 1; nPathSizeIndex++; + nSubPathIndex++; } while (nSubpathStartIndex < aSegments.getLength()); mpFS->endElementNS(XML_a, XML_pathLst); commit d0e60c6f815de9b066c0697aabec87a776952f51 Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Mon Apr 18 19:31:26 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:01 2022 +0200 tdf#148501 color shades only for filled PolyPolygons For shading parts of a shape not of Type 'ooxml-*', ColorData is used, introduced about 2005. For 'mso_spt*' shapes they are set directly, for others they are encoded as 'col-********' into the Type value. During OOo time two changes were made, resulting in the bugs, that colors are assigned to wrong segments and that shadings are too dark. More details are in the bug report. With this patch the colors are assigned to the correct segments again. The too dark colors are visible in our preset shapes 'Octagon Bevel'. The shape 'Diamond Bevel' with corrected color assignment is also affected. Both need new ColorData. Since it is important for Libreoffice to have good compatibility with OOXML, I have decided to use only the four shading values available in OOXML. In the long run, these shapes should be replaced by ones that contain the shading information inside the <enhanced-path> element. Change-Id: I4b8323c45bf702fc371d6e6c82dd9102d0fd9929 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133132 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133153 diff --git a/svx/qa/unit/customshapes.cxx b/svx/qa/unit/customshapes.cxx index ca6ece320afa..690d6c02aa45 100644 --- a/svx/qa/unit/customshapes.cxx +++ b/svx/qa/unit/customshapes.cxx @@ -18,6 +18,7 @@ #include <basegfx/polygon/b2dpolypolygon.hxx> #include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/point/b2dpoint.hxx> +#include <comphelper/propertyvalue.hxx> #include <sfx2/request.hxx> #include <sfx2/viewfrm.hxx> #include <sfx2/viewsh.hxx> @@ -31,12 +32,15 @@ #include <svx/unoapi.hxx> #include <unotools/mediadescriptor.hxx> #include <unotools/tempfile.hxx> +#include <vcl/filter/PngImageReader.hxx> +#include <vcl/BitmapReadAccess.hxx> #include <cppunit/TestAssert.h> #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/drawing/XDrawPagesSupplier.hpp> #include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/drawing/GraphicExportFilter.hpp> #include <com/sun/star/awt/Rectangle.hpp> #include <com/sun/star/frame/Desktop.hpp> #include <com/sun/star/frame/XStorable.hpp> @@ -1156,6 +1160,60 @@ CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf136176) } } } + +CPPUNIT_TEST_FIXTURE(CustomshapesTest, testTdf148501_OctagonBevel) +{ + // The document contains a shape "Octagon Bevel". It should use shadings 40%, 20%, -20%, -40% + // from left-top to bottom-right. The test examines actual color, not the geometry. + // Load document + OUString aURL = m_directories.getURLFromSrc(sDataDirectory) + "tdf148501_OctagonBevel.odp"; + mxComponent = loadFromDesktop(aURL, "com.sun.star.presentation.PresentationDocument"); + + // Generate bitmap from shape + uno::Reference<drawing::XShape> xShape = getShape(0); + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + + uno::Reference<uno::XComponentContext> xContext = getComponentContext(); + CPPUNIT_ASSERT(xContext.is()); + uno::Reference<drawing::XGraphicExportFilter> xGraphicExporter + = drawing::GraphicExportFilter::create(xContext); + + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("URL", aTempFile.GetURL()), + comphelper::makePropertyValue("FilterName", OUString("PNG")) + }; + + uno::Reference<lang::XComponent> xSourceDocument(xShape, uno::UNO_QUERY_THROW); + xGraphicExporter->setSourceDocument(xSourceDocument); + xGraphicExporter->filter(aDescriptor); + + // Read bitmap and test color + // expected in order top-left, top, top-right, right, bottom-right: + // RGB(165|195|266), RGB(139|176|217), RGB(91|127|166), RGB(68|95|124), RGB(68|95|124) + // Without applied patch the colors were: + // RGB(193|214,236), RGB(193|214,236), RGB(80|111|145), RGB(23|32|41), RGB(193|214|236) + // So we test segments top, right and bottom-right. + SvFileStream aFileStream(aTempFile.GetURL(), StreamMode::READ); + vcl::PngImageReader aPNGReader(aFileStream); + BitmapEx aBMPEx = aPNGReader.read(); + Bitmap aBMP = aBMPEx.GetBitmap(); + Bitmap::ScopedReadAccess pRead(aBMP); + Size aSize = aBMP.GetSizePixel(); + + // GetColor(Y,X). The chosen threshold for the ColorDistance can be adapted if necessary. + Color aActualColor = pRead->GetColor(aSize.Height() * 0.17, aSize.Width() * 0.5); // top + Color aExpectedColor(139, 176, 217); + sal_uInt16 nColorDistance = aExpectedColor.GetColorError(aActualColor); + CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance); + aActualColor = pRead->GetColor(aSize.Height() * 0.5, aSize.Width() * 0.83); // right + aExpectedColor = Color(68, 95, 124); // same for right and bottom-right + nColorDistance = aExpectedColor.GetColorError(aActualColor); + CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance); + aActualColor = pRead->GetColor(aSize.Height() * 0.75, aSize.Width() * 0.75); // bottom-right + nColorDistance = aExpectedColor.GetColorError(aActualColor); + CPPUNIT_ASSERT_LESS(sal_uInt16(6), nColorDistance); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/qa/unit/data/tdf148501_OctagonBevel.odp b/svx/qa/unit/data/tdf148501_OctagonBevel.odp new file mode 100644 index 000000000000..9dafaf7c2624 Binary files /dev/null and b/svx/qa/unit/data/tdf148501_OctagonBevel.odp differ diff --git a/svx/source/customshapes/EnhancedCustomShape2d.cxx b/svx/source/customshapes/EnhancedCustomShape2d.cxx index 1f05a3782a7c..14f054ddce73 100644 --- a/svx/source/customshapes/EnhancedCustomShape2d.cxx +++ b/svx/source/customshapes/EnhancedCustomShape2d.cxx @@ -777,8 +777,19 @@ EnhancedCustomShape2d::EnhancedCustomShape2d(SdrObjCustomShape& rSdrObjCustomSha case mso_sptSmileyFace : nColorData = 0x20e00000; break; case mso_sptNil : { - if( sShapeType.getLength() > 4 && - sShapeType.match( "col-" )) + // Because calculation method has changed in #i102797 original color encoding for + // Octagon Bevel and Diamond Bevel can no longer be used. We keep the color coding + // only for self-created shapes, as authors may have already considered the change. + // We use ColorData compatible to OOXML. + if (sShapeType == "col-60da8460") // Octagon Bevel + { + nColorData = 0x60ecc240; + } + else if (sShapeType == "col-502ad400") // Diamond Bevel + { + nColorData = 0x502ce400; + } + else if (sShapeType.getLength() > 4 && sShapeType.match( "col-" )) { nColorData = sShapeType.copy( 4 ).toUInt32( 16 ); } @@ -2755,6 +2766,9 @@ void EnhancedCustomShape2d::AdaptObjColor( return; const drawing::FillStyle eFillStyle = rObj.GetMergedItem(XATTR_FILLSTYLE).GetValue(); + if (eFillStyle == drawing::FillStyle_NONE) + return; + switch( eFillStyle ) { default: commit 19cc327fa6cf91e123be5b97267202fdb08d0aa4 Author: Xisco Fauli <xiscofa...@libreoffice.org> AuthorDate: Tue Apr 26 00:00:18 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:01 2022 +0200 tdf#147586: Initialize mbBulletColorFollowText to false Otherwise, once it's set to true, it's never reset Change-Id: Ie8a752da4162775f40c2f84f480e6a103eb55942 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133422 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> (cherry picked from commit a0bae88a9cd47185a71cbfd4c86bbd86ae44d30e) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133404 Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/oox/source/drawingml/textparagraphproperties.cxx b/oox/source/drawingml/textparagraphproperties.cxx index 70f1ac0c937b..07624e83d1fa 100644 --- a/oox/source/drawingml/textparagraphproperties.cxx +++ b/oox/source/drawingml/textparagraphproperties.cxx @@ -57,7 +57,8 @@ using ::com::sun::star::awt::FontDescriptor; namespace oox::drawingml { BulletList::BulletList( ) -: maBulletColorPtr( std::make_shared<Color>() ) +: maBulletColorPtr( std::make_shared<Color>() ), + mbBulletColorFollowText ( false ) { } diff --git a/sd/qa/unit/data/pptx/tdf147586.pptx b/sd/qa/unit/data/pptx/tdf147586.pptx new file mode 100644 index 000000000000..723facf82071 Binary files /dev/null and b/sd/qa/unit/data/pptx/tdf147586.pptx differ diff --git a/sd/qa/unit/export-tests-ooxml3.cxx b/sd/qa/unit/export-tests-ooxml3.cxx index 96642cdc73ec..84366e1be9f3 100644 --- a/sd/qa/unit/export-tests-ooxml3.cxx +++ b/sd/qa/unit/export-tests-ooxml3.cxx @@ -55,6 +55,7 @@ class SdOOXMLExportTest3 : public SdModelTestBaseXML public: void testTdf129430(); void testTdf114848(); + void testTdf147586(); void testTdf68759(); void testTdf127901(); void testTdf48735(); @@ -135,6 +136,7 @@ public: CPPUNIT_TEST(testTdf129430); CPPUNIT_TEST(testTdf114848); + CPPUNIT_TEST(testTdf147586); CPPUNIT_TEST(testTdf68759); CPPUNIT_TEST(testTdf127901); CPPUNIT_TEST(testTdf48735); @@ -246,6 +248,26 @@ void SdOOXMLExportTest3::testTdf114848() "1f497d"); } +void SdOOXMLExportTest3::testTdf147586() +{ + ::sd::DrawDocShellRef xDocShRef + = loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/pptx/tdf147586.pptx"), PPTX); + utl::TempFile tempFile; + xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile); + xDocShRef->DoClose(); + + xmlDocUniquePtr pXmlDocContent = parseExport(tempFile, "ppt/slides/slide1.xml"); + // Without the fix in place, this test would have failed with + // - Expected: 227fc7 + // - Actual : 4f4f4f + assertXPath(pXmlDocContent, + "/p:sld/p:cSld/p:spTree/p:sp[1]/p:txBody/a:p[1]/a:pPr/a:buClr/a:srgbClr", "val", + "227fc7"); + assertXPath(pXmlDocContent, + "/p:sld/p:cSld/p:spTree/p:sp[1]/p:txBody/a:p[2]/a:pPr/a:buClr/a:srgbClr", "val", + "227fc7"); +} + void SdOOXMLExportTest3::testTdf68759() { ::sd::DrawDocShellRef xDocShRef commit 8c5238c7a80e2be45a326afd0b751c9aa00f51bd Author: Vasily Melenchuk <vasily.melenc...@cib.de> AuthorDate: Fri Apr 1 16:35:40 2022 +0300 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:01 2022 +0200 tdf#139948: docx and rtf import: emulate border in between Writer does not support border in between available in all MS formats. Since this feature is missing in core it will be better to emulate it with top borders than to ignore it completely. Change-Id: I4e5a99cde5908066c4bb483136cfe9a1316df53c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132429 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> (cherry picked from commit e3a996bf72a16f5b22e6ff021745af5cec70a632) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132594 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/extras/ooxmlexport/data/tdf139948.docx b/sw/qa/extras/ooxmlexport/data/tdf139948.docx new file mode 100644 index 000000000000..1b3f7df00031 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf139948.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx index d0e3c151282b..b72a470978cc 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport17.cxx @@ -327,6 +327,32 @@ DECLARE_OOXMLEXPORT_TEST(testTdf81507, "tdf81507.docx") xmlXPathFreeObject(pXmlObj); } +DECLARE_OOXMLEXPORT_TEST(testTdf139948, "tdf139948.docx") +{ + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(1, "No border"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(2, "Border below"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), + getProperty<table::BorderLine2>(getParagraph(3, "Borders below and above"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), + getProperty<table::BorderLine2>(getParagraph(4, "Border above"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(5, "No border"), "TopBorder").LineWidth); + + + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(1), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(2), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(3), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(4), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(5), "BottomBorder").LineWidth); +} + DECLARE_OOXMLEXPORT_TEST(testTdf144563, "tdf144563.docx") { uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY); diff --git a/sw/qa/extras/rtfexport/data/tdf139948.rtf b/sw/qa/extras/rtfexport/data/tdf139948.rtf new file mode 100644 index 000000000000..0b601a764adf --- /dev/null +++ b/sw/qa/extras/rtfexport/data/tdf139948.rtf @@ -0,0 +1,8 @@ +{\rtf1\ansi +No border\par +\pard\brdrbtw\brdrs\brdrw50 +Border below\par +Borders below and above\par +Border above\par +\pard No border\par +} \ No newline at end of file diff --git a/sw/qa/extras/rtfexport/rtfexport4.cxx b/sw/qa/extras/rtfexport/rtfexport4.cxx index 36c908c222bd..98bf1042b212 100644 --- a/sw/qa/extras/rtfexport/rtfexport4.cxx +++ b/sw/qa/extras/rtfexport/rtfexport4.cxx @@ -603,6 +603,38 @@ CPPUNIT_TEST_FIXTURE(Test, testClearingBreak) verify(); } +DECLARE_RTFEXPORT_TEST(testTdf139948, "tdf139948.rtf") +{ + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(1, "No border"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(2, "Border below"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(88), + getProperty<table::BorderLine2>(getParagraph(3, "Borders below and above"), "TopBorder") + .LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(88), + getProperty<table::BorderLine2>(getParagraph(4, "Border above"), "TopBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), + getProperty<table::BorderLine2>(getParagraph(5, "No border"), "TopBorder").LineWidth); + + // And let's ensure that there are no other horizontal borders + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), getProperty<table::BorderLine2>(getParagraph(1), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), getProperty<table::BorderLine2>(getParagraph(2), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), getProperty<table::BorderLine2>(getParagraph(3), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), getProperty<table::BorderLine2>(getParagraph(4), "BottomBorder").LineWidth); + CPPUNIT_ASSERT_EQUAL( + sal_uInt32(0), getProperty<table::BorderLine2>(getParagraph(5), "BottomBorder").LineWidth); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index 2ae843c9834f..59215a9d6aa5 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -1492,7 +1492,16 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) eBorderDistId = PROP_RIGHT_BORDER_DISTANCE ; break; case NS_ooxml::LN_CT_PBdr_between: - //not supported + if (m_pImpl->handlePreviousParagraphBorderInBetween()) + { + // If previous paragraph also had border in between property + // then it is possible to emulate this border as top border + // for current paragraph + eBorderId = PROP_TOP_BORDER; + eBorderDistId = PROP_TOP_BORDER_DISTANCE; + } + // Since there are borders in between, each paragraph will have own borders. No more joining + rContext->Insert(PROP_PARA_CONNECT_BORDERS, uno::makeAny(false)); break; default:; } diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 1e9d8f55c587..3edffc00121f 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -8410,6 +8410,31 @@ void DomainMapper_Impl::commentProps(const OUString& sId, const CommentPropertie m_aCommentProps[sId] = rProps; } + +bool DomainMapper_Impl::handlePreviousParagraphBorderInBetween() const +{ + if (!m_xPreviousParagraph.is()) + return false; + + // Connected borders ("ParaIsConnectBorder") are always on by default + // and never changed by DomainMapper. Except one case when border in + // between is used. So this is not the best, but easiest way to check + // is previous paragraph has border in between. + bool bConnectBorders = true; + m_xPreviousParagraph->getPropertyValue(getPropertyName(PROP_PARA_CONNECT_BORDERS)) >>= bConnectBorders; + + if (bConnectBorders) + return false; + + // Previous paragraph has border in between. Current one also has (since this + // method is called). So current paragraph will get border above, but + // also need to ensure, that no unexpected bottom border are remaining in previous + // paragraph: since ParaIsConnectBorder=false it will be displayed in unexpected way. + m_xPreviousParagraph->setPropertyValue(getPropertyName(PROP_BOTTOM_BORDER), uno::makeAny(table::BorderLine2())); + + return true; +} + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx index fcd410c6ce9d..3b4602f252b5 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -1153,6 +1153,9 @@ public: /// start/end node. void ClearPreviousParagraph(); + /// Check if previous paragraph has borders in between and do the border magic to it if so + bool handlePreviousParagraphBorderInBetween() const; + /// Handle redline text portions in a frame, footnotes and redlines: /// store their data, and create them after frame creation or footnote/endnote copying bool m_bIsActualParagraphFramed; diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx index b339a83f8ae6..1189bc0b1410 100644 --- a/writerfilter/source/dmapper/PropertyIds.cxx +++ b/writerfilter/source/dmapper/PropertyIds.cxx @@ -364,6 +364,7 @@ OUString getPropertyName( PropertyIds eId ) sName = "RtlGutter"; break; case PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF: sName = "CursorNotIgnoreTables"; break; + case PROP_PARA_CONNECT_BORDERS: sName= "ParaIsConnectBorder"; break; } assert(sName.getLength()>0); return sName; diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx index b09170d2da36..7b6fe9a05275 100644 --- a/writerfilter/source/dmapper/PropertyIds.hxx +++ b/writerfilter/source/dmapper/PropertyIds.hxx @@ -361,6 +361,7 @@ enum PropertyIds ,PROP_GUTTER_MARGIN ,PROP_RTL_GUTTER ,PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF + ,PROP_PARA_CONNECT_BORDERS }; //Returns the UNO string equivalent to eId. diff --git a/writerfilter/source/rtftok/rtfdispatchflag.cxx b/writerfilter/source/rtftok/rtfdispatchflag.cxx index 3aee1e1dda75..9d891384360c 100644 --- a/writerfilter/source/rtftok/rtfdispatchflag.cxx +++ b/writerfilter/source/rtftok/rtfdispatchflag.cxx @@ -672,6 +672,7 @@ RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword) case RTFKeyword::BRDRL: case RTFKeyword::BRDRB: case RTFKeyword::BRDRR: + case RTFKeyword::BRDRBTW: { RTFSprms aAttributes; RTFSprms aSprms; @@ -690,6 +691,9 @@ RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword) case RTFKeyword::BRDRR: nParam = getParagraphBorder(3); break; + case RTFKeyword::BRDRBTW: + nParam = getParagraphBorder(4); + break; default: break; } diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx index 9e77cbf4602e..0aa22aa96f55 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -81,8 +81,9 @@ namespace writerfilter::rtftok { Id getParagraphBorder(sal_uInt32 nIndex) { - static const Id aBorderIds[] = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left, - NS_ooxml::LN_CT_PBdr_bottom, NS_ooxml::LN_CT_PBdr_right }; + static const Id aBorderIds[] + = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left, NS_ooxml::LN_CT_PBdr_bottom, + NS_ooxml::LN_CT_PBdr_right, NS_ooxml::LN_CT_PBdr_between }; return aBorderIds[nIndex]; } commit 9e7ce733b83efd9780b8847baa882e3a5377e900 Author: Julien Nabet <serval2...@yahoo.fr> AuthorDate: Sat Apr 23 18:17:57 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:01 2022 +0200 tdf#136429: use FieldUnit value for min/max of some fields of numbering dialog Change-Id: I01428703a7bdb2090fd145e5373c37aff0d386ac Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133348 (cherry picked from commit 176021427fece6363df56022b63813e93505fdc2) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133399 Tested-by: Jenkins Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/cui/source/tabpages/numpages.cxx b/cui/source/tabpages/numpages.cxx index ade080a1436a..7d2683ab3ed4 100644 --- a/cui/source/tabpages/numpages.cxx +++ b/cui/source/tabpages/numpages.cxx @@ -29,6 +29,7 @@ #include <svl/eitem.hxx> #include <vcl/svapp.hxx> #include <svx/colorbox.hxx> +#include <svx/dlgutil.hxx> #include <svx/strarray.hxx> #include <svx/gallery.hxx> #include <editeng/brushitem.hxx> @@ -2506,6 +2507,13 @@ SvxNumPositionTabPage::SvxNumPositionTabPage(weld::Container* pPage, weld::Dialo { SetExchangeSupport(); + // set metric + FieldUnit eFUnit = GetModuleFieldUnit(rSet); + + SetFieldUnit( *m_xDistBorderMF, eFUnit ); + SetFieldUnit( *m_xIndentMF, eFUnit ); + SetFieldUnit( *m_xDistNumMF, eFUnit ); + m_xAlignedAtMF->set_range(0, SAL_MAX_INT32, FieldUnit::NONE); m_xListtabMF->set_range(0, SAL_MAX_INT32, FieldUnit::NONE); m_xIndentAtMF->set_range(0, SAL_MAX_INT32, FieldUnit::NONE); commit 3c5646e3468c3d0bfbbd5b6b208e97c3864010dc Author: László Németh <nem...@numbertext.org> AuthorDate: Fri Apr 22 13:23:55 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:01 2022 +0200 tdf#147453 sw: disable Delete Table functions on tracked deletions In Show Changes mode, disable "Delete Selected Rows" icons and the same menu options in the following cases: - no table selection, but the text cursor in a deleted table row; - with table selection, all selected cells in deleted table rows. Disable also "Deleted Selected Columns" and "Delete Table" icons and the same menu options, when the cursor is there in a deleted table. Follow-up to commit c4f6fee3bea0d8618b5815e60304ff9359ccd21c "tdf#147435 sw: enable Accept Change for table selection". Change-Id: Ic6781ccee794c7458e6979f2e981840339cd3883 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133320 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> (cherry picked from commit 0204c00f241313e1d292b4c3ea117d42af7dec69) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133292 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/source/uibase/shells/tabsh.cxx b/sw/source/uibase/shells/tabsh.cxx index 7798b6c0da9c..84a090b111fd 100644 --- a/sw/source/uibase/shells/tabsh.cxx +++ b/sw/source/uibase/shells/tabsh.cxx @@ -55,6 +55,7 @@ #include <fmtfsize.hxx> #include <swmodule.hxx> #include <wrtsh.hxx> +#include <rootfrm.hxx> #include <wview.hxx> #include <frmatr.hxx> #include <uitool.hxx> @@ -445,6 +446,43 @@ static void lcl_TabGetMaxLineWidth(const SvxBorderLine* pBorderLine, SvxBorderLi rBorderLine.SetColor(pBorderLine->GetColor()); } +static bool lcl_BoxesInDeletedRows(SwWrtShell &rSh, const SwSelBoxes& rBoxes) +{ + // cursor and selection are there only in deleted rows in Show Changes mode + if ( rSh.GetLayout()->IsHideRedlines() ) + return false; + + // not selected or all selected rows are deleted + bool bRet = true; + SwRedlineTable::size_type nRedlinePos = 0; + if ( rBoxes.empty() ) + bRet = rSh.GetCursor()->GetNode().GetTableBox()->GetUpper()->IsDeleted(nRedlinePos); + else + { + tools::Long nBoxes = rBoxes.size(); + SwTableLine* pPrevLine = nullptr; + for ( tools::Long i = 0; i < nBoxes; i++ ) + { + SwTableLine* pLine = rBoxes[i]->GetUpper(); + if ( pLine != pPrevLine ) + bRet &= pLine->IsDeleted(nRedlinePos); + pPrevLine = pLine; + } + } + + return bRet; +} + +static bool lcl_CursorInDeletedTable(SwWrtShell &rSh) +{ + // cursor and selection are there only in deleted table in Show Changes mode + if ( rSh.GetLayout()->IsHideRedlines() ) + return false; + + SwTableNode* pTableNd = rSh.GetCursor()->GetPoint()->nNode.GetNode().FindTableNode(); + return pTableNd && pTableNd->GetTable().IsDeleted(); +} + void SwTableShell::Execute(SfxRequest &rReq) { const SfxItemSet* pArgs = rReq.GetArgs(); @@ -1381,7 +1419,7 @@ void SwTableShell::GetState(SfxItemSet &rSet) { SwSelBoxes aBoxes; ::GetTableSel( rSh, aBoxes, SwTableSearchType::Row ); - if( ::HasProtectedCells( aBoxes )) + if( ::HasProtectedCells( aBoxes ) || lcl_BoxesInDeletedRows( rSh, aBoxes ) ) rSet.DisableItem( nSlot ); } break; @@ -1389,10 +1427,14 @@ void SwTableShell::GetState(SfxItemSet &rSet) { SwSelBoxes aBoxes; ::GetTableSel( rSh, aBoxes, SwTableSearchType::Col ); - if( ::HasProtectedCells( aBoxes )) + if( ::HasProtectedCells( aBoxes ) || lcl_CursorInDeletedTable( rSh ) ) rSet.DisableItem( nSlot ); } break; + case FN_TABLE_DELETE_TABLE: + if( lcl_CursorInDeletedTable( rSh ) ) + rSet.DisableItem( nSlot ); + break; case FN_TABLE_UNSET_READ_ONLY_CELLS: // disable in readonly sections, but enable in protected cells commit e590b2ad63eaf35b3377815c922bce25b15c238c Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Mon Apr 25 12:34:53 2022 +0100 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:01 2022 +0200 tdf#148654 stop playing sound with presentation ends Change-Id: I74aa4cd966b6966f7826d241fcf9bb9d64955464 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133394 Tested-by: Jenkins Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx b/sd/source/ui/slideshow/slideshowimpl.cxx index 0c775d275913..ecb9663ed8ba 100644 --- a/sd/source/ui/slideshow/slideshowimpl.cxx +++ b/sd/source/ui/slideshow/slideshowimpl.cxx @@ -1350,6 +1350,8 @@ IMPL_LINK_NOARG(SlideshowImpl, endPresentationHdl, void*, void) { mnEndShowEvent = nullptr; + stopSound(); + if( mxPresentation.is() ) mxPresentation->end(); } commit ac13cd2323fcd0fe1d84ed9c2ec749991d284358 Author: László Németh <nem...@numbertext.org> AuthorDate: Fri Apr 22 20:34:32 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:00 2022 +0200 tdf#141391 sw: don't paste as nested table in first cell paragraph Paste table content overwrote cells of the row(s) only if the text cursor was at the beginning of the table cell, otherwise the table cells on the clipboard were inserted as a nested table. This was a UX regression from commit 7600a2942ce2b9dac66836105bed6620d55abec2 "fdo#37156 insert table copy as nested table in non-starting cell position" especially when the user clicked not exactly at the beginning of a cell, which containing a 1-line text or data. Since commit 1e278d1d0cfb1d5375195aa764739f00633f21e8 "tdf#37156 Writer menu: Paste as Nested table" it's possible to force nesting (but not overwriting yet), this commit revert partially commit 7600a2942ce2b9dac66836105bed6620d55abec2: if the text cursor is there in the first paragraph of the cell, Paste table content overwrites the row, not embedding a nested table in the cell at the cursor position. This change results also better interoperability with the existing document editors. Note: table and text selection were checked with the change, too. Details: Heuristics to allow copying table rows or nesting tables without using Edit -> Paste Special -> Paste as Nested Table: At "table selection" (i.e. when cell(s) completely selected), or if the text selection starts in the first paragraph, or if there is no selection and the text cursor is there in the first paragraph, overwrite content of the cell(s). Otherwise insert a nested table, i.e. if nothing selected and the cursor is not in the first paragraph, or the selected text doesn't contain the first paragraph of the cell. Change-Id: I7746c6a464123bef6fb7dbff415ff0414e48365d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133377 Tested-by: Jenkins Reviewed-by: László Németh <nem...@numbertext.org> (cherry picked from commit b97116791047f89b768ab4aa8126e543df826be3) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133402 Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org> diff --git a/sw/qa/extras/uiwriter/uiwriter3.cxx b/sw/qa/extras/uiwriter/uiwriter3.cxx index 03b2bc92b307..26ca4e8db96f 100644 --- a/sw/qa/extras/uiwriter/uiwriter3.cxx +++ b/sw/qa/extras/uiwriter/uiwriter3.cxx @@ -2176,6 +2176,97 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf148345) assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf141391) +{ + // table insertion in the first paragraph of the cell + // overwrites the row content, instead of inserting a nested table + + // load a 2-row table + SwDoc* pDoc = createSwDoc(DATA_DIRECTORY, "tdf116789.fodt"); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // select the table, and copy it into at paragraph start of cell "A2" + + dispatchCommand(mxComponent, ".uno:SelectTable", {}); + dispatchCommand(mxComponent, ".uno:Copy", {}); + // remove the selection and positionate the cursor at beginning of A2 + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + dispatchCommand(mxComponent, ".uno:Paste", {}); + Scheduler::ProcessEventsToIdle(); + + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + // 3-row, overwriting cells of the second row and inserting a new row + // with the 2-row clipboard table content + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 3); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt/Text", "Portion", "hello"); + + // Undo + + dispatchCommand(mxComponent, ".uno:Undo", {}); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // 2 rows again, no copied text content + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/Text", 0); + + // insert the 2-row table into the second paragraph of cell "A2" as a nested table + // For this it's enough to positionate the text cursor not in the first paragraph + + // insert some text and an empty paragraph + pWrtShell->Insert("Some text..."); + pWrtShell->SplitNode(); + Scheduler::ProcessEventsToIdle(); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt[1]/Text", "Portion", + "Some text..."); + // the empty paragraph in A2 + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt[2]/Text", 0); + + // insert the table, as a nested one in cell "A2" + dispatchCommand(mxComponent, ".uno:Paste", {}); + Scheduler::ProcessEventsToIdle(); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/tab", 1); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/tab/row", 2); + + // Undo + + dispatchCommand(mxComponent, ".uno:Undo", {}); + Scheduler::ProcessEventsToIdle(); + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + // 2 rows again, no copied text content + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt[1]/Text", "Portion", + "Some text..."); + + // copy the 2-row table into the fist paragraph of cell "A2", + // but not at paragraph start (changed behaviour) + + pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, /*bBasicCall=*/false); + pWrtShell->Insert("and some text again in the first paragraph to be sure..."); + dispatchCommand(mxComponent, ".uno:Paste", {}); + Scheduler::ProcessEventsToIdle(); + + discardDumpedLayout(); + pXmlDoc = parseLayoutDump(); + + // 3-row, overwriting cells of the second row and inserting a new row + // with the 2-row clipboard table content + + // This was 2 (nested table) + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row", 3); + // This was "Some text..." with a nested table + assertXPath(pXmlDoc, "/root/page[1]/body/tab/row[2]/cell[1]/txt/Text", "Portion", "hello"); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest3, testTdf135014) { createSwDoc(); diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx index 7a57c5f4e709..b7a12a828b90 100644 --- a/sw/source/core/frmedt/fecopy.cxx +++ b/sw/source/core/frmedt/fecopy.cxx @@ -966,11 +966,15 @@ bool SwFEShell::Paste(SwDoc& rClpDoc, bool bNestedTable) if (pSrcNd && nullptr != pDestNd && // not a forced nested table insertion !bNestedTable && - // are we at the beginning of the cell? (if not, we will insert a nested table) - // first paragraph of the cell? - rPaM.GetNode().GetIndex() == rPaM.GetNode().FindTableBoxStartNode()->GetIndex()+1 && - // beginning of the paragraph? - !rPaM.GetPoint()->nContent.GetIndex()) + // Heuristics to allow copying table rows or nesting tables without + // using Edit -> Paste Special -> Paste as Nested Table: + // Using table cursor, or if the text selection starts in the + // first paragraph, or if there is no selection and the text cursor + // is there in the first paragraph, overwrite content of the cell(s) + // (else insert a nested table later, i.e. if nothing selected and + // the cursor is not in the first paragraph, or the selected text + // doesn't contain the first paragraph of the cell) + rPaM.GetNode().GetIndex() == rPaM.GetNode().FindTableBoxStartNode()->GetIndex() + 1) { SwPosition aDestPos( *rPaM.GetPoint() ); commit 2b223e0a1ed529b95f095725b356f5fa91551209 Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Mon Apr 25 15:55:31 2022 +0100 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:00 2022 +0200 Resolves: tdf#141441 get and set selected entry when "unfrozen" Change-Id: I4229460fb27ae3dc133c0f6a53c7792a87bf4db3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133396 Tested-by: Jenkins Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com> diff --git a/svtools/source/control/ctrlbox.cxx b/svtools/source/control/ctrlbox.cxx index 72b995d5ad83..0b1c67e31980 100644 --- a/svtools/source/control/ctrlbox.cxx +++ b/svtools/source/control/ctrlbox.cxx @@ -844,9 +844,10 @@ FontStyleBox::FontStyleBox(std::unique_ptr<weld::ComboBox> p) void FontStyleBox::Fill( const OUString& rName, const FontList* pList ) { - m_xComboBox->freeze(); OUString aOldText = m_xComboBox->get_active_text(); int nPos = m_xComboBox->get_active(); + + m_xComboBox->freeze(); m_xComboBox->clear(); // does a font with this name already exist? @@ -949,19 +950,6 @@ void FontStyleBox::Fill( const OUString& rName, const FontList* pList ) if ( bNormal || bItalic || bBold ) m_xComboBox->append_text(pList->GetBoldItalicStr()); } - if (!aOldText.isEmpty()) - { - int nFound = m_xComboBox->find_text(aOldText); - if (nFound != -1) - m_xComboBox->set_active(nFound); - else - { - if (nPos >= m_xComboBox->get_count()) - m_xComboBox->set_active(0); - else - m_xComboBox->set_active(nPos); - } - } } else { @@ -970,7 +958,16 @@ void FontStyleBox::Fill( const OUString& rName, const FontList* pList ) m_xComboBox->append_text(pList->GetItalicStr()); m_xComboBox->append_text(pList->GetBoldStr()); m_xComboBox->append_text(pList->GetBoldItalicStr()); - if (!aOldText.isEmpty()) + } + + m_xComboBox->thaw(); + + if (!aOldText.isEmpty()) + { + int nFound = m_xComboBox->find_text(aOldText); + if (nFound != -1) + m_xComboBox->set_active(nFound); + else { if (nPos >= m_xComboBox->get_count()) m_xComboBox->set_active(0); @@ -978,7 +975,6 @@ void FontStyleBox::Fill( const OUString& rName, const FontList* pList ) m_xComboBox->set_active(nPos); } } - m_xComboBox->thaw(); } FontSizeBox::FontSizeBox(std::unique_ptr<weld::ComboBox> p) commit e9786a1439db437cc378f426c7cb496db20959fd Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Sat Apr 23 21:52:29 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:00 2022 +0200 Do not start a:path with lnTo in export to OOXML This is a follow up to commit 2029b2f6dd0109c5892e5ac5640022b31fe42fd2 The commands A, W, T or L of a draw:enhanced-path draw a line from current point to start of the arc or end of line, respectivly. If there is no current point the path is faulty and behavior is not defined in ODF. LibreOffice is tolerant and makes a move to the start point of the arc or to the end point of the line. With this patch we do the same now in export to OOXML, so the user gets the same shape geometry as in LO. If a path starts with lnTo, MS Office will show nothing. I wouldn't care about user-created faulty paths, but LO produces such faulty path when an EllipseRibbon shape from binary MS Office is imported. Even when that will be fixed, we need the fix here, because the faulty path had been written to document markup and will be used when such document is opened. Change-Id: Ic52ec3bc78231b26efb592ddadee2e3042fdc065 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133349 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.hensc...@t-online.de> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133370 diff --git a/oox/qa/unit/data/FaultyPathStart.odp b/oox/qa/unit/data/FaultyPathStart.odp new file mode 100644 index 000000000000..219795ffa58b Binary files /dev/null and b/oox/qa/unit/data/FaultyPathStart.odp differ diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx index e104b4effdd7..26d1a0e973bb 100644 --- a/oox/qa/unit/export.cxx +++ b/oox/qa/unit/export.cxx @@ -529,6 +529,26 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath) assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "w", "80"); assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80"); } + +CPPUNIT_TEST_FIXTURE(Test, testFaultyPathCommandsAWT) +{ + // The odp file contains shapes whose path starts with command A, W, T or L. That is a faulty + // path. LO is tolerant and renders it so that is makes a moveTo to the start point of the arc or + // the end of the line respectively. Export to OOXML does the same now and writes a moveTo + // instead of the normally used lnTo. If a lnTo is written, MS Office shows nothing of the shape. + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "FaultyPathStart.odp"; + + 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()); + // First child of a:path should be a moveTo in all four shapes. + assertXPath(pXmlDoc, "//p:spTree/p:sp[1]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); + assertXPath(pXmlDoc, "//p:spTree/p:sp[2]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); + assertXPath(pXmlDoc, "//p:spTree/p:sp[3]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); + assertXPath(pXmlDoc, "//p:spTree/p:sp[4]/p:spPr/a:custGeom/a:pathLst/a:path/a:moveTo"); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 311669bd5cbe..c2bdf39042a4 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -3997,10 +3997,21 @@ bool DrawingML::WriteCustomGeometrySegment( { if (rnPairIndex >= rPairs.getLength()) return false; - - mpFS->startElementNS(XML_a, XML_lnTo); - WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d); - mpFS->endElementNS(XML_a, XML_lnTo); + // LINETO without valid current point is a faulty path. LO is tolerant and makes a + // moveTo instead. Do the same on export. MS OFFICE requires a current point for lnTo, + // otherwise it shows nothing of the shape. + if (rbCurrentValid) + { + mpFS->startElementNS(XML_a, XML_lnTo); + WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d); + mpFS->endElementNS(XML_a, XML_lnTo); + } + else + { + mpFS->startElementNS(XML_a, XML_moveTo); + WriteCustomGeometryPoint(rPairs[rnPairIndex], rCustomShape2d); + mpFS->endElementNS(XML_a, XML_moveTo); + } rCustomShape2d.GetParameter(rfCurrentX, rPairs[rnPairIndex].First, false, false); rCustomShape2d.GetParameter(rfCurrentY, rPairs[rnPairIndex].Second, false, false); rbCurrentValid = true; @@ -4063,7 +4074,8 @@ bool DrawingML::WriteCustomGeometrySegment( getEllipsePointFromViewAngle(fSx, fSy, fWR, fHR, fCx, fCy, fStartAngle); // write markup for going to start point - if (eCommand == ANGLEELLIPSETO) + // lnTo requires a valid current point + if (eCommand == ANGLEELLIPSETO && rbCurrentValid) { mpFS->startElementNS(XML_a, XML_lnTo); mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fSx)), @@ -4126,7 +4138,8 @@ bool DrawingML::WriteCustomGeometrySegment( getEllipsePointAndAngleFromRayPoint(fStartAngle, fPx, fPy, fWR, fHR, fCx, fCy, fX3, fY3); // markup for going to start point - if (eCommand == ARCTO || eCommand == CLOCKWISEARCTO) + // lnTo requires a valid current point. + if ((eCommand == ARCTO || eCommand == CLOCKWISEARCTO) && rbCurrentValid) { mpFS->startElementNS(XML_a, XML_lnTo); mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(std::lround(fPx)), commit d00bcd99a01e09259afa3c2ef17424aa2282a48b Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Mon Apr 25 11:09:07 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Wed Apr 27 13:20:00 2022 +0200 Extract 'switch' block out of WriteCustomGeometry This is a follow up to commit 2029b2f6dd0109c5892e5ac5640022b31fe42fd2. That commit has increased the line count of WriteCustomGeometry to more than 500. This patch splits it to a main part of about 230 lines and a new method for the previous 'switch' block of about 300 lines. That makes the loops in the main part better readable. Change-Id: Ied4378f54e7c8dc7965a5b1db15baf0b35f63f59 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132274 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133369 diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index 7fbb015b4ce4..dc01e20f9199 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -201,6 +201,11 @@ protected: void WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet); void WriteCustomGeometryPoint(const css::drawing::EnhancedCustomShapeParameterPair& rParamPair, const EnhancedCustomShape2d& rCustomShape2d); + bool WriteCustomGeometrySegment( + const sal_Int16 eCommand, const sal_Int32 nCount, + const css::uno::Sequence<css::drawing::EnhancedCustomShapeParameterPair>& rPairs, + sal_Int32& rnPairIndex, double& rfCurrentX, double& rfCurrentY, bool& rbCurrentValid, + const EnhancedCustomShape2d& rCustomShape2d); public: DrawingML( ::sax_fastparser::FSHelperPtr pFS, ::oox::core::XmlFilterBase* pFB, DocumentType eDocumentType = DOCUMENT_PPTX, DMLTextExport* pTextExport = nullptr ) diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index d4dbef899b96..311669bd5cbe 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -3951,352 +3951,9 @@ bool DrawingML::WriteCustomGeometry( } for (sal_Int32 k = 0; k < rSegment.Count && bOK; ++k) { - switch (rSegment.Command) - { - case MOVETO: - { - 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; - } - case LINETO: - { - 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; - } - case CURVETO: - { - 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], aCustomShape2d); - } - 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 - { - nSwingAng = std::lround((fEndAngle - fStartAngle) * 60000); - if (nSwingAng < 0) - nSwingAng += 360 * 60000; - } - - // 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) - { - 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); - } - else - { - 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); - } - // 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) - { - 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); - } - else - { - 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); - } - // calculate swing angle - double fEndAngle = 0.0; - getEllipsePointAndAngleFromRayPoint(fEndAngle, fPx, fPy, fWR, fHR, fCx, - fCy, fX4, fY4); - double fSwingAngle(fEndAngle - fStartAngle); - const bool bIsClockwise(rSegment.Command == CLOCKWISEARCTO - || rSegment.Command == CLOCKWISEARC); - if (bIsClockwise && fSwingAngle < 0) - fSwingAngle += 360.0; - else if (!bIsClockwise && fSwingAngle > 0) - fSwingAngle -= 360.0; - // markup for arcTo - // ToDo: write markup for case zero width or height of ellipse - const sal_Int32 nStartAng(std::lround(fStartAngle * 60000)); - const sal_Int32 nSwingAng(std::lround(fSwingAngle * 60000)); - 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)); - fCurrentX = fPx; - fCurrentY = fPy; - bCurrentValid = true; - nPairIndex += 4; - } - break; - } - case ELLIPTICALQUADRANTX: - case ELLIPTICALQUADRANTY: - { - if (nPairIndex >= aPairs.getLength()) - bOK = false; - else - { - // read parameters - double fX = 0.0; - aCustomShape2d.GetParameter(fX, aPairs[nPairIndex].First, false, false); - double fY = 0.0; - aCustomShape2d.GetParameter(fY, aPairs[nPairIndex].Second, false, - false); - - // Prepare parameters for arcTo - if (bCurrentValid) - { - double fWR = std::abs(fCurrentX - fX); - double fHR = std::abs(fCurrentY - fY); - double fStartAngle(0.0); - double fSwingAngle(0.0); - // The starting direction of the arc toggles beween X and Y - if ((rSegment.Command == ELLIPTICALQUADRANTX && !(k % 2)) - || (rSegment.Command == ELLIPTICALQUADRANTY && (k % 2))) - { - // arc starts horizontal - fStartAngle = fY < fCurrentY ? 90.0 : 270.0; - const bool bClockwise = (fX < fCurrentX && fY < fCurrentY) - || (fX > fCurrentX && fY > fCurrentY); - fSwingAngle = bClockwise ? 90.0 : -90.0; - } - else - { - // arc starts vertical - fStartAngle = fX < fCurrentX ? 0.0 : 180.0; - const bool bClockwise = (fX < fCurrentX && fY > fCurrentY) - || (fX > fCurrentX && fY < fCurrentY); - fSwingAngle = bClockwise ? 90.0 : -90.0; - } ... etc. - the rest is truncated