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 <osl/diagnose.h>
23 #include <com/sun/star/i18n/CharacterClassification.hpp>
24 #include <com/sun/star/i18n/UnicodeType.hpp>
25 #include <com/sun/star/util/MeasureUnit.hpp>
26 #include <sax/tools/converter.hxx>
27 #include <comphelper/processfactory.hxx>
28 #include <xmloff/nmspmap.hxx>
29 #include <xmloff/xmlnmspe.hxx>
30 #include "IgnoreTContext.hxx"
31 #include "RenameElemTContext.hxx"
32 #include "ProcAttrTContext.hxx"
33 #include "ProcAddAttrTContext.hxx"
34 #include "MergeElemTContext.hxx"
35 #include "CreateElemTContext.hxx"
36 #include "MutableAttrList.hxx"
37 #include "TransformerActions.hxx"
38 #include "ElemTransformerAction.hxx"
39 #include "PropertyActionsOOo.hxx"
40 #include "TransformerTokenMap.hxx"
42 #include "TransformerBase.hxx"
43 #include <xmloff/xmlimp.hxx>
45 using namespace ::osl
;
46 using namespace ::xmloff::token
;
47 using namespace ::com::sun::star
;
48 using namespace ::com::sun::star::uno
;
49 using namespace ::com::sun::star::beans
;
50 using namespace ::com::sun::star::lang
;
51 using namespace ::com::sun::star::i18n
;
52 using namespace ::com::sun::star::xml::sax
;
56 bool lcl_ConvertAttr( OUString
& rOutAttribute
, sal_Int32 nParam
)
59 enum XMLTokenEnum eTokenToRename
=
60 static_cast< enum XMLTokenEnum
>( nParam
& 0xffff );
61 if( eTokenToRename
!= XML_TOKEN_INVALID
&&
62 IsXMLToken( rOutAttribute
, eTokenToRename
))
64 enum XMLTokenEnum eReplacementToken
=
65 static_cast< enum XMLTokenEnum
>( nParam
>> 16 );
66 rOutAttribute
= GetXMLToken( eReplacementToken
);
71 } // anonymous namespace
73 XMLTransformerContext
*XMLTransformerBase::CreateContext( sal_uInt16 nPrefix
,
74 const OUString
& rLocalName
, const OUString
& rQName
)
76 XMLTransformerActions::key_type
aKey( nPrefix
, rLocalName
);
77 XMLTransformerActions::const_iterator aIter
=
78 GetElemActions().find( aKey
);
80 if( aIter
!= GetElemActions().end() )
82 sal_uInt32 nActionType
= (*aIter
).second
.m_nActionType
;
83 if( (nActionType
& XML_ETACTION_USER_DEFINED
) != 0 )
85 XMLTransformerContext
*pContext
=
86 CreateUserDefinedContext( (*aIter
).second
,
88 OSL_ENSURE( pContext
&& !pContext
->IsPersistent(),
89 "unknown or not persistent action" );
95 case XML_ETACTION_COPY_CONTENT
:
96 return new XMLIgnoreTransformerContext( *this, rQName
, false,
98 case XML_ETACTION_COPY
:
99 return new XMLTransformerContext( *this, rQName
);
100 case XML_ETACTION_RENAME_ELEM
:
101 return new XMLRenameElemTransformerContext( *this, rQName
,
102 (*aIter
).second
.GetQNamePrefixFromParam1(),
103 (*aIter
).second
.GetQNameTokenFromParam1() );
104 case XML_ETACTION_RENAME_ELEM_ADD_ATTR
:
105 return new XMLRenameElemTransformerContext( *this, rQName
,
106 (*aIter
).second
.GetQNamePrefixFromParam1(),
107 (*aIter
).second
.GetQNameTokenFromParam1(),
108 (*aIter
).second
.GetQNamePrefixFromParam2(),
109 (*aIter
).second
.GetQNameTokenFromParam2(),
110 static_cast< XMLTokenEnum
>( (*aIter
).second
.m_nParam3
) );
111 case XML_ETACTION_RENAME_ELEM_PROC_ATTRS
:
112 return new XMLProcAttrTransformerContext( *this, rQName
,
113 (*aIter
).second
.GetQNamePrefixFromParam1(),
114 (*aIter
).second
.GetQNameTokenFromParam1(),
115 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam2
) );
116 case XML_ETACTION_RENAME_ELEM_ADD_PROC_ATTR
:
117 return new XMLProcAddAttrTransformerContext( *this, rQName
,
118 (*aIter
).second
.GetQNamePrefixFromParam1(),
119 (*aIter
).second
.GetQNameTokenFromParam1(),
120 static_cast< sal_uInt16
>(
121 (*aIter
).second
.m_nParam3
>> 16 ),
122 (*aIter
).second
.GetQNamePrefixFromParam2(),
123 (*aIter
).second
.GetQNameTokenFromParam2(),
124 static_cast< XMLTokenEnum
>(
125 (*aIter
).second
.m_nParam3
& 0xffff ) );
126 case XML_ETACTION_RENAME_ELEM_PROC_ATTRS_COND
:
128 const XMLTransformerContext
*pCurrent
= GetCurrentContext();
129 if( pCurrent
->HasQName(
130 (*aIter
).second
.GetQNamePrefixFromParam3(),
131 (*aIter
).second
.GetQNameTokenFromParam3() ) )
132 return new XMLProcAttrTransformerContext( *this, rQName
,
133 (*aIter
).second
.GetQNamePrefixFromParam1(),
134 (*aIter
).second
.GetQNameTokenFromParam1(),
135 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam2
) );
137 return new XMLProcAttrTransformerContext( *this, rQName
,
138 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam2
) );
140 case XML_ETACTION_PROC_ATTRS
:
141 return new XMLProcAttrTransformerContext( *this, rQName
,
142 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam1
) );
143 case XML_ETACTION_PROC_ATTRS_COND
:
145 const XMLTransformerContext
*pCurrent
= GetCurrentContext();
146 if( pCurrent
->HasQName(
147 (*aIter
).second
.GetQNamePrefixFromParam1(),
148 (*aIter
).second
.GetQNameTokenFromParam1() ) )
149 return new XMLProcAttrTransformerContext( *this, rQName
,
150 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam2
) );
153 case XML_ETACTION_MOVE_ATTRS_TO_ELEMS
:
154 return new XMLCreateElemTransformerContext( *this, rQName
,
155 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam1
) );
156 case XML_ETACTION_MOVE_ELEMS_TO_ATTRS
:
157 return new XMLMergeElemTransformerContext( *this, rQName
,
158 static_cast< sal_uInt16
>( (*aIter
).second
.m_nParam1
) );
160 OSL_ENSURE( false, "unknown action" );
165 // default is copying
166 return new XMLTransformerContext( *this, rQName
);
169 XMLTransformerActions
*XMLTransformerBase::GetUserDefinedActions( sal_uInt16
)
174 XMLTransformerBase::XMLTransformerBase( XMLTransformerActionInit
const *pInit
,
175 ::xmloff::token::XMLTokenEnum
const *pTKMapInit
)
177 m_pNamespaceMap( new SvXMLNamespaceMap
),
178 m_ElemActions( pInit
),
179 m_TokenMap( pTKMapInit
)
181 GetNamespaceMap().Add( GetXMLToken(XML_NP_XLINK
), GetXMLToken(XML_N_XLINK
), XML_NAMESPACE_XLINK
);
182 GetNamespaceMap().Add( GetXMLToken(XML_NP_DC
), GetXMLToken(XML_N_DC
), XML_NAMESPACE_DC
);
183 GetNamespaceMap().Add( GetXMLToken(XML_NP_MATH
), GetXMLToken(XML_N_MATH
), XML_NAMESPACE_MATH
);
184 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOO
), GetXMLToken(XML_N_OOO
), XML_NAMESPACE_OOO
);
185 GetNamespaceMap().Add( GetXMLToken(XML_NP_DOM
), GetXMLToken(XML_N_DOM
), XML_NAMESPACE_DOM
);
186 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOOW
), GetXMLToken(XML_N_OOOW
), XML_NAMESPACE_OOOW
);
187 GetNamespaceMap().Add( GetXMLToken(XML_NP_OOOC
), GetXMLToken(XML_N_OOOC
), XML_NAMESPACE_OOOC
);
190 XMLTransformerBase::~XMLTransformerBase() throw ()
194 void SAL_CALL
XMLTransformerBase::startDocument()
196 m_xHandler
->startDocument();
199 void SAL_CALL
XMLTransformerBase::endDocument()
201 m_xHandler
->endDocument();
204 void SAL_CALL
XMLTransformerBase::startElement( const OUString
& rName
,
205 const Reference
< XAttributeList
>& rAttrList
)
207 std::unique_ptr
<SvXMLNamespaceMap
> pRewindMap
;
209 // Process namespace attributes. This must happen before creating the
210 // context, because namespace declaration apply to the element name itself.
211 XMLMutableAttributeList
*pMutableAttrList
= nullptr;
212 Reference
< XAttributeList
> xAttrList( rAttrList
);
213 sal_Int16 nAttrCount
= xAttrList
.is() ? xAttrList
->getLength() : 0;
214 for( sal_Int16 i
=0; i
< nAttrCount
; i
++ )
216 const OUString
& rAttrName
= xAttrList
->getNameByIndex( i
);
217 if( ( rAttrName
.getLength() >= 5 ) &&
218 ( rAttrName
.startsWith( GetXMLToken(XML_XMLNS
) ) ) &&
219 ( rAttrName
.getLength() == 5 || ':' == rAttrName
[5] ) )
223 pRewindMap
= std::move(m_pNamespaceMap
);
224 m_pNamespaceMap
.reset( new SvXMLNamespaceMap( *pRewindMap
) );
226 const OUString
& rAttrValue
= xAttrList
->getValueByIndex( i
);
228 OUString
aPrefix( ( rAttrName
.getLength() == 5 )
230 : rAttrName
.copy( 6 ) );
231 // Add namespace, but only if it is known.
232 sal_uInt16 nKey
= m_pNamespaceMap
->AddIfKnown( aPrefix
, rAttrValue
);
233 // If namespace is unknown, try to match a name with similar
235 if( XML_NAMESPACE_UNKNOWN
== nKey
)
237 OUString
aTestName( rAttrValue
);
238 if( SvXMLNamespaceMap::NormalizeOasisURN( aTestName
) )
239 nKey
= m_pNamespaceMap
->AddIfKnown( aPrefix
, aTestName
);
241 // If that namespace is not known, too, add it as unknown
242 if( XML_NAMESPACE_UNKNOWN
== nKey
)
243 nKey
= m_pNamespaceMap
->Add( aPrefix
, rAttrValue
);
245 const OUString
& rRepName
= m_vReplaceNamespaceMap
.GetNameByKey( nKey
);
246 if( !rRepName
.isEmpty() )
248 if( !pMutableAttrList
)
250 pMutableAttrList
= new XMLMutableAttributeList( xAttrList
);
251 xAttrList
= pMutableAttrList
;
254 pMutableAttrList
->SetValueByIndex( i
, rRepName
);
259 // Get element's namespace and local name.
262 m_pNamespaceMap
->GetKeyByAttrName( rName
, &aLocalName
);
264 // If there are contexts already, call a CreateChildContext at the topmost
265 // context. Otherwise, create a default context.
266 ::rtl::Reference
< XMLTransformerContext
> xContext
;
267 if( !m_vContexts
.empty() )
269 xContext
= m_vContexts
.back()->CreateChildContext( nPrefix
,
276 xContext
= CreateContext( nPrefix
, aLocalName
, rName
);
279 OSL_ENSURE( xContext
.is(), "XMLTransformerBase::startElement: missing context" );
281 xContext
= new XMLTransformerContext( *this, rName
);
283 // Remember old namespace map.
285 xContext
->PutRewindMap( std::move(pRewindMap
) );
287 // Push context on stack.
288 m_vContexts
.push_back( xContext
);
290 // Call a startElement at the new context.
291 xContext
->StartElement( xAttrList
);
294 void SAL_CALL
XMLTransformerBase::endElement( const OUString
&
295 #if OSL_DEBUG_LEVEL > 0
300 if( !m_vContexts
.empty() )
302 // Get topmost context
303 ::rtl::Reference
< XMLTransformerContext
> xContext
= m_vContexts
.back();
305 #if OSL_DEBUG_LEVEL > 0
306 OSL_ENSURE( xContext
->GetQName() == rName
,
307 "XMLTransformerBase::endElement: popped context has wrong lname" );
310 // Call a EndElement at the current context.
311 xContext
->EndElement();
313 // and remove it from the stack.
314 m_vContexts
.pop_back();
316 // Get a namespace map to rewind.
317 std::unique_ptr
<SvXMLNamespaceMap
> pRewindMap
= xContext
->TakeRewindMap();
319 // Delete the current context.
322 // Rewind a namespace map.
325 m_pNamespaceMap
= std::move( pRewindMap
);
330 void SAL_CALL
XMLTransformerBase::characters( const OUString
& rChars
)
332 if( !m_vContexts
.empty() )
334 m_vContexts
.back()->Characters( rChars
);
338 void SAL_CALL
XMLTransformerBase::ignorableWhitespace( const OUString
& rWhitespaces
)
340 m_xHandler
->ignorableWhitespace( rWhitespaces
);
343 void SAL_CALL
XMLTransformerBase::processingInstruction( const OUString
& rTarget
,
344 const OUString
& rData
)
346 m_xHandler
->processingInstruction( rTarget
, rData
);
349 void SAL_CALL
XMLTransformerBase::setDocumentLocator( const Reference
< XLocator
>& )
353 // XExtendedDocumentHandler
354 void SAL_CALL
XMLTransformerBase::startCDATA()
358 void SAL_CALL
XMLTransformerBase::endCDATA()
362 void SAL_CALL
XMLTransformerBase::comment( const OUString
& /*rComment*/ )
366 void SAL_CALL
XMLTransformerBase::allowLineBreak()
370 void SAL_CALL
XMLTransformerBase::unknown( const OUString
& /*rString*/ )
375 void SAL_CALL
XMLTransformerBase::initialize( const Sequence
< Any
>& aArguments
)
377 for( const auto& rArgument
: aArguments
)
379 // use isAssignableFrom instead of comparing the types to
380 // allow XExtendedDocumentHandler instead of XDocumentHandler (used in
381 // writeOasis2OOoLibraryElement in sfx2).
382 // The Any shift operator can't be used to query the type because it
383 // uses queryInterface, and the model also has a XPropertySet interface.
386 if( cppu::UnoType
<XDocumentHandler
>::get().isAssignableFrom( rArgument
.getValueType() ) )
388 m_xHandler
.set( rArgument
, UNO_QUERY
);
389 // Type change to avoid crashing of dynamic_cast
390 if (SvXMLImport
*pFastHandler
= dynamic_cast<SvXMLImport
*>(
391 uno::Reference
< XFastDocumentHandler
>( m_xHandler
, uno::UNO_QUERY
).get() ) )
392 m_xHandler
.set( new SvXMLLegacyToFastDocHandler( pFastHandler
) );
395 // property set to transport data across
396 if( cppu::UnoType
<XPropertySet
>::get().isAssignableFrom( rArgument
.getValueType() ) )
397 m_xPropSet
.set( rArgument
, UNO_QUERY
);
400 if( cppu::UnoType
<css::frame::XModel
>::get().isAssignableFrom( rArgument
.getValueType() ) )
401 mxModel
.set( rArgument
, UNO_QUERY
);
404 if( m_xPropSet
.is() )
407 OUString sRelPath
, sName
;
408 Reference
< XPropertySetInfo
> xPropSetInfo
=
409 m_xPropSet
->getPropertySetInfo();
410 OUString
sPropName( "StreamRelPath" );
411 if( xPropSetInfo
->hasPropertyByName(sPropName
) )
413 aAny
= m_xPropSet
->getPropertyValue(sPropName
);
416 sPropName
= "StreamName";
417 if( xPropSetInfo
->hasPropertyByName(sPropName
) )
419 aAny
= m_xPropSet
->getPropertyValue(sPropName
);
422 if( !sName
.isEmpty() )
424 m_aExtPathPrefix
= "../";
426 // If there is a rel path within a package, then append
427 // additional '../'. If the rel path contains an ':', then it is
428 // an absolute URI (or invalid URI, because zip files don't
429 // permit ':'), and it will be ignored.
430 if( !sRelPath
.isEmpty() )
432 sal_Int32 nColPos
= sRelPath
.indexOf( ':' );
433 OSL_ENSURE( -1 == nColPos
,
434 "StreamRelPath contains ':', absolute URI?" );
438 OUString sTmp
= m_aExtPathPrefix
;
442 m_aExtPathPrefix
+= sTmp
;
443 nPos
= sRelPath
.indexOf( '/', nPos
+ 1 );
452 assert(m_xHandler
.is()); // can't do anything without that
455 static sal_Int16
lcl_getUnit( const OUString
& rValue
)
457 if( rValue
.endsWithIgnoreAsciiCase( "cm" ) )
458 return util::MeasureUnit::CM
;
459 else if ( rValue
.endsWithIgnoreAsciiCase( "mm" ) )
460 return util::MeasureUnit::MM
;
462 return util::MeasureUnit::INCH
;
465 XMLMutableAttributeList
*XMLTransformerBase::ProcessAttrList(
466 Reference
< XAttributeList
>& rAttrList
, sal_uInt16 nActionMap
,
469 XMLMutableAttributeList
*pMutableAttrList
= nullptr;
470 XMLTransformerActions
*pActions
= GetUserDefinedActions( nActionMap
);
471 OSL_ENSURE( pActions
, "go no actions" );
474 sal_Int16 nAttrCount
= rAttrList
.is() ? rAttrList
->getLength() : 0;
475 for( sal_Int16 i
=0; i
< nAttrCount
; ++i
)
477 const OUString
& rAttrName
= rAttrList
->getNameByIndex( i
);
478 const OUString
& rAttrValue
= rAttrList
->getValueByIndex( i
);
480 sal_uInt16 nPrefix
= GetNamespaceMap().GetKeyByAttrName( rAttrName
,
483 XMLTransformerActions::key_type
aKey( nPrefix
, aLocalName
);
484 XMLTransformerActions::const_iterator aIter
=
485 pActions
->find( aKey
);
486 if( aIter
!= pActions
->end() )
488 if( !pMutableAttrList
)
490 pMutableAttrList
= new XMLMutableAttributeList( rAttrList
,
492 rAttrList
= pMutableAttrList
;
495 sal_uInt32 nAction
= (*aIter
).second
.m_nActionType
;
496 bool bRename
= false;
499 case XML_ATACTION_RENAME
:
502 case XML_ATACTION_COPY
:
504 case XML_ATACTION_REMOVE
:
505 case XML_ATACTION_STYLE_DISPLAY_NAME
:
506 pMutableAttrList
->RemoveAttributeByIndex( i
);
510 case XML_ATACTION_RENAME_IN2INCH
:
513 case XML_ATACTION_IN2INCH
:
515 OUString
aAttrValue( rAttrValue
);
516 if( ReplaceSingleInWithInch( aAttrValue
) )
517 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
520 case XML_ATACTION_INS2INCHS
:
522 OUString
aAttrValue( rAttrValue
);
523 if( ReplaceInWithInch( aAttrValue
) )
524 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
527 case XML_ATACTION_RENAME_INCH2IN
:
530 case XML_ATACTION_INCH2IN
:
532 OUString
aAttrValue( rAttrValue
);
533 if( ReplaceSingleInchWithIn( aAttrValue
) )
534 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
537 case XML_ATACTION_INCHS2INS
:
539 OUString
aAttrValue( rAttrValue
);
540 if( ReplaceInchWithIn( aAttrValue
) )
541 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
544 case XML_ATACTION_TWIPS2IN
:
546 OUString
aAttrValue( rAttrValue
);
548 XMLTransformerBase::ReplaceSingleInchWithIn( aAttrValue
);
551 sal_Int16
const nDestUnit
= lcl_getUnit(aAttrValue
);
553 // convert twips value to inch
555 if (::sax::Converter::convertMeasure(nMeasure
,
559 // #i13778#,#i36248# apply correct twip-to-1/100mm
560 nMeasure
= static_cast<sal_Int32
>( nMeasure
>= 0
561 ? ((nMeasure
*127+36)/72)
562 : ((nMeasure
*127-36)/72) );
564 OUStringBuffer aBuffer
;
565 ::sax::Converter::convertMeasure(aBuffer
,
566 nMeasure
, util::MeasureUnit::MM_100TH
,
568 aAttrValue
= aBuffer
.makeStringAndClear();
572 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
575 case XML_ATACTION_RENAME_DECODE_STYLE_NAME_REF
:
578 case XML_ATACTION_DECODE_STYLE_NAME
:
579 case XML_ATACTION_DECODE_STYLE_NAME_REF
:
581 OUString
aAttrValue( rAttrValue
);
582 if( DecodeStyleName(aAttrValue
) )
583 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
586 case XML_ATACTION_ENCODE_STYLE_NAME
:
588 OUString
aAttrValue( rAttrValue
);
589 if( EncodeStyleName(aAttrValue
) )
591 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
592 OUString
aNewAttrQName(
593 GetNamespaceMap().GetQNameByKey(
595 ::xmloff::token::GetXMLToken(
596 XML_DISPLAY_NAME
) ) );
597 pMutableAttrList
->AddAttribute( aNewAttrQName
,
602 case XML_ATACTION_RENAME_ENCODE_STYLE_NAME_REF
:
605 case XML_ATACTION_ENCODE_STYLE_NAME_REF
:
607 OUString
aAttrValue( rAttrValue
);
608 if( EncodeStyleName(aAttrValue
) )
609 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
612 case XML_ATACTION_RENAME_NEG_PERCENT
:
615 case XML_ATACTION_NEG_PERCENT
:
617 OUString
aAttrValue( rAttrValue
);
618 if( NegPercent( aAttrValue
) )
619 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
622 case XML_ATACTION_RENAME_ADD_NAMESPACE_PREFIX
:
625 case XML_ATACTION_ADD_NAMESPACE_PREFIX
:
627 OUString
aAttrValue( rAttrValue
);
628 sal_uInt16 nValPrefix
=
629 static_cast<sal_uInt16
>(
630 bRename
? (*aIter
).second
.m_nParam2
631 : (*aIter
).second
.m_nParam1
);
632 AddNamespacePrefix( aAttrValue
, nValPrefix
);
633 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
636 case XML_ATACTION_ADD_APP_NAMESPACE_PREFIX
:
638 OUString
aAttrValue( rAttrValue
);
639 sal_uInt16 nValPrefix
=
640 static_cast<sal_uInt16
>((*aIter
).second
.m_nParam1
);
641 if( IsXMLToken( GetClass(), XML_SPREADSHEET
) )
642 nValPrefix
= XML_NAMESPACE_OOOC
;
643 else if( IsXMLToken( GetClass(), XML_TEXT
) )
644 nValPrefix
= XML_NAMESPACE_OOOW
;
645 AddNamespacePrefix( aAttrValue
, nValPrefix
);
646 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
649 case XML_ATACTION_RENAME_REMOVE_NAMESPACE_PREFIX
:
652 case XML_ATACTION_REMOVE_NAMESPACE_PREFIX
:
654 OUString
aAttrValue( rAttrValue
);
655 sal_uInt16 nValPrefix
=
656 static_cast<sal_uInt16
>(
657 bRename
? (*aIter
).second
.m_nParam2
658 : (*aIter
).second
.m_nParam1
);
659 if( RemoveNamespacePrefix( aAttrValue
, nValPrefix
) )
660 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
663 case XML_ATACTION_REMOVE_ANY_NAMESPACE_PREFIX
:
665 OUString
aAttrValue( rAttrValue
);
666 if( RemoveNamespacePrefix( aAttrValue
) )
667 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
670 case XML_ATACTION_URI_OOO
:
672 OUString
aAttrValue( rAttrValue
);
673 if( ConvertURIToOASIS( aAttrValue
,
674 static_cast< bool >((*aIter
).second
.m_nParam1
)))
675 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
678 case XML_ATACTION_URI_OASIS
:
680 OUString
aAttrValue( rAttrValue
);
681 if( ConvertURIToOOo( aAttrValue
,
682 static_cast< bool >((*aIter
).second
.m_nParam1
)))
683 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
686 case XML_ATACTION_RENAME_ATTRIBUTE
:
688 OUString
aAttrValue( rAttrValue
);
689 RenameAttributeValue(
691 (*aIter
).second
.m_nParam1
,
692 (*aIter
).second
.m_nParam2
,
693 (*aIter
).second
.m_nParam3
);
694 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
697 case XML_ATACTION_RNG2ISO_DATETIME
:
699 OUString
aAttrValue( rAttrValue
);
700 if( ConvertRNGDateTimeToISO( aAttrValue
))
701 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
704 case XML_ATACTION_RENAME_RNG2ISO_DATETIME
:
706 OUString
aAttrValue( rAttrValue
);
707 if( ConvertRNGDateTimeToISO( aAttrValue
))
708 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
712 case XML_ATACTION_IN2TWIPS
:
714 OUString
aAttrValue( rAttrValue
);
715 XMLTransformerBase::ReplaceSingleInWithInch( aAttrValue
);
719 sal_Int16
const nDestUnit
= lcl_getUnit(aAttrValue
);
721 // convert inch value to twips and export as faked inch
723 if (::sax::Converter::convertMeasure(nMeasure
,
727 // #i13778#,#i36248#/ apply correct 1/100mm-to-twip conversion
728 nMeasure
= static_cast<sal_Int32
>( nMeasure
>= 0
729 ? ((nMeasure
*72+63)/127)
730 : ((nMeasure
*72-63)/127) );
732 OUStringBuffer aBuffer
;
733 ::sax::Converter::convertMeasure( aBuffer
,
734 nMeasure
, util::MeasureUnit::MM_100TH
,
736 aAttrValue
= aBuffer
.makeStringAndClear();
740 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
743 case XML_ATACTION_SVG_WIDTH_HEIGHT_OOO
:
745 OUString
aAttrValue( rAttrValue
);
746 ReplaceSingleInchWithIn( aAttrValue
);
748 sal_Int16
const nDestUnit
= lcl_getUnit( aAttrValue
);
751 if (::sax::Converter::convertMeasure(nMeasure
,
757 else if( nMeasure
< 0 )
761 OUStringBuffer aBuffer
;
762 ::sax::Converter::convertMeasure(aBuffer
, nMeasure
,
763 util::MeasureUnit::MM_100TH
, nDestUnit
);
764 aAttrValue
= aBuffer
.makeStringAndClear();
767 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
770 case XML_ATACTION_SVG_WIDTH_HEIGHT_OASIS
:
772 OUString
aAttrValue( rAttrValue
);
773 ReplaceSingleInWithInch( aAttrValue
);
775 sal_Int16
const nDestUnit
= lcl_getUnit( aAttrValue
);
778 if (::sax::Converter::convertMeasure(nMeasure
,
784 else if( nMeasure
< 0 )
788 OUStringBuffer aBuffer
;
789 ::sax::Converter::convertMeasure(aBuffer
, nMeasure
,
790 util::MeasureUnit::MM_100TH
, nDestUnit
);
791 aAttrValue
= aBuffer
.makeStringAndClear();
794 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
797 case XML_ATACTION_DECODE_ID
:
799 const sal_Int32 nLen
= rAttrValue
.getLength();
800 OUStringBuffer aBuffer
;
803 for( pos
= 0; pos
< nLen
; pos
++ )
805 sal_Unicode c
= rAttrValue
[pos
];
806 if( (c
>= '0') && (c
<= '9') )
809 aBuffer
.append( static_cast<sal_Int32
>(c
) );
812 pMutableAttrList
->SetValueByIndex( i
, aBuffer
.makeStringAndClear() );
815 // #i50322# - special handling for the
816 // transparency of writer background graphics.
817 case XML_ATACTION_WRITER_BACK_GRAPHIC_TRANSPARENCY
:
819 // determine, if it's the transparency of a document style
820 XMLTransformerContext
* pFirstContext
= m_vContexts
[0].get();
821 OUString aFirstContextLocalName
;
822 /* sal_uInt16 nFirstContextPrefix = */
823 GetNamespaceMap().GetKeyByAttrName( pFirstContext
->GetQName(),
824 &aFirstContextLocalName
);
825 bool bIsDocumentStyle(
826 ::xmloff::token::IsXMLToken( aFirstContextLocalName
,
827 XML_DOCUMENT_STYLES
) );
828 // no conversion of transparency value for document
829 // styles, because former OpenOffice.org version writes
830 // writes always a transparency value of 100% and doesn't
831 // read the value. Thus, it's interpreted as 0%
832 if ( !bIsDocumentStyle
)
834 OUString
aAttrValue( rAttrValue
);
835 NegPercent(aAttrValue
);
836 pMutableAttrList
->SetValueByIndex( i
, aAttrValue
);
841 case XML_ATACTION_SHAPEID
:
843 OUString sNewValue
= "shape" + rAttrValue
;
844 pMutableAttrList
->SetValueByIndex( i
, sNewValue
);
849 OSL_ENSURE( false, "unknown action" );
855 OUString
aNewAttrQName(
856 GetNamespaceMap().GetQNameByKey(
857 (*aIter
).second
.GetQNamePrefixFromParam1(),
858 ::xmloff::token::GetXMLToken(
859 (*aIter
).second
.GetQNameTokenFromParam1()) ) );
860 pMutableAttrList
->RenameAttributeByIndex( i
,
867 return pMutableAttrList
;
870 bool XMLTransformerBase::ReplaceSingleInchWithIn( OUString
& rValue
)
873 sal_Int32 nPos
= rValue
.getLength();
874 while( nPos
&& rValue
[nPos
-1] <= ' ' )
877 ('c'==rValue
[nPos
-2] || 'C'==rValue
[nPos
-2]) &&
878 ('h'==rValue
[nPos
-1] || 'H'==rValue
[nPos
-1]) )
880 rValue
=rValue
.copy( 0, nPos
-2 );
887 bool XMLTransformerBase::ReplaceInchWithIn( OUString
& rValue
)
891 while( nPos
< rValue
.getLength()-3 )
893 sal_Unicode c
= rValue
[nPos
];
894 if( 'i'==c
|| 'I'==c
)
897 if( (c
>= '0' && c
<= '9') || '.' == c
)
900 if( 'n'==c
|| 'N'==c
)
903 if( 'c'==c
|| 'C'==c
)
906 if( 'h'==c
|| 'H'==c
)
908 rValue
= rValue
.replaceAt( nPos
,
909 4, GetXMLToken(XML_UNIT_INCH
) );
924 bool XMLTransformerBase::ReplaceSingleInWithInch( OUString
& rValue
)
928 sal_Int32 nPos
= rValue
.getLength();
929 while( nPos
&& rValue
[nPos
-1] <= ' ' )
932 ('i'==rValue
[nPos
-2] ||
933 'I'==rValue
[nPos
-2]) &&
934 ('n'==rValue
[nPos
-1] ||
935 'N'==rValue
[nPos
-1]) )
938 rValue
= rValue
.replaceAt( nPos
, rValue
.getLength() - nPos
,
939 GetXMLToken(XML_INCH
) );
946 bool XMLTransformerBase::ReplaceInWithInch( OUString
& rValue
)
950 while( nPos
< rValue
.getLength()-1 )
952 sal_Unicode c
= rValue
[nPos
];
953 if( 'i'==c
|| 'I'==c
)
956 if( (c
>= '0' && c
<= '9') || '.' == c
)
959 if( 'n'==c
|| 'N'==c
)
961 rValue
= rValue
.replaceAt( nPos
,
962 2, GetXMLToken(XML_INCH
) );
975 bool XMLTransformerBase::EncodeStyleName( OUString
& rName
) const
977 static const sal_Char aHexTab
[] = "0123456789abcdef";
979 bool bEncoded
= false;
981 sal_Int32 nLen
= rName
.getLength();
982 OUStringBuffer
aBuffer( nLen
);
984 for( sal_Int32 i
= 0; i
< nLen
; i
++ )
986 sal_Unicode c
= rName
[i
];
987 bool bValidChar
= false;
991 (c
>= 0x0041 && c
<= 0x005a) ||
992 (c
>= 0x0061 && c
<= 0x007a) ||
993 (c
>= 0x00c0 && c
<= 0x00d6) ||
994 (c
>= 0x00d8 && c
<= 0x00f6) ||
995 (c
>= 0x00f8 && c
<= 0x00ff) ||
996 ( i
> 0 && ( (c
>= 0x0030 && c
<= 0x0039) ||
997 c
== 0x00b7 || c
== '-' || c
== '.') );
1001 if( (c
>= 0xf900U
&& c
<= 0xfffeU
) ||
1002 (c
>= 0x20ddU
&& c
<= 0x20e0U
))
1006 else if( (c
>= 0x02bbU
&& c
<= 0x02c1U
) || c
== 0x0559 ||
1007 c
== 0x06e5 || c
== 0x06e6 )
1011 else if( c
== 0x0387 )
1017 if( !xCharClass
.is() )
1019 const_cast < XMLTransformerBase
* >(this)
1020 ->xCharClass
= CharacterClassification::create( comphelper::getProcessComponentContext() );
1022 sal_Int16 nType
= xCharClass
->getType( rName
, i
);
1026 case UnicodeType::UPPERCASE_LETTER
: // Lu
1027 case UnicodeType::LOWERCASE_LETTER
: // Ll
1028 case UnicodeType::TITLECASE_LETTER
: // Lt
1029 case UnicodeType::OTHER_LETTER
: // Lo
1030 case UnicodeType::LETTER_NUMBER
: // Nl
1033 case UnicodeType::NON_SPACING_MARK
: // Ms
1034 case UnicodeType::ENCLOSING_MARK
: // Me
1035 case UnicodeType::COMBINING_SPACING_MARK
: //Mc
1036 case UnicodeType::MODIFIER_LETTER
: // Lm
1037 case UnicodeType::DECIMAL_DIGIT_NUMBER
: // Nd
1045 aBuffer
.append( c
);
1049 aBuffer
.append( '_' );
1051 aBuffer
.append( static_cast< sal_Unicode
>(
1052 aHexTab
[ (c
>> 12) & 0x0f ] ) );
1054 aBuffer
.append( static_cast< sal_Unicode
>(
1055 aHexTab
[ (c
>> 8) & 0x0f ] ) );
1057 aBuffer
.append( static_cast< sal_Unicode
>(
1058 aHexTab
[ (c
>> 4) & 0x0f ] ) );
1059 aBuffer
.append( static_cast< sal_Unicode
>(
1060 aHexTab
[ c
& 0x0f ] ) );
1061 aBuffer
.append( '_' );
1066 if( aBuffer
.getLength() > (1<<15)-1 )
1070 rName
= aBuffer
.makeStringAndClear();
1074 bool XMLTransformerBase::DecodeStyleName( OUString
& rName
)
1076 bool bEncoded
= false;
1078 sal_Int32 nLen
= rName
.getLength();
1079 OUStringBuffer
aBuffer( nLen
);
1081 bool bWithinHex
= false;
1082 sal_Unicode cEnc
= 0;
1083 for( sal_Int32 i
= 0; i
< nLen
; i
++ )
1085 sal_Unicode c
= rName
[i
];
1090 aBuffer
.append( cEnc
);
1097 bWithinHex
= !bWithinHex
;
1099 else if( bWithinHex
)
1102 if( c
>= '0' && c
<= '9' )
1106 else if( c
>= 'a' && c
<= 'f' )
1108 cDigit
= c
- 'a' + 10;
1110 else if( c
>= 'A' && c
<= 'F' )
1112 cDigit
= c
- 'A' + 10;
1120 cEnc
= (cEnc
<< 4) + cDigit
;
1124 aBuffer
.append( c
);
1129 rName
= aBuffer
.makeStringAndClear();
1133 bool XMLTransformerBase::NegPercent( OUString
& rValue
)
1140 sal_Int32 nLen
= rValue
.getLength();
1143 while( nPos
< nLen
&& ' ' == rValue
[nPos
] )
1146 if( nPos
< nLen
&& '-' == rValue
[nPos
] )
1153 while( nPos
< nLen
&&
1154 '0' <= rValue
[nPos
] &&
1155 '9' >= rValue
[nPos
] )
1157 // TODO: check overflow!
1159 nVal
+= (rValue
[nPos
] - '0');
1162 if( nPos
< nLen
&& '.' == rValue
[nPos
] )
1167 while( nPos
< nLen
&&
1168 '0' <= rValue
[nPos
] &&
1169 '9' >= rValue
[nPos
] )
1171 // TODO: check overflow!
1173 nVal
+= ( static_cast<double>(rValue
[nPos
] - '0') / nDiv
);
1179 while( nPos
< nLen
&& ' ' == rValue
[nPos
] )
1182 if( nPos
< nLen
&& '%' == rValue
[nPos
] )
1188 sal_Int32 nIntVal
= 100 - static_cast<sal_Int32
>( nVal
);
1190 rValue
= OUString::number(nIntVal
) + "%";
1198 void XMLTransformerBase::AddNamespacePrefix( OUString
& rName
,
1199 sal_uInt16 nPrefix
) const
1201 rName
= GetNamespaceMap().GetQNameByKey( nPrefix
, rName
, false );
1204 bool XMLTransformerBase::RemoveNamespacePrefix( OUString
& rName
,
1205 sal_uInt16 nPrefixOnly
) const
1207 OUString aLocalName
;
1208 sal_uInt16 nPrefix
=
1209 GetNamespaceMap().GetKeyByAttrName_( rName
, &aLocalName
);
1210 bool bRet
= XML_NAMESPACE_UNKNOWN
!= nPrefix
&&
1211 (USHRT_MAX
== nPrefixOnly
|| nPrefix
== nPrefixOnly
);
1218 bool XMLTransformerBase::ConvertURIToOASIS( OUString
& rURI
,
1219 bool bSupportPackage
) const
1222 if( !m_aExtPathPrefix
.isEmpty() && !rURI
.isEmpty() )
1229 // for package URIs, the '#' has to be removed
1230 if( bSupportPackage
)
1232 rURI
= rURI
.copy( 1 );
1237 // no rel path; nothing to do
1240 // a rel path; to keep URI simple, remove './', if there
1242 if( rURI
.getLength() > 1 && '/' == rURI
[1] )
1244 rURI
= rURI
.copy( 2 );
1249 // check for a RFC2396 schema
1253 sal_Int32 nLen
= rURI
.getLength();
1254 while( nPos
< nLen
)
1256 switch( rURI
[nPos
] )
1259 // a relative path segment
1260 nPos
= nLen
; // leave loop
1265 nPos
= nLen
; // leave loop
1268 // we don't care about any other characters
1278 rURI
= m_aExtPathPrefix
+ rURI
;
1286 bool XMLTransformerBase::ConvertURIToOOo( OUString
& rURI
,
1287 bool bSupportPackage
) const
1290 if( !rURI
.isEmpty() )
1292 bool bPackage
= false;
1296 // no rel path; nothing to do
1300 if( rURI
.startsWith( m_aExtPathPrefix
) )
1302 // an external URI; remove '../'
1303 rURI
= rURI
.copy( m_aExtPathPrefix
.getLength() );
1312 // check for a RFC2396 schema
1316 sal_Int32 nLen
= rURI
.getLength();
1317 while( nPos
< nLen
)
1319 switch( rURI
[nPos
] )
1322 // a relative path segment within the package
1323 nPos
= nLen
; // leave loop
1328 nPos
= nLen
; // leave loop
1331 // we don't care about any other characters
1339 if( bPackage
&& bSupportPackage
)
1341 OUString
sTmp( '#' );
1342 if( rURI
.startsWith( "./" ) )
1343 rURI
= rURI
.copy( 2 );
1353 bool XMLTransformerBase::RenameAttributeValue(
1354 OUString
& rOutAttributeValue
,
1359 return ( lcl_ConvertAttr( rOutAttributeValue
, nParam1
) ||
1360 lcl_ConvertAttr( rOutAttributeValue
, nParam2
) ||
1361 lcl_ConvertAttr( rOutAttributeValue
, nParam3
) );
1365 bool XMLTransformerBase::ConvertRNGDateTimeToISO( OUString
& rDateTime
)
1367 if( !rDateTime
.isEmpty() &&
1368 rDateTime
.indexOf( '.' ) != -1 )
1370 rDateTime
= rDateTime
.replace( '.', ',');
1377 XMLTokenEnum
XMLTransformerBase::GetToken( const OUString
& rStr
) const
1379 XMLTransformerTokenMap::const_iterator aIter
=
1380 m_TokenMap
.find( rStr
);
1381 if( aIter
== m_TokenMap
.end() )
1382 return XML_TOKEN_END
;
1384 return (*aIter
).second
;
1388 const XMLTransformerContext
*XMLTransformerBase::GetCurrentContext() const
1390 OSL_ENSURE( !m_vContexts
.empty(), "empty stack" );
1393 return m_vContexts
.empty() ? nullptr : m_vContexts
.back().get();
1396 const XMLTransformerContext
*XMLTransformerBase::GetAncestorContext(
1397 sal_uInt32 n
) const
1399 auto nSize
= m_vContexts
.size();
1401 OSL_ENSURE( nSize
> n
+ 2 , "invalid context" );
1403 return nSize
> n
+ 2 ? m_vContexts
[nSize
- (n
+ 2)].get() : nullptr;
1406 bool XMLTransformerBase::isWriter() const
1408 Reference
< XServiceInfo
> xSI( mxModel
, UNO_QUERY
);
1410 ( xSI
->supportsService("com.sun.star.text.TextDocument") ||
1411 xSI
->supportsService("com.sun.star.text.WebDocument") ||
1412 xSI
->supportsService("com.sun.star.text.GlobalDocument") );
1415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */