diff options
Diffstat (limited to '')
-rw-r--r-- | xmloff/source/table/XMLTableExport.cxx | 707 | ||||
-rw-r--r-- | xmloff/source/table/XMLTableImport.cxx | 808 | ||||
-rw-r--r-- | xmloff/source/table/table.hxx | 38 |
3 files changed, 1553 insertions, 0 deletions
diff --git a/xmloff/source/table/XMLTableExport.cxx b/xmloff/source/table/XMLTableExport.cxx new file mode 100644 index 000000000..c9e306f5e --- /dev/null +++ b/xmloff/source/table/XMLTableExport.cxx @@ -0,0 +1,707 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <xmloff/table/XMLTableExport.hxx> + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <rtl/ustring.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/table/XCellRange.hpp> +#include <com/sun/star/table/XColumnRowRange.hpp> +#include <com/sun/star/table/XMergeableCell.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertySetInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <tools/diagnose_ex.h> + +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/xmlexppr.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmltypes.hxx> +#include <xmlsdtypes.hxx> +#include <xmloff/maptype.hxx> +#include <xmloff/prhdlfac.hxx> +#include <xmloff/txtprmap.hxx> +#include "table.hxx" + +using namespace ::xmloff::token; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::style; + +#define MAP_(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_010, false } +#define CMAP(name,prefix,token,type,context) MAP_(name,prefix,token,type|XML_TYPE_PROP_TABLE_COLUMN,context) +#define RMAP(name,prefix,token,type,context) MAP_(name,prefix,token,type|XML_TYPE_PROP_TABLE_ROW,context) +#define CELLMAP(name,prefix,token,type,context) MAP_(name,prefix,token,type|XML_TYPE_PROP_TABLE_CELL,context) +#define MAP_END { nullptr, 0, XML_EMPTY, 0, 0, SvtSaveOptions::ODFSVER_010, false } + +const XMLPropertyMapEntry* getColumnPropertiesMap() +{ + static const XMLPropertyMapEntry aXMLColumnProperties[] = + { + CMAP( "Width", XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH, XML_TYPE_MEASURE, 0 ), + CMAP( "OptimalWidth", XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_COLUMN_WIDTH, XML_TYPE_BOOL, 0 ), + MAP_END + }; + + return &aXMLColumnProperties[0]; +} + +const XMLPropertyMapEntry* getRowPropertiesMap() +{ + static const XMLPropertyMapEntry aXMLRowProperties[] = + { + RMAP( "Height", XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_MEASURE, 0 ), + RMAP( "MinHeight", XML_NAMESPACE_STYLE, XML_MIN_ROW_HEIGHT, XML_TYPE_MEASURE, 0 ), + RMAP( "OptimalHeight", XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_BOOL, 0 ), + MAP_END + }; + + return &aXMLRowProperties[0]; +} + +const XMLPropertyMapEntry* getCellPropertiesMap() +{ + static const XMLPropertyMapEntry aXMLCellProperties[] = + { + CELLMAP( "RotateAngle", XML_NAMESPACE_STYLE, XML_ROTATION_ANGLE, XML_SD_TYPE_CELL_ROTATION_ANGLE, 0), + MAP_END + }; + + return &aXMLCellProperties[0]; +} + +namespace { + +class StringStatisticHelper +{ +private: + std::map< OUString, sal_Int32 > mStats; + +public: + void add( const OUString& rStyleName ); + void clear() { mStats.clear(); } + + sal_Int32 getModeString( /* out */ OUString& rModeString ); +}; + +} + +void StringStatisticHelper::add( const OUString& rStyleName ) +{ + std::map< OUString, sal_Int32 >::iterator iter( mStats.find( rStyleName ) ); + if( iter == mStats.end() ) + { + mStats[rStyleName] = 1; + } + else + { + (*iter).second += 1; + } +} + +sal_Int32 StringStatisticHelper::getModeString( OUString& rStyleName ) +{ + sal_Int32 nMax = 0; + for( const auto& rStatsEntry : mStats ) + { + if( rStatsEntry.second > nMax ) + { + rStyleName = rStatsEntry.first; + nMax = rStatsEntry.second; + } + } + + return nMax; +} + +namespace { + +class XMLCellExportPropertyMapper : public SvXMLExportPropertyMapper +{ +public: + using SvXMLExportPropertyMapper::SvXMLExportPropertyMapper; + /** this method is called for every item that has the + MID_FLAG_SPECIAL_ITEM_EXPORT flag set */ + virtual void handleSpecialItem(SvXMLAttributeList&, const XMLPropertyState&, const SvXMLUnitConverter&, + const SvXMLNamespaceMap&, const std::vector<XMLPropertyState>*, sal_uInt32) const override + { + // the SpecialItem NumberFormat must not be handled by this method + } +}; + +} + +XMLTableExport::XMLTableExport(SvXMLExport& rExp, const rtl::Reference< SvXMLExportPropertyMapper >& xExportPropertyMapper, const rtl::Reference< XMLPropertyHandlerFactory >& xFactoryRef ) +: mrExport( rExp ) +, mbExportTables( false ) +, mbWriter( false ) +{ + Reference< XMultiServiceFactory > xFac( rExp.GetModel(), UNO_QUERY ); + if( xFac.is() ) try + { + const Sequence< OUString > sSNS( xFac->getAvailableServiceNames() ); + const OUString* pSNS = std::find_if(sSNS.begin(), sSNS.end(), + [](const OUString& rSNS) { + return rSNS == "com.sun.star.drawing.TableShape" + || rSNS == "com.sun.star.style.TableStyle"; }); + if (pSNS != sSNS.end()) + { + mbExportTables = true; + mbWriter = (*pSNS == "com.sun.star.style.TableStyle"); + } + } + catch(const Exception&) + { + } + + if (mbWriter) + { + mxCellExportPropertySetMapper = new XMLCellExportPropertyMapper(new XMLTextPropertySetMapper(TextPropMap::CELL, true)); + } + else + { + mxCellExportPropertySetMapper = xExportPropertyMapper; + mxCellExportPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaExtPropMapper(rExp)); + mxCellExportPropertySetMapper->ChainExportMapper(new SvXMLExportPropertyMapper(new XMLPropertySetMapper(getCellPropertiesMap(), xFactoryRef, true))); + } + + mxRowExportPropertySetMapper = new SvXMLExportPropertyMapper( new XMLPropertySetMapper( getRowPropertiesMap(), xFactoryRef, true ) ); + mxColumnExportPropertySetMapper = new SvXMLExportPropertyMapper( new XMLPropertySetMapper( getColumnPropertiesMap(), xFactoryRef, true ) ); + + mrExport.GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_COLUMN, + OUString(XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_NAME), + mxColumnExportPropertySetMapper.get(), + OUString(XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX)); + mrExport.GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_ROW, + OUString(XML_STYLE_FAMILY_TABLE_ROW_STYLES_NAME), + mxRowExportPropertySetMapper.get(), + OUString(XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX)); + mrExport.GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_CELL, + OUString(XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME), + mxCellExportPropertySetMapper.get(), + OUString(XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX)); +} + +XMLTableExport::~XMLTableExport () +{ +} + +static bool has_states( const std::vector< XMLPropertyState >& xPropStates ) +{ + return std::any_of(xPropStates.cbegin(), xPropStates.cend(), + [](const XMLPropertyState& rPropertyState) { return rPropertyState.mnIndex != -1; }); +} + + void XMLTableExport::collectTableAutoStyles(const Reference < XColumnRowRange >& xColumnRowRange) + { + if( !mbExportTables ) + return; + + auto xTableInfo = std::make_shared<XMLTableInfo>(); + maTableInfoMap[xColumnRowRange] = xTableInfo; + + try + { + Reference< XIndexAccess > xIndexAccessCols( xColumnRowRange->getColumns(), UNO_QUERY_THROW ); + const sal_Int32 nColumnCount = xIndexAccessCols->getCount(); + for( sal_Int32 nColumn = 0; nColumn < nColumnCount; ++nColumn ) try + { + Reference< XPropertySet > xPropSet( xIndexAccessCols->getByIndex(nColumn) , UNO_QUERY_THROW ); + std::vector<XMLPropertyState> aPropStates(mxColumnExportPropertySetMapper->Filter(mrExport, xPropSet)); + + if( has_states( aPropStates ) ) + { + const OUString sStyleName( mrExport.GetAutoStylePool()->Add(XmlStyleFamily::TABLE_COLUMN, std::move(aPropStates)) ); + Reference< XInterface > xKey( xPropSet, UNO_QUERY ); + xTableInfo->maColumnStyleMap[xKey] = sStyleName; + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "exception during column style collection!"); + } + + Reference< XIndexAccess > xIndexAccessRows( xColumnRowRange->getRows(), UNO_QUERY_THROW ); + const sal_Int32 nRowCount = xIndexAccessRows->getCount(); + xTableInfo->maDefaultRowCellStyles.resize(nRowCount); + + StringStatisticHelper aStringStatistic; + + for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow ) + try + { + Reference< XPropertySet > xPropSet( xIndexAccessRows->getByIndex(nRow) , UNO_QUERY_THROW ); + std::vector<XMLPropertyState> aRowPropStates(mxRowExportPropertySetMapper->Filter(mrExport, xPropSet)); + + if( has_states( aRowPropStates ) ) + { + const OUString sStyleName( mrExport.GetAutoStylePool()->Add(XmlStyleFamily::TABLE_ROW, std::move(aRowPropStates)) ); + Reference< XInterface > xKey( xPropSet, UNO_QUERY ); + xTableInfo->maRowStyleMap[xKey] = sStyleName; + } + + // get the current row + Reference< XCellRange > xCellRange( xPropSet, UNO_QUERY_THROW ); + for ( sal_Int32 nColumn = 0; nColumn < nColumnCount; ++nColumn ) + { + // get current cell, remarks row index is 0, because we get the range for each row separate + Reference< XPropertySet > xCellSet( xCellRange->getCellByPosition(nColumn, 0), UNO_QUERY_THROW ); + + // get style + OUString sParentStyleName; + Reference< XPropertySetInfo > xPropertySetInfo( xCellSet->getPropertySetInfo() ); + if( xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName("Style") ) + { + Reference< XStyle > xStyle( xCellSet->getPropertyValue("Style"), UNO_QUERY ); + if( xStyle.is() ) + sParentStyleName = xStyle->getName(); + } + + // create auto style, if needed + OUString sStyleName; + std::vector<XMLPropertyState> aCellPropStates(mxCellExportPropertySetMapper->Filter(mrExport, xCellSet)); + if( has_states( aCellPropStates ) ) + sStyleName = mrExport.GetAutoStylePool()->Add(XmlStyleFamily::TABLE_CELL, std::move(aCellPropStates)); + else + sStyleName = sParentStyleName; + + if( !sStyleName.isEmpty() ) + { + Reference< XInterface > xKey( xCellSet, UNO_QUERY ); + xTableInfo->maCellStyleMap[xKey] = sStyleName; + } + + // create auto style for text + Reference< XText > xText(xCellSet, UNO_QUERY); + if(xText.is() && !xText->getString().isEmpty()) + GetExport().GetTextParagraphExport()->collectTextAutoStyles( xText ); + + aStringStatistic.add( sStyleName ); + } + + OUString sDefaultCellStyle; + if( aStringStatistic.getModeString( sDefaultCellStyle ) > 1 ) + xTableInfo->maDefaultRowCellStyles[nRow] = sDefaultCellStyle; + + aStringStatistic.clear(); + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "exception during column style collection!"); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "exception caught!"); + } + } + + void XMLTableExport::exportTable( const Reference < XColumnRowRange >& xColumnRowRange ) + { + if( !mbExportTables ) + return; + + try + { + std::shared_ptr< XMLTableInfo > xTableInfo( maTableInfoMap[xColumnRowRange] ); + + // get row and column count + Reference< XIndexAccess > xIndexAccess( xColumnRowRange->getRows(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xIndexAccessCols( xColumnRowRange->getColumns(), UNO_QUERY_THROW ); + + const sal_Int32 rowCount = xIndexAccess->getCount(); + const sal_Int32 columnCount = xIndexAccessCols->getCount(); + + SvXMLElementExport tableElement( mrExport, XML_NAMESPACE_TABLE, XML_TABLE, true, true ); + + // export table columns + ExportTableColumns( xIndexAccessCols, xTableInfo ); + + // start iterating rows and columns + for ( sal_Int32 rowIndex = 0; rowIndex < rowCount; rowIndex++ ) + { + // get the current row + Reference< XCellRange > xCellRange( xIndexAccess->getByIndex(rowIndex), UNO_QUERY_THROW ); + + OUString sDefaultCellStyle; + + // table:style-name + if( xTableInfo ) + { + Reference< XInterface > xKey( xCellRange, UNO_QUERY ); + const OUString sStyleName( xTableInfo->maRowStyleMap[xKey] ); + if( !sStyleName.isEmpty() ) + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, sStyleName ); + + sDefaultCellStyle = xTableInfo->maDefaultRowCellStyles[rowIndex]; + if( !sDefaultCellStyle.isEmpty() ) + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DEFAULT_CELL_STYLE_NAME, sDefaultCellStyle ); + } + + // write row element + SvXMLElementExport tableRowElement( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true ); + + for ( sal_Int32 columnIndex = 0; columnIndex < columnCount; columnIndex++ ) + { + // get current cell, remarks row index is 0, because we get the range for each row separate + Reference< XCell > xCell( xCellRange->getCellByPosition(columnIndex, 0), UNO_SET_THROW ); + + // use XMergeableCell interface from offapi + Reference< XMergeableCell > xMergeableCell( xCell, UNO_QUERY_THROW ); + + // export cell + ExportCell( xCell, xTableInfo, sDefaultCellStyle ); + } + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "" ); + } + } + +// Export the table columns + + void XMLTableExport::ExportTableColumns( const Reference < XIndexAccess >& xtableColumnsIndexAccess, const std::shared_ptr< XMLTableInfo >& rTableInfo ) + { + const sal_Int32 nColumnCount = xtableColumnsIndexAccess->getCount(); + for( sal_Int32 nColumn = 0; nColumn < nColumnCount; ++nColumn ) + { + Reference< XPropertySet > xColumnProperties( xtableColumnsIndexAccess->getByIndex(nColumn) , UNO_QUERY ); + if ( xColumnProperties.is() ) + { + // table:style-name + if( rTableInfo ) + { + Reference< XInterface > xKey( xColumnProperties, UNO_QUERY ); + const OUString sStyleName( rTableInfo->maColumnStyleMap[xKey] ); + if( !sStyleName.isEmpty() ) + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, sStyleName ); + } + + // TODO: all columns first have to be checked if someone + // have identical properties. If yes, attr table:number-columns-repeated + // has to be written. + SvXMLElementExport tableColumnElement( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true ); + } + } + } + +// ODF export for a table cell. + + void XMLTableExport::ExportCell( const Reference < XCell >& xCell, const std::shared_ptr< XMLTableInfo >& rTableInfo, std::u16string_view rDefaultCellStyle ) + { + bool bIsMerged = false; + sal_Int32 nRowSpan = 0; + sal_Int32 nColSpan = 0; + + try + { + if( rTableInfo ) + { + // table:style-name + Reference< XInterface > xKey( xCell, UNO_QUERY ); + const OUString sStyleName( rTableInfo->maCellStyleMap[xKey] ); + if( !sStyleName.isEmpty() && (sStyleName != rDefaultCellStyle) ) + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, sStyleName ); + } + + Reference< XMergeableCell > xMerge( xCell, UNO_QUERY ); + if( xMerge.is() ) + { + bIsMerged = xMerge->isMerged(); + nRowSpan = xMerge->getRowSpan(); + nColSpan = xMerge->getColumnSpan(); + } + SAL_WARN_IF( (nRowSpan < 1) || (nColSpan < 1), "xmloff", "xmloff::XMLTableExport::ExportCell(), illegal row or col span < 1?" ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "exception while exporting a table cell"); + } + + // table:number-columns-repeated + // todo + + // table:number-columns-spanned + if( nColSpan > 1 ) + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, OUString::number( nColSpan ) ); + + // table:number-rows-spanned + if( nRowSpan > 1 ) + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, OUString::number( nRowSpan ) ); + + // <table:table-cell> or <table:covered-table-cell> + SvXMLElementExport tableCellElement( mrExport, XML_NAMESPACE_TABLE, bIsMerged ? XML_COVERED_TABLE_CELL : XML_TABLE_CELL, true, true ); + + // export cells text content + ImpExportText( xCell ); + } + +// ODF export of the text contents of a table cell. +// Remarks: Up to now we only export text contents! +// TODO: Check against nested tables... + + void XMLTableExport::ImpExportText( const Reference< XCell >& xCell ) + { + Reference< XText > xText( xCell, UNO_QUERY ); + if( xText.is() && !xText->getString().isEmpty()) + mrExport.GetTextParagraphExport()->exportText( xText ); + } + +void XMLTableExport::exportTableStyles() +{ + if( !mbExportTables ) + return; + + rtl::Reference<XMLStyleExport> aStEx; + OUString sCellStyleName; + if (mbWriter) + { + sCellStyleName = "CellStyles"; + aStEx.set(new XMLCellStyleExport(mrExport)); + } + else + { + // write graphic family styles + sCellStyleName = "cell"; + aStEx.set(new XMLStyleExport(mrExport, mrExport.GetAutoStylePool().get())); + } + + aStEx->exportStyleFamily(sCellStyleName, OUString(XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME), mxCellExportPropertySetMapper, true, XmlStyleFamily::TABLE_CELL); + + exportTableTemplates(); +} + +// Export the collected automatic styles + +void XMLTableExport::exportAutoStyles() +{ + if( !mbExportTables ) + return; + + mrExport.GetAutoStylePool()->exportXML( XmlStyleFamily::TABLE_COLUMN ); + mrExport.GetAutoStylePool()->exportXML( XmlStyleFamily::TABLE_ROW ); + mrExport.GetAutoStylePool()->exportXML( XmlStyleFamily::TABLE_CELL ); +} + +const TableStyleElement* getTableStyleMap() +{ + static const struct TableStyleElement gTableStyleElements[] = + { + { XML_FIRST_ROW, OUString("first-row") }, + { XML_LAST_ROW, OUString("last-row") }, + { XML_FIRST_COLUMN, OUString("first-column") }, + { XML_LAST_COLUMN, OUString("last-column") }, + { XML_BODY, OUString("body") }, + { XML_EVEN_ROWS, OUString("even-rows") }, + { XML_ODD_ROWS, OUString("odd-rows") }, + { XML_EVEN_COLUMNS, OUString("even-columns") }, + { XML_ODD_COLUMNS, OUString("odd-columns") }, + { XML_BACKGROUND, OUString("background") }, + { XML_TOKEN_END, OUString() } + }; + + return &gTableStyleElements[0]; +} + +const TableStyleElement* getWriterSpecificTableStyleMap() +{ + static const struct TableStyleElement gWriterSpecificTableStyleElements[] = + { + { XML_FIRST_ROW_EVEN_COLUMN, OUString("first-row-even-column") }, + { XML_LAST_ROW_EVEN_COLUMN, OUString("last-row-even-column") }, + { XML_FIRST_ROW_END_COLUMN, OUString("first-row-end-column") }, + { XML_FIRST_ROW_START_COLUMN, OUString("first-row-start-column") }, + { XML_LAST_ROW_END_COLUMN, OUString("last-row-end-column") }, + { XML_LAST_ROW_START_COLUMN, OUString("last-row-start-column") }, + { XML_TOKEN_END, OUString() } + }; + + return &gWriterSpecificTableStyleElements[0]; +} + +static const TableStyleElement* getWriterSpecificTableStyleAttributes() +{ + static const struct TableStyleElement gWriterSpecifitTableStyleAttributes[] = + { + { XML_FIRST_ROW_END_COLUMN, OUString("FirstRowEndColumn") }, + { XML_FIRST_ROW_START_COLUMN, OUString("FirstRowStartColumn") }, + { XML_LAST_ROW_END_COLUMN, OUString("LastRowEndColumn") }, + { XML_LAST_ROW_START_COLUMN, OUString("LastRowStartColumn") }, + { XML_TOKEN_END, OUString() } + }; + + return &gWriterSpecifitTableStyleAttributes[0]; +} + +void XMLTableExport::exportTableTemplates() +{ + if( !mbExportTables ) + return; + + try + { + Reference< XStyleFamiliesSupplier > xFamiliesSupp( mrExport.GetModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() ); + OUString sFamilyName; + if (mbWriter) + sFamilyName = "TableStyles"; + else + sFamilyName = "table"; + + Reference< XIndexAccess > xTableFamily( xFamilies->getByName( sFamilyName ), UNO_QUERY_THROW ); + + for( sal_Int32 nIndex = 0; nIndex < xTableFamily->getCount(); nIndex++ ) try + { + SvtSaveOptions::ODFSaneDefaultVersion eVersion = mrExport.getSaneDefaultVersion(); + + Reference< XStyle > xTableStyle( xTableFamily->getByIndex( nIndex ), UNO_QUERY_THROW ); + if( !xTableStyle->isInUse() ) + continue; + + const TableStyleElement* pElements; + if (mbWriter) + { + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, xTableStyle->getName()); + Reference<XPropertySet> xTableStylePropSet(xTableStyle, UNO_QUERY_THROW); + pElements = getWriterSpecificTableStyleAttributes(); + while(pElements->meElement != XML_TOKEN_END) + { + try + { + OUString sVal; + xTableStylePropSet->getPropertyValue(pElements->msStyleName) >>= sVal; + mrExport.AddAttribute(XML_NAMESPACE_TABLE, pElements->meElement, sVal); + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff", "XMLTableExport::exportTableTemplates(), export Writer specific attributes, exception caught!"); + } + pElements++; + } + } + else + { + // checks if any of the extended version of ODF are set + if (eVersion == SvtSaveOptions::ODFSVER_012_EXT_COMPAT) + { + // tdf#106780 historically this wrong attribute was used + // for the name; write it if extended because LO < 5.3 can + // read only text:style-name, not the correct table:name + mrExport.AddAttribute(XML_NAMESPACE_TEXT, XML_STYLE_NAME, GetExport().EncodeStyleName( xTableStyle->getName() ) ); + } + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, GetExport().EncodeStyleName(xTableStyle->getName())); + } + + SvXMLElementExport tableTemplate( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_TEMPLATE, true, true ); + + Reference< XNameAccess > xStyleNames( xTableStyle, UNO_QUERY_THROW ); + pElements = getTableStyleMap(); + while( pElements->meElement != XML_TOKEN_END ) + { + try + { + Reference< XStyle > xStyle( xStyleNames->getByName( pElements->msStyleName ), UNO_QUERY ); + if( xStyle.is() ) + { + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, GetExport().EncodeStyleName( xStyle->getName() ) ); + SvXMLElementExport element( mrExport, XML_NAMESPACE_TABLE, pElements->meElement, true, true ); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } + + pElements++; + } + + if (mbWriter && ((eVersion & SvtSaveOptions::ODFSVER_EXTENDED) != 0)) + { + pElements = getWriterSpecificTableStyleMap(); + while(pElements->meElement != XML_TOKEN_END) + { + try + { + Reference<XStyle> xStyle(xStyleNames->getByName(pElements->msStyleName), UNO_QUERY); + if(xStyle.is()) + { + mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, GetExport().EncodeStyleName(xStyle->getName())); + SvXMLElementExport element(mrExport, XML_NAMESPACE_LO_EXT, pElements->meElement, true, true); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff", "XMLTableExport::exportTableTemplates(), export Writer specific styles, exception caught!"); + } + pElements++; + } + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff", "XMLTableExport::exportTableDesigns(), exception caught while exporting a table design!"); + } + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff", "XMLTableExport::exportTableDesigns()"); + } +} + +void XMLCellStyleExport::exportStyleContent(const Reference<XStyle>& /*rStyle*/) +{ +} + +void XMLCellStyleExport::exportStyleAttributes(const Reference<XStyle>& rStyle) +{ + Reference<XPropertySet> xPropSet(rStyle, UNO_QUERY); + if (!xPropSet.is()) + return; + + Reference<XPropertySetInfo> xPropSetInfo(xPropSet->getPropertySetInfo()); + static const OUStringLiteral sNumberFormat(u"NumberFormat"); + if (xPropSetInfo->hasPropertyByName(sNumberFormat)) + { + Reference<XPropertyState> xPropState(xPropSet, UNO_QUERY); + if (xPropState.is() && (PropertyState_DIRECT_VALUE == + xPropState->getPropertyState(sNumberFormat))) + { + sal_Int32 nNumberFormat = 0; + if (xPropSet->getPropertyValue(sNumberFormat) >>= nNumberFormat) + GetExport().AddAttribute(XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, + GetExport().getDataStyleName(nNumberFormat)); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/table/XMLTableImport.cxx b/xmloff/source/table/XMLTableImport.cxx new file mode 100644 index 000000000..47923d66c --- /dev/null +++ b/xmloff/source/table/XMLTableImport.cxx @@ -0,0 +1,808 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/table/XTableRows.hpp> +#include <com/sun/star/table/XMergeableCell.hpp> +#include <com/sun/star/table/XMergeableCellRange.hpp> +#include <com/sun/star/table/XTable.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <comphelper/sequence.hxx> +#include <tools/diagnose_ex.h> + +#include <xmloff/table/XMLTableImport.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/txtimp.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/prstylei.hxx> + +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmluconv.hxx> +#include "table.hxx" + +#include <sal/log.hxx> + +#include <memory> + +using namespace ::xmloff::token; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::table; +using namespace ::com::sun::star::xml::sax; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; + +namespace { + +struct ColumnInfo +{ + OUString msStyleName; + OUString msDefaultCellStyleName; +}; + +class XMLProxyContext : public SvXMLImportContext +{ +public: + XMLProxyContext( SvXMLImport& rImport, const SvXMLImportContextRef& xParent ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + +private: + SvXMLImportContextRef mxParent; +}; + +struct MergeInfo +{ + sal_Int32 mnStartColumn; + sal_Int32 mnStartRow; + sal_Int32 mnEndColumn; + sal_Int32 mnEndRow; + + MergeInfo( sal_Int32 nStartColumn, sal_Int32 nStartRow, sal_Int32 nColumnSpan, sal_Int32 nRowSpan ) + : mnStartColumn( nStartColumn ), mnStartRow( nStartRow ), mnEndColumn( nStartColumn + nColumnSpan - 1 ), mnEndRow( nStartRow + nRowSpan - 1 ) {}; +}; + +} + +class XMLTableImportContext : public SvXMLImportContext +{ +public: + XMLTableImportContext( const rtl::Reference< XMLTableImport >& xThis, Reference< XColumnRowRange > const & xColumnRowRange ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + void InitColumns(); + + SvXMLImportContextRef ImportColumn( const Reference< XFastAttributeList >& xAttrList ); + SvXMLImportContext * ImportRow( const Reference< XFastAttributeList >& xAttrList ); + SvXMLImportContextRef ImportCell( sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList ); + + OUString GetDefaultCellStyleName() const; + + css::uno::Reference< css::table::XTable > mxTable; + Reference< XTableColumns > mxColumns; + Reference< XTableRows > mxRows; + + std::vector< std::shared_ptr< ColumnInfo > > maColumnInfos; + sal_Int32 mnCurrentRow; + sal_Int32 mnCurrentColumn; + + // default cell style name for the current row + OUString msDefaultCellStyleName; + + std::vector< std::shared_ptr< MergeInfo > > maMergeInfos; +}; + +namespace { + +class XMLCellImportContext : public SvXMLImportContext +{ +public: + XMLCellImportContext( SvXMLImport& rImport, + const Reference< XMergeableCell >& xCell, + const OUString& sDefaultCellStyleName, + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + sal_Int32 getColumnSpan() const { return mnColSpan; } + sal_Int32 getRowSpan() const { return mnRowSpan; } + sal_Int32 getRepeated() const { return mnRepeated; } + + Reference< XMergeableCell > mxCell; + Reference< XTextCursor > mxCursor; + Reference< XTextCursor > mxOldCursor; + bool mbListContextPushed; + + sal_Int32 mnColSpan, mnRowSpan, mnRepeated; +}; + +class XMLTableTemplateContext : public SvXMLStyleContext +{ +public: + XMLTableTemplateContext( SvXMLImport& rImport ); + + // Create child element. + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + virtual void CreateAndInsert( bool bOverwrite ) override; +protected: + virtual void SetAttribute( sal_Int32 nElement, + const OUString& rValue ) override; +private: + XMLTableTemplate maTableTemplate; + OUString msTemplateStyleName; +}; + +} + + +XMLProxyContext::XMLProxyContext( SvXMLImport& rImport, const SvXMLImportContextRef& xParent ) +: SvXMLImportContext( rImport ) +, mxParent( xParent ) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLProxyContext::createFastChildContext( sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList ) +{ + if( mxParent.is() ) + return mxParent->createFastChildContext( nElement, xAttrList ); + return nullptr; +} + + +XMLTableImport::XMLTableImport( SvXMLImport& rImport, const rtl::Reference< XMLPropertySetMapper >& xCellPropertySetMapper, const rtl::Reference< XMLPropertyHandlerFactory >& xFactoryRef ) +: mrImport( rImport ) +{ + bool bWriter = false; + // check if called by Writer + Reference<XMultiServiceFactory> xFac(rImport.GetModel(), UNO_QUERY); + if (xFac.is()) try + { + Sequence<OUString> sSNS = xFac->getAvailableServiceNames(); + bWriter = comphelper::findValue(sSNS, "com.sun.star.style.TableStyle") != -1; + } + catch(const Exception&) + { + SAL_WARN("xmloff.table", "Error while checking available service names"); + } + + if (bWriter) + { + mxCellImportPropertySetMapper = XMLTextImportHelper::CreateTableCellExtPropMapper(rImport); + } + else + { + mxCellImportPropertySetMapper = new SvXMLImportPropertyMapper( xCellPropertySetMapper, rImport ); + mxCellImportPropertySetMapper->ChainImportMapper(XMLTextImportHelper::CreateParaExtPropMapper(rImport)); + mxCellImportPropertySetMapper->ChainImportMapper(new SvXMLImportPropertyMapper(new XMLPropertySetMapper(getCellPropertiesMap(), xFactoryRef, true), rImport)); + } + + rtl::Reference < XMLPropertySetMapper > xRowMapper( new XMLPropertySetMapper( getRowPropertiesMap(), xFactoryRef, false ) ); + mxRowImportPropertySetMapper = new SvXMLImportPropertyMapper( xRowMapper, rImport ); + + rtl::Reference < XMLPropertySetMapper > xColMapper( new XMLPropertySetMapper( getColumnPropertiesMap(), xFactoryRef, false ) ); + mxColumnImportPropertySetMapper = new SvXMLImportPropertyMapper( xColMapper, rImport ); +} + +XMLTableImport::~XMLTableImport() +{ +} + +SvXMLImportContext* XMLTableImport::CreateTableContext( Reference< XColumnRowRange > const & xColumnRowRange ) +{ + rtl::Reference< XMLTableImport > xThis( this ); + return new XMLTableImportContext( xThis, xColumnRowRange ); +} + +SvXMLStyleContext* XMLTableImport::CreateTableTemplateContext( sal_Int32 /*nElement*/, const Reference< XFastAttributeList >& /*xAttrList*/ ) +{ + return new XMLTableTemplateContext( mrImport ); +} + +void XMLTableImport::addTableTemplate( const OUString& rsStyleName, XMLTableTemplate& xTableTemplate ) +{ + auto xPtr = std::make_shared<XMLTableTemplate>(); + xPtr->swap( xTableTemplate ); + maTableTemplates[rsStyleName] = xPtr; +} + +void XMLTableImport::insertTabletemplate(const OUString& rsStyleName, bool bOverwrite) +{ + XMLTableTemplateMap::iterator it = maTableTemplates.find(rsStyleName); + if (it == maTableTemplates.end()) + return; + + try + { + Reference<XStyleFamiliesSupplier> xFamiliesSupp(mrImport.GetModel(), UNO_QUERY_THROW); + Reference<XNameAccess> xFamilies(xFamiliesSupp->getStyleFamilies()); + + Reference<XNameContainer> xTableFamily(xFamilies->getByName("TableStyles"), UNO_QUERY_THROW); + Reference<XIndexAccess> xCellFamily(xFamilies->getByName("CellStyles"), UNO_QUERY_THROW); + + const OUString sTemplateName(it->first); + Reference<XMultiServiceFactory> xFactory(mrImport.GetModel(), UNO_QUERY_THROW); + Reference<XNameReplace> xTemplate(xFactory->createInstance("com.sun.star.style.TableStyle"), UNO_QUERY_THROW); + + std::shared_ptr<XMLTableTemplate> xT(it->second); + + for (const auto& rStyle : *xT) try + { + const OUString sPropName(rStyle.first); + const OUString sStyleName(rStyle.second); + // Internally unassigned cell styles are stored by display name. + // However table-template elements reference cell styles by its encoded name. + // This loop is looking for cell style by their encoded names. + sal_Int32 nCount = xCellFamily->getCount(); + for (sal_Int32 i=0; i < nCount; ++i) + { + Any xCellStyle = xCellFamily->getByIndex(i); + OUString sEncodedStyleName = mrImport.GetMM100UnitConverter().encodeStyleName( + xCellStyle.get<Reference<XStyle>>()->getName()); + if (sEncodedStyleName == sStyleName) + { + xTemplate->replaceByName(sPropName, xCellStyle); + break; + } + } + } + catch (Exception const &) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "XMLTableImport::insertTabletemplate()"); + } + + if (xTemplate.is()) + { + if (xTableFamily->hasByName(sTemplateName) && bOverwrite) + xTableFamily->replaceByName(sTemplateName, Any(xTemplate)); + else + xTableFamily->insertByName(sTemplateName, Any(xTemplate)); + } + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.table", "XMLTableImport::insertTabletemplate()"); + } +} + +void XMLTableImport::finishStyles() +{ + if( maTableTemplates.empty() ) + return; + + try + { + Reference< XStyleFamiliesSupplier > xFamiliesSupp( mrImport.GetModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() ); + + Reference< XNameContainer > xTableFamily( xFamilies->getByName( "table" ), UNO_QUERY_THROW ); + Reference< XNameAccess > xCellFamily( xFamilies->getByName( "cell" ), UNO_QUERY_THROW ); + + Reference< XSingleServiceFactory > xFactory( xTableFamily, UNO_QUERY_THROW ); + + for( const auto& rTemplate : maTableTemplates ) try + { + const OUString sTemplateName( rTemplate.first ); + Reference< XNameReplace > xTemplate( xFactory->createInstance(), UNO_QUERY_THROW ); + + std::shared_ptr< XMLTableTemplate > xT( rTemplate.second ); + + for( const auto& rStyle : *xT ) try + { + const OUString sPropName( rStyle.first ); + const OUString sStyleName( rStyle.second ); + xTemplate->replaceByName( sPropName, xCellFamily->getByName( sStyleName ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } + + if( xTemplate.is() ) + { + if( xTableFamily->hasByName( sTemplateName ) ) + xTableFamily->replaceByName( sTemplateName, Any( xTemplate ) ); + else + xTableFamily->insertByName( sTemplateName, Any( xTemplate ) ); + } + + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } +} + + +XMLTableImportContext::XMLTableImportContext( const rtl::Reference< XMLTableImport >& xImporter, Reference< XColumnRowRange > const & xColumnRowRange ) +: SvXMLImportContext( xImporter->mrImport ) +, mxTable( xColumnRowRange, UNO_QUERY ) +, mxColumns( xColumnRowRange->getColumns() ) +, mxRows( xColumnRowRange->getRows() ) +, mnCurrentRow( -1 ) +, mnCurrentColumn( -1 ) +{ +} + +SvXMLImportContextRef XMLTableImportContext::ImportColumn( const Reference< XFastAttributeList >& xAttrList ) +{ + if( mxColumns.is() && (mnCurrentRow == -1) ) try + { + auto xInfo = std::make_shared<ColumnInfo>(); + + sal_Int32 nRepeated = 1; + + // read attributes for the table-column + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED): + nRepeated = aIter.toInt32(); + break; + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + xInfo->msStyleName = aIter.toString(); + break; + case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME): + xInfo->msDefaultCellStyleName = aIter.toString(); + break; + case XML_ELEMENT(XML, XML_ID): + //FIXME: TODO + break; + } + } + + if( nRepeated <= 1 ) + { + maColumnInfos.push_back( xInfo ); + } + else + { + maColumnInfos.insert( maColumnInfos.end(), nRepeated, xInfo ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } + + return nullptr; +} + +void XMLTableImportContext::InitColumns() +{ + if( !mxColumns.is() ) + return; + + try + { + const sal_Int32 nCount1 = mxColumns->getCount(); + const sal_Int32 nCount2 = sal::static_int_cast< sal_Int32 >( maColumnInfos.size() ); + if( nCount1 < nCount2 ) + mxColumns->insertByIndex( nCount1, nCount2 - nCount1 ); + + SvXMLStylesContext * pAutoStyles = GetImport().GetShapeImport()->GetAutoStylesContext(); + + for( sal_Int32 nCol = 0; nCol < nCount2; nCol++ ) + { + std::shared_ptr< ColumnInfo > xInfo( maColumnInfos[nCol] ); + + if( pAutoStyles && !xInfo->msStyleName.isEmpty() ) + { + const XMLPropStyleContext* pStyle = + dynamic_cast< const XMLPropStyleContext* >( + pAutoStyles->FindStyleChildContext(XmlStyleFamily::TABLE_COLUMN, xInfo->msStyleName) ); + + if( pStyle ) + { + Reference< XPropertySet > xColProps( mxColumns->getByIndex(nCol), UNO_QUERY_THROW ); + const_cast< XMLPropStyleContext* >( pStyle )->FillPropertySet( xColProps ); + } + } + + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } +} + +SvXMLImportContext * XMLTableImportContext::ImportRow( const Reference< XFastAttributeList >& xAttrList ) +{ + if( mxRows.is() ) + { + mnCurrentRow++; + if( mnCurrentRow == 0 ) + InitColumns(); // first init columns + + mnCurrentColumn = -1; + + const sal_Int32 nRowCount = mxRows->getCount(); + if( ( nRowCount - 1) < mnCurrentRow ) + { + const sal_Int32 nCount = mnCurrentRow - nRowCount + 1; + mxRows->insertByIndex( nRowCount, nCount ); + } + + Reference< XPropertySet > xRowSet( mxRows->getByIndex(mnCurrentRow), UNO_QUERY ); + + OUString sStyleName; + + // read attributes for the table-row + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + sStyleName = aIter.toString(); + break; + case XML_ELEMENT(TABLE, XML_DEFAULT_CELL_STYLE_NAME): + msDefaultCellStyleName = aIter.toString(); + break; + case XML_ELEMENT(XML, XML_ID): + //FIXME: TODO + break; + } + } + + if( !sStyleName.isEmpty() ) + { + SvXMLStylesContext * pAutoStyles = GetImport().GetShapeImport()->GetAutoStylesContext(); + if( pAutoStyles ) + { + const XMLPropStyleContext* pStyle = + dynamic_cast< const XMLPropStyleContext* >( + pAutoStyles->FindStyleChildContext(XmlStyleFamily::TABLE_ROW, sStyleName) ); + + if( pStyle ) + { + const_cast< XMLPropStyleContext* >( pStyle )->FillPropertySet( xRowSet ); + } + } + } + } + + SvXMLImportContextRef xThis( this ); + return new XMLProxyContext( GetImport(), xThis ); +} + +SvXMLImportContextRef XMLTableImportContext::ImportCell( sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList ) +{ + mnCurrentColumn++; + if( mxColumns.is() ) try + { + if( mxColumns->getCount() <= mnCurrentColumn ) + mxColumns->insertByIndex( mxColumns->getCount(), mnCurrentColumn - mxColumns->getCount() + 1 ); + + Reference< XMergeableCell > xCell( mxTable->getCellByPosition( mnCurrentColumn, mnCurrentRow ), UNO_QUERY_THROW ); + XMLCellImportContext* pCellContext = new XMLCellImportContext( GetImport(), xCell, GetDefaultCellStyleName(), nElement, xAttrList ); + + const sal_Int32 nColumnSpan = pCellContext->getColumnSpan(); + const sal_Int32 nRowSpan = pCellContext->getRowSpan(); + if( (nColumnSpan > 1) || (nRowSpan > 1) ) + maMergeInfos.push_back( std::make_shared< MergeInfo >( mnCurrentColumn, mnCurrentRow, nColumnSpan, nRowSpan ) ); + + const sal_Int32 nRepeated = pCellContext->getRepeated(); + if( nRepeated > 1 ) + { + OSL_FAIL("xmloff::XMLTableImportContext::ImportCell(), import of repeated Cells not implemented (TODO)"); + mnCurrentColumn += nRepeated - 1; + } + + return pCellContext; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } + + return nullptr; +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableImportContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + switch (nElement) + { + case XML_ELEMENT(TABLE, XML_TABLE_CELL): + case XML_ELEMENT(TABLE, XML_COVERED_TABLE_CELL): + return ImportCell( nElement, xAttrList ); + case XML_ELEMENT(TABLE, XML_TABLE_COLUMN): + return ImportColumn( xAttrList ); + case XML_ELEMENT(TABLE, XML_TABLE_ROW): + return ImportRow( xAttrList ); + case XML_ELEMENT(TABLE, XML_TABLE_COLUMNS): + case XML_ELEMENT(TABLE, XML_TABLE_ROWS): + { + SvXMLImportContextRef xThis( this ); + return new XMLProxyContext( GetImport(), xThis ); + } + } + SAL_WARN("xmloff", "unknown element"); + return nullptr; +} + +void XMLTableImportContext::endFastElement(sal_Int32 ) +{ + for( const std::shared_ptr< MergeInfo >& xInfo : maMergeInfos ) + { + if( xInfo ) try + { + Reference< XCellRange > xRange( mxTable->getCellRangeByPosition( xInfo->mnStartColumn, xInfo->mnStartRow, xInfo->mnEndColumn, xInfo->mnEndRow ) ); + Reference< XMergeableCellRange > xCursor( mxTable->createCursorByRange( xRange ), UNO_QUERY_THROW ); + xCursor->merge(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION("xmloff.table", ""); + } + } +} + +OUString XMLTableImportContext::GetDefaultCellStyleName() const +{ + OUString sStyleName( msDefaultCellStyleName ); + + // if there is still no style name, try default style name from column + if( (sStyleName.isEmpty()) && (mnCurrentColumn < sal::static_int_cast<sal_Int32>(maColumnInfos.size())) ) + sStyleName = maColumnInfos[mnCurrentColumn]->msDefaultCellStyleName; + + return sStyleName; +} + +// XMLCellImportContext + +XMLCellImportContext::XMLCellImportContext( SvXMLImport& rImport, + const Reference< XMergeableCell >& xCell, + const OUString& sDefaultCellStyleName, + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +: SvXMLImportContext( rImport ) +, mxCell( xCell ) +, mbListContextPushed( false ) +, mnColSpan( 1 ) +, mnRowSpan( 1 ) +, mnRepeated( 1 ) +{ + OUString sStyleName; + + // read attributes for the table-cell + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED): + mnRepeated = aIter.toInt32(); + break; + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_SPANNED): + mnColSpan = aIter.toInt32(); + break; + case XML_ELEMENT(TABLE, XML_NUMBER_ROWS_SPANNED): + mnRowSpan = aIter.toInt32(); + break; + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + sStyleName = aIter.toString(); + break; + case XML_ELEMENT(XML, XML_ID): +//FIXME: TODO + break; +//FIXME: RDFa (table:table-cell) + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + // if there is no style name at the cell, try default style name from row + if( sStyleName.isEmpty() ) + sStyleName = sDefaultCellStyleName; + + if( sStyleName.isEmpty() ) + return; + + SvXMLStylesContext * pAutoStyles = GetImport().GetShapeImport()->GetAutoStylesContext(); + if( !pAutoStyles ) + return; + + const XMLPropStyleContext* pStyle = + dynamic_cast< const XMLPropStyleContext* >( + pAutoStyles->FindStyleChildContext(XmlStyleFamily::TABLE_CELL, sStyleName) ); + + if( pStyle ) + { + Reference< XPropertySet > xCellSet( mxCell, UNO_QUERY ); + if( xCellSet.is() ) + const_cast< XMLPropStyleContext* >( pStyle )->FillPropertySet( xCellSet ); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLCellImportContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + // create text cursor on demand + if( !mxCursor.is() ) + { + Reference< XText > xText( mxCell, UNO_QUERY ); + if( xText.is() ) + { + rtl::Reference < XMLTextImportHelper > xTxtImport( GetImport().GetTextImport() ); + mxOldCursor = xTxtImport->GetCursor(); + mxCursor = xText->createTextCursor(); + if( mxCursor.is() ) + xTxtImport->SetCursor( mxCursor ); + + // remember old list item and block (#91964#) and reset them + // for the text frame + xTxtImport->PushListContext(); + mbListContextPushed = true; + } + } + + SvXMLImportContext * pContext = nullptr; + + // if we have a text cursor, lets try to import some text + if( mxCursor.is() ) + { + pContext = GetImport().GetTextImport()->CreateTextChildContext( GetImport(), nElement, xAttrList ); + } + + if (!pContext) + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return pContext; +} + +void XMLCellImportContext::endFastElement(sal_Int32 ) +{ + if(mxCursor.is()) + { + // delete addition newline + mxCursor->gotoEnd( false ); + mxCursor->goLeft( 1, true ); + mxCursor->setString( "" ); + + // reset cursor + GetImport().GetTextImport()->ResetCursor(); + } + + if(mxOldCursor.is()) + GetImport().GetTextImport()->SetCursor( mxOldCursor ); + + // reinstall old list item (if necessary) #91964# + if (mbListContextPushed) { + GetImport().GetTextImport()->PopListContext(); + } +} + + +XMLTableTemplateContext::XMLTableTemplateContext( SvXMLImport& rImport ) +: SvXMLStyleContext( rImport, XmlStyleFamily::TABLE_TEMPLATE_ID, false ) +{ +} + +void XMLTableTemplateContext::SetAttribute( sal_Int32 nElement, + const OUString& rValue ) +{ + if( nElement == XML_ELEMENT(TEXT, XML_STYLE_NAME) + // Writer specific: according to oasis odf 1.2 prefix should be "table" and element name should be "name" + || nElement == XML_ELEMENT(TABLE, XML_NAME) ) + { + msTemplateStyleName = rValue; + } +} + +void XMLTableTemplateContext::endFastElement(sal_Int32 ) +{ + rtl::Reference< XMLTableImport > xTableImport( GetImport().GetShapeImport()->GetShapeTableImport() ); + if( xTableImport.is() ) + xTableImport->addTableTemplate( msTemplateStyleName, maTableTemplate ); +} + +void XMLTableTemplateContext::CreateAndInsert(bool bOverwrite) +{ + rtl::Reference<XMLTableImport> xTableImport(GetImport().GetShapeImport()->GetShapeTableImport()); + if(xTableImport.is()) + xTableImport->insertTabletemplate(msTemplateStyleName, bOverwrite); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableTemplateContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + if( IsTokenInNamespace(nElement, XML_NAMESPACE_TABLE) ) + { + const TableStyleElement* pElements = getTableStyleMap(); + sal_Int32 nLocalName = nElement & TOKEN_MASK; + while( (pElements->meElement != XML_TOKEN_END) && pElements->meElement != nLocalName) + pElements++; + + if( pElements->meElement != XML_TOKEN_END ) + { + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(TEXT, XML_STYLE_NAME): + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + maTableTemplate[pElements->msStyleName] = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + } + } else if (IsTokenInNamespace(nElement, XML_NAMESPACE_LO_EXT)) // Writer specific cell styles + { + const TableStyleElement* pElements = getWriterSpecificTableStyleMap(); + sal_Int32 nLocalName = nElement & TOKEN_MASK; + while( (pElements->meElement != XML_TOKEN_END) && pElements->meElement != nLocalName) + pElements++; + + if (pElements->meElement != XML_TOKEN_END) + { + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(TEXT, XML_STYLE_NAME): + case XML_ELEMENT(TABLE, XML_STYLE_NAME): + maTableTemplate[pElements->msStyleName] = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + } + } + + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/table/table.hxx b/xmloff/source/table/table.hxx new file mode 100644 index 000000000..686972065 --- /dev/null +++ b/xmloff/source/table/table.hxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <xmloff/xmltoken.hxx> + +struct XMLPropertyMapEntry; + +struct TableStyleElement +{ + ::xmloff::token::XMLTokenEnum meElement; + OUString msStyleName; +}; + +extern const TableStyleElement* getTableStyleMap(); +extern const TableStyleElement* getWriterSpecificTableStyleMap(); +extern const XMLPropertyMapEntry* getColumnPropertiesMap(); +extern const XMLPropertyMapEntry* getRowPropertiesMap(); +extern const XMLPropertyMapEntry* getCellPropertiesMap(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |