/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
void lclProcessAttribs( OStringBuffer& rBuffer, constchar* pcBeg, constchar* pcEnd )
{ /* Map attribute names to char-pointer of all attributes. This map is used to find multiple occurrences of attributes with the same name. The
mapped pointers are used as map key in the next map below. */ typedef ::std::map< OString, constchar* > AttributeNameMap;
AttributeNameMap aAttributeNames;
/* Map the char-pointers of all attributes to the full attribute definition
string. This preserves the original order of the used attributes. */ typedef ::std::map< constchar*, OString > AttributeDataMap;
AttributeDataMap aAttributes;
bool bOk = true; constchar* pcNameBeg = pcBeg; while( bOk && (pcNameBeg < pcEnd) )
{ // pcNameBeg points to begin of attribute name, find equality sign constchar* pcEqualSign = lclFindCharacter( pcNameBeg, pcEnd, '=' );
bOk = (pcEqualSign < pcEnd); if (bOk)
{ // find end of attribute name (ignore whitespace between name and equality sign) constchar* pcNameEnd = lclTrimWhiteSpaceFromEnd( pcNameBeg, pcEqualSign );
bOk = (pcNameBeg < pcNameEnd); if( bOk )
{ // find begin of attribute value (must be single or double quote) constchar* pcValueBeg = lclFindNonWhiteSpace( pcEqualSign + 1, pcEnd );
bOk = (pcValueBeg < pcEnd) && ((*pcValueBeg == '\'') || (*pcValueBeg == '"')); if( bOk )
{ // find end of attribute value (matching quote character) constchar* pcValueEnd = lclFindCharacter( pcValueBeg + 1, pcEnd, *pcValueBeg );
bOk = (pcValueEnd < pcEnd); if( bOk )
{
++pcValueEnd;
OString aAttribName( pcNameBeg, static_cast< sal_Int32 >( pcNameEnd - pcNameBeg ) );
OString aAttribData( pcNameBeg, static_cast< sal_Int32 >( pcValueEnd - pcNameBeg ) ); // search for an existing attribute with the same name
AttributeNameMap::iterator aIt = aAttributeNames.find( aAttribName ); // remove its definition from the data map if( aIt != aAttributeNames.end() )
aAttributes.erase( aIt->second ); // insert the attribute into both maps
aAttributeNames[ aAttribName ] = pcNameBeg;
aAttributes[ pcNameBeg ] = aAttribData; // continue with next attribute (skip whitespace after this attribute)
pcNameBeg = pcValueEnd; if( pcNameBeg < pcEnd )
{
bOk = lclIsWhiteSpace( *pcNameBeg ); if( bOk )
pcNameBeg = lclFindNonWhiteSpace( pcNameBeg + 1, pcEnd );
}
}
}
}
}
}
// if no error has occurred, build the resulting attribute list if( bOk ) for (autoconst& attrib : aAttributes)
rBuffer.append( " " + attrib.second ); // on error, just append the complete passed string else
lclAppendToBuffer( rBuffer, pcBeg, pcEnd );
}
void lclProcessElement( OStringBuffer& rBuffer, const OString& rElement )
{ // check that passed string starts and ends with the brackets of an XML element
sal_Int32 nElementLen = rElement.getLength(); if( nElementLen == 0 ) return;
// check start elements and simple elements for repeated attributes elseif( pcOpen[ 1 ] != '/' )
{ // find positions of text content inside brackets, exclude '/' in '<simpleelement/>' constchar* pcContentBeg = pcOpen + 1; bool bIsEmptyElement = pcClose[ -1 ] == '/'; constchar* pcContentEnd = bIsEmptyElement ? (pcClose - 1) : pcClose; // append opening bracket and element name to buffer constchar* pcWhiteSpace = lclFindWhiteSpace( pcContentBeg, pcContentEnd );
lclAppendToBuffer( rBuffer, pcOpen, pcWhiteSpace ); // find begin of attributes, and process all attributes constchar* pcAttribBeg = lclFindNonWhiteSpace( pcWhiteSpace, pcContentEnd ); if( pcAttribBeg < pcContentEnd )
lclProcessAttribs( rBuffer, pcAttribBeg, pcContentEnd ); // close the element if( bIsEmptyElement )
rBuffer.append( '/' );
rBuffer.append( '>' );
}
// append end elements without further processing else
{
rBuffer.append( rElement );
}
}
bool lclProcessCharacters( OStringBuffer& rBuffer, const OString& rChars )
{ /* MSO has a very weird way to store and handle whitespaces. The stream may contain lots of spaces, tabs, and newlines which have to be handled as single space character. This will be done in this function.
If the element text contains a literal line break, it will be stored as <br> tag (without matching </br> element). This input stream wrapper will replace this element with a literal LF character (see below).
A single space character for its own is stored as is. Example: The element <font> </font> represents a single space character. The XML parser will ignore this space character completely without issuing a 'characters' event. The VML import filter implementation has to react on this case manually.
A single space character following another character is stored literally and must not be stripped away here. Example: The element <font>abc </font> contains the three letters a, b, and c, followed by a space character.
Consecutive space characters, or a leading single space character, are stored in a <span> element. If there are N space characters (N > 1), then the <span> element contains exactly (N-1) NBSP (non-breaking space) characters, followed by a regular space character. Examples: The element <font><span style='mso-spacerun:yes'>\xA0\xA0\xA0 </span></font> represents 4 consecutive space characters. Has to be handled by the implementation. The element <font><span style='mso-spacerun:yes'> abc</span></font> represents a space characters followed by the letters a, b, c. These strings have to be handled by the VML import filter implementation.
*/
// passed string ends with the leading opening bracket of an XML element constchar* pcBeg = rChars.getStr(); constchar* pcEnd = pcBeg + rChars.getLength(); bool bHasBracket = (pcBeg < pcEnd) && (pcEnd[ -1 ] == '<'); if( bHasBracket ) --pcEnd;
void InputStream::updateBuffer()
{ while( (mnBufferPos >= maBuffer.getLength()) && !mxTextStrm->isEOF() )
{ // collect new contents in a string buffer
OStringBuffer aBuffer;
// read and process characters until the opening bracket of the next XML element
OString aChars = readToElementBegin(); bool bHasOpeningBracket = lclProcessCharacters( aBuffer, aChars );
// read and process characters until (and including) closing bracket (an XML element)
OSL_ENSURE( bHasOpeningBracket || mxTextStrm->isEOF(), "InputStream::updateBuffer - missing opening bracket of XML element" ); if( bHasOpeningBracket && !mxTextStrm->isEOF() )
{ // read the element text (add the leading opening bracket manually)
OString aElement = "<" + readToElementEnd(); // check for CDATA part, starting with '<![CDATA[' if( aElement.match( gaOpeningCData ) )
{ // search the end tag ']]>' while( ((aElement.getLength() < gaClosingCData.getLength()) || !aElement.endsWith( gaClosingCData )) && !mxTextStrm->isEOF() )
aElement += readToElementEnd(); // copy the entire CDATA part
aBuffer.append( aElement );
} else
{ // no CDATA part - process the contents of the element
lclProcessElement( aBuffer, aElement );
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.