diff options
Diffstat (limited to 'writerfilter')
30 files changed, 781 insertions, 313 deletions
diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx b/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx index 3719a09e3323..962aa63f9ebc 100644 --- a/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/qa/cppunittests/dmapper/DomainMapper_Impl.cxx @@ -19,6 +19,8 @@ #include <com/sun/star/text/XTextTablesSupplier.hpp> #include <com/sun/star/text/XTextTable.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/document/XDocumentInsertable.hpp> #include <vcl/scheduler.hxx> @@ -230,6 +232,58 @@ CPPUNIT_TEST_FIXTURE(Test, testChartZOrder) // of the shape. CPPUNIT_ASSERT(xChart->supportsService("com.sun.star.text.TextEmbeddedObject")); } + +CPPUNIT_TEST_FIXTURE(Test, testPTab) +{ + // Given a document that has a <w:ptab> to render a linebreak: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "ptab.docx"; + + // When opening that file: + getComponent() = loadFromDesktop(aURL); + + // Then make sure that the Writer doc model contains that linebreak: + uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(getComponent(), + uno::UNO_QUERY); + uno::Reference<container::XNameAccess> xStyleFamilies + = xStyleFamiliesSupplier->getStyleFamilies(); + uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xStyle(xStyleFamily->getByName("Standard"), uno::UNO_QUERY); + auto xFooter = xStyle->getPropertyValue("FooterText").get<uno::Reference<text::XTextRange>>(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: <space><newline>1\n + // - Actual: <space><tab>1\n + // i.e. the layout height of the footer text was incorrect, the page number field was not + // visually inside the background shape. + CPPUNIT_ASSERT_EQUAL(OUString(" \n1" SAL_NEWLINE_STRING), xFooter->getString()); +} + +CPPUNIT_TEST_FIXTURE(Test, testPasteOle) +{ + // Given an empty document: + getComponent() = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + + // When pasting RTF into that document: + uno::Reference<text::XTextDocument> xTextDocument(getComponent(), uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<document::XDocumentInsertable> xCursor( + xText->createTextCursorByRange(xText->getStart()), uno::UNO_QUERY); + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "paste-ole.rtf"; + xCursor->insertDocumentFromURL(aURL, {}); + + // Then make sure that all the 3 paragraphs of the paste data (empty para, OLE obj, text) are + // inserted to the document: + uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xText, uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration(); + xParaEnum->nextElement(); + // Without the accompanying fix in place, this test would have failed, as the paste result was a + // single paragaph, containing the OLE object, and the content after the OLE object was lost. + CPPUNIT_ASSERT(xParaEnum->hasMoreElements()); + xParaEnum->nextElement(); + CPPUNIT_ASSERT(xParaEnum->hasMoreElements()); + uno::Reference<text::XTextRange> xPara(xParaEnum->nextElement(), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("hello"), xPara->getString()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx b/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx index edd6e02ff8ea..2f9faefab55f 100644 --- a/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx +++ b/writerfilter/qa/cppunittests/dmapper/GraphicImport.cxx @@ -366,6 +366,28 @@ CPPUNIT_TEST_FIXTURE(Test, testTextboxTextlineTop) sal_Int16 nExpectedOrient = text::VertOrientation::BOTTOM; CPPUNIT_ASSERT_EQUAL(nExpectedOrient, nActualOrient); } + +CPPUNIT_TEST_FIXTURE(Test, testLayoutInCellWrapnoneColumn) +{ + // Given a file with a table, then a shape anchored inside the cell: + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "layout-in-cell-wrapnone-column.docx"; + + // When loading that document: + getComponent() = loadFromDesktop(aURL); + + // Then make sure the shape can leave the cell: + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage(); + uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(1), uno::UNO_QUERY); + uno::Reference<container::XNamed> xNamedShape(xShape, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(OUString("Text Box 1"), xNamedShape->getName()); + bool bFollowingTextFlow = true; + // Without the accompanying fix in place, this test would have failed, the shape was not allowed + // to leave the cell, leading to incorrect layout. + CPPUNIT_ASSERT(xShape->getPropertyValue("IsFollowingTextFlow") >>= bFollowingTextFlow); + CPPUNIT_ASSERT(!bFollowingTextFlow); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx b/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx index 7b552af94bb0..ae32ce1e4dc7 100644 --- a/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx +++ b/writerfilter/qa/cppunittests/dmapper/PropertyMap.cxx @@ -15,6 +15,7 @@ #include <com/sun/star/text/XTextViewCursorSupplier.hpp> #include <com/sun/star/style/XStyleFamiliesSupplier.hpp> #include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> using namespace ::com::sun::star; @@ -87,6 +88,26 @@ CPPUNIT_TEST_FIXTURE(Test, testFollowPageTopMargin) // i.e. the top margin on page 2 was too large. CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(250), nTopMargin); } + +CPPUNIT_TEST_FIXTURE(Test, testTableNegativeVerticalPos) +{ + // Given a document with a table which has a negative vertical position (moves up to overlap + // with the header): + OUString aURL + = m_directories.getURLFromSrc(DATA_DIRECTORY) + "table-negative-vertical-pos.docx"; + + // When loading that document: + getComponent() = loadFromDesktop(aURL); + + // Then make sure we don't import that as a plain table, which can't have a negative top margin: + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage(); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // i.e. this was imported as a plain table, resulting in a 0 top margin (y pos too large). + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), xDrawPage->getCount()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/qa/cppunittests/dmapper/data/layout-in-cell-wrapnone-column.docx b/writerfilter/qa/cppunittests/dmapper/data/layout-in-cell-wrapnone-column.docx Binary files differnew file mode 100644 index 000000000000..d88761421154 --- /dev/null +++ b/writerfilter/qa/cppunittests/dmapper/data/layout-in-cell-wrapnone-column.docx diff --git a/writerfilter/qa/cppunittests/dmapper/data/paste-ole.rtf b/writerfilter/qa/cppunittests/dmapper/data/paste-ole.rtf new file mode 100644 index 000000000000..27ce59baa50b --- /dev/null +++ b/writerfilter/qa/cppunittests/dmapper/data/paste-ole.rtf @@ -0,0 +1,30 @@ +{\rtf1 +\pard\plain\par +\pard\plain +{\object\objemb\objw1287\objh832\objscalex100\objscaley99 +{\*\objclass Package} +{\*\objdata 0105000002000000080000005061636b616765000000000000000000eb010000 +020030322e73766700443a5c446e445c54657374646174656e5c416c6c654461746569547970656e5c30322e737667000000030036000000443a5c54454d505c7b42433241443335362d363732422d344345302d394136342d3033373544464134324334377d5c30322e73766700ab0000003c7376672076657273696f6e +3d22312e31222076696577426f783d223020302034342032362220786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737667223e0a203c7265637420783d222e352220793d222e35222077696474683d22343322206865696768743d223235222072783d2232222072793d2232222066696c6c3d +222366666622207374726f6b653d2223303037616666222f3e0a3c2f7376673e0a3500000044003a005c00540045004d0050005c007b00420043003200410044003300350036002d0036003700320042002d0034004300450030002d0039004100360034002d003000330037003500440046004100340032004300340037 +007d005c00300032002e0073007600670006000000300032002e007300760067002600000044003a005c0044006e0044005c00540065007300740064006100740065006e005c0041006c006c0065004400610074006500690054007900700065006e005c00300032002e007300760067000105000000000000} +{\result +{\*\shppict +{\pict +\picscalex100\picscaley99\picw2270\pich1468\picwgoal1287\pichgoal832\emfblip +010000006c00000000000000000000009500000095000000000000000000 +0000670f0000630f000020454d4600000100280100000700000002000000 +00000000000000000000000038070000bd030000e9010000fd0000000000 +00000000000000000000f675070016dd0300210000000800000062000000 +0c0000000100000027000000180000000100000000000000ff0000000000 +000047000000700000000000000000000000950000009500000050000000 +010000002000000001000000030000003000000000000000000000009600 +000096000000320000000000000064000000320000000000000032000000 +960000006400000032000000640000006400000096000000220000000c00 +0000ffffffff0e00000014000000000000001000000014000000} +} +} +} +\pard\plain\par +\pard\plain hello\par +} diff --git a/writerfilter/qa/cppunittests/dmapper/data/ptab.docx b/writerfilter/qa/cppunittests/dmapper/data/ptab.docx Binary files differnew file mode 100644 index 000000000000..d1ae18a27a55 --- /dev/null +++ b/writerfilter/qa/cppunittests/dmapper/data/ptab.docx diff --git a/writerfilter/qa/cppunittests/dmapper/data/table-negative-vertical-pos.docx b/writerfilter/qa/cppunittests/dmapper/data/table-negative-vertical-pos.docx Binary files differnew file mode 100644 index 000000000000..2031f4769877 --- /dev/null +++ b/writerfilter/qa/cppunittests/dmapper/data/table-negative-vertical-pos.docx diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index b5e08cfe5bc9..99fb28fda05f 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -372,19 +372,14 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) if (m_pImpl->GetTopContext()) { m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME, uno::makeAny( sStringValue )); - if (m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH) && m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH)->isSet(PROP_NUMBERING_RULES)) - { - // Font of the paragraph mark should be used for the numbering as well. - uno::Reference<beans::XPropertySet> xCharStyle(m_pImpl->GetCurrentNumberingCharStyle()); - if (xCharStyle.is()) - xCharStyle->setPropertyValue("CharFontName", uno::makeAny(sStringValue)); - } } break; case NS_ooxml::LN_CT_Fonts_asciiTheme: m_pImpl->appendGrabBag(m_pImpl->m_aSubInteropGrabBag, "asciiTheme", ThemeTable::getStringForTheme(nIntValue)); if (m_pImpl->GetTopContext()) { + // note: overwrite Fonts_ascii with Fonts_asciiTheme *even if* + // theme font is empty - this is apparently what Word 2013 does uno::Any aPropValue = uno::makeAny( m_pImpl->GetThemeTable()->getFontNameForTheme( nIntValue ) ); m_pImpl->GetTopContext()->Insert(PROP_CHAR_FONT_NAME, aPropValue ); m_pImpl->GetTopContext()->Insert(PROP_CHAR_THEME_FONT_NAME_ASCII, aPropValue, true, CHAR_GRAB_BAG ); @@ -1167,7 +1162,9 @@ void DomainMapper::lcl_attribute(Id nName, Value & val) break; case NS_ooxml::LN_CT_PTab_leader: case NS_ooxml::LN_CT_PTab_relativeTo: + break; case NS_ooxml::LN_CT_PTab_alignment: + m_pImpl->HandlePTab(nIntValue); break; case NS_ooxml::LN_CT_Cnf_lastRowLastColumn: m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "lastRowLastColumn", OUString::number(nIntValue)); @@ -1468,7 +1465,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:; } @@ -1779,9 +1785,6 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) if( nSprmId != NS_ooxml::LN_EG_RPrBase_bCs ) rContext->Insert(PROP_CHAR_WEIGHT_ASIAN, aBold ); - uno::Reference<beans::XPropertySet> xCharStyle(m_pImpl->GetCurrentNumberingCharStyle()); - if (xCharStyle.is()) - xCharStyle->setPropertyValue(getPropertyName(PROP_CHAR_WEIGHT), aBold); if (nSprmId == NS_ooxml::LN_EG_RPrBase_b) m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, "b", OUString::number(nIntValue)); else if (nSprmId == NS_ooxml::LN_EG_RPrBase_bCs) @@ -1859,10 +1862,6 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) //Asian get the same value as Western rContext->Insert( PROP_CHAR_HEIGHT, aVal ); rContext->Insert( PROP_CHAR_HEIGHT_ASIAN, aVal ); - - uno::Reference<beans::XPropertySet> xCharStyle(m_pImpl->GetCurrentNumberingCharStyle()); - if (xCharStyle.is()) - xCharStyle->setPropertyValue(getPropertyName(PROP_CHAR_HEIGHT), aVal); } m_pImpl->appendGrabBag(m_pImpl->m_aInteropGrabBag, (nSprmId == NS_ooxml::LN_EG_RPrBase_sz ? OUString("sz") : OUString("szCs")), OUString::number(nIntValue)); } @@ -3223,6 +3222,18 @@ void DomainMapper::lcl_startParagraphGroup() void DomainMapper::lcl_endParagraphGroup() { + if (m_pImpl->isBreakDeferred(LINE_BREAK)) + { + if (m_pImpl->GetIsLastParagraphInSection()) + m_pImpl->clearDeferredBreak(LINE_BREAK); + + while (m_pImpl->isBreakDeferred(LINE_BREAK)) + { + m_pImpl->clearDeferredBreak(LINE_BREAK); + m_pImpl->appendTextPortion("\n", m_pImpl->GetTopContext()); + } + } + m_pImpl->PopProperties(CONTEXT_PARAGRAPH); if (m_pImpl->hasTableManager()) m_pImpl->getTableManager().endParagraphGroup(); @@ -3372,6 +3383,13 @@ void DomainMapper::lcl_text(const sal_uInt8 * data_, size_t len) case 0x0e: //column break m_pImpl->deferBreak(COLUMN_BREAK); return; + case 0x0a: //line break + if (m_pImpl->GetIsLastParagraphInSection()) + { + m_pImpl->deferBreak(LINE_BREAK); + return; + } + break; case 0x07: m_pImpl->getTableManager().text(data_, len); return; @@ -3404,6 +3422,13 @@ void DomainMapper::lcl_text(const sal_uInt8 * data_, size_t len) // GetTopContext() is changed by inserted breaks, but we want to keep the current context PropertyMapPtr pContext = m_pImpl->GetTopContext(); + + while (m_pImpl->isBreakDeferred(LINE_BREAK)) + { + m_pImpl->clearDeferredBreak(LINE_BREAK); + m_pImpl->appendTextPortion("\n", pContext); + } + if (!m_pImpl->GetFootnoteContext()) { if (m_pImpl->isBreakDeferred(PAGE_BREAK)) @@ -3627,6 +3652,18 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) m_pImpl->m_bHasFtnSep = true; return; } + else if (len == 1 && sText[0] == '\r') + { + // Clear "last" one linebreak at end of section + if (m_pImpl->GetIsLastParagraphInSection() && m_pImpl->isBreakDeferred(LINE_BREAK)) + m_pImpl->clearDeferredBreak(LINE_BREAK); + // And emit all other linebreaks + while (m_pImpl->isBreakDeferred(LINE_BREAK)) + { + m_pImpl->clearDeferredBreak(LINE_BREAK); + m_pImpl->appendTextPortion("\n", m_pImpl->GetTopContext()); + } + } else if (len == 1 && sText[0] == '\t' ) { if ( m_pImpl->m_bCheckFirstFootnoteTab && m_pImpl->IsInFootOrEndnote() ) @@ -3657,6 +3694,12 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) try { + while (m_pImpl->isBreakDeferred(LINE_BREAK)) + { + m_pImpl->clearDeferredBreak(LINE_BREAK); + m_pImpl->appendTextPortion("\n", m_pImpl->GetTopContext()); + } + m_pImpl->getTableManager().utext(data_, len); if (bNewLine) @@ -3722,10 +3765,10 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, size_t len) static_cast<ParagraphPropertyMap*>(xContext.get())->SetListId(-1);; xContext->Erase(PROP_NUMBERING_LEVEL); } - m_pImpl->SetParaSectpr(false); finishParagraph(bRemove, bNoNumbering); if (bRemove) m_pImpl->RemoveLastParagraph(); + m_pImpl->SetParaSectpr(false); } else { diff --git a/writerfilter/source/dmapper/DomainMapperTableManager.cxx b/writerfilter/source/dmapper/DomainMapperTableManager.cxx index f0b692783a44..c3e44542d5f7 100644 --- a/writerfilter/source/dmapper/DomainMapperTableManager.cxx +++ b/writerfilter/source/dmapper/DomainMapperTableManager.cxx @@ -387,6 +387,8 @@ bool DomainMapperTableManager::sprm(Sprm & rSprm) DomainMapperTableManager::IntVectorPtr const & DomainMapperTableManager::getCurrentGrid( ) { + if (m_aTableGrid.empty()) + throw std::out_of_range("no current grid"); return m_aTableGrid.back( ); } @@ -551,6 +553,8 @@ void DomainMapperTableManager::endOfRowAction() // Compare the table position and style with the previous ones. We may need to split // into two tables if those are different. We surely don't want to do anything // if we don't have any row yet. + if (m_aTmpPosition.empty()) + throw std::out_of_range("row without a position"); TablePositionHandlerPtr pTmpPosition = m_aTmpPosition.back(); TablePropertyMapPtr pTablePropMap = m_aTmpTableProperties.back( ); TablePositionHandlerPtr pCurrentPosition = m_aTablePositions.back(); @@ -793,9 +797,6 @@ void DomainMapperTableManager::endOfRowAction() size_t nWidthsBound = pCellWidths->size() - 1; if (nWidthsBound) { - if (nFullWidthRelative == 0) - throw o3tl::divide_by_zero(); - // At incomplete table grids, last cell width can be smaller, than its final width. // Correct it based on the last but one column width and their span values. if ( bIsIncompleteGrid && rCurrentSpans.size()-1 == nWidthsBound ) @@ -806,6 +807,9 @@ void DomainMapperTableManager::endOfRowAction() nFullWidthRelative += nFixLastCellWidth - (*pCellWidths)[nWidthsBound]; } + if (nFullWidthRelative == 0) + throw o3tl::divide_by_zero(); + for (size_t i = 0; i < nWidthsBound; ++i) { nSum += (*pCellWidths)[i]; diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 19868ae74d78..4c0c79085273 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -183,6 +183,45 @@ static void lcl_handleTextField( const uno::Reference< beans::XPropertySet >& rx } } +/** + Very similar to DomainMapper_Impl::GetPropertyFromStyleSheet + It is focused on paragraph properties search in current & parent stylesheet entries. + But it will not take into account propeties with listid: these are "list paragraph styles" and + not used in some cases. +*/ +static uno::Any lcl_GetPropertyFromParaStyleSheetNoNum(PropertyIds eId, StyleSheetEntryPtr pEntry, const StyleSheetTablePtr& rStyleSheet) +{ + while (pEntry) + { + if (pEntry->pProperties) + { + std::optional<PropertyMap::Property> aProperty = + pEntry->pProperties->getProperty(eId); + if (aProperty) + { + if (pEntry->pProperties->GetListId()) + // It is a paragraph style with list. Paragraph list styles are not taken into account + return uno::Any(); + else + return aProperty->second; + } + } + //search until the property is set or no parent is available + StyleSheetEntryPtr pNewEntry; + if (!pEntry->sBaseStyleIdentifier.isEmpty()) + pNewEntry = rStyleSheet->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier); + + SAL_WARN_IF(pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?"); + + if (pEntry == pNewEntry) //fdo#49587 + break; + + pEntry = pNewEntry; + } + return uno::Any(); +} + + namespace { struct FieldConversion @@ -286,6 +325,7 @@ DomainMapper_Impl::DomainMapper_Impl( m_bIsFirstSection( true ), m_bIsColumnBreakDeferred( false ), m_bIsPageBreakDeferred( false ), + m_nLineBreaksDeferred( 0 ), m_bSdtEndDeferred(false), m_bParaSdtEndDeferred(false), m_bStartTOC(false), @@ -676,6 +716,14 @@ void DomainMapper_Impl::RemoveLastParagraph( ) // delete xCursor->setString(OUString()); + // While removing paragraphs that contain section properties, reset list + // related attributes to prevent them leaking into the following section's lists + if (GetParaSectpr()) + { + uno::Reference<beans::XPropertySet> XCursorProps(xCursor, uno::UNO_QUERY); + XCursorProps->setPropertyValue("ResetParagraphListAttributes", uno::Any()); + } + // call to xCursor->setString possibly did remove final bookmark // from previous paragraph. We need to restore it, if there was any. if (sLastBookmarkName.getLength()) @@ -1113,9 +1161,12 @@ void DomainMapper_Impl::deferBreak( BreakType deferredBreakType) { switch (deferredBreakType) { - case COLUMN_BREAK: - m_bIsColumnBreakDeferred = true; + case LINE_BREAK: + m_nLineBreaksDeferred++; break; + case COLUMN_BREAK: + m_bIsColumnBreakDeferred = true; + break; case PAGE_BREAK: // See SwWW8ImplReader::HandlePageBreakChar(), page break should be // ignored inside tables. @@ -1133,6 +1184,8 @@ bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType ) { switch (deferredBreakType) { + case LINE_BREAK: + return m_nLineBreaksDeferred > 0; case COLUMN_BREAK: return m_bIsColumnBreakDeferred; case PAGE_BREAK: @@ -1146,6 +1199,10 @@ void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType) { switch (deferredBreakType) { + case LINE_BREAK: + assert(m_nLineBreaksDeferred > 0); + m_nLineBreaksDeferred--; + break; case COLUMN_BREAK: m_bIsColumnBreakDeferred = false; break; @@ -1159,6 +1216,7 @@ void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType) void DomainMapper_Impl::clearDeferredBreaks() { + m_nLineBreaksDeferred = 0; m_bIsColumnBreakDeferred = false; m_bIsPageBreakDeferred = false; } @@ -1733,6 +1791,10 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con if (nParaLeftMargin != 0) pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::makeAny(nParaLeftMargin), /*bOverwrite=*/false); + // Override right margin value with value from current style, if any + if (pStyleSheetProperties && pStyleSheetProperties->isSet(PROP_PARA_RIGHT_MARGIN)) + nParaRightMargin = pStyleSheetProperties->getProperty(PROP_PARA_RIGHT_MARGIN)->second.get<sal_Int32>(); + pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, uno::makeAny(nParaRightMargin), /*bOverwrite=*/false); } } @@ -1756,11 +1818,21 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con if (nListId == 0 && !pList) { - // Seems situation with listid=0 and missing list definition is used by DOCX - // to remove numbering defined previously. But some default numbering attributes - // are still applied. This is first line indent, probably something more? - if (!pParaContext->isSet(PROP_PARA_FIRST_LINE_INDENT)) - pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::makeAny(sal_Int16(0)), false); + // listid = 0 and no list definition is used in DOCX to stop numbering + // defined somewhere in parent styles + // And here we should explicitly set left margin and first-line margin. + // They can be taken from referred style, but not from styles with listid! + uno::Any aProp = lcl_GetPropertyFromParaStyleSheetNoNum(PROP_PARA_FIRST_LINE_INDENT, pEntry, m_pStyleSheetTable); + if (aProp.hasValue()) + pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, aProp, false); + else + pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::makeAny(sal_uInt32(0)), false); + + aProp = lcl_GetPropertyFromParaStyleSheetNoNum(PROP_PARA_LEFT_MARGIN, pEntry, m_pStyleSheetTable); + if (aProp.hasValue()) + pParaContext->Insert(PROP_PARA_LEFT_MARGIN, aProp, false); + else + pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::makeAny(sal_uInt32(0)), false); } } @@ -1915,8 +1987,7 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con { aProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(pPropertyMap->GetPropertyValues()); } - // TODO: this *should* work for RTF but there are test failures, maybe rtftok doesn't distinguish between formatting for the paragraph marker and for the paragraph as a whole; needs investigation - if (pPropertyMap && IsOOXMLImport()) + if (pPropertyMap) { // tdf#64222 filter out the "paragraph marker" formatting and // set it as a separate paragraph property, not a empty hint at @@ -2097,9 +2168,15 @@ void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, con } } } - if (pList->GetCurrentLevel()) + + sal_Int16 nCurrentLevel = GetListLevel(pEntry, pPropertyMap); + if (nCurrentLevel == -1) + nCurrentLevel = 0; + + const ListLevel::Pointer pListLevel = pList->GetLevel(nCurrentLevel); + if (pListLevel) { - sal_Int16 nOverrideLevel = pList->GetCurrentLevel()->GetStartOverride(); + sal_Int16 nOverrideLevel = pListLevel->GetStartOverride(); if (nOverrideLevel != -1 && m_aListOverrideApplied.find(nListId) == m_aListOverrideApplied.end()) { // Apply override: we have override instruction for this level @@ -3080,7 +3157,16 @@ void DomainMapper_Impl::CreateRedline(uno::Reference<text::XTextRange> const& xR pRedlineProperties[0].Name = getPropertyName( PROP_REDLINE_AUTHOR ); pRedlineProperties[0].Value <<= pRedline->m_sAuthor; pRedlineProperties[1].Name = getPropertyName( PROP_REDLINE_DATE_TIME ); - pRedlineProperties[1].Value <<= ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate ); + util::DateTime aDateTime = ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate ); + // tdf#146171 import not specified w:date (or specified as zero date "0-00-00") + // as Epoch time to avoid of losing change tracking data during ODF roundtrip + if ( aDateTime.Year == 0 && aDateTime.Month == 0 && aDateTime.Day == 0 ) + { + aDateTime.Year = 1970; + aDateTime.Month = 1; + aDateTime.Day = 1; + } + pRedlineProperties[1].Value <<= aDateTime; pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES ); pRedlineProperties[2].Value <<= pRedline->m_aRevertProperties; pRedlineProperties[3].Name = "RedlineMoved"; @@ -3553,10 +3639,17 @@ void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape } else { - uno::Reference< text::XTextRange > xShapeText( xShape, uno::UNO_QUERY_THROW); + uno::Reference<text::XTextRange> xShapeTextRange(xShape, uno::UNO_QUERY_THROW); // Add the shape to the text append stack - m_aTextAppendStack.push( TextAppendContext(uno::Reference< text::XTextAppend >( xShape, uno::UNO_QUERY_THROW ), - m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : m_xBodyText->createTextCursorByRange(xShapeText->getStart() ))); + uno::Reference<text::XTextAppend> xShapeTextAppend(xShape, uno::UNO_QUERY_THROW); + uno::Reference<text::XTextCursor> xTextCursor; + if (!m_bIsNewDoc) + { + xTextCursor = xShapeTextRange->getText()->createTextCursorByRange( + xShapeTextRange->getStart()); + } + TextAppendContext aContext(xShapeTextAppend, xTextCursor); + m_aTextAppendStack.push(aContext); // Add the shape to the anchored objects stack uno::Reference< text::XTextContent > xTxtContent( xShape, uno::UNO_QUERY_THROW ); @@ -3866,6 +3959,62 @@ void DomainMapper_Impl::HandleAltChunk(const OUString& rStreamName) } } +void DomainMapper_Impl::HandlePTab(sal_Int32 nAlignment) +{ + // We only handle the case when the line already has content, so the left-aligned ptab is + // equivalent to a line break. + if (nAlignment != NS_ooxml::LN_Value_ST_PTabAlignment_left) + { + return; + } + + if (m_aTextAppendStack.empty()) + { + return; + } + + uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend; + if (!xTextAppend.is()) + { + return; + } + + uno::Reference<css::text::XTextRange> xInsertPosition + = m_aTextAppendStack.top().xInsertPosition; + if (!xInsertPosition.is()) + { + xInsertPosition = xTextAppend->getEnd(); + } + uno::Reference<text::XTextCursor> xCursor + = xTextAppend->createTextCursorByRange(xInsertPosition); + + // Assume that we just inserted a tab character. + xCursor->goLeft(1, true); + if (xCursor->getString() != "\t") + { + return; + } + + // Assume that there is some content before the tab character. + uno::Reference<text::XParagraphCursor> xParagraphCursor(xCursor, uno::UNO_QUERY); + if (!xParagraphCursor.is()) + { + return; + } + + xCursor->collapseToStart(); + xParagraphCursor->gotoStartOfParagraph(true); + if (xCursor->isCollapsed()) + { + return; + } + + // Then select the tab again and replace with a line break. + xCursor->collapseToEnd(); + xCursor->goRight(1, true); + xTextAppend->insertControlCharacter(xCursor, text::ControlCharacter::LINE_BREAK, true); +} + static sal_Int16 lcl_ParseNumberingType( const OUString& rCommand ) { sal_Int16 nRet = style::NumberingType::PAGE_DESCRIPTOR; @@ -4147,7 +4296,7 @@ static OUString lcl_ExtractVariableAndHint( const OUString& rCommand, OUString& sal_Int32 nIndex = rCommand.indexOf( ' ', 2); //find last space after 'ASK' if (nIndex == -1) return OUString(); - while(rCommand[nIndex] == ' ') + while (nIndex < rCommand.getLength() && rCommand[nIndex] == ' ') ++nIndex; OUString sShortCommand( rCommand.copy( nIndex ) ); //cut off the " ASK " @@ -4271,14 +4420,15 @@ void DomainMapper_Impl::ChainTextFrames() sal_Int32 nId; sal_Int32 nSeq; OUString s_mso_next_textbox; - bool bShapeNameSet; - TextFramesForChaining(): nId(0), nSeq(0), bShapeNameSet(false) {} + OUString shapeName; + TextFramesForChaining() : nId(0), nSeq(0) {} } ; typedef std::map <OUString, TextFramesForChaining> ChainMap; try { ChainMap aTextFramesForChainingHelper; + ::std::vector<TextFramesForChaining> chainingWPS; OUString sChainNextName("ChainNextName"); //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order. @@ -4316,19 +4466,22 @@ void DomainMapper_Impl::ChainTextFrames() //Sometimes the shape names have not been imported. If not, we may have a fallback name. //Set name later, only if required for linking. - if( sShapeName.isEmpty() ) - aChainStruct.bShapeNameSet = false; - else - { - aChainStruct.bShapeNameSet = true; - sLinkChainName = sShapeName; - } + aChainStruct.shapeName = sShapeName; - if( !sLinkChainName.isEmpty() ) + if (!sLinkChainName.isEmpty()) { aChainStruct.xShape = rTextFrame; aTextFramesForChainingHelper[sLinkChainName] = aChainStruct; } + if (aChainStruct.s_mso_next_textbox.isEmpty()) + { // no VML chaining => try to chain DrawingML via IDs + aChainStruct.xShape = rTextFrame; + if (!sLinkChainName.isEmpty()) + { // for member of group shapes, TestTdf73499 + aChainStruct.shapeName = sLinkChainName; + } + chainingWPS.emplace_back(aChainStruct); + } } //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links. @@ -4343,22 +4496,22 @@ void DomainMapper_Impl::ChainTextFrames() if( nextFinder != aTextFramesForChainingHelper.end() ) { //if the frames have no name yet, then set them. LinkDisplayName / ChainName are read-only. - if( !msoItem.second.bShapeNameSet ) + if (msoItem.second.shapeName.isEmpty()) { uno::Reference< container::XNamed > xNamed( msoItem.second.xShape, uno::UNO_QUERY ); if ( xNamed.is() ) { xNamed->setName( msoItem.first ); - msoItem.second.bShapeNameSet = true; + msoItem.second.shapeName = msoItem.first; } } - if( !nextFinder->second.bShapeNameSet ) + if (nextFinder->second.shapeName.isEmpty()) { uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY ); if ( xNamed.is() ) { xNamed->setName( nextFinder->first ); - nextFinder->second.bShapeNameSet = true; + nextFinder->second.shapeName = msoItem.first; } } @@ -4366,7 +4519,7 @@ void DomainMapper_Impl::ChainTextFrames() uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY); //The reverse chaining happens automatically, so only one direction needs to be set - xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(nextFinder->first)); + xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(nextFinder->second.shapeName)); //the last item in an mso-next-textbox chain is indistinguishable from id/seq items. Now that it is handled, remove it. if( nextFinder->second.s_mso_next_textbox.isEmpty() ) @@ -4379,26 +4532,23 @@ void DomainMapper_Impl::ChainTextFrames() const sal_Int32 nDirection = 1; //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style). - for (const auto& rOuter : aTextFramesForChainingHelper) + for (const auto& rOuter : chainingWPS) { - if( rOuter.second.s_mso_next_textbox.isEmpty() ) //non-empty ones already handled earlier - so skipping them now. - { - for (const auto& rInner : aTextFramesForChainingHelper) + for (const auto& rInner : chainingWPS) { - if ( rInner.second.nId == rOuter.second.nId ) + if (rInner.nId == rOuter.nId) { - if ( rInner.second.nSeq == ( rOuter.second.nSeq + nDirection ) ) + if (rInner.nSeq == (rOuter.nSeq + nDirection)) { - uno::Reference<text::XTextContent> xTextContent(rOuter.second.xShape, uno::UNO_QUERY_THROW); + uno::Reference<text::XTextContent> const xTextContent(rOuter.xShape, uno::UNO_QUERY_THROW); uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY); //The reverse chaining happens automatically, so only one direction needs to be set - xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(rInner.first)); + xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(rInner.shapeName)); break ; //there cannot be more than one next frame } } } - } } m_vTextFramesForChaining.clear(); //clear the vector } @@ -6886,13 +7036,23 @@ void DomainMapper_Impl::PopFieldContext() // End of index is the first item on a new paragraph - this paragraph // should not be part of index auto xCursor - = xTextAppend->createTextCursorByRange(xTextAppend->getEnd()); - xCursor->gotoEnd(false); + = xTextAppend->createTextCursorByRange( + m_aTextAppendStack.top().xInsertPosition.is() + ? m_aTextAppendStack.top().xInsertPosition + : xTextAppend->getEnd()); xCursor->goLeft(1, true); // delete xCursor->setString(OUString()); // But a new paragraph should be started after the index instead - xTextAppend->finishParagraph(css::beans::PropertyValues()); + if (m_bIsNewDoc) // this check - see testTdf129402 + { // where finishParagraph inserts between 2 EndNode + xTextAppend->finishParagraph(css::beans::PropertyValues()); + } + else + { + xTextAppend->finishParagraphInsert(css::beans::PropertyValues(), + m_aTextAppendStack.top().xInsertPosition); + } } m_bStartedTOC = false; m_aTextAppendStack.pop(); @@ -6954,7 +7114,14 @@ void DomainMapper_Impl::PopFieldContext() } else if (!pContext->GetHyperlinkURL().isEmpty() && xCrsr.is()) { - xCrsr->gotoEnd( true ); + if (m_aTextAppendStack.top().xInsertPosition.is()) + { + xCrsr->gotoRange(m_aTextAppendStack.top().xInsertPosition, true); + } + else + { + xCrsr->gotoEnd(true); + } // Draw components (like comments) need hyperlinks set differently SvxUnoTextRangeBase* pDrawText = dynamic_cast<SvxUnoTextRangeBase*>(xCrsr.get()); @@ -7390,6 +7557,7 @@ void DomainMapper_Impl::ImportGraphic(const writerfilter::Reference< Properties { uno::Reference<beans::XPropertySet> xEmbeddedProps(m_xEmbedded, uno::UNO_QUERY); xEmbeddedProps->setPropertyValue("AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER)); + xEmbeddedProps->setPropertyValue("IsFollowingTextFlow", uno::makeAny(m_pGraphicImport->GetLayoutInCell())); uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY); xEmbeddedProps->setPropertyValue("HoriOrient", xShapeProps->getPropertyValue("HoriOrient")); xEmbeddedProps->setPropertyValue("HoriOrientPosition", xShapeProps->getPropertyValue("HoriOrientPosition")); @@ -7826,70 +7994,6 @@ uno::Reference<container::XIndexAccess> DomainMapper_Impl::GetCurrentNumberingRu return xRet; } -uno::Reference<beans::XPropertySet> DomainMapper_Impl::GetCurrentNumberingCharStyle() -{ - uno::Reference<beans::XPropertySet> xRet; - try - { - sal_Int32 nListLevel = -1; - uno::Reference<container::XIndexAccess> xLevels; - if ( GetTopContextType() == CONTEXT_PARAGRAPH ) - xLevels = GetCurrentNumberingRules(&nListLevel); - if (!xLevels.is()) - { - if (IsOOXMLImport()) - return xRet; - - PropertyMapPtr pContext = m_pTopContext; - if (IsRTFImport() && !IsOpenField()) - { - // Looking up the paragraph context explicitly (and not just taking - // the top context) is necessary for RTF, where formatting of a run - // and of the paragraph mark is not separated. - // We know that the formatting inside a field won't affect the - // paragraph marker formatting, though. - pContext = GetTopContextOfType(CONTEXT_PARAGRAPH); - if (!pContext) - return xRet; - } - - // In case numbering rules is not found via a style, try the direct formatting instead. - std::optional<PropertyMap::Property> oProp = pContext->getProperty(PROP_NUMBERING_RULES); - if (oProp) - { - xLevels.set(oProp->second, uno::UNO_QUERY); - // Found the rules, then also try to look up our numbering level. - oProp = pContext->getProperty(PROP_NUMBERING_LEVEL); - if (oProp) - oProp->second >>= nListLevel; - else - nListLevel = 0; - } - - if (!xLevels.is()) - return xRet; - } - uno::Sequence<beans::PropertyValue> aProps; - xLevels->getByIndex(nListLevel) >>= aProps; - auto pProp = std::find_if(std::cbegin(aProps), std::cend(aProps), - [](const beans::PropertyValue& rProp) { return rProp.Name == "CharStyleName"; }); - if (pProp != std::cend(aProps)) - { - OUString aCharStyle; - pProp->Value >>= aCharStyle; - uno::Reference<container::XNameAccess> xCharacterStyles; - uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY); - uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies(); - xStyleFamilies->getByName("CharacterStyles") >>= xCharacterStyles; - xRet.set(xCharacterStyles->getByName(aCharStyle), uno::UNO_QUERY_THROW); - } - } - catch( const uno::Exception& ) - { - } - return xRet; -} - SectionPropertyMap * DomainMapper_Impl::GetSectionContext() { SectionPropertyMap* pSectionContext = nullptr; @@ -8132,6 +8236,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 ee6813f9d293..2abd229ba3d3 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -112,7 +112,8 @@ enum { NUMBER_OF_CONTEXTS = CONTEXT_LIST + 1 }; enum BreakType { PAGE_BREAK, - COLUMN_BREAK + COLUMN_BREAK, + LINE_BREAK }; /** @@ -476,6 +477,7 @@ private: bool m_bIsFirstSection; bool m_bIsColumnBreakDeferred; bool m_bIsPageBreakDeferred; + sal_Int32 m_nLineBreaksDeferred; /// If we want to set "sdt end" on the next character context. bool m_bSdtEndDeferred; /// If we want to set "paragraph sdt end" on the next paragraph context. @@ -1032,8 +1034,6 @@ public: } SectionPropertyMap * GetSectionContext(); - /// If the current paragraph has a numbering style associated, this method returns its character style (part of the numbering rules) - css::uno::Reference<css::beans::XPropertySet> GetCurrentNumberingCharStyle(); /// If the current paragraph has a numbering style associated, this method returns its numbering rules css::uno::Reference<css::container::XIndexAccess> GetCurrentNumberingRules(sal_Int32* pListLevel); @@ -1142,6 +1142,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; @@ -1154,6 +1157,9 @@ public: /// Handles <w:altChunk>. void HandleAltChunk(const OUString& rStreamName); + /// Handles <w:ptab>. + void HandlePTab(sal_Int32 nAlignment); + void commentProps(const OUString& sId, const CommentProperties& rProps); private: diff --git a/writerfilter/source/dmapper/GraphicImport.cxx b/writerfilter/source/dmapper/GraphicImport.cxx index 704774ec504a..6871c7c0974f 100644 --- a/writerfilter/source/dmapper/GraphicImport.cxx +++ b/writerfilter/source/dmapper/GraphicImport.cxx @@ -403,11 +403,12 @@ public: { try { - // Ask the graphic naming helper to find out the name for this - // object: It's around till the end of the import, so it remembers - // what's the first free name. - uno::Reference< container::XNamed > xNamed( xGraphicObjectProperties, uno::UNO_QUERY_THROW ); - xNamed->setName(rDomainMapper.GetGraphicNamingHelper().NameGraphic(sName)); + if (!sName.isEmpty()) + { + uno::Reference<container::XNamed> const xNamed(xGraphicObjectProperties, uno::UNO_QUERY_THROW); + xNamed->setName(sName); + } + // else: name is automatically generated by SwDoc::MakeFlySection_() xGraphicObjectProperties->setPropertyValue(getPropertyName( PROP_DESCRIPTION ), uno::makeAny( sAlternativeText )); @@ -474,6 +475,11 @@ com::sun::star::awt::Point GraphicImport::GetGraphicObjectPosition() const return (com::sun::star::awt::Point(m_pImpl->nLeftPosition, m_pImpl->nTopPosition)); } +bool GraphicImport::GetLayoutInCell() const +{ + return m_pImpl->bLayoutInCell; +} + void GraphicImport::handleWrapTextValue(sal_uInt32 nVal) { switch (nVal) @@ -1228,6 +1234,13 @@ void GraphicImport::lcl_attribute(Id nName, Value& rValue) // But they aren't Writer pictures, either (which are already handled above). uno::Reference< beans::XPropertySet > xShapeProps(m_xShape, uno::UNO_QUERY_THROW); + if (m_pImpl->nWrap == text::WrapTextMode_THROUGH && m_pImpl->nHoriRelation == text::RelOrientation::FRAME) + { + // text::RelOrientation::FRAME is OOXML's "column", which behaves as if + // layout-in-cell would be always off. + m_pImpl->bLayoutInCell = false; + } + // Anchored: Word only supports at-char in that case. text::TextContentAnchorType eAnchorType = text::TextContentAnchorType_AT_CHARACTER; diff --git a/writerfilter/source/dmapper/GraphicImport.hxx b/writerfilter/source/dmapper/GraphicImport.hxx index 7ca4e09ed30d..3ff83de5b713 100644 --- a/writerfilter/source/dmapper/GraphicImport.hxx +++ b/writerfilter/source/dmapper/GraphicImport.hxx @@ -100,6 +100,8 @@ public: com::sun::star::awt::Point GetGraphicObjectPosition() const; + bool GetLayoutInCell() const; + private: // Properties virtual void lcl_attribute(Id Name, Value & val) override; diff --git a/writerfilter/source/dmapper/NumberingManager.cxx b/writerfilter/source/dmapper/NumberingManager.cxx index 8829aa52697f..1c79e0eece99 100644 --- a/writerfilter/source/dmapper/NumberingManager.cxx +++ b/writerfilter/source/dmapper/NumberingManager.cxx @@ -371,9 +371,12 @@ void AbstractListDef::AddLevel( sal_uInt16 nLvl ) if ( nLvl >= m_aLevels.size() ) m_aLevels.resize( nLvl+1 ); - ListLevel::Pointer pLevel( new ListLevel ); - m_pCurrentLevel = pLevel; - m_aLevels[nLvl] = pLevel; + if (!m_aLevels[nLvl]) + { + m_aLevels[nLvl] = new ListLevel; + } + + m_pCurrentLevel = m_aLevels[nLvl]; } uno::Sequence<uno::Sequence<beans::PropertyValue>> AbstractListDef::GetPropertyValues(bool bDefaults) 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/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx index 8634d66e1ae5..34058d9c4702 100644 --- a/writerfilter/source/dmapper/PropertyMap.cxx +++ b/writerfilter/source/dmapper/PropertyMap.cxx @@ -882,13 +882,18 @@ void SectionPropertyMap::CopyHeaderFooterTextProperty( const uno::Reference< bea } // Copy headers and footers from the previous page style. -void SectionPropertyMap::CopyHeaderFooter( const uno::Reference< beans::XPropertySet >& xPrevStyle, +void SectionPropertyMap::CopyHeaderFooter( DomainMapper_Impl& rDM_Impl, + const uno::Reference< beans::XPropertySet >& xPrevStyle, const uno::Reference< beans::XPropertySet >& xStyle, bool bOmitRightHeader, bool bOmitLeftHeader, bool bOmitRightFooter, bool bOmitLeftFooter ) { + if (!rDM_Impl.IsNewDoc()) + { // see also DomainMapper_Impl::PushPageHeaderFooter() + return; // tdf#139737 SwUndoInserts cannot deal with new header/footer + } bool bHasPrevHeader = false; bool bHeaderIsShared = true; OUString sHeaderIsOn = getPropertyName( PROP_HEADER_IS_ON ); @@ -966,13 +971,13 @@ void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Imp if ( bFirstPage ) { - CopyHeaderFooter( xPrevStyle, xStyle, + CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle, !m_bFirstPageHeaderLinkToPrevious, true, !m_bFirstPageFooterLinkToPrevious, true ); } else { - CopyHeaderFooter( xPrevStyle, xStyle, + CopyHeaderFooter(rDM_Impl, xPrevStyle, xStyle, !m_bDefaultHeaderLinkToPrevious, !(m_bEvenPageHeaderLinkToPrevious && bUseEvenPages), !m_bDefaultFooterLinkToPrevious, @@ -1146,6 +1151,15 @@ bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_I if (rDM_Impl.m_bConvertedTable && !rDM_Impl.GetIsLastSectionGroup() && rInfo.m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextPage) return false; + sal_Int32 nVertOrientPosition = rInfo.getPropertyValue(u"VertOrientPosition").get<sal_Int32>(); + sal_Int16 nHoriOrientRelation = rInfo.getPropertyValue( u"HoriOrientRelation" ).get<sal_Int16>(); + if (nVertOrientPosition < 0 && nHoriOrientRelation != text::RelOrientation::PAGE_FRAME) + { + // Negative vertical position: then need a floating table, as normal tables can't have + // negative top margins. + return true; + } + sal_Int32 nPageWidth = GetPageWidth(); sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin(); // Count the layout width of the table. @@ -1161,7 +1175,6 @@ bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_I if ( rInfo.getPropertyValue( u"RightMargin" ) >>= nRightMargin ) nTableWidth += nRightMargin; - sal_Int16 nHoriOrientRelation = rInfo.getPropertyValue( u"HoriOrientRelation" ).get<sal_Int16>(); sal_Int16 nVertOrientRelation = rInfo.getPropertyValue( u"VertOrientRelation" ).get<sal_Int16>(); if ( nHoriOrientRelation == text::RelOrientation::PAGE_FRAME && nVertOrientRelation == text::RelOrientation::PAGE_FRAME ) { @@ -1174,7 +1187,6 @@ bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_I // The more close we are to the bottom, the more likely the table will span over to the next page // So if we're in the bottom left quarter, don't do any conversion. sal_Int32 nHoriOrientPosition = rInfo.getPropertyValue( u"HoriOrientPosition" ).get<sal_Int32>(); - sal_Int32 nVertOrientPosition = rInfo.getPropertyValue( u"VertOrientPosition" ).get<sal_Int32>(); sal_Int32 nPageHeight = getProperty( PROP_HEIGHT )->second.get<sal_Int32>(); if ( nHoriOrientPosition < (nPageWidth / 2) && nVertOrientPosition >( nPageHeight / 2 ) ) return false; @@ -1456,9 +1468,26 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) } } - xBodyText->convertToTextFrame(rInfo.m_xStart, rInfo.m_xEnd, + const uno::Reference< text::XTextContent >& xTextContent = + xBodyText->convertToTextFrame(rInfo.m_xStart, rInfo.m_xEnd, rInfo.m_aFrameProperties); + // paragraph of the anchoring point of the floating table needs zero top and bottom + // margins, if the table was a not floating table in the footnote, otherwise + // docDefault margins could result bigger vertical spaces around the table + if ( rInfo.m_bConvertToFloatingInFootnote && xTextContent.is() ) + { + uno::Reference<beans::XPropertySet> xParagraph( + xTextContent->getAnchor(), uno::UNO_QUERY); + if ( xParagraph.is() ) + { + xParagraph->setPropertyValue("ParaTopMargin", + uno::makeAny(static_cast<sal_Int32>(0))); + xParagraph->setPropertyValue("ParaBottomMargin", + uno::makeAny(static_cast<sal_Int32>(0))); + } + } + uno::Reference<text::XTextTablesSupplier> xTextDocument(rDM_Impl.GetTextDocument(), uno::UNO_QUERY); uno::Reference<container::XNameAccess> xTables = xTextDocument->getTextTables(); for( size_t i = 0; i < aFramedRedlines.size(); i+=3) @@ -1844,7 +1873,7 @@ void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl ) rDM_Impl.GetPageStyles()->insertByName( evenOddStyleName, uno::makeAny( evenOddStyle ) ); evenOddStyle->setPropertyValue( "HeaderIsOn", uno::makeAny( false ) ); evenOddStyle->setPropertyValue( "FooterIsOn", uno::makeAny( false ) ); - CopyHeaderFooter( pageProperties, evenOddStyle ); + CopyHeaderFooter(rDM_Impl, pageProperties, evenOddStyle); *pageStyle = evenOddStyleName; // And use it instead of the original one (which is set as follow of this one). if ( m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_evenPage) ) evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::makeAny( style::PageStyleLayout_LEFT ) ); diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx index 3314ce7451ae..b03ef7bd664c 100644 --- a/writerfilter/source/dmapper/PropertyMap.hxx +++ b/writerfilter/source/dmapper/PropertyMap.hxx @@ -299,7 +299,8 @@ private: void CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl ); - static void CopyHeaderFooter( const css::uno::Reference< css::beans::XPropertySet >& xPrevStyle, + static void CopyHeaderFooter( DomainMapper_Impl& rDM_Impl, + const css::uno::Reference< css::beans::XPropertySet >& xPrevStyle, const css::uno::Reference< css::beans::XPropertySet >& xStyle, bool bOmitRightHeader = false, bool bOmitLeftHeader = false, bool bOmitRightFooter = false, bool bOmitLeftFooter = false ); diff --git a/writerfilter/source/dmapper/SdtHelper.cxx b/writerfilter/source/dmapper/SdtHelper.cxx index c5ec47f2be23..f4b02fab4d02 100644 --- a/writerfilter/source/dmapper/SdtHelper.cxx +++ b/writerfilter/source/dmapper/SdtHelper.cxx @@ -197,7 +197,8 @@ std::optional<OUString> SdtHelper::getValueFromDataBinding() { uno::Reference<XXPathObject> xResult = xXpathAPI->eval(xDocument, m_sDataBindingXPath); - if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength()) + if (xResult.is() && xResult->getNodeList() && xResult->getNodeList()->getLength() + && xResult->getString().getLength()) { return xResult->getString(); } @@ -263,8 +264,7 @@ void SdtHelper::createDropDownControl() } // clean up - m_aDropDownItems.clear(); - setControlType(SdtControlType::unknown); + clear(); } void SdtHelper::createPlainTextControl() @@ -295,8 +295,7 @@ void SdtHelper::createPlainTextControl() uno::makeAny(getInteropGrabBagAndClear())); // clean up - m_aDropDownItems.clear(); - setControlType(SdtControlType::unknown); + clear(); } void SdtHelper::createDateContentControl() @@ -371,11 +370,11 @@ void SdtHelper::createDateContentControl() uno::UNO_QUERY); xRefreshable->refresh(); - setControlType(SdtControlType::unknown); - // Store all unused sdt parameters from grabbag xNameCont->insertByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG, uno::makeAny(getInteropGrabBagAndClear())); + + clear(); } void SdtHelper::createControlShape(awt::Size aSize, @@ -422,6 +421,16 @@ bool SdtHelper::containedInInteropGrabBag(const OUString& rValueName) [&rValueName](const beans::PropertyValue& i) { return i.Name == rValueName; }); } +void SdtHelper::clear() +{ + m_aDropDownItems.clear(); + setControlType(SdtControlType::unknown); + m_sDataBindingPrefixMapping.clear(); + m_sDataBindingXPath.clear(); + m_sDataBindingStoreItemID.clear(); + m_aGrabBag.clear(); +} + } // namespace writerfilter::dmapper /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/dmapper/SdtHelper.hxx b/writerfilter/source/dmapper/SdtHelper.hxx index e58d73168d79..d9a6115a1251 100644 --- a/writerfilter/source/dmapper/SdtHelper.hxx +++ b/writerfilter/source/dmapper/SdtHelper.hxx @@ -102,6 +102,9 @@ class SdtHelper final : public virtual SvRefBase void loadPropertiesXMLs(); + /// Clear all collected attributes for futher reuse + void clear(); + public: explicit SdtHelper(DomainMapper_Impl& rDM_Impl, css::uno::Reference<css::uno::XComponentContext> const& xContext); diff --git a/writerfilter/source/dmapper/SettingsTable.cxx b/writerfilter/source/dmapper/SettingsTable.cxx index 732b96ff96db..3e6afe7b5fa1 100644 --- a/writerfilter/source/dmapper/SettingsTable.cxx +++ b/writerfilter/source/dmapper/SettingsTable.cxx @@ -539,11 +539,13 @@ static bool lcl_isDefault(const uno::Reference<beans::XPropertyState>& xProperty void SettingsTable::ApplyProperties(uno::Reference<text::XTextDocument> const& xDoc) { uno::Reference< beans::XPropertySet> xDocProps( xDoc, uno::UNO_QUERY ); + uno::Reference<lang::XMultiServiceFactory> xTextFactory(xDoc, uno::UNO_QUERY_THROW); + uno::Reference<beans::XPropertySet> xDocumentSettings(xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW); + + xDocumentSettings->setPropertyValue("TableRowKeep", uno::makeAny(true)); if (GetWordCompatibilityMode() <= 14) { - uno::Reference<lang::XMultiServiceFactory> xTextFactory(xDoc, uno::UNO_QUERY_THROW); - uno::Reference<beans::XPropertySet> xDocumentSettings(xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY_THROW); xDocumentSettings->setPropertyValue("MsWordCompMinLineHeightByFly", uno::makeAny(true)); xDocumentSettings->setPropertyValue("TabOverMargin", uno::makeAny(true)); } diff --git a/writerfilter/source/dmapper/StyleSheetTable.cxx b/writerfilter/source/dmapper/StyleSheetTable.cxx index 5f45672fe915..8b3bc02b44a6 100644 --- a/writerfilter/source/dmapper/StyleSheetTable.cxx +++ b/writerfilter/source/dmapper/StyleSheetTable.cxx @@ -464,8 +464,12 @@ void StyleSheetTable::lcl_attribute(Id Name, Value & val) if (m_pImpl->m_pCurrentEntry->nStyleTypeCode != STYLE_TYPE_UNKNOWN) { // "If this attribute is specified by multiple styles, then the last instance shall be used." - if ( m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_PARA && !m_pImpl->m_pCurrentEntry->sStyleIdentifierD.isEmpty() ) + if (m_pImpl->m_pCurrentEntry->bIsDefaultStyle + && m_pImpl->m_pCurrentEntry->nStyleTypeCode == STYLE_TYPE_PARA + && !m_pImpl->m_pCurrentEntry->sStyleIdentifierD.isEmpty()) + { m_pImpl->m_sDefaultParaStyleName = m_pImpl->m_pCurrentEntry->sStyleIdentifierD; + } beans::PropertyValue aValue; aValue.Name = "default"; @@ -1489,13 +1493,13 @@ OUString StyleSheetTable::ConvertStyleName( const OUString& rWWName, bool bExten // { "Table of Figures", "" }, { "Envelope Address", "Addressee" }, { "Envelope Return", "Sender" }, - { "footnote reference", "Footnote Characters" }, - { "Footnote Reference", "Footnote Characters" }, + { "footnote reference", "Footnote Symbol" }, + { "Footnote Reference", "Footnote Symbol" }, // { "Annotation Reference", "" }, { "Line Number", "Line numbering" }, { "Page Number", "Page Number" }, - { "endnote reference", "Endnote Characters" }, - { "Endnote Reference", "Endnote Characters" }, + { "endnote reference", "Endnote Symbol" }, + { "Endnote Reference", "Endnote Symbol" }, { "endnote text", "Endnote" }, { "Endnote Text", "Endnote" }, // { "Table of Authorities", "" }, diff --git a/writerfilter/source/filter/WriterFilter.cxx b/writerfilter/source/filter/WriterFilter.cxx index f62dff1fe19a..945a00f95258 100644 --- a/writerfilter/source/filter/WriterFilter.cxx +++ b/writerfilter/source/filter/WriterFilter.cxx @@ -314,7 +314,6 @@ void WriterFilter::setTargetDocument(const uno::Reference<lang::XComponent>& xDo xSettings->setPropertyValue("UseFormerObjectPositioning", uno::makeAny(false)); xSettings->setPropertyValue("ConsiderTextWrapOnObjPos", uno::makeAny(true)); xSettings->setPropertyValue("UseFormerTextWrapping", uno::makeAny(false)); - xSettings->setPropertyValue("TableRowKeep", uno::makeAny(true)); xSettings->setPropertyValue("IgnoreTabsAndBlanksForLineCalculation", uno::makeAny(true)); xSettings->setPropertyValue("InvertBorderSpacing", uno::makeAny(true)); xSettings->setPropertyValue("CollapseEmptyCellPara", uno::makeAny(true)); diff --git a/writerfilter/source/rtftok/rtfcharsets.cxx b/writerfilter/source/rtftok/rtfcharsets.cxx index 69e416ce8bc5..886e161e3c89 100644 --- a/writerfilter/source/rtftok/rtfcharsets.cxx +++ b/writerfilter/source/rtftok/rtfcharsets.cxx @@ -9,6 +9,7 @@ #include "rtfcharsets.hxx" #include <sal/macros.h> +#include <rtl/textenc.h> namespace writerfilter::rtftok { @@ -50,6 +51,14 @@ RTFEncoding const aRTFEncodings[] = { int nRTFEncodings = SAL_N_ELEMENTS(aRTFEncodings); +RTFFontNameSuffix const aRTFFontNameSuffixes[] = { + { "Baltic", RTL_TEXTENCODING_MS_1257 }, { "CE", RTL_TEXTENCODING_MS_1250 }, + { "Cyr", RTL_TEXTENCODING_MS_1251 }, { "Greek", RTL_TEXTENCODING_MS_1253 }, + { "Tur", RTL_TEXTENCODING_MS_1254 }, { "(Hebrew)", RTL_TEXTENCODING_MS_1255 }, + { "(Arabic)", RTL_TEXTENCODING_MS_1256 }, { "(Vietnamese)", RTL_TEXTENCODING_MS_1258 }, + { "", RTL_TEXTENCODING_DONTKNOW } // End of array +}; + } // namespace writerfilter::rtftok /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfcharsets.hxx b/writerfilter/source/rtftok/rtfcharsets.hxx index 865a9310289e..826dea271f6b 100644 --- a/writerfilter/source/rtftok/rtfcharsets.hxx +++ b/writerfilter/source/rtftok/rtfcharsets.hxx @@ -19,6 +19,19 @@ struct RTFEncoding }; extern RTFEncoding const aRTFEncodings[]; extern int nRTFEncodings; + +/// Font name can contain special suffixes used +/// to determine encoding for given font table entry +/// For example "Arial CE" is "Arial" with CP1250 encoding +/// List of these suffixes is not official and detected in a empirical +/// way thus may be inexact and incomplete. +struct RTFFontNameSuffix +{ + const char* suffix; + int codepage; +}; +extern RTFFontNameSuffix const aRTFFontNameSuffixes[]; + } // namespace writerfilter::rtftok /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdispatchflag.cxx b/writerfilter/source/rtftok/rtfdispatchflag.cxx index 2acb4d59f977..9d891384360c 100644 --- a/writerfilter/source/rtftok/rtfdispatchflag.cxx +++ b/writerfilter/source/rtftok/rtfdispatchflag.cxx @@ -421,8 +421,7 @@ RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword) nParam = NS_ooxml::LN_CT_PPrBase_keepLines; break; case RTFKeyword::KEEPN: - if (m_aStates.top().getCurrentBuffer() != &m_aTableBufferStack.back()) - nParam = NS_ooxml::LN_CT_PPrBase_keepNext; + nParam = NS_ooxml::LN_CT_PPrBase_keepNext; break; case RTFKeyword::INTBL: { @@ -520,12 +519,8 @@ RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword) { m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_pStyle, new RTFValue(aName)); - m_aStates.top().setCurrentStyleIndex(0); - } - else - { - m_aStates.top().setCurrentStyleIndex(-1); } + m_aStates.top().setCurrentStyleIndex(0); } // Need to send paragraph properties again, if there will be any. m_bNeedPap = true; @@ -677,6 +672,7 @@ RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword) case RTFKeyword::BRDRL: case RTFKeyword::BRDRB: case RTFKeyword::BRDRR: + case RTFKeyword::BRDRBTW: { RTFSprms aAttributes; RTFSprms aSprms; @@ -695,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/rtfdispatchvalue.cxx b/writerfilter/source/rtftok/rtfdispatchvalue.cxx index 14d0b3993a54..467ef8589a5b 100644 --- a/writerfilter/source/rtftok/rtfdispatchvalue.cxx +++ b/writerfilter/source/rtftok/rtfdispatchvalue.cxx @@ -194,7 +194,14 @@ bool RTFDocumentImpl::dispatchCharacterSprmValue(RTFKeyword nKeyword, int nParam } if (nSprm > 0) { - m_aStates.top().getCharacterSprms().set(nSprm, pIntValue); + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + m_aStates.top().getTableSprms().set(nSprm, pIntValue); + } + else + { + m_aStates.top().getCharacterSprms().set(nSprm, pIntValue); + } return true; } @@ -485,84 +492,64 @@ bool RTFDocumentImpl::dispatchTableValue(RTFKeyword nKeyword, int nParam) case RTFKeyword::CLSHDNG: { int nValue = -1; - switch (nParam) - { - case 500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct5; - break; - case 1000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct10; - break; - case 1200: - nValue = NS_ooxml::LN_Value_ST_Shd_pct12; - break; - case 1500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct15; - break; - case 2000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct20; - break; - case 2500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct25; - break; - case 3000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct30; - break; - case 3500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct35; - break; - case 3700: - nValue = NS_ooxml::LN_Value_ST_Shd_pct37; - break; - case 4000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct40; - break; - case 4500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct45; - break; - case 5000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct50; - break; - case 5500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct55; - break; - case 6000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct60; - break; - case 6200: - nValue = NS_ooxml::LN_Value_ST_Shd_pct62; - break; - case 6500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct65; - break; - case 7000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct70; - break; - case 7500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct75; - break; - case 8000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct80; - break; - case 8500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct85; - break; - case 8700: - nValue = NS_ooxml::LN_Value_ST_Shd_pct87; - break; - case 9000: - nValue = NS_ooxml::LN_Value_ST_Shd_pct90; - break; - case 9500: - nValue = NS_ooxml::LN_Value_ST_Shd_pct95; - break; - default: - break; - } - if (nValue != -1) - putNestedAttribute(m_aStates.top().getTableCellSprms(), - NS_ooxml::LN_CT_TcPrBase_shd, NS_ooxml::LN_CT_Shd_val, - new RTFValue(nValue)); + + if (nParam < 1) + nValue = NS_ooxml::LN_Value_ST_Shd_clear; + else if (nParam < 750) + // Values in between 1 and 250 visually closer to 0% shading (white) + // But this will mean "no shading" while cell actually have some. + // So lets use minimal available value. + nValue = NS_ooxml::LN_Value_ST_Shd_pct5; + else if (nParam < 1100) + nValue = NS_ooxml::LN_Value_ST_Shd_pct10; + else if (nParam < 1350) + nValue = NS_ooxml::LN_Value_ST_Shd_pct12; + else if (nParam < 1750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct15; + else if (nParam < 2250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct20; + else if (nParam < 2750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct25; + else if (nParam < 3250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct30; + else if (nParam < 3600) + nValue = NS_ooxml::LN_Value_ST_Shd_pct35; + else if (nParam < 3850) + nValue = NS_ooxml::LN_Value_ST_Shd_pct37; + else if (nParam < 4250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct40; + else if (nParam < 4750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct45; + else if (nParam < 5250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct50; + else if (nParam < 5750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct55; + else if (nParam < 6100) + nValue = NS_ooxml::LN_Value_ST_Shd_pct60; + else if (nParam < 6350) + nValue = NS_ooxml::LN_Value_ST_Shd_pct62; + else if (nParam < 6750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct65; + else if (nParam < 7250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct70; + else if (nParam < 7750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct75; + else if (nParam < 8250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct80; + else if (nParam < 8600) + nValue = NS_ooxml::LN_Value_ST_Shd_pct85; + else if (nParam < 8850) + nValue = NS_ooxml::LN_Value_ST_Shd_pct87; + else if (nParam < 9250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct90; + else if (nParam < 9750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct95; + else + // Solid fill + nValue = NS_ooxml::LN_Value_ST_Shd_solid; + + putNestedAttribute(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_shd, + NS_ooxml::LN_CT_Shd_val, new RTFValue(nValue)); return true; } break; @@ -782,6 +769,10 @@ RTFError RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam) if (m_aStates.top().getDestination() == Destination::FONTTABLE || m_aStates.top().getDestination() == Destination::FONTENTRY) { + // Some text in buffer? It is font name. So previous font definition is complete + if (m_aStates.top().getCurrentDestinationText()->getLength()) + handleFontTableEntry(); + m_aFontIndexes.push_back(nParam); m_nCurrentFontIndex = getFontIndex(nParam); } diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx index 663ac935047d..41fc0cdec523 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -50,6 +50,7 @@ #include "rtfskipdestination.hxx" #include "rtftokenizer.hxx" #include "rtflookahead.hxx" +#include "rtfcharsets.hxx" using namespace com::sun::star; @@ -80,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]; } @@ -324,6 +326,8 @@ RTFDocumentImpl::RTFDocumentImpl(uno::Reference<uno::XComponentContext> const& x m_pTokenizer = new RTFTokenizer(*this, m_pInStream.get(), m_xStatusIndicator); m_pSdrImport = new RTFSdrImport(*this, m_xDstDoc); + + m_pStyleTableEntries = std::make_shared<RTFReferenceTable::Entries_t>(); } RTFDocumentImpl::~RTFDocumentImpl() = default; @@ -363,6 +367,7 @@ void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId, OUString const& m_aAuthorInitials.clear(); } pImpl->m_nDefaultFontIndex = m_nDefaultFontIndex; + pImpl->m_pStyleTableEntries = m_pStyleTableEntries; pImpl->Strm().Seek(nPos); SAL_INFO("writerfilter.rtf", "substream start"); Mapper().substream(nId, pImpl); @@ -394,16 +399,22 @@ void RTFDocumentImpl::checkFirstRun() assert(!m_bNeedSect || m_bFirstRunException); setNeedSect(true); // first call that succeeds - // set the requested default font, if there are none + // set the requested default font, if there are none for each state in stack RTFValue::Pointer_t pFont = getNestedAttribute(m_aDefaultState.getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii); - RTFValue::Pointer_t pCurrentFont - = getNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, - NS_ooxml::LN_CT_Fonts_ascii); - if (pFont && !pCurrentFont) - putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, - NS_ooxml::LN_CT_Fonts_ascii, pFont); + if (!pFont) + return; + + for (size_t i = 0; i < m_aStates.size(); i++) + { + RTFValue::Pointer_t pCurrentFont + = getNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, + NS_ooxml::LN_CT_Fonts_ascii); + if (!pCurrentFont) + putNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, + NS_ooxml::LN_CT_Fonts_ascii, pFont); + } } void RTFDocumentImpl::setNeedPar(bool bNeedPar) { m_bNeedPar = bNeedPar; } @@ -497,16 +508,16 @@ RTFDocumentImpl::getProperties(const RTFSprms& rAttributes, RTFSprms const& rSpr int nStyle = 0; if (!m_aStates.empty()) nStyle = m_aStates.top().getCurrentStyleIndex(); - auto it = m_aStyleTableEntries.find(nStyle); - if (it != m_aStyleTableEntries.end()) + auto it = m_pStyleTableEntries->find(nStyle); + if (it != m_pStyleTableEntries->end()) { // cloneAndDeduplicate() wants to know about only a single "style", so // let's merge paragraph and character style properties here. - auto itChar = m_aStyleTableEntries.end(); + auto itChar = m_pStyleTableEntries->end(); if (!m_aStates.empty()) { int nCharStyle = m_aStates.top().getCurrentCharacterStyleIndex(); - itChar = m_aStyleTableEntries.find(nCharStyle); + itChar = m_pStyleTableEntries->find(nCharStyle); } RTFSprms aStyleSprms; @@ -517,7 +528,7 @@ RTFDocumentImpl::getProperties(const RTFSprms& rAttributes, RTFSprms const& rSpr RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get()); lcl_copyFlatten(rProps, aStyleAttributes, aStyleSprms); - if (itChar != m_aStyleTableEntries.end()) + if (itChar != m_pStyleTableEntries->end()) { // Found active character style, then update aStyleSprms/Attributes. if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character) @@ -1322,6 +1333,74 @@ void RTFDocumentImpl::singleChar(sal_uInt8 nValue, bool bRunProps) } } +void RTFDocumentImpl::handleFontTableEntry() +{ + OUString aName = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + + if (aName.isEmpty()) + return; + + if (aName.endsWith(";")) + { + aName = aName.copy(0, aName.getLength() - 1); + } + + // Old documents can contain no encoding information in fontinfo, + // but there can be font name suffixes: Arial CE is not a special + // font, it is ordinal Arial, but with used cp 1250 encoding. + // Moreover these suffixes have priority over \cpgN and \fcharsetN + // in MS Word. + OUString aFontSuffix; + OUString aNameNoSuffix(aName); + sal_Int32 nLastSpace = aName.lastIndexOf(' '); + if (nLastSpace >= 0) + { + aFontSuffix = aName.copy(nLastSpace + 1); + aNameNoSuffix = aName.copy(0, nLastSpace); + sal_Int32 nEncoding = RTL_TEXTENCODING_DONTKNOW; + for (int i = 0; aRTFFontNameSuffixes[i].codepage != RTL_TEXTENCODING_DONTKNOW; i++) + { + if (aFontSuffix.equalsAscii(aRTFFontNameSuffixes[i].suffix)) + { + nEncoding = aRTFFontNameSuffixes[i].codepage; + break; + } + } + if (nEncoding > RTL_TEXTENCODING_DONTKNOW) + { + m_nCurrentEncoding = nEncoding; + m_aStates.top().setCurrentEncoding(m_nCurrentEncoding); + } + else + { + // Unknown suffix: looks like it is just a part of font name, restore it + aNameNoSuffix = aName; + } + } + + m_aFontNames[m_nCurrentFontIndex] = aNameNoSuffix; + if (m_nCurrentEncoding >= 0) + { + m_aFontEncodings[m_nCurrentFontIndex] = m_nCurrentEncoding; + m_nCurrentEncoding = -1; + } + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name, + new RTFValue(aNameNoSuffix)); + + writerfilter::Reference<Properties>::Pointer_t const pProp(new RTFReferenceProperties( + m_aStates.top().getTableAttributes(), m_aStates.top().getTableSprms())); + + //See fdo#47347 initial invalid font entry properties are inserted first, + //so when we attempt to insert the correct ones, there's already an + //entry in the map for them, so the new ones aren't inserted. + auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex); + if (lb != m_aFontTableEntries.end() + && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first))) + lb->second = pProp; + else + m_aFontTableEntries.insert(lb, std::make_pair(m_nCurrentFontIndex, pProp)); +} + void RTFDocumentImpl::text(OUString& rString) { if (rString.getLength() == 1 && m_aStates.top().getDestination() != Destination::DOCCOMM) @@ -1335,10 +1414,7 @@ void RTFDocumentImpl::text(OUString& rString) bool bRet = true; switch (m_aStates.top().getDestination()) { - // Note: in fonttbl there may or may not be groups; in stylesheet - // and revtbl groups are mandatory - case Destination::FONTTABLE: - case Destination::FONTENTRY: + // Note: in stylesheet and revtbl groups are mandatory case Destination::STYLEENTRY: case Destination::LISTNAME: case Destination::REVISIONENTRY: @@ -1358,34 +1434,6 @@ void RTFDocumentImpl::text(OUString& rString) = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); switch (m_aStates.top().getDestination()) { - case Destination::FONTTABLE: - case Destination::FONTENTRY: - { - m_aFontNames[m_nCurrentFontIndex] = aName; - if (m_nCurrentEncoding >= 0) - { - m_aFontEncodings[m_nCurrentFontIndex] = m_nCurrentEncoding; - m_nCurrentEncoding = -1; - } - m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name, - new RTFValue(aName)); - - writerfilter::Reference<Properties>::Pointer_t const pProp( - new RTFReferenceProperties(m_aStates.top().getTableAttributes(), - m_aStates.top().getTableSprms())); - - //See fdo#47347 initial invalid font entry properties are inserted first, - //so when we attempt to insert the correct ones, there's already an - //entry in the map for them, so the new ones aren't inserted. - auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex); - if (lb != m_aFontTableEntries.end() - && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first))) - lb->second = pProp; - else - m_aFontTableEntries.insert(lb, - std::make_pair(m_nCurrentFontIndex, pProp)); - } - break; case Destination::STYLEENTRY: { RTFValue::Pointer_t pType @@ -1402,7 +1450,7 @@ void RTFDocumentImpl::text(OUString& rString) writerfilter::Reference<Properties>::Pointer_t const pProp( createStyleProperties()); - m_aStyleTableEntries.insert( + m_pStyleTableEntries->insert( std::make_pair(m_nCurrentStyleIndex, pProp)); } else @@ -1423,6 +1471,8 @@ void RTFDocumentImpl::text(OUString& rString) } } break; + case Destination::FONTTABLE: + case Destination::FONTENTRY: case Destination::LEVELTEXT: case Destination::SHAPEPROPERTYNAME: case Destination::SHAPEPROPERTYVALUE: @@ -1862,13 +1912,13 @@ RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int n case RTFParserState::RunType::HICH: case RTFParserState::RunType::RTLCH_LTRCH_1: case RTFParserState::RunType::LTRCH_RTLCH_2: - case RTFParserState::RunType::DBCH: nSprm = NS_ooxml::LN_EG_RPrBase_bCs; break; case RTFParserState::RunType::NONE: case RTFParserState::RunType::LOCH: case RTFParserState::RunType::LTRCH_RTLCH_1: case RTFParserState::RunType::RTLCH_LTRCH_2: + case RTFParserState::RunType::DBCH: default: nSprm = NS_ooxml::LN_EG_RPrBase_b; break; @@ -1881,13 +1931,13 @@ RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int n case RTFParserState::RunType::HICH: case RTFParserState::RunType::RTLCH_LTRCH_1: case RTFParserState::RunType::LTRCH_RTLCH_2: - case RTFParserState::RunType::DBCH: nSprm = NS_ooxml::LN_EG_RPrBase_iCs; break; case RTFParserState::RunType::NONE: case RTFParserState::RunType::LOCH: case RTFParserState::RunType::LTRCH_RTLCH_1: case RTFParserState::RunType::RTLCH_LTRCH_2: + case RTFParserState::RunType::DBCH: default: nSprm = NS_ooxml::LN_EG_RPrBase_i; break; @@ -1922,7 +1972,14 @@ RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int n } if (nSprm >= 0) { - m_aStates.top().getCharacterSprms().set(nSprm, pBoolValue); + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + m_aStates.top().getTableSprms().set(nSprm, pBoolValue); + } + else + { + m_aStates.top().getCharacterSprms().set(nSprm, pBoolValue); + } return RTFError::OK; } @@ -2091,7 +2148,7 @@ writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::createStylePrope /** 2 different representations of the styles are needed: 1) flat content, as read from the input file: - stored in m_aStyleTableEntries, used as reference input for + stored in m_pStyleTableEntries, used as reference input for deduplication both here and for hard formatting in getProperties() 2) real content, with proper override of sprms/attributes where it differs @@ -2100,7 +2157,7 @@ writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::createStylePrope RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable() { RTFReferenceTable::Entries_t ret; - for (auto const& it : m_aStyleTableEntries) + for (auto const& it : *m_pStyleTableEntries) { auto pStyle = it.second; ret[it.first] = pStyle; @@ -2116,8 +2173,8 @@ RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable() if (it.first == nBasedOn) continue; - auto const itParent(m_aStyleTableEntries.find(nBasedOn)); // definition as read! - if (itParent != m_aStyleTableEntries.end()) + auto const itParent(m_pStyleTableEntries->find(nBasedOn)); // definition as read! + if (itParent != m_pStyleTableEntries->end()) { auto const pStyleType( static_cast<RTFReferenceProperties&>(*pStyle).getAttributes().find( @@ -2143,7 +2200,7 @@ RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable() } } } - assert(ret.size() == m_aStyleTableEntries.size()); + assert(ret.size() == m_pStyleTableEntries->size()); return ret; } @@ -2172,17 +2229,26 @@ RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState) { switch (rState.getDestination()) { + //Note: in fonttbl there may or may not be groups, so process it as no groups case Destination::FONTTABLE: + case Destination::FONTENTRY: { - writerfilter::Reference<Table>::Pointer_t const pTable( - new RTFReferenceTable(m_aFontTableEntries)); - Mapper().table(NS_ooxml::LN_FONTTABLE, pTable); - if (m_nDefaultFontIndex >= 0) + // Some text unhandled? Seems it is last font name + if (m_aStates.top().getCurrentDestinationText()->getLength()) + handleFontTableEntry(); + + if (rState.getDestination() == Destination::FONTTABLE) { - auto pValue = new RTFValue(m_aFontNames[getFontIndex(m_nDefaultFontIndex)]); - putNestedAttribute(m_aDefaultState.getCharacterSprms(), - NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii, - pValue); + writerfilter::Reference<Table>::Pointer_t const pTable( + new RTFReferenceTable(m_aFontTableEntries)); + Mapper().table(NS_ooxml::LN_FONTTABLE, pTable); + if (m_nDefaultFontIndex >= 0) + { + auto pValue = new RTFValue(m_aFontNames[getFontIndex(m_nDefaultFontIndex)]); + putNestedAttribute(m_aDefaultState.getCharacterSprms(), + NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii, + pValue); + } } } break; @@ -3668,7 +3734,7 @@ RTFParserState::RTFParserState(RTFDocumentImpl* pDocumentImpl) , m_nHour(0) , m_nMinute(0) , m_pCurrentDestinationText(nullptr) - , m_nCurrentStyleIndex(-1) + , m_nCurrentStyleIndex(0) , m_nCurrentCharacterStyleIndex(-1) , m_pCurrentBuffer(nullptr) , m_bInListpicture(false) diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.hxx b/writerfilter/source/rtftok/rtfdocumentimpl.hxx index fc5c8802b5d7..14ffc2f630a4 100644 --- a/writerfilter/source/rtftok/rtfdocumentimpl.hxx +++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx @@ -777,6 +777,7 @@ private: writerfilter::Reference<Properties>::Pointer_t getProperties(const RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType); void checkNeedPap(); + void handleFontTableEntry(); void sectBreak(bool bFinal = false); void prepareProperties(RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties, @@ -933,7 +934,8 @@ private: /// Raw default font index, use getFont() on it to get a real one. int m_nDefaultFontIndex; - RTFReferenceTable::Entries_t m_aStyleTableEntries; + /// To avoid copying entries between DomainMapper instances it is stored as pointer + std::shared_ptr<RTFReferenceTable::Entries_t> m_pStyleTableEntries; int m_nCurrentStyleIndex; bool m_bFormField; /// For the INCLUDEPICTURE field's argument. diff --git a/writerfilter/source/rtftok/rtfsprm.cxx b/writerfilter/source/rtftok/rtfsprm.cxx index 8c54fe95e345..2edfec829edf 100644 --- a/writerfilter/source/rtftok/rtfsprm.cxx +++ b/writerfilter/source/rtftok/rtfsprm.cxx @@ -159,12 +159,24 @@ 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) { + case NS_ooxml::LN_EG_RPrBase_szCs: + case NS_ooxml::LN_EG_RPrBase_sz: + return new RTFValue(24); + case NS_ooxml::LN_CT_Color_val: + return new RTFValue(0); case NS_ooxml::LN_EG_RPrBase_b: + case NS_ooxml::LN_EG_RPrBase_i: return new RTFValue(0); + case NS_ooxml::LN_CT_Underline_val: + return new RTFValue(NS_ooxml::LN_Value_ST_Underline_none); + case NS_ooxml::LN_CT_Fonts_ascii: + case NS_ooxml::LN_CT_Fonts_eastAsia: + case NS_ooxml::LN_CT_Fonts_cs: + return new RTFValue("Times New Roman"); default: break; } |