nss: upgrade to release 3.73
[LibreOffice.git] / xmloff / source / transform / TransformerBase.cxx
blobc55e29562558f58c17d0c24f09ed22528ee9a857
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <rtl/ref.hxx>
21 #include <rtl/ustrbuf.hxx>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <com/sun/star/i18n/CharacterClassification.hpp>
25 #include <com/sun/star/i18n/UnicodeType.hpp>
26 #include <com/sun/star/util/MeasureUnit.hpp>
27 #include <sax/tools/converter.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <xmloff/namespacemap.hxx>
30 #include <xmloff/xmlnamespace.hxx>
31 #include "IgnoreTContext.hxx"
32 #include "RenameElemTContext.hxx"
33 #include "ProcAttrTContext.hxx"
34 #include "ProcAddAttrTContext.hxx"
35 #include "MergeElemTContext.hxx"
36 #include "CreateElemTContext.hxx"
37 #include "MutableAttrList.hxx"
38 #include "TransformerActions.hxx"
39 #include "ElemTransformerAction.hxx"
40 #include "PropertyActionsOOo.hxx"
41 #include "TransformerTokenMap.hxx"
43 #include "TransformerBase.hxx"
44 #include <xmloff/xmlimp.hxx>
46 using namespace ::osl;
47 using namespace ::xmloff::token;
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::beans;
51 using namespace ::com::sun::star::lang;
52 using namespace ::com::sun::star::i18n;
53 using namespace ::com::sun::star::xml::sax;
55 namespace
57 bool lcl_ConvertAttr( OUString & rOutAttribute, sal_Int32 nParam )
59 bool bResult = false;
60 enum XMLTokenEnum eTokenToRename =
61 static_cast< enum XMLTokenEnum >( nParam & 0xffff );
62 if( eTokenToRename != XML_TOKEN_INVALID &&
63 IsXMLToken( rOutAttribute, eTokenToRename ))
65 enum XMLTokenEnum eReplacementToken =
66 static_cast< enum XMLTokenEnum >( nParam >> 16 );
67 rOutAttribute = GetXMLToken( eReplacementToken );
68 bResult = true;
70 return bResult;
72 } // anonymous namespace
74 XMLTransformerContext *XMLTransformerBase::CreateContext( sal_uInt16 nPrefix,
75 const OUString& rLocalName, const OUString& rQName )
77 XMLTransformerActions::key_type aKey( nPrefix, rLocalName );
78 XMLTransformerActions::const_iterator aIter =
79 GetElemActions().find( aKey );
81 if( aIter != GetElemActions().end() )
83 sal_uInt32 nActionType = (*aIter).second.m_nActionType;
84 if( (nActionType & XML_ETACTION_USER_DEFINED) != 0 )
86 XMLTransformerContext *pContext =
87 CreateUserDefinedContext( (*aIter).second,
88 rQName );
89 OSL_ENSURE( pContext && !pContext->IsPersistent(),
90 "unknown or not persistent action" );
91 return pContext;
94 switch( nActionType )
96 case XML_ETACTION_COPY_CONTENT:
97 return new XMLIgnoreTransformerContext( *this, rQName, false,
98 false );
99 case XML_ETACTION_COPY:
100 return new XMLTransformerContext( *this, rQName );
101 case XML_ETACTION_RENAME_ELEM:
102 return new XMLRenameElemTransformerContext( *this, rQName,
103 (*aIter).second.GetQNamePrefixFromParam1(),
104 (*aIter).second.GetQNameTokenFromParam1() );
105 case XML_ETACTION_RENAME_ELEM_ADD_ATTR:
106 return new XMLRenameElemTransformerContext( *this, rQName,
107 (*aIter).second.GetQNamePrefixFromParam1(),
108 (*aIter).second.GetQNameTokenFromParam1(),
109 (*aIter).second.GetQNamePrefixFromParam2(),
110 (*aIter).second.GetQNameTokenFromParam2(),
111 static_cast< XMLTokenEnum >( (*aIter).second.m_nParam3 ) );
112 case XML_ETACTION_RENAME_ELEM_PROC_ATTRS:
113 return new XMLProcAttrTransformerContext( *this, rQName,
114 (*aIter).second.GetQNamePrefixFromParam1(),
115 (*aIter).second.GetQNameTokenFromParam1(),
116 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
117 case XML_ETACTION_RENAME_ELEM_ADD_PROC_ATTR:
118 return new XMLProcAddAttrTransformerContext( *this, rQName,
119 (*aIter).second.GetQNamePrefixFromParam1(),
120 (*aIter).second.GetQNameTokenFromParam1(),
121 static_cast< sal_uInt16 >(
122 (*aIter).second.m_nParam3 >> 16 ),
123 (*aIter).second.GetQNamePrefixFromParam2(),
124 (*aIter).second.GetQNameTokenFromParam2(),
125 static_cast< XMLTokenEnum >(
126 (*aIter).second.m_nParam3 & 0xffff ) );
127 case XML_ETACTION_RENAME_ELEM_PROC_ATTRS_COND:
129 const XMLTransformerContext *pCurrent = GetCurrentContext();
130 if( pCurrent->HasQName(
131 (*aIter).second.GetQNamePrefixFromParam3(),
132 (*aIter).second.GetQNameTokenFromParam3() ) )
133 return new XMLProcAttrTransformerContext( *this, rQName,
134 (*aIter).second.GetQNamePrefixFromParam1(),
135 (*aIter).second.GetQNameTokenFromParam1(),
136 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
137 else
138 return new XMLProcAttrTransformerContext( *this, rQName,
139 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
141 case XML_ETACTION_PROC_ATTRS:
142 return new XMLProcAttrTransformerContext( *this, rQName,
143 static_cast< sal_uInt16 >( (*aIter).second.m_nParam1 ) );
144 case XML_ETACTION_PROC_ATTRS_COND:
146 const XMLTransformerContext *pCurrent = GetCurrentContext();
147 if( pCurrent->HasQName(
148 (*aIter).second.GetQNamePrefixFromParam1(),
149 (*aIter).second.GetQNameTokenFromParam1() ) )
150 return new XMLProcAttrTransformerContext( *this, rQName,
151 static_cast< sal_uInt16 >( (*aIter).second.m_nParam2 ) );
153 break;
154 case XML_ETACTION_MOVE_ATTRS_TO_ELEMS:
155 return new XMLCreateElemTransformerContext( *this, rQName,
156 static_cast< sal_uInt16 >( (*aIter).second.m_nParam1 ) );
157 case XML_ETACTION_MOVE_ELEMS_TO_ATTRS:
158 return new XMLMergeElemTransformerContext( *this, rQName,
159 static_cast< sal_uInt16 >( (*aIter).second.m_nParam1 ) );
160 default:
161 OSL_ENSURE( false, "unknown action" );
162 break;
166 // default is copying
167 return new XMLTransformerContext( *this, rQName );
170 XMLTransformerActions *XMLTransformerBase::GetUserDefinedActions( sal_uInt16 )
172 return nullptr;
175 XMLTransformerBase::XMLTransformerBase( XMLTransformerActionInit const *pInit,
176 ::xmloff::token::XMLTokenEnum const *pTKMapInit )
177 throw () :
178 m_pNamespaceMap( new SvXMLNamespaceMap ),
179 m_ElemActions( pInit ),
180 m_TokenMap( pTKMapInit )
182 GetNamespaceMap().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
183 GetNamespaceMap().Add( GetXMLToken(XML_NP_DC), GetXMLToken(XML_N_DC), XML_NAMESPACE_DC );
184 GetNamespaceMap().Add( GetXMLToken(XML_NP_MATH), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH );
185 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOO), GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
186 GetNamespaceMap().Add( GetXMLToken(XML_NP_DOM), GetXMLToken(XML_N_DOM), XML_NAMESPACE_DOM );
187 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOOW), GetXMLToken(XML_N_OOOW), XML_NAMESPACE_OOOW );
188 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOOC), GetXMLToken(XML_N_OOOC), XML_NAMESPACE_OOOC );
191 XMLTransformerBase::~XMLTransformerBase() throw ()
195 void SAL_CALL XMLTransformerBase::startDocument()
197 m_xHandler->startDocument();
200 void SAL_CALL XMLTransformerBase::endDocument()
202 m_xHandler->endDocument();
205 void SAL_CALL XMLTransformerBase::startElement( const OUString& rName,
206 const Reference< XAttributeList >& rAttrList )
208 std::unique_ptr<SvXMLNamespaceMap> pRewindMap;
210 // Process namespace attributes. This must happen before creating the
211 // context, because namespace declaration apply to the element name itself.
212 XMLMutableAttributeList *pMutableAttrList = nullptr;
213 Reference< XAttributeList > xAttrList( rAttrList );
214 sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
215 for( sal_Int16 i=0; i < nAttrCount; i++ )
217 const OUString& rAttrName = xAttrList->getNameByIndex( i );
218 if( ( rAttrName.getLength() >= 5 ) &&
219 ( rAttrName.startsWith( GetXMLToken(XML_XMLNS) ) ) &&
220 ( rAttrName.getLength() == 5 || ':' == rAttrName[5] ) )
222 if( !pRewindMap )
224 pRewindMap = std::move(m_pNamespaceMap);
225 m_pNamespaceMap.reset( new SvXMLNamespaceMap( *pRewindMap ) );
227 const OUString& rAttrValue = xAttrList->getValueByIndex( i );
229 OUString aPrefix( ( rAttrName.getLength() == 5 )
230 ? OUString()
231 : rAttrName.copy( 6 ) );
232 // Add namespace, but only if it is known.
233 sal_uInt16 nKey = m_pNamespaceMap->AddIfKnown( aPrefix, rAttrValue );
234 // If namespace is unknown, try to match a name with similar
235 // TC Id and version
236 if( XML_NAMESPACE_UNKNOWN == nKey )
238 OUString aTestName( rAttrValue );
239 if( SvXMLNamespaceMap::NormalizeOasisURN( aTestName ) )
240 nKey = m_pNamespaceMap->AddIfKnown( aPrefix, aTestName );
242 // If that namespace is not known, too, add it as unknown
243 if( XML_NAMESPACE_UNKNOWN == nKey )
244 nKey = m_pNamespaceMap->Add( aPrefix, rAttrValue );
246 const OUString& rRepName = m_vReplaceNamespaceMap.GetNameByKey( nKey );
247 if( !rRepName.isEmpty() )
249 if( !pMutableAttrList )
251 pMutableAttrList = new XMLMutableAttributeList( xAttrList );
252 xAttrList = pMutableAttrList;
255 pMutableAttrList->SetValueByIndex( i, rRepName );
260 // Get element's namespace and local name.
261 OUString aLocalName;
262 sal_uInt16 nPrefix =
263 m_pNamespaceMap->GetKeyByAttrName( rName, &aLocalName );
265 // If there are contexts already, call a CreateChildContext at the topmost
266 // context. Otherwise, create a default context.
267 ::rtl::Reference < XMLTransformerContext > xContext;
268 if( !m_vContexts.empty() )
270 xContext = m_vContexts.back()->CreateChildContext( nPrefix,
271 aLocalName,
272 rName,
273 xAttrList );
275 else
277 xContext = CreateContext( nPrefix, aLocalName, rName );
280 OSL_ENSURE( xContext.is(), "XMLTransformerBase::startElement: missing context" );
281 if( !xContext.is() )
282 xContext = new XMLTransformerContext( *this, rName );
284 // Remember old namespace map.
285 if( pRewindMap )
286 xContext->PutRewindMap( std::move(pRewindMap) );
288 // Push context on stack.
289 m_vContexts.push_back( xContext );
291 // Call a startElement at the new context.
292 xContext->StartElement( xAttrList );
295 void SAL_CALL XMLTransformerBase::endElement( const OUString&
296 #if OSL_DEBUG_LEVEL > 0
297 rName
298 #endif
301 if( m_vContexts.empty() )
302 return;
304 // Get topmost context
305 ::rtl::Reference< XMLTransformerContext > xContext = m_vContexts.back();
307 #if OSL_DEBUG_LEVEL > 0
308 OSL_ENSURE( xContext->GetQName() == rName,
309 "XMLTransformerBase::endElement: popped context has wrong lname" );
310 #endif
312 // Call a EndElement at the current context.
313 xContext->EndElement();
315 // and remove it from the stack.
316 m_vContexts.pop_back();
318 // Get a namespace map to rewind.
319 std::unique_ptr<SvXMLNamespaceMap> pRewindMap = xContext->TakeRewindMap();
321 // Delete the current context.
322 xContext = nullptr;
324 // Rewind a namespace map.
325 if( pRewindMap )
327 m_pNamespaceMap = std::move( pRewindMap );
331 void SAL_CALL XMLTransformerBase::characters( const OUString& rChars )
333 if( !m_vContexts.empty() )
335 m_vContexts.back()->Characters( rChars );
339 void SAL_CALL XMLTransformerBase::ignorableWhitespace( const OUString& rWhitespaces )
341 m_xHandler->ignorableWhitespace( rWhitespaces );
344 void SAL_CALL XMLTransformerBase::processingInstruction( const OUString& rTarget,
345 const OUString& rData )
347 m_xHandler->processingInstruction( rTarget, rData );
350 void SAL_CALL XMLTransformerBase::setDocumentLocator( const Reference< XLocator >& )
354 // XExtendedDocumentHandler
355 void SAL_CALL XMLTransformerBase::startCDATA()
359 void SAL_CALL XMLTransformerBase::endCDATA()
363 void SAL_CALL XMLTransformerBase::comment( const OUString& /*rComment*/ )
367 void SAL_CALL XMLTransformerBase::allowLineBreak()
371 void SAL_CALL XMLTransformerBase::unknown( const OUString& /*rString*/ )
375 // XInitialize
376 void SAL_CALL XMLTransformerBase::initialize( const Sequence< Any >& aArguments )
378 for( const auto& rArgument : aArguments )
380 // use isAssignableFrom instead of comparing the types to
381 // allow XExtendedDocumentHandler instead of XDocumentHandler (used in
382 // writeOasis2OOoLibraryElement in sfx2).
383 // The Any shift operator can't be used to query the type because it
384 // uses queryInterface, and the model also has a XPropertySet interface.
386 css::uno::Reference< XFastDocumentHandler > xFastHandler;
387 if( (rArgument >>= xFastHandler) && xFastHandler )
389 SvXMLImport *pFastHandler = dynamic_cast<SvXMLImport*>( xFastHandler.get() );
390 assert(pFastHandler);
391 m_xHandler.set( new SvXMLLegacyToFastDocHandler( pFastHandler ) );
393 // document handler
394 else if( cppu::UnoType<XDocumentHandler>::get().isAssignableFrom( rArgument.getValueType() ) )
396 m_xHandler.set( rArgument, UNO_QUERY );
398 // property set to transport data across
399 else if( cppu::UnoType<XPropertySet>::get().isAssignableFrom( rArgument.getValueType() ) )
400 m_xPropSet.set( rArgument, UNO_QUERY );
401 // xmodel
402 else if( cppu::UnoType<css::frame::XModel>::get().isAssignableFrom( rArgument.getValueType() ) )
403 mxModel.set( rArgument, UNO_QUERY );
406 if( m_xPropSet.is() )
408 Any aAny;
409 OUString sRelPath, sName;
410 Reference< XPropertySetInfo > xPropSetInfo =
411 m_xPropSet->getPropertySetInfo();
412 OUString sPropName( "StreamRelPath" );
413 if( xPropSetInfo->hasPropertyByName(sPropName) )
415 aAny = m_xPropSet->getPropertyValue(sPropName);
416 aAny >>= sRelPath;
418 sPropName = "StreamName";
419 if( xPropSetInfo->hasPropertyByName(sPropName) )
421 aAny = m_xPropSet->getPropertyValue(sPropName);
422 aAny >>= sName;
424 if( !sName.isEmpty() )
426 m_aExtPathPrefix = "../";
428 // If there is a rel path within a package, then append
429 // additional '../'. If the rel path contains an ':', then it is
430 // an absolute URI (or invalid URI, because zip files don't
431 // permit ':'), and it will be ignored.
432 if( !sRelPath.isEmpty() )
434 sal_Int32 nColPos = sRelPath.indexOf( ':' );
435 OSL_ENSURE( -1 == nColPos,
436 "StreamRelPath contains ':', absolute URI?" );
438 if( -1 == nColPos )
440 OUString sTmp = m_aExtPathPrefix;
441 sal_Int32 nPos = 0;
444 m_aExtPathPrefix += sTmp;
445 nPos = sRelPath.indexOf( '/', nPos + 1 );
447 while( -1 != nPos );
454 assert(m_xHandler.is()); // can't do anything without that
457 static sal_Int16 lcl_getUnit( const OUString& rValue )
459 if( rValue.endsWithIgnoreAsciiCase( "cm" ) )
460 return util::MeasureUnit::CM;
461 else if ( rValue.endsWithIgnoreAsciiCase( "mm" ) )
462 return util::MeasureUnit::MM;
463 else
464 return util::MeasureUnit::INCH;
467 XMLMutableAttributeList *XMLTransformerBase::ProcessAttrList(
468 Reference< XAttributeList >& rAttrList, sal_uInt16 nActionMap,
469 bool bClone )
471 XMLMutableAttributeList *pMutableAttrList = nullptr;
472 XMLTransformerActions *pActions = GetUserDefinedActions( nActionMap );
473 OSL_ENSURE( pActions, "go no actions" );
474 if( pActions )
476 sal_Int16 nAttrCount = rAttrList.is() ? rAttrList->getLength() : 0;
477 for( sal_Int16 i=0; i < nAttrCount; ++i )
479 const OUString& rAttrName = rAttrList->getNameByIndex( i );
480 const OUString& rAttrValue = rAttrList->getValueByIndex( i );
481 OUString aLocalName;
482 sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName( rAttrName,
483 &aLocalName );
485 XMLTransformerActions::key_type aKey( nPrefix, aLocalName );
486 XMLTransformerActions::const_iterator aIter =
487 pActions->find( aKey );
488 if( aIter != pActions->end() )
490 if( !pMutableAttrList )
492 pMutableAttrList = new XMLMutableAttributeList( rAttrList,
493 bClone );
494 rAttrList = pMutableAttrList;
497 sal_uInt32 nAction = (*aIter).second.m_nActionType;
498 bool bRename = false;
499 switch( nAction )
501 case XML_ATACTION_RENAME:
502 bRename = true;
503 break;
504 case XML_ATACTION_COPY:
505 break;
506 case XML_ATACTION_REMOVE:
507 case XML_ATACTION_STYLE_DISPLAY_NAME:
508 pMutableAttrList->RemoveAttributeByIndex( i );
509 --i;
510 --nAttrCount;
511 break;
512 case XML_ATACTION_RENAME_IN2INCH:
513 bRename = true;
514 [[fallthrough]];
515 case XML_ATACTION_IN2INCH:
517 OUString aAttrValue( rAttrValue );
518 if( ReplaceSingleInWithInch( aAttrValue ) )
519 pMutableAttrList->SetValueByIndex( i, aAttrValue );
521 break;
522 case XML_ATACTION_INS2INCHS:
524 OUString aAttrValue( rAttrValue );
525 if( ReplaceInWithInch( aAttrValue ) )
526 pMutableAttrList->SetValueByIndex( i, aAttrValue );
528 break;
529 case XML_ATACTION_RENAME_INCH2IN:
530 bRename = true;
531 [[fallthrough]];
532 case XML_ATACTION_INCH2IN:
534 OUString aAttrValue( rAttrValue );
535 if( ReplaceSingleInchWithIn( aAttrValue ) )
536 pMutableAttrList->SetValueByIndex( i, aAttrValue );
538 break;
539 case XML_ATACTION_INCHS2INS:
541 OUString aAttrValue( rAttrValue );
542 if( ReplaceInchWithIn( aAttrValue ) )
543 pMutableAttrList->SetValueByIndex( i, aAttrValue );
545 break;
546 case XML_ATACTION_TWIPS2IN:
548 OUString aAttrValue( rAttrValue );
550 XMLTransformerBase::ReplaceSingleInchWithIn( aAttrValue );
551 if( isWriter() )
553 sal_Int16 const nDestUnit = lcl_getUnit(aAttrValue);
555 // convert twips value to inch
556 sal_Int32 nMeasure;
557 if (::sax::Converter::convertMeasure(nMeasure,
558 aAttrValue))
561 // #i13778#,#i36248# apply correct twip-to-1/100mm
562 nMeasure = static_cast<sal_Int32>( nMeasure >= 0
563 ? ((nMeasure*127+36)/72)
564 : ((nMeasure*127-36)/72) );
566 OUStringBuffer aBuffer;
567 ::sax::Converter::convertMeasure(aBuffer,
568 nMeasure, util::MeasureUnit::MM_100TH,
569 nDestUnit );
570 aAttrValue = aBuffer.makeStringAndClear();
574 pMutableAttrList->SetValueByIndex( i, aAttrValue );
576 break;
577 case XML_ATACTION_RENAME_DECODE_STYLE_NAME_REF:
578 bRename = true;
579 [[fallthrough]];
580 case XML_ATACTION_DECODE_STYLE_NAME:
581 case XML_ATACTION_DECODE_STYLE_NAME_REF:
583 OUString aAttrValue( rAttrValue );
584 if( DecodeStyleName(aAttrValue) )
585 pMutableAttrList->SetValueByIndex( i, aAttrValue );
587 break;
588 case XML_ATACTION_ENCODE_STYLE_NAME:
590 OUString aAttrValue( rAttrValue );
591 if( EncodeStyleName(aAttrValue) )
593 pMutableAttrList->SetValueByIndex( i, aAttrValue );
594 OUString aNewAttrQName(
595 GetNamespaceMap().GetQNameByKey(
596 nPrefix,
597 ::xmloff::token::GetXMLToken(
598 XML_DISPLAY_NAME ) ) );
599 pMutableAttrList->AddAttribute( aNewAttrQName,
600 rAttrValue );
603 break;
604 case XML_ATACTION_RENAME_ENCODE_STYLE_NAME_REF:
605 bRename = true;
606 [[fallthrough]];
607 case XML_ATACTION_ENCODE_STYLE_NAME_REF:
609 OUString aAttrValue( rAttrValue );
610 if( EncodeStyleName(aAttrValue) )
611 pMutableAttrList->SetValueByIndex( i, aAttrValue );
613 break;
614 case XML_ATACTION_RENAME_NEG_PERCENT:
615 bRename = true;
616 [[fallthrough]];
617 case XML_ATACTION_NEG_PERCENT:
619 OUString aAttrValue( rAttrValue );
620 if( NegPercent( aAttrValue ) )
621 pMutableAttrList->SetValueByIndex( i, aAttrValue );
623 break;
624 case XML_ATACTION_RENAME_ADD_NAMESPACE_PREFIX:
625 bRename = true;
626 [[fallthrough]];
627 case XML_ATACTION_ADD_NAMESPACE_PREFIX:
629 OUString aAttrValue( rAttrValue );
630 sal_uInt16 nValPrefix =
631 static_cast<sal_uInt16>(
632 bRename ? (*aIter).second.m_nParam2
633 : (*aIter).second.m_nParam1);
634 AddNamespacePrefix( aAttrValue, nValPrefix );
635 pMutableAttrList->SetValueByIndex( i, aAttrValue );
637 break;
638 case XML_ATACTION_ADD_APP_NAMESPACE_PREFIX:
640 OUString aAttrValue( rAttrValue );
641 sal_uInt16 nValPrefix =
642 static_cast<sal_uInt16>((*aIter).second.m_nParam1);
643 if( IsXMLToken( GetClass(), XML_SPREADSHEET ) )
644 nValPrefix = XML_NAMESPACE_OOOC;
645 else if( IsXMLToken( GetClass(), XML_TEXT ) )
646 nValPrefix = XML_NAMESPACE_OOOW;
647 AddNamespacePrefix( aAttrValue, nValPrefix );
648 pMutableAttrList->SetValueByIndex( i, aAttrValue );
650 break;
651 case XML_ATACTION_RENAME_REMOVE_NAMESPACE_PREFIX:
652 bRename = true;
653 [[fallthrough]];
654 case XML_ATACTION_REMOVE_NAMESPACE_PREFIX:
656 OUString aAttrValue( rAttrValue );
657 sal_uInt16 nValPrefix =
658 static_cast<sal_uInt16>(
659 bRename ? (*aIter).second.m_nParam2
660 : (*aIter).second.m_nParam1);
661 if( RemoveNamespacePrefix( aAttrValue, nValPrefix ) )
662 pMutableAttrList->SetValueByIndex( i, aAttrValue );
664 break;
665 case XML_ATACTION_REMOVE_ANY_NAMESPACE_PREFIX:
667 OUString aAttrValue( rAttrValue );
668 if( RemoveNamespacePrefix( aAttrValue ) )
669 pMutableAttrList->SetValueByIndex( i, aAttrValue );
671 break;
672 case XML_ATACTION_URI_OOO:
674 OUString aAttrValue( rAttrValue );
675 if( ConvertURIToOASIS( aAttrValue,
676 static_cast< bool >((*aIter).second.m_nParam1)))
677 pMutableAttrList->SetValueByIndex( i, aAttrValue );
679 break;
680 case XML_ATACTION_URI_OASIS:
682 OUString aAttrValue( rAttrValue );
683 if( ConvertURIToOOo( aAttrValue,
684 static_cast< bool >((*aIter).second.m_nParam1)))
685 pMutableAttrList->SetValueByIndex( i, aAttrValue );
687 break;
688 case XML_ATACTION_RENAME_ATTRIBUTE:
690 OUString aAttrValue( rAttrValue );
691 RenameAttributeValue(
692 aAttrValue,
693 (*aIter).second.m_nParam1,
694 (*aIter).second.m_nParam2,
695 (*aIter).second.m_nParam3 );
696 pMutableAttrList->SetValueByIndex( i, aAttrValue );
698 break;
699 case XML_ATACTION_RNG2ISO_DATETIME:
701 OUString aAttrValue( rAttrValue );
702 if( ConvertRNGDateTimeToISO( aAttrValue ))
703 pMutableAttrList->SetValueByIndex( i, aAttrValue );
705 break;
706 case XML_ATACTION_RENAME_RNG2ISO_DATETIME:
708 OUString aAttrValue( rAttrValue );
709 if( ConvertRNGDateTimeToISO( aAttrValue ))
710 pMutableAttrList->SetValueByIndex( i, aAttrValue );
711 bRename = true;
713 break;
714 case XML_ATACTION_IN2TWIPS:
716 OUString aAttrValue( rAttrValue );
717 XMLTransformerBase::ReplaceSingleInWithInch( aAttrValue );
719 if( isWriter() )
721 sal_Int16 const nDestUnit = lcl_getUnit(aAttrValue);
723 // convert inch value to twips and export as faked inch
724 sal_Int32 nMeasure;
725 if (::sax::Converter::convertMeasure(nMeasure,
726 aAttrValue))
729 // #i13778#,#i36248#/ apply correct 1/100mm-to-twip conversion
730 nMeasure = static_cast<sal_Int32>( nMeasure >= 0
731 ? ((nMeasure*72+63)/127)
732 : ((nMeasure*72-63)/127) );
734 OUStringBuffer aBuffer;
735 ::sax::Converter::convertMeasure( aBuffer,
736 nMeasure, util::MeasureUnit::MM_100TH,
737 nDestUnit );
738 aAttrValue = aBuffer.makeStringAndClear();
742 pMutableAttrList->SetValueByIndex( i, aAttrValue );
744 break;
745 case XML_ATACTION_SVG_WIDTH_HEIGHT_OOO:
747 OUString aAttrValue( rAttrValue );
748 ReplaceSingleInchWithIn( aAttrValue );
750 sal_Int16 const nDestUnit = lcl_getUnit( aAttrValue );
752 sal_Int32 nMeasure;
753 if (::sax::Converter::convertMeasure(nMeasure,
754 aAttrValue))
757 if( nMeasure > 0 )
758 nMeasure -= 1;
759 else if( nMeasure < 0 )
760 nMeasure += 1;
763 OUStringBuffer aBuffer;
764 ::sax::Converter::convertMeasure(aBuffer, nMeasure,
765 util::MeasureUnit::MM_100TH, nDestUnit);
766 aAttrValue = aBuffer.makeStringAndClear();
769 pMutableAttrList->SetValueByIndex( i, aAttrValue );
771 break;
772 case XML_ATACTION_SVG_WIDTH_HEIGHT_OASIS:
774 OUString aAttrValue( rAttrValue );
775 ReplaceSingleInWithInch( aAttrValue );
777 sal_Int16 const nDestUnit = lcl_getUnit( aAttrValue );
779 sal_Int32 nMeasure;
780 if (::sax::Converter::convertMeasure(nMeasure,
781 aAttrValue))
784 if( nMeasure > 0 )
785 nMeasure += 1;
786 else if( nMeasure < 0 )
787 nMeasure -= 1;
790 OUStringBuffer aBuffer;
791 ::sax::Converter::convertMeasure(aBuffer, nMeasure,
792 util::MeasureUnit::MM_100TH, nDestUnit );
793 aAttrValue = aBuffer.makeStringAndClear();
796 pMutableAttrList->SetValueByIndex( i, aAttrValue );
798 break;
799 case XML_ATACTION_DECODE_ID:
801 const sal_Int32 nLen = rAttrValue.getLength();
802 OUStringBuffer aBuffer;
804 sal_Int32 pos;
805 for( pos = 0; pos < nLen; pos++ )
807 sal_Unicode c = rAttrValue[pos];
808 if( (c >= '0') && (c <= '9') )
809 aBuffer.append( c );
810 else
811 aBuffer.append( static_cast<sal_Int32>(c) );
814 pMutableAttrList->SetValueByIndex( i, aBuffer.makeStringAndClear() );
816 break;
817 // #i50322# - special handling for the
818 // transparency of writer background graphics.
819 case XML_ATACTION_WRITER_BACK_GRAPHIC_TRANSPARENCY:
821 // determine, if it's the transparency of a document style
822 XMLTransformerContext* pFirstContext = m_vContexts[0].get();
823 OUString aFirstContextLocalName;
824 /* sal_uInt16 nFirstContextPrefix = */
825 GetNamespaceMap().GetKeyByAttrName( pFirstContext->GetQName(),
826 &aFirstContextLocalName );
827 bool bIsDocumentStyle(
828 ::xmloff::token::IsXMLToken( aFirstContextLocalName,
829 XML_DOCUMENT_STYLES ) );
830 // no conversion of transparency value for document
831 // styles, because former OpenOffice.org version writes
832 // writes always a transparency value of 100% and doesn't
833 // read the value. Thus, it's interpreted as 0%
834 if ( !bIsDocumentStyle )
836 OUString aAttrValue( rAttrValue );
837 NegPercent(aAttrValue);
838 pMutableAttrList->SetValueByIndex( i, aAttrValue );
840 bRename = true;
842 break;
843 case XML_ATACTION_SHAPEID:
845 OUString sNewValue = "shape" + rAttrValue;
846 pMutableAttrList->SetValueByIndex( i, sNewValue );
847 break;
850 default:
851 OSL_ENSURE( false, "unknown action" );
852 break;
855 if( bRename )
857 OUString aNewAttrQName(
858 GetNamespaceMap().GetQNameByKey(
859 (*aIter).second.GetQNamePrefixFromParam1(),
860 ::xmloff::token::GetXMLToken(
861 (*aIter).second.GetQNameTokenFromParam1()) ) );
862 pMutableAttrList->RenameAttributeByIndex( i,
863 aNewAttrQName );
869 return pMutableAttrList;
872 bool XMLTransformerBase::ReplaceSingleInchWithIn( OUString& rValue )
874 bool bRet = false;
875 sal_Int32 nPos = rValue.getLength();
876 while( nPos && rValue[nPos-1] <= ' ' )
877 --nPos;
878 if( nPos > 2 &&
879 ('c'==rValue[nPos-2] || 'C'==rValue[nPos-2]) &&
880 ('h'==rValue[nPos-1] || 'H'==rValue[nPos-1]) )
882 rValue =rValue.copy( 0, nPos-2 );
883 bRet = true;
886 return bRet;
889 bool XMLTransformerBase::ReplaceInchWithIn( OUString& rValue )
891 bool bRet = false;
892 sal_Int32 nPos = 1;
893 while( nPos < rValue.getLength()-3 )
895 sal_Unicode c = rValue[nPos];
896 if( 'i'==c || 'I'==c )
898 c = rValue[nPos-1];
899 if( (c >= '0' && c <= '9') || '.' == c )
901 c = rValue[nPos+1];
902 if( 'n'==c || 'N'==c )
904 c = rValue[nPos+2];
905 if( 'c'==c || 'C'==c )
907 c = rValue[nPos+3];
908 if( 'h'==c || 'H'==c )
910 rValue = rValue.replaceAt( nPos,
911 4, GetXMLToken(XML_IN) );
912 nPos += 2;
913 bRet = true;
914 continue;
920 ++nPos;
923 return bRet;
926 bool XMLTransformerBase::ReplaceSingleInWithInch( OUString& rValue )
928 bool bRet = false;
930 sal_Int32 nPos = rValue.getLength();
931 while( nPos && rValue[nPos-1] <= ' ' )
932 --nPos;
933 if( nPos > 2 &&
934 ('i'==rValue[nPos-2] ||
935 'I'==rValue[nPos-2]) &&
936 ('n'==rValue[nPos-1] ||
937 'N'==rValue[nPos-1]) )
939 nPos -= 2;
940 rValue = rValue.replaceAt( nPos, rValue.getLength() - nPos,
941 GetXMLToken(XML_INCH) );
942 bRet = true;
945 return bRet;
948 bool XMLTransformerBase::ReplaceInWithInch( OUString& rValue )
950 bool bRet = false;
951 sal_Int32 nPos = 1;
952 while( nPos < rValue.getLength()-1 )
954 sal_Unicode c = rValue[nPos];
955 if( 'i'==c || 'I'==c )
957 c = rValue[nPos-1];
958 if( (c >= '0' && c <= '9') || '.' == c )
960 c = rValue[nPos+1];
961 if( 'n'==c || 'N'==c )
963 rValue = rValue.replaceAt( nPos,
964 2, GetXMLToken(XML_INCH) );
965 nPos += 4;
966 bRet = true;
967 continue;
971 ++nPos;
974 return bRet;
977 bool XMLTransformerBase::EncodeStyleName( OUString& rName ) const
979 static const char aHexTab[] = "0123456789abcdef";
981 bool bEncoded = false;
983 sal_Int32 nLen = rName.getLength();
984 OUStringBuffer aBuffer( nLen );
986 for( sal_Int32 i = 0; i < nLen; i++ )
988 sal_Unicode c = rName[i];
989 bool bValidChar = false;
990 if( c < 0x00ffU )
992 bValidChar =
993 (c >= 0x0041 && c <= 0x005a) ||
994 (c >= 0x0061 && c <= 0x007a) ||
995 (c >= 0x00c0 && c <= 0x00d6) ||
996 (c >= 0x00d8 && c <= 0x00f6) ||
997 (c >= 0x00f8 && c <= 0x00ff) ||
998 ( i > 0 && ( (c >= 0x0030 && c <= 0x0039) ||
999 c == 0x00b7 || c == '-' || c == '.') );
1001 else
1003 if( (c >= 0xf900U && c <= 0xfffeU) ||
1004 (c >= 0x20ddU && c <= 0x20e0U))
1006 bValidChar = false;
1008 else if( (c >= 0x02bbU && c <= 0x02c1U) || c == 0x0559 ||
1009 c == 0x06e5 || c == 0x06e6 )
1011 bValidChar = true;
1013 else if( c == 0x0387 )
1015 bValidChar = i > 0;
1017 else
1019 if( !xCharClass.is() )
1021 const_cast < XMLTransformerBase * >(this)
1022 ->xCharClass = CharacterClassification::create( comphelper::getProcessComponentContext() );
1024 sal_Int16 nType = xCharClass->getType( rName, i );
1026 switch( nType )
1028 case UnicodeType::UPPERCASE_LETTER: // Lu
1029 case UnicodeType::LOWERCASE_LETTER: // Ll
1030 case UnicodeType::TITLECASE_LETTER: // Lt
1031 case UnicodeType::OTHER_LETTER: // Lo
1032 case UnicodeType::LETTER_NUMBER: // Nl
1033 bValidChar = true;
1034 break;
1035 case UnicodeType::NON_SPACING_MARK: // Ms
1036 case UnicodeType::ENCLOSING_MARK: // Me
1037 case UnicodeType::COMBINING_SPACING_MARK: //Mc
1038 case UnicodeType::MODIFIER_LETTER: // Lm
1039 case UnicodeType::DECIMAL_DIGIT_NUMBER: // Nd
1040 bValidChar = i > 0;
1041 break;
1045 if( bValidChar )
1047 aBuffer.append( c );
1049 else
1051 aBuffer.append( '_' );
1052 if( c > 0x0fff )
1053 aBuffer.append( static_cast< sal_Unicode >(
1054 aHexTab[ (c >> 12) & 0x0f ] ) );
1055 if( c > 0x00ff )
1056 aBuffer.append( static_cast< sal_Unicode >(
1057 aHexTab[ (c >> 8) & 0x0f ] ) );
1058 if( c > 0x000f )
1059 aBuffer.append( static_cast< sal_Unicode >(
1060 aHexTab[ (c >> 4) & 0x0f ] ) );
1061 aBuffer.append( static_cast< sal_Unicode >(
1062 aHexTab[ c & 0x0f ] ) );
1063 aBuffer.append( '_' );
1064 bEncoded = true;
1068 if( aBuffer.getLength() > (1<<15)-1 )
1069 bEncoded = false;
1071 if( bEncoded )
1072 rName = aBuffer.makeStringAndClear();
1073 return bEncoded;
1076 bool XMLTransformerBase::DecodeStyleName( OUString& rName )
1078 bool bEncoded = false;
1080 sal_Int32 nLen = rName.getLength();
1081 OUStringBuffer aBuffer( nLen );
1083 bool bWithinHex = false;
1084 sal_Unicode cEnc = 0;
1085 for( sal_Int32 i = 0; i < nLen; i++ )
1087 sal_Unicode c = rName[i];
1088 if( '_' == c )
1090 if( bWithinHex )
1092 aBuffer.append( cEnc );
1093 cEnc = 0;
1095 else
1097 bEncoded = true;
1099 bWithinHex = !bWithinHex;
1101 else if( bWithinHex )
1103 sal_Unicode cDigit;
1104 if( c >= '0' && c <= '9' )
1106 cDigit = c - '0';
1108 else if( c >= 'a' && c <= 'f' )
1110 cDigit = c - 'a' + 10;
1112 else if( c >= 'A' && c <= 'F' )
1114 cDigit = c - 'A' + 10;
1116 else
1118 // error
1119 bEncoded = false;
1120 break;
1122 cEnc = (cEnc << 4) + cDigit;
1124 else
1126 aBuffer.append( c );
1130 if( bEncoded )
1131 rName = aBuffer.makeStringAndClear();
1132 return bEncoded;
1135 bool XMLTransformerBase::NegPercent( OUString& rValue )
1137 bool bRet = false;
1138 bool bNeg = false;
1139 double nVal = 0;
1141 sal_Int32 nPos = 0;
1142 sal_Int32 nLen = rValue.getLength();
1144 // skip white space
1145 while( nPos < nLen && ' ' == rValue[nPos] )
1146 nPos++;
1148 if( nPos < nLen && '-' == rValue[nPos] )
1150 bNeg = true;
1151 nPos++;
1154 // get number
1155 while( nPos < nLen &&
1156 '0' <= rValue[nPos] &&
1157 '9' >= rValue[nPos] )
1159 // TODO: check overflow!
1160 nVal *= 10;
1161 nVal += (rValue[nPos] - '0');
1162 nPos++;
1164 if( nPos < nLen && '.' == rValue[nPos] )
1166 nPos++;
1167 double nDiv = 1.;
1169 while( nPos < nLen &&
1170 '0' <= rValue[nPos] &&
1171 '9' >= rValue[nPos] )
1173 // TODO: check overflow!
1174 nDiv *= 10;
1175 nVal += ( static_cast<double>(rValue[nPos] - '0') / nDiv );
1176 nPos++;
1180 // skip white space
1181 while( nPos < nLen && ' ' == rValue[nPos] )
1182 nPos++;
1184 if( nPos < nLen && '%' == rValue[nPos] )
1186 if( bNeg )
1187 nVal = -nVal;
1188 nVal += .5;
1190 sal_Int32 nIntVal = 100 - static_cast<sal_Int32>( nVal );
1192 rValue = OUString::number(nIntVal) + "%";
1194 bRet = true;
1197 return bRet;
1200 void XMLTransformerBase::AddNamespacePrefix( OUString& rName,
1201 sal_uInt16 nPrefix ) const
1203 rName = GetNamespaceMap().GetQNameByKey( nPrefix, rName, false );
1206 bool XMLTransformerBase::RemoveNamespacePrefix( OUString& rName,
1207 sal_uInt16 nPrefixOnly ) const
1209 OUString aLocalName;
1210 sal_uInt16 nPrefix =
1211 GetNamespaceMap().GetKeyByAttrValueQName(rName, &aLocalName);
1212 bool bRet = XML_NAMESPACE_UNKNOWN != nPrefix &&
1213 (USHRT_MAX == nPrefixOnly || nPrefix == nPrefixOnly);
1214 if( bRet )
1215 rName = aLocalName;
1217 return bRet;
1220 bool XMLTransformerBase::ConvertURIToOASIS( OUString& rURI,
1221 bool bSupportPackage ) const
1223 bool bRet = false;
1224 if( !m_aExtPathPrefix.isEmpty() && !rURI.isEmpty() )
1226 bool bRel = false;
1227 switch( rURI[0] )
1229 case '#':
1230 // no rel path, but
1231 // for package URIs, the '#' has to be removed
1232 if( bSupportPackage )
1234 rURI = rURI.copy( 1 );
1235 bRet = true;
1237 break;
1238 case '/':
1239 // no rel path; nothing to do
1240 break;
1241 case '.':
1242 // a rel path; to keep URI simple, remove './', if there
1243 bRel = true;
1244 if( rURI.getLength() > 1 && '/' == rURI[1] )
1246 rURI = rURI.copy( 2 );
1247 bRet = true;
1249 break;
1250 default:
1251 // check for a RFC2396 schema
1253 bRel = true;
1254 sal_Int32 nPos = 1;
1255 sal_Int32 nLen = rURI.getLength();
1256 while( nPos < nLen )
1258 switch( rURI[nPos] )
1260 case '/':
1261 // a relative path segment
1262 nPos = nLen; // leave loop
1263 break;
1264 case ':':
1265 // a schema
1266 bRel = false;
1267 nPos = nLen; // leave loop
1268 break;
1269 default:
1270 // we don't care about any other characters
1271 break;
1273 ++nPos;
1278 if( bRel )
1280 rURI = m_aExtPathPrefix + rURI;
1281 bRet = true;
1285 return bRet;
1288 bool XMLTransformerBase::ConvertURIToOOo( OUString& rURI,
1289 bool bSupportPackage ) const
1291 bool bRet = false;
1292 if( !rURI.isEmpty() )
1294 bool bPackage = false;
1295 switch( rURI[0] )
1297 case '/':
1298 // no rel path; nothing to do
1299 break;
1300 case '.':
1301 // a rel path
1302 if( rURI.startsWith( m_aExtPathPrefix ) )
1304 // an external URI; remove '../'
1305 rURI = rURI.copy( m_aExtPathPrefix.getLength() );
1306 bRet = true;
1308 else
1310 bPackage = true;
1312 break;
1313 default:
1314 // check for a RFC2396 schema
1316 bPackage = true;
1317 sal_Int32 nPos = 1;
1318 sal_Int32 nLen = rURI.getLength();
1319 while( nPos < nLen )
1321 switch( rURI[nPos] )
1323 case '/':
1324 // a relative path segment within the package
1325 nPos = nLen; // leave loop
1326 break;
1327 case ':':
1328 // a schema
1329 bPackage = false;
1330 nPos = nLen; // leave loop
1331 break;
1332 default:
1333 // we don't care about any other characters
1334 break;
1336 ++nPos;
1341 if( bPackage && bSupportPackage )
1343 OUString sTmp( '#' );
1344 if( rURI.startsWith( "./" ) )
1345 rURI = rURI.copy( 2 );
1346 sTmp += rURI;
1347 rURI = sTmp;
1348 bRet = true;
1352 return bRet;
1355 bool XMLTransformerBase::RenameAttributeValue(
1356 OUString& rOutAttributeValue,
1357 sal_Int32 nParam1,
1358 sal_Int32 nParam2,
1359 sal_Int32 nParam3 )
1361 return ( lcl_ConvertAttr( rOutAttributeValue, nParam1) ||
1362 lcl_ConvertAttr( rOutAttributeValue, nParam2) ||
1363 lcl_ConvertAttr( rOutAttributeValue, nParam3) );
1366 // static
1367 bool XMLTransformerBase::ConvertRNGDateTimeToISO( OUString& rDateTime )
1369 if( !rDateTime.isEmpty() &&
1370 rDateTime.indexOf( '.' ) != -1 )
1372 rDateTime = rDateTime.replace( '.', ',');
1373 return true;
1376 return false;
1379 XMLTokenEnum XMLTransformerBase::GetToken( const OUString& rStr ) const
1381 XMLTransformerTokenMap::const_iterator aIter =
1382 m_TokenMap.find( rStr );
1383 if( aIter == m_TokenMap.end() )
1384 return XML_TOKEN_END;
1385 else
1386 return (*aIter).second;
1390 const XMLTransformerContext *XMLTransformerBase::GetCurrentContext() const
1392 OSL_ENSURE( !m_vContexts.empty(), "empty stack" );
1395 return m_vContexts.empty() ? nullptr : m_vContexts.back().get();
1398 const XMLTransformerContext *XMLTransformerBase::GetAncestorContext(
1399 sal_uInt32 n ) const
1401 auto nSize = m_vContexts.size();
1403 OSL_ENSURE( nSize > n + 2 , "invalid context" );
1405 return nSize > n + 2 ? m_vContexts[nSize - (n + 2)].get() : nullptr;
1408 bool XMLTransformerBase::isWriter() const
1410 Reference< XServiceInfo > xSI( mxModel, UNO_QUERY );
1411 return xSI.is() &&
1412 ( xSI->supportsService("com.sun.star.text.TextDocument") ||
1413 xSI->supportsService("com.sun.star.text.WebDocument") ||
1414 xSI->supportsService("com.sun.star.text.GlobalDocument") );
1417 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */