1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
57 bool lcl_ConvertAttr( OUString
& rOutAttribute
, sal_Int32 nParam
)
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
);
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
,
89 OSL_ENSURE( pContext
&& !pContext
->IsPersistent(),
90 "unknown or not persistent action" );
96 case XML_ETACTION_COPY_CONTENT
:
97 return new XMLIgnoreTransformerContext( *this, rQName
, 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
) );
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
) );
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
) );
161 OSL_ENSURE( false, "unknown action" );
166 // default is copying
167 return new XMLTransformerContext( *this, rQName
);
170 XMLTransformerActions
*XMLTransformerBase::GetUserDefinedActions( sal_uInt16
)
175 XMLTransformerBase::XMLTransformerBase( XMLTransformerActionInit
const *pInit
,
176 ::xmloff::token::XMLTokenEnum
const *pTKMapInit
)
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] ) )
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 )
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
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.
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
,
277 xContext
= CreateContext( nPrefix
, aLocalName
, rName
);
280 OSL_ENSURE( xContext
.is(), "XMLTransformerBase::startElement: missing context" );
282 xContext
= new XMLTransformerContext( *this, rName
);
284 // Remember old namespace map.
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
301 if( m_vContexts
.empty() )
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" );
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.
324 // Rewind a namespace map.
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*/ )
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
) );
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
);
402 else if( cppu::UnoType
<css::frame::XModel
>::get().isAssignableFrom( rArgument
.getValueType() ) )
403 mxModel
.set( rArgument
, UNO_QUERY
);
406 if( m_xPropSet
.is() )
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
);
418 sPropName
= "StreamName";
419 if( xPropSetInfo
->hasPropertyByName(sPropName
) )
421 aAny
= m_xPropSet
->getPropertyValue(sPropName
);
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?" );
440 OUString sTmp
= m_aExtPathPrefix
;
444 m_aExtPathPrefix
+= sTmp
;
445 nPos
= sRelPath
.indexOf( '/', nPos
+ 1 );
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
;
464 return util::MeasureUnit::INCH
;
467 XMLMutableAttributeList
*XMLTransformerBase::ProcessAttrList(
468 Reference
< XAttributeList
>& rAttrList
, sal_uInt16 nActionMap
,
471 XMLMutableAttributeList
*pMutableAttrList
= nullptr;
472 XMLTransformerActions
*pActions
= GetUserDefinedActions( nActionMap
);
473 OSL_ENSURE( pActions
, "go no actions" );
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
);
482 sal_uInt16 nPrefix
= GetNamespaceMap().GetKeyByAttrName( rAttrName
,
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
,
494 rAttrList
= pMutableAttrList
;
497 sal_uInt32 nAction
= (*aIter
).second
.m_nActionType
;
498 bool bRename
= false;
501 case XML_ATACTION_RENAME
:
504 case XML_ATACTION_COPY
:
506 case XML_ATACTION_REMOVE
:
507 case XML_ATACTION_STYLE_DISPLAY_NAME
:
508 pMutableAttrList
->RemoveAttributeByIndex( i
);
512 case XML_ATACTION_RENAME_IN2INCH
:
515 case XML_ATACTION_IN2INCH
:
517 OUString
aAttrValue( rAttrValue
);
518 if( ReplaceSingleInWithInch( aAttrValue
) )
519 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
522 case XML_ATACTION_INS2INCHS
:
524 OUString
aAttrValue( rAttrValue
);
525 if( ReplaceInWithInch( aAttrValue
) )
526 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
529 case XML_ATACTION_RENAME_INCH2IN
:
532 case XML_ATACTION_INCH2IN
:
534 OUString
aAttrValue( rAttrValue
);
535 if( ReplaceSingleInchWithIn( aAttrValue
) )
536 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
539 case XML_ATACTION_INCHS2INS
:
541 OUString
aAttrValue( rAttrValue
);
542 if( ReplaceInchWithIn( aAttrValue
) )
543 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
546 case XML_ATACTION_TWIPS2IN
:
548 OUString
aAttrValue( rAttrValue
);
550 XMLTransformerBase::ReplaceSingleInchWithIn( aAttrValue
);
553 sal_Int16
const nDestUnit
= lcl_getUnit(aAttrValue
);
555 // convert twips value to inch
557 if (::sax::Converter::convertMeasure(nMeasure
,
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
,
570 aAttrValue
= aBuffer
.makeStringAndClear();
574 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
577 case XML_ATACTION_RENAME_DECODE_STYLE_NAME_REF
:
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
);
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(
597 ::xmloff::token::GetXMLToken(
598 XML_DISPLAY_NAME
) ) );
599 pMutableAttrList
->AddAttribute( aNewAttrQName
,
604 case XML_ATACTION_RENAME_ENCODE_STYLE_NAME_REF
:
607 case XML_ATACTION_ENCODE_STYLE_NAME_REF
:
609 OUString
aAttrValue( rAttrValue
);
610 if( EncodeStyleName(aAttrValue
) )
611 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
614 case XML_ATACTION_RENAME_NEG_PERCENT
:
617 case XML_ATACTION_NEG_PERCENT
:
619 OUString
aAttrValue( rAttrValue
);
620 if( NegPercent( aAttrValue
) )
621 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
624 case XML_ATACTION_RENAME_ADD_NAMESPACE_PREFIX
:
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
);
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
);
651 case XML_ATACTION_RENAME_REMOVE_NAMESPACE_PREFIX
:
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
);
665 case XML_ATACTION_REMOVE_ANY_NAMESPACE_PREFIX
:
667 OUString
aAttrValue( rAttrValue
);
668 if( RemoveNamespacePrefix( aAttrValue
) )
669 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
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
);
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
);
688 case XML_ATACTION_RENAME_ATTRIBUTE
:
690 OUString
aAttrValue( rAttrValue
);
691 RenameAttributeValue(
693 (*aIter
).second
.m_nParam1
,
694 (*aIter
).second
.m_nParam2
,
695 (*aIter
).second
.m_nParam3
);
696 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
699 case XML_ATACTION_RNG2ISO_DATETIME
:
701 OUString
aAttrValue( rAttrValue
);
702 if( ConvertRNGDateTimeToISO( aAttrValue
))
703 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
706 case XML_ATACTION_RENAME_RNG2ISO_DATETIME
:
708 OUString
aAttrValue( rAttrValue
);
709 if( ConvertRNGDateTimeToISO( aAttrValue
))
710 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
714 case XML_ATACTION_IN2TWIPS
:
716 OUString
aAttrValue( rAttrValue
);
717 XMLTransformerBase::ReplaceSingleInWithInch( aAttrValue
);
721 sal_Int16
const nDestUnit
= lcl_getUnit(aAttrValue
);
723 // convert inch value to twips and export as faked inch
725 if (::sax::Converter::convertMeasure(nMeasure
,
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
,
738 aAttrValue
= aBuffer
.makeStringAndClear();
742 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
745 case XML_ATACTION_SVG_WIDTH_HEIGHT_OOO
:
747 OUString
aAttrValue( rAttrValue
);
748 ReplaceSingleInchWithIn( aAttrValue
);
750 sal_Int16
const nDestUnit
= lcl_getUnit( aAttrValue
);
753 if (::sax::Converter::convertMeasure(nMeasure
,
759 else if( nMeasure
< 0 )
763 OUStringBuffer aBuffer
;
764 ::sax::Converter::convertMeasure(aBuffer
, nMeasure
,
765 util::MeasureUnit::MM_100TH
, nDestUnit
);
766 aAttrValue
= aBuffer
.makeStringAndClear();
769 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
772 case XML_ATACTION_SVG_WIDTH_HEIGHT_OASIS
:
774 OUString
aAttrValue( rAttrValue
);
775 ReplaceSingleInWithInch( aAttrValue
);
777 sal_Int16
const nDestUnit
= lcl_getUnit( aAttrValue
);
780 if (::sax::Converter::convertMeasure(nMeasure
,
786 else if( nMeasure
< 0 )
790 OUStringBuffer aBuffer
;
791 ::sax::Converter::convertMeasure(aBuffer
, nMeasure
,
792 util::MeasureUnit::MM_100TH
, nDestUnit
);
793 aAttrValue
= aBuffer
.makeStringAndClear();
796 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
799 case XML_ATACTION_DECODE_ID
:
801 const sal_Int32 nLen
= rAttrValue
.getLength();
802 OUStringBuffer aBuffer
;
805 for( pos
= 0; pos
< nLen
; pos
++ )
807 sal_Unicode c
= rAttrValue
[pos
];
808 if( (c
>= '0') && (c
<= '9') )
811 aBuffer
.append( static_cast<sal_Int32
>(c
) );
814 pMutableAttrList
->SetValueByIndex( i
, aBuffer
.makeStringAndClear() );
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
);
843 case XML_ATACTION_SHAPEID
:
845 OUString sNewValue
= "shape" + rAttrValue
;
846 pMutableAttrList
->SetValueByIndex( i
, sNewValue
);
851 OSL_ENSURE( false, "unknown action" );
857 OUString
aNewAttrQName(
858 GetNamespaceMap().GetQNameByKey(
859 (*aIter
).second
.GetQNamePrefixFromParam1(),
860 ::xmloff::token::GetXMLToken(
861 (*aIter
).second
.GetQNameTokenFromParam1()) ) );
862 pMutableAttrList
->RenameAttributeByIndex( i
,
869 return pMutableAttrList
;
872 bool XMLTransformerBase::ReplaceSingleInchWithIn( OUString
& rValue
)
875 sal_Int32 nPos
= rValue
.getLength();
876 while( nPos
&& rValue
[nPos
-1] <= ' ' )
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 );
889 bool XMLTransformerBase::ReplaceInchWithIn( OUString
& rValue
)
893 while( nPos
< rValue
.getLength()-3 )
895 sal_Unicode c
= rValue
[nPos
];
896 if( 'i'==c
|| 'I'==c
)
899 if( (c
>= '0' && c
<= '9') || '.' == c
)
902 if( 'n'==c
|| 'N'==c
)
905 if( 'c'==c
|| 'C'==c
)
908 if( 'h'==c
|| 'H'==c
)
910 rValue
= rValue
.replaceAt( nPos
,
911 4, GetXMLToken(XML_IN
) );
926 bool XMLTransformerBase::ReplaceSingleInWithInch( OUString
& rValue
)
930 sal_Int32 nPos
= rValue
.getLength();
931 while( nPos
&& rValue
[nPos
-1] <= ' ' )
934 ('i'==rValue
[nPos
-2] ||
935 'I'==rValue
[nPos
-2]) &&
936 ('n'==rValue
[nPos
-1] ||
937 'N'==rValue
[nPos
-1]) )
940 rValue
= rValue
.replaceAt( nPos
, rValue
.getLength() - nPos
,
941 GetXMLToken(XML_INCH
) );
948 bool XMLTransformerBase::ReplaceInWithInch( OUString
& rValue
)
952 while( nPos
< rValue
.getLength()-1 )
954 sal_Unicode c
= rValue
[nPos
];
955 if( 'i'==c
|| 'I'==c
)
958 if( (c
>= '0' && c
<= '9') || '.' == c
)
961 if( 'n'==c
|| 'N'==c
)
963 rValue
= rValue
.replaceAt( nPos
,
964 2, GetXMLToken(XML_INCH
) );
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;
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
== '.') );
1003 if( (c
>= 0xf900U
&& c
<= 0xfffeU
) ||
1004 (c
>= 0x20ddU
&& c
<= 0x20e0U
))
1008 else if( (c
>= 0x02bbU
&& c
<= 0x02c1U
) || c
== 0x0559 ||
1009 c
== 0x06e5 || c
== 0x06e6 )
1013 else if( c
== 0x0387 )
1019 if( !xCharClass
.is() )
1021 const_cast < XMLTransformerBase
* >(this)
1022 ->xCharClass
= CharacterClassification::create( comphelper::getProcessComponentContext() );
1024 sal_Int16 nType
= xCharClass
->getType( rName
, i
);
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
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
1047 aBuffer
.append( c
);
1051 aBuffer
.append( '_' );
1053 aBuffer
.append( static_cast< sal_Unicode
>(
1054 aHexTab
[ (c
>> 12) & 0x0f ] ) );
1056 aBuffer
.append( static_cast< sal_Unicode
>(
1057 aHexTab
[ (c
>> 8) & 0x0f ] ) );
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( '_' );
1068 if( aBuffer
.getLength() > (1<<15)-1 )
1072 rName
= aBuffer
.makeStringAndClear();
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
];
1092 aBuffer
.append( cEnc
);
1099 bWithinHex
= !bWithinHex
;
1101 else if( bWithinHex
)
1104 if( c
>= '0' && c
<= '9' )
1108 else if( c
>= 'a' && c
<= 'f' )
1110 cDigit
= c
- 'a' + 10;
1112 else if( c
>= 'A' && c
<= 'F' )
1114 cDigit
= c
- 'A' + 10;
1122 cEnc
= (cEnc
<< 4) + cDigit
;
1126 aBuffer
.append( c
);
1131 rName
= aBuffer
.makeStringAndClear();
1135 bool XMLTransformerBase::NegPercent( OUString
& rValue
)
1142 sal_Int32 nLen
= rValue
.getLength();
1145 while( nPos
< nLen
&& ' ' == rValue
[nPos
] )
1148 if( nPos
< nLen
&& '-' == rValue
[nPos
] )
1155 while( nPos
< nLen
&&
1156 '0' <= rValue
[nPos
] &&
1157 '9' >= rValue
[nPos
] )
1159 // TODO: check overflow!
1161 nVal
+= (rValue
[nPos
] - '0');
1164 if( nPos
< nLen
&& '.' == rValue
[nPos
] )
1169 while( nPos
< nLen
&&
1170 '0' <= rValue
[nPos
] &&
1171 '9' >= rValue
[nPos
] )
1173 // TODO: check overflow!
1175 nVal
+= ( static_cast<double>(rValue
[nPos
] - '0') / nDiv
);
1181 while( nPos
< nLen
&& ' ' == rValue
[nPos
] )
1184 if( nPos
< nLen
&& '%' == rValue
[nPos
] )
1190 sal_Int32 nIntVal
= 100 - static_cast<sal_Int32
>( nVal
);
1192 rValue
= OUString::number(nIntVal
) + "%";
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
);
1220 bool XMLTransformerBase::ConvertURIToOASIS( OUString
& rURI
,
1221 bool bSupportPackage
) const
1224 if( !m_aExtPathPrefix
.isEmpty() && !rURI
.isEmpty() )
1231 // for package URIs, the '#' has to be removed
1232 if( bSupportPackage
)
1234 rURI
= rURI
.copy( 1 );
1239 // no rel path; nothing to do
1242 // a rel path; to keep URI simple, remove './', if there
1244 if( rURI
.getLength() > 1 && '/' == rURI
[1] )
1246 rURI
= rURI
.copy( 2 );
1251 // check for a RFC2396 schema
1255 sal_Int32 nLen
= rURI
.getLength();
1256 while( nPos
< nLen
)
1258 switch( rURI
[nPos
] )
1261 // a relative path segment
1262 nPos
= nLen
; // leave loop
1267 nPos
= nLen
; // leave loop
1270 // we don't care about any other characters
1280 rURI
= m_aExtPathPrefix
+ rURI
;
1288 bool XMLTransformerBase::ConvertURIToOOo( OUString
& rURI
,
1289 bool bSupportPackage
) const
1292 if( !rURI
.isEmpty() )
1294 bool bPackage
= false;
1298 // no rel path; nothing to do
1302 if( rURI
.startsWith( m_aExtPathPrefix
) )
1304 // an external URI; remove '../'
1305 rURI
= rURI
.copy( m_aExtPathPrefix
.getLength() );
1314 // check for a RFC2396 schema
1318 sal_Int32 nLen
= rURI
.getLength();
1319 while( nPos
< nLen
)
1321 switch( rURI
[nPos
] )
1324 // a relative path segment within the package
1325 nPos
= nLen
; // leave loop
1330 nPos
= nLen
; // leave loop
1333 // we don't care about any other characters
1341 if( bPackage
&& bSupportPackage
)
1343 OUString
sTmp( '#' );
1344 if( rURI
.startsWith( "./" ) )
1345 rURI
= rURI
.copy( 2 );
1355 bool XMLTransformerBase::RenameAttributeValue(
1356 OUString
& rOutAttributeValue
,
1361 return ( lcl_ConvertAttr( rOutAttributeValue
, nParam1
) ||
1362 lcl_ConvertAttr( rOutAttributeValue
, nParam2
) ||
1363 lcl_ConvertAttr( rOutAttributeValue
, nParam3
) );
1367 bool XMLTransformerBase::ConvertRNGDateTimeToISO( OUString
& rDateTime
)
1369 if( !rDateTime
.isEmpty() &&
1370 rDateTime
.indexOf( '.' ) != -1 )
1372 rDateTime
= rDateTime
.replace( '.', ',');
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
;
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
);
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: */