diff options
Diffstat (limited to 'sw/source/filter/html/parcss1.cxx')
-rw-r--r-- | sw/source/filter/html/parcss1.cxx | 1429 |
1 files changed, 1429 insertions, 0 deletions
diff --git a/sw/source/filter/html/parcss1.cxx b/sw/source/filter/html/parcss1.cxx new file mode 100644 index 000000000000..9177d5106ed4 --- /dev/null +++ b/sw/source/filter/html/parcss1.cxx @@ -0,0 +1,1429 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: parcss1.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sw.hxx" + + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <limits.h> +#include <rtl/ustrbuf.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <svtools/htmltokn.h> + +#include "css1kywd.hxx" +#include "parcss1.hxx" + + +// Loop-Check: Um Endlos-Schleifen zu vermeiden, wird in jeder +// Schalife geprueft, ob ein Fortschritt in der Eingabe-Position +// stattgefunden hat +#define LOOP_CHECK + +#ifdef LOOP_CHECK + +#define LOOP_CHECK_DECL \ + xub_StrLen nOldInPos = STRING_MAXLEN; +#define LOOP_CHECK_RESTART \ + nOldInPos = STRING_MAXLEN; +#define LOOP_CHECK_CHECK( where ) \ + DBG_ASSERT( nOldInPos!=nInPos || cNextCh==(sal_Unicode)EOF, where ); \ + if( nOldInPos==nInPos && cNextCh!=(sal_Unicode)EOF ) \ + break; \ + else \ + nOldInPos = nInPos; + +#else + +#define LOOP_CHECK_DECL +#define LOOP_CHECK_RESTART +#define LOOP_CHECK_CHECK( where ) + +#endif + + + +const sal_Int32 MAX_LEN = 1024; + +/* */ + +void CSS1Parser::InitRead( const String& rIn ) +{ + nlLineNr = 0; + nlLinePos = 0; + + bWhiteSpace = TRUE; // Wenn noch nichts gelesen wurde ist das wie WS + bEOF = FALSE; + eState = CSS1_PAR_WORKING; + nValue = 0.; + + aIn = rIn; + nInPos = 0; + cNextCh = GetNextChar(); + nToken = GetNextToken(); +} + +sal_Unicode CSS1Parser::GetNextChar() +{ + if( nInPos >= aIn.Len() ) + { + bEOF = TRUE; + return (sal_Unicode)EOF; + } + + sal_Unicode c = aIn.GetChar( nInPos ); + nInPos++; + + if( c == '\n' ) + { + IncLineNr(); + SetLinePos( 1L ); + } + else + IncLinePos(); + + return c; +} + +/* */ + +// Diese Funktion realisiert den in +// +// http://www.w3.orh/pub/WWW/TR/WD-css1.html +// bzw. http://www.w3.orh/pub/WWW/TR/WD-css1-960220.html +// +// beschriebenen Scanner fuer CSS1. Es handelt sich um eine direkte +// Umsetzung der dort beschriebenen Lex-Grammatik +// +CSS1Token CSS1Parser::GetNextToken() +{ + CSS1Token nRet = CSS1_NULL; + aToken.Erase(); + + do { + // Merken, ob davor White-Space gelesen wurde + BOOL bPrevWhiteSpace = bWhiteSpace; + bWhiteSpace = FALSE; + + BOOL bNextCh = TRUE; + switch( cNextCh ) + { + case '/': // COMMENT | '/' + { + cNextCh = GetNextChar(); + if( '*' == cNextCh ) + { + // COMMENT + cNextCh = GetNextChar(); + + BOOL bAsterix = FALSE; + while( !(bAsterix && '/'==cNextCh) && !IsEOF() ) + { + bAsterix = ('*'==cNextCh); + cNextCh = GetNextChar(); + } + } + else + { + // '/' + bNextCh = FALSE; + nRet = CSS1_SLASH; + } + } + break; + + case '@': // '@import' | '@XXX' + { + cNextCh = GetNextChar(); + if( ('A' <= cNextCh && cNextCh <= 'Z') || + ('a' <= cNextCh && cNextCh <= 'z') ) + { + // den naechsten Identifer scannen + ::rtl::OUStringBuffer sTmpBuffer( 32L ); + do { + sTmpBuffer.append( cNextCh ); + cNextCh = GetNextChar(); + } while( ('A' <= cNextCh && cNextCh <= 'Z') || + ('a' <= cNextCh && cNextCh <= 'z') || + ('0' <= cNextCh && cNextCh <= '9') || + '-'==cNextCh && !IsEOF() ); + + aToken += String(sTmpBuffer.makeStringAndClear()); + + // und schauen, ob wir ihn kennen + switch( aToken.GetChar(0) ) + { + case 'i': + case 'I': + if( aToken.EqualsIgnoreCaseAscii(sCSS1_import) ) + nRet = CSS1_IMPORT_SYM; + break; +// /Feature: PrintExt + case 'p': + case 'P': + if( aToken.EqualsIgnoreCaseAscii(sCSS1_page) ) + nRet = CSS1_PAGE_SYM; + break; +// /Feature: PrintExt + } + + // Fehlerbehandlung: '@ident' und alles bis + // zu einem Semikolon der dem Ende des folgenden + // Blocks ignorieren + if( CSS1_NULL==nRet ) + { + aToken.Erase(); + USHORT nBlockLvl = 0; + sal_Unicode cQuoteCh = 0; + BOOL bDone = FALSE, bEscape = FALSE; + while( !bDone && !IsEOF() ) + { + BOOL bOldEscape = bEscape; + bEscape = FALSE; + switch( cNextCh ) + { + case '{': + if( !cQuoteCh && !bOldEscape ) + nBlockLvl++;; + break; + case ';': + if( !cQuoteCh && !bOldEscape ) + bDone = nBlockLvl==0; + break; + case '}': + if( !cQuoteCh && !bOldEscape ) + bDone = --nBlockLvl==0; + break; + case '\"': + case '\'': + if( !bOldEscape ) + { + if( cQuoteCh ) + { + if( cQuoteCh == cNextCh ) + cQuoteCh = 0; + } + else + { + cQuoteCh = cNextCh; + } + } + break; + case '\\': + if( !bOldEscape ) + bEscape = TRUE; + break; + } + cNextCh = GetNextChar(); + } + } + + bNextCh = FALSE; + } + } + break; + + case '!': // '!' 'legal' | '!' 'important' | syntax error + { + // White Space ueberlesen + cNextCh = GetNextChar(); + while( ( ' ' == cNextCh || + (cNextCh >= 0x09 && cNextCh <= 0x0d) ) && !IsEOF() ) + { + bWhiteSpace = TRUE; + cNextCh = GetNextChar(); + } + + if( 'i'==cNextCh || 'I'==cNextCh) + { + // den naechsten Identifer scannen + ::rtl::OUStringBuffer sTmpBuffer( 32L ); + do { + sTmpBuffer.append( cNextCh ); + cNextCh = GetNextChar(); + } while( ('A' <= cNextCh && cNextCh <= 'Z') || + ('a' <= cNextCh && cNextCh <= 'z') || + ('0' <= cNextCh && cNextCh <= '9') || + '-' == cNextCh && !IsEOF() ); + + aToken += String(sTmpBuffer.makeStringAndClear()); + + if( ('i'==aToken.GetChar(0) || 'I'==aToken.GetChar(0)) && + aToken.EqualsIgnoreCaseAscii(sCSS1_important) ) + { + // '!' 'important' + nRet = CSS1_IMPORTANT_SYM; + } + else + { + // Fehlerbehandlung: '!' ignorieren, IDENT nicht + nRet = CSS1_IDENT; + } + + bWhiteSpace = FALSE; + bNextCh = FALSE; + } + else + { + // Fehlerbehandlung: '!' ignorieren + bNextCh = FALSE; + } + } + break; + + case '\"': + case '\'': // STRING + { + // \... geht noch nicht!!! + sal_Unicode cQuoteChar = cNextCh; + cNextCh = GetNextChar(); + + ::rtl::OUStringBuffer sTmpBuffer( MAX_LEN ); + do { + sTmpBuffer.append( cNextCh ); + cNextCh = GetNextChar(); + } while( cQuoteChar != cNextCh && !IsEOF() ); + + aToken += String(sTmpBuffer.makeStringAndClear()); + + nRet = CSS1_STRING; + } + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': // NUMBER | PERCENTAGE | LENGTH + { + // die aktuelle Position retten + xub_StrLen nInPosSave = nInPos; + sal_Unicode cNextChSave = cNextCh; + sal_uInt32 nlLineNrSave = nlLineNr; + sal_uInt32 nlLinePosSave = nlLinePos; + BOOL bEOFSave = bEOF; + + // erstmal versuchen eine Hex-Zahl zu scannen + ::rtl::OUStringBuffer sTmpBuffer( 16 ); + do { + sTmpBuffer.append( cNextCh ); + cNextCh = GetNextChar(); + } while( sTmpBuffer.getLength() < 7 && + ( ('0'<=cNextCh && '9'>=cNextCh) || + ('A'<=cNextCh && 'F'>=cNextCh) || + ('a'<=cNextCh && 'f'>=cNextCh) ) && + !IsEOF() ); + + if( sTmpBuffer.getLength()==6 ) + { + // wir haben eine hexadezimale Farbe gefunden + aToken += String(sTmpBuffer.makeStringAndClear()); + nRet = CSS1_HEXCOLOR; + bNextCh = FALSE; + + break; + } + + // sonst versuchen wir es mit einer Zahl + nInPos = nInPosSave; + cNextCh = cNextChSave; + nlLineNr = nlLineNrSave; + nlLinePos = nlLinePosSave; + bEOF = bEOFSave; + + // erstmal die Zahl scannen + sTmpBuffer.setLength( 0L ); + do { + sTmpBuffer.append( cNextCh ); + cNextCh = GetNextChar(); + } while( (('0'<=cNextCh && '9'>=cNextCh) || '.'==cNextCh) && + !IsEOF() ); + + aToken += String(sTmpBuffer.makeStringAndClear()); + nValue = aToken.ToDouble(); + + // White Space ueberlesen + while( ( ' ' == cNextCh || + (cNextCh >= 0x09 && cNextCh <= 0x0d) ) && !IsEOF() ) + { + bWhiteSpace = TRUE; + cNextCh = GetNextChar(); + } + + // und nun Schauen, ob es eine Einheit gibt + switch( cNextCh ) + { + case '%': // PERCENTAGE + bWhiteSpace = FALSE; + nRet = CSS1_PERCENTAGE; + break; + + case 'c': + case 'C': // LENGTH cm | LENGTH IDENT + case 'e': + case 'E': // LENGTH (em | ex) | LENGTH IDENT + case 'i': + case 'I': // LENGTH inch | LENGTH IDENT + case 'p': + case 'P': // LENGTH (pt | px | pc) | LENGTH IDENT + case 'm': + case 'M': // LENGTH mm | LENGTH IDENT + { + // die aktuelle Position retten + xub_StrLen nInPosOld = nInPos; + sal_Unicode cNextChOld = cNextCh; + ULONG nlLineNrOld = nlLineNr; + ULONG nlLinePosOld = nlLinePos; + BOOL bEOFOld = bEOF; + + // den naechsten Identifer scannen + String aIdent; + ::rtl::OUStringBuffer sTmpBuffer2( 64L ); + do { + sTmpBuffer2.append( cNextCh ); + cNextCh = GetNextChar(); + } while( ( ('A' <= cNextCh && cNextCh <= 'Z') || + ('a' <= cNextCh && cNextCh <= 'z') || + ('0' <= cNextCh && cNextCh <= '9') || + '-'==cNextCh) && !IsEOF() ); + + aIdent += String(sTmpBuffer2.makeStringAndClear()); + + // Ist es eine Einheit? + const sal_Char *pCmp1 = 0, *pCmp2 = 0, *pCmp3 = 0; + double nScale1 = 1., nScale2 = 1., nScale3 = 1.; + CSS1Token nToken1 = CSS1_LENGTH, + nToken2 = CSS1_LENGTH, + nToken3 = CSS1_LENGTH; + switch( aIdent.GetChar(0) ) + { + case 'c': + case 'C': + pCmp1 = sCSS1_UNIT_cm; + nScale1 = (72.*20.)/2.54; // twip + break; + case 'e': + case 'E': + pCmp1 = sCSS1_UNIT_em; + nToken1 = CSS1_EMS; + + pCmp2 = sCSS1_UNIT_ex; + nToken2 = CSS1_EMX; + break; + case 'i': + case 'I': + pCmp1 = sCSS1_UNIT_inch; + nScale1 = 72.*20.; // twip + break; + case 'm': + case 'M': + pCmp1 = sCSS1_UNIT_mm; + nScale1 = (72.*20.)/25.4; // twip + break; + case 'p': + case 'P': + pCmp1 = sCSS1_UNIT_pt; + nScale1 = 20.; // twip + + pCmp2 = sCSS1_UNIT_pc; + nScale2 = 12.*20.; // twip + + pCmp3 = sCSS1_UNIT_px; + nToken3 = CSS1_PIXLENGTH; + break; + } + + double nScale = 0.0; + DBG_ASSERT( pCmp1, "Wo kommt das erste Zeichen her?" ); + if( aIdent.EqualsIgnoreCaseAscii(pCmp1) ) + { + nScale = nScale1; + nRet = nToken1; + } + else if( pCmp2 && + aIdent.EqualsIgnoreCaseAscii(pCmp2) ) + { + nScale = nScale2; + nRet = nToken2; + } + else if( pCmp3 && + aIdent.EqualsIgnoreCaseAscii(pCmp3) ) + { + nScale = nScale3; + nRet = nToken3; + } + else + { + nRet = CSS1_NUMBER; + } + + if( CSS1_LENGTH==nRet && nScale!=1.0 ) + nValue *= nScale; + + if( nRet == CSS1_NUMBER ) + { + nInPos = nInPosOld; + cNextCh = cNextChOld; + nlLineNr = nlLineNrOld; + nlLinePos = nlLinePosOld; + bEOF = bEOFOld; + } + else + { + bWhiteSpace = FALSE; + } + bNextCh = FALSE; + } + break; + default: // NUMBER IDENT + bNextCh = FALSE; + nRet = CSS1_NUMBER; + break; + } + } + break; + + case ':': // ':' + // link/visited/active abfangen !!! + nRet = CSS1_COLON; + break; + + case '.': // DOT_W_WS | DOT_WO_WS + nRet = bPrevWhiteSpace ? CSS1_DOT_W_WS : CSS1_DOT_WO_WS; + break; + + // case '/': siehe oben + + case '+': // '+' + nRet = CSS1_PLUS; + break; + + case '-': // '-' + nRet = CSS1_MINUS; + break; + + case '{': // '{' + nRet = CSS1_OBRACE; + break; + + case '}': // '}' + nRet = CSS1_CBRACE; + break; + + case ';': // ';' + nRet = CSS1_SEMICOLON; + break; + + case ',': // ',' + nRet = CSS1_COMMA; + break; + + case '#': // '#' + cNextCh = GetNextChar(); + if( ('0'<=cNextCh && '9'>=cNextCh) || + ('a'<=cNextCh && 'f'>=cNextCh) || + ('A'<=cNextCh && 'F'>=cNextCh) ) + { + // die aktuelle Position retten + xub_StrLen nInPosSave = nInPos; + sal_Unicode cNextChSave = cNextCh; + ULONG nlLineNrSave = nlLineNr; + ULONG nlLinePosSave = nlLinePos; + BOOL bEOFSave = bEOF; + + // erstmal versuchen eine Hex-Zahl zu scannen + ::rtl::OUStringBuffer sTmpBuffer( 6L ); + do { + sTmpBuffer.append( cNextCh ); + cNextCh = GetNextChar(); + } while( sTmpBuffer.getLength() < 7 && + ( ('0'<=cNextCh && '9'>=cNextCh) || + ('A'<=cNextCh && 'F'>=cNextCh) || + ('a'<=cNextCh && 'f'>=cNextCh) ) && + !IsEOF() ); + + if( sTmpBuffer.getLength()==6 || sTmpBuffer.getLength()==3 ) + { + // wir haben eine hexadezimale Farbe gefunden + aToken += String(sTmpBuffer.makeStringAndClear()); + nRet = CSS1_HEXCOLOR; + bNextCh = FALSE; + + break; + } + + // sonst versuchen wir es mit einer Zahl + nInPos = nInPosSave; + cNextCh = cNextChSave; + nlLineNr = nlLineNrSave; + nlLinePos = nlLinePosSave; + bEOF = bEOFSave; + } + + nRet = CSS1_HASH; + bNextCh = FALSE; + break; + + case ' ': + case '\t': + case '\r': + case '\n': // White-Space + bWhiteSpace = TRUE; + break; + + case (sal_Unicode)EOF: + if( IsEOF() ) + { + eState = CSS1_PAR_ACCEPTED; + bNextCh = FALSE; + break; + } + // kein break; + + default: // IDENT | syntax error + // TODO IsAlpha + if( ('A' <= cNextCh && cNextCh <= 'Z') || + ('a' <= cNextCh && cNextCh <= 'z') ) + { + // IDENT + + BOOL bHexColor = TRUE; + + // den naechsten Identifer scannen + ::rtl::OUStringBuffer sTmpBuffer( 64L ); + do { + sTmpBuffer.append( cNextCh ); + if( bHexColor ) + { + bHexColor = sTmpBuffer.getLength()<7 && + ( ('0'<=cNextCh && '9'>=cNextCh) || + ('A'<=cNextCh && 'F'>=cNextCh) || + ('a'<=cNextCh && 'f'>=cNextCh) ); + } + cNextCh = GetNextChar(); + // TODO: AlphaNumeric + } while( ( ('0'<=cNextCh && '9'>=cNextCh) || + ('A'<=cNextCh && 'Z'>=cNextCh) || + ('a'<=cNextCh && 'z'>=cNextCh) || + '-'==cNextCh ) && + !IsEOF() ); + + aToken += String(sTmpBuffer.makeStringAndClear()); + + if( bHexColor && sTmpBuffer.getLength()==6 ) + { + bNextCh = FALSE; + nRet = CSS1_HEXCOLOR; + + break; + } + if( '('==cNextCh && + ( (('u'==aToken.GetChar(0) || 'U'==aToken.GetChar(0)) && + aToken.EqualsIgnoreCaseAscii(sCSS1_url)) || + (('r'==aToken.GetChar(0) || 'R'==aToken.GetChar(0)) && + aToken.EqualsIgnoreCaseAscii(sCSS1_rgb)) ) ) + { + USHORT nNestCnt = 0; + ::rtl::OUStringBuffer sTmpBuffer2( 64L ); + do { + sTmpBuffer2.append( cNextCh ); + switch( cNextCh ) + { + case '(': nNestCnt++; break; + case ')': nNestCnt--; break; + } + cNextCh = GetNextChar(); + } while( (nNestCnt>1 || ')'!=cNextCh) && !IsEOF() ); + sTmpBuffer2.append( cNextCh ); + aToken += String(sTmpBuffer2.makeStringAndClear()); + bNextCh = TRUE; + nRet = 'u'==aToken.GetChar(0) || 'U'==aToken.GetChar(0) + ? CSS1_URL + : CSS1_RGB; + } + else + { + bNextCh = FALSE; + nRet = CSS1_IDENT; + } + } + // Fehlerbehandlung: Zeichen ignorieren + break; + } + if( bNextCh ) + cNextCh = GetNextChar(); + + } while( CSS1_NULL==nRet && IsParserWorking() ); + + return nRet; +} + + +/* */ + + +// Dies folegenden Funktionen realisieren den in +// +// http://www.w3.orh/pub/WWW/TR/WD-css1.html +// bzw. http://www.w3.orh/pub/WWW/TR/WD-css1-960220.html +// +// beschriebenen Parser fuer CSS1. Es handelt sich um eine direkte +// Umsetzung der dort beschriebenen Grammatik + +// stylesheet +// : import* rule* +// +// import +// : IMPORT_SYM url +// +// url +// : STRING +// +void CSS1Parser::ParseStyleSheet() +{ + LOOP_CHECK_DECL + + // import* + BOOL bDone = FALSE; + while( !bDone && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleSheet()/import *" ) + + switch( nToken ) + { + case CSS1_IMPORT_SYM: + // IMPORT_SYM url + // url ueberspringen wir ungeprueft + nToken = GetNextToken(); + break; + case CSS1_IDENT: // Look-Aheads + case CSS1_DOT_W_WS: + case CSS1_HASH: +// /Feature: PrintExt + case CSS1_PAGE_SYM: +// /Feature: PrintExt + // rule + bDone = TRUE; + break; + default: + // Fehlerbehandlung: ueberlesen + break; + } + + if( !bDone ) + nToken = GetNextToken(); + } + + LOOP_CHECK_RESTART + + // rule * + while( IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleSheet()/rule *" ) + + switch( nToken ) + { + case CSS1_IDENT: // Look-Aheads + case CSS1_DOT_W_WS: + case CSS1_HASH: +// /Feature: PrintExt + case CSS1_PAGE_SYM: +// /Feature: PrintExt + // rule + ParseRule(); + break; + default: + // Fehlerbehandlung: ueberlesen + nToken = GetNextToken(); + break; + } + } +} + +// rule +// : selector [ ',' selector ]* +// '{' declaration [ ';' declaration ]* '}' +// +void CSS1Parser::ParseRule() +{ + // selector + CSS1Selector *pSelector = ParseSelector(); + if( !pSelector ) + return; + + // Selektor verarbeiten + if( SelectorParsed( pSelector, TRUE ) ) + delete pSelector; + + LOOP_CHECK_DECL + + // [ ',' selector ]* + while( CSS1_COMMA==nToken && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseRule()/selector *" ) + + // ',' ueberelesen + nToken = GetNextToken(); + + // selector + pSelector = ParseSelector(); + if( !pSelector ) + return; + + // Selektor verarbeiten + if( SelectorParsed( pSelector, FALSE ) ) + delete pSelector; + } + + // '{' + if( CSS1_OBRACE != nToken ) + return; + nToken = GetNextToken(); + + // declaration + String aProperty; + CSS1Expression *pExpr = ParseDeclaration( aProperty ); + if( !pExpr ) + return; + + // expression verarbeiten + if( DeclarationParsed( aProperty, pExpr ) ) + delete pExpr; + + LOOP_CHECK_RESTART + + // [ ';' declaration ]* + while( CSS1_SEMICOLON==nToken && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseRule()/declaration *" ) + + // ';' + nToken = GetNextToken(); + + // declaration + if( CSS1_IDENT == nToken ) + { + CSS1Expression *pExp = ParseDeclaration( aProperty ); + if( pExp ) + { + // expression verarbeiten + if( DeclarationParsed( aProperty, pExp ) ) + delete pExp; + } + } + } + + // '}' + if( CSS1_CBRACE == nToken ) + nToken = GetNextToken(); +} + +// selector +// : simple_selector+ [ ':' pseudo_element ]? +// +// simple_selector +// : element_name [ DOT_WO_WS class ]? +// | DOT_W_WS class +// | id_selector +// +// element_name +// : IDENT +// +// class +// : IDENT +// +// id_selector +// : '#' IDENT +// +// pseude_element +// : IDENT +// +CSS1Selector *CSS1Parser::ParseSelector() +{ + CSS1Selector *pRoot = 0, *pLast = 0; + + BOOL bDone = FALSE; + CSS1Selector *pNew = 0; + + LOOP_CHECK_DECL + + // simple_selector+ + while( !bDone && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseSelector()" ) + + BOOL bNextToken = TRUE; + + switch( nToken ) + { + case CSS1_IDENT: + { + // element_name [ DOT_WO_WS class ]? + + // element_name + String aElement = aToken; + CSS1SelectorType eType = CSS1_SELTYPE_ELEMENT; + nToken = GetNextToken(); + + if( CSS1_DOT_WO_WS == nToken ) + { + // DOT_WO_WS + nToken = GetNextToken(); + + // class + if( CSS1_IDENT == nToken ) + { + (aElement += '.') += aToken; + eType = CSS1_SELTYPE_ELEM_CLASS; + } + else + { + // class fehlt + return pRoot; + } + } + else + { + // das war jetzt ein Look-Ahead + bNextToken = FALSE; + } + pNew = new CSS1Selector( eType, aElement ); + } + break; + case CSS1_DOT_W_WS: + // DOT_W_WS class + + // DOT_W_WS + nToken = GetNextToken(); + + if( CSS1_IDENT==nToken ) + { + // class + pNew = new CSS1Selector( CSS1_SELTYPE_CLASS, aToken ); + } + else + { + // class fehlt + return pRoot; + } + break; + case CSS1_HASH: + // '#' id_selector + + // '#' + nToken = GetNextToken(); + + if( CSS1_IDENT==nToken ) + { + // id_selector + pNew = new CSS1Selector( CSS1_SELTYPE_ID, aToken ); + } + else + { + // id_selector fehlt + return pRoot; + } + break; + +// /Feature: PrintExt + case CSS1_PAGE_SYM: + { + // @page + pNew = new CSS1Selector( CSS1_SELTYPE_PAGE, aToken ); + } + break; +// /Feature: PrintExt + + default: + // wir wissen nicht was kommt, also aufhoehren + bDone = TRUE; + break; + } + + // falls ein Selektor angelegt wurd, ihn speichern + if( pNew ) + { + DBG_ASSERT( (pRoot!=0) == (pLast!=0), + "Root-Selektor, aber kein Last" ); + if( pLast ) + pLast->SetNext( pNew ); + else + pRoot = pNew; + + pLast = pNew; + pNew = 0; + } + + if( bNextToken && !bDone ) + nToken = GetNextToken(); + } + + if( !pRoot ) + { + // simple_selector fehlt + return pRoot; + } + + // [ ':' pseudo_element ]? + if( CSS1_COLON==nToken && IsParserWorking() ) + { + // ':' pseudo element + nToken = GetNextToken(); + if( CSS1_IDENT==nToken ) + { + pLast->SetNext( new CSS1Selector(CSS1_SELTYPE_PSEUDO,aToken) ); + nToken = GetNextToken(); + } + else + { + // pseudo_element fehlt + return pRoot; + } + } + + return pRoot; +} + +// declaration +// : property ':' expr prio? +// | /* empty */ +// +// expression +// : term [ operator term ]* +// +// term +// : unary_operator? +// [ NUMBER | STRING | PERCENTAGE | LENGTH | EMS | EXS | IDENT | +// HEXCOLOR | URL | RGB ] +// +// operator +// : '/' | ',' | /* empty */ +// +// unary_operator +// : '-' | '+' +// +// property +// : ident +// +// das Vorzeichen wird nur fuer numerische Werte (ausser PERCENTAGE) +// beruecksichtigt und wird auf nValue angewendet! +CSS1Expression *CSS1Parser::ParseDeclaration( String& rProperty ) +{ + CSS1Expression *pRoot = 0, *pLast = 0; + + // property + if( CSS1_IDENT != nToken ) + { + // property fehlt + return pRoot; + } + rProperty = aToken; + + nToken = GetNextToken(); + + + // ':' + if( CSS1_COLON != nToken ) + { + // ':' fehlt + return pRoot; + } + nToken = GetNextToken(); + + // term [operator term]* + // hier sind wir sehr lax, was die Syntax angeht, sollte aber kein + // Problem sein + BOOL bDone = FALSE; + sal_Unicode cSign = 0, cOp = 0; + CSS1Expression *pNew = 0; + + LOOP_CHECK_DECL + + while( !bDone && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseDeclaration()" ) + + switch( nToken ) + { + case CSS1_MINUS: + cSign = '-'; + break; + + case CSS1_PLUS: + cSign = '+'; + break; + + case CSS1_NUMBER: + case CSS1_LENGTH: + case CSS1_PIXLENGTH: + case CSS1_EMS: + case CSS1_EMX: + if( '-'==cSign ) + nValue = -nValue; + case CSS1_STRING: + case CSS1_PERCENTAGE: + case CSS1_IDENT: + case CSS1_URL: + case CSS1_RGB: + case CSS1_HEXCOLOR: + pNew = new CSS1Expression( nToken, aToken, nValue, cOp ); + nValue = 0; // sonst landet das auch im naechsten Ident + cSign = 0; + cOp = 0; + break; + + case CSS1_SLASH: + cOp = '/'; + cSign = 0; + break; + + case CSS1_COMMA: + cOp = ','; + cSign = 0; + break; + + default: + bDone = TRUE; + break; + } + + // falls ein Expression angelegt wurde, diesen speichern + if( pNew ) + { + DBG_ASSERT( (pRoot!=0) == (pLast!=0), + "Root-Selektor, aber kein Last" ); + if( pLast ) + pLast->SetNext( pNew ); + else + pRoot = pNew; + + pLast = pNew; + pNew = 0; + } + + if( !bDone ) + nToken = GetNextToken(); + } + + if( !pRoot ) + { + // term fehlt + return pRoot; + } + + // prio? + if( CSS1_IMPORTANT_SYM==nToken ) + { + // IMPORTANT_SYM + nToken = GetNextToken(); + } + + return pRoot; +} + +/* */ + +CSS1Parser::CSS1Parser() +{ +} + +CSS1Parser::~CSS1Parser() +{ +} + +/* */ + +BOOL CSS1Parser::ParseStyleSheet( const String& rIn ) +{ + String aTmp( rIn ); + + sal_Unicode c; + while( aTmp.Len() && + ( ' '==(c=aTmp.GetChar(0)) || '\t'==c || '\r'==c || '\n'==c ) ) + aTmp.Erase( 0, 1 ); + + while( aTmp.Len() && ( ' '==(c=aTmp.GetChar( aTmp.Len()-1)) + || '\t'==c || '\r'==c || '\n'==c ) ) + aTmp.Erase( aTmp.Len()-1 ); + + // SGML-Kommentare entfernen + if( aTmp.Len() >= 4 && + aTmp.CompareToAscii("<!--",4) == COMPARE_EQUAL ) + aTmp.Erase( 0, 4 ); + + if( aTmp.Len() >=3 && + aTmp.Copy(aTmp.Len()-3).CompareToAscii("-->") == COMPARE_EQUAL ) + aTmp.Erase( aTmp.Len()-3 ); + + if( !aTmp.Len() ) + return TRUE; + + InitRead( aTmp ); + + ParseStyleSheet(); + + return TRUE; +} + + +BOOL CSS1Parser::ParseStyleOption( const String& rIn ) +{ + if( !rIn.Len() ) + return TRUE; + + InitRead( rIn ); + + String aProperty; + CSS1Expression *pExpr = ParseDeclaration( aProperty ); + if( !pExpr ) + { + return FALSE; + } + + // expression verarbeiten + if( DeclarationParsed( aProperty, pExpr ) ) + delete pExpr; + + LOOP_CHECK_DECL + + // [ ';' declaration ]* + while( CSS1_SEMICOLON==nToken && IsParserWorking() ) + { + LOOP_CHECK_CHECK( "Endlos-Schleife in ParseStyleOption()" ) + + nToken = GetNextToken(); + if( CSS1_IDENT==nToken ) + { + CSS1Expression *pExp = ParseDeclaration( aProperty ); + if( pExp ) + { + // expression verarbeiten + if( DeclarationParsed( aProperty, pExp ) ) + delete pExp; + } + } + } + + return TRUE; +} + +BOOL CSS1Parser::SelectorParsed( const CSS1Selector * /* pSelector */, BOOL /*bFirst*/ ) +{ + // Selektor loeschen + return TRUE; +} + +BOOL CSS1Parser::DeclarationParsed( const String& /*rProperty*/, + const CSS1Expression * /* pExpr */ ) +{ + // Deklaration loeschen + return TRUE; +} + + +/* */ + +CSS1Selector::~CSS1Selector() +{ + delete pNext; +} + +/* */ + +CSS1Expression::~CSS1Expression() +{ + delete pNext; +} + +BOOL CSS1Expression::GetURL( String& rURL ) const +{ + DBG_ASSERT( CSS1_URL==eType, "CSS1-Ausruck ist keine Farbe URL" ); + + DBG_ASSERT( aValue.CompareIgnoreCaseToAscii( sCSS1_url, 3 ) == + COMPARE_EQUAL && + aValue.Len() > 5 && + '(' == aValue.GetChar(3) && + ')' == aValue.GetChar(aValue.Len()-1), + "keine gueltiges URL(...)" ); + + BOOL bRet = FALSE; + + if( aValue.Len() > 5 ) + { + rURL = aValue.Copy( 4, aValue.Len()-5 ); + rURL.EraseTrailingChars(); + rURL.EraseLeadingChars(); + bRet = TRUE; + } + + return bRet; +} + +BOOL CSS1Expression::GetColor( Color &rColor ) const +{ + DBG_ASSERT( CSS1_IDENT==eType || CSS1_RGB==eType || + CSS1_HEXCOLOR==eType || CSS1_STRING==eType, + "CSS1-Ausruck kann keine Farbe sein" ); + + BOOL bRet = FALSE; + ULONG nColor = ULONG_MAX; + + switch( eType ) + { + case CSS1_RGB: + { + BYTE aColors[3] = { 0, 0, 0 }; + + DBG_ASSERT( aValue.CompareIgnoreCaseToAscii( sCSS1_rgb, 3 ) + == COMPARE_EQUAL && + aValue.Len() > 5 && + '(' == aValue.GetChar( 3 ) && + ')' == aValue.GetChar( aValue.Len()-1), + "keine gueltiges RGB(...)" ); + + String aColorStr( aValue.Copy( 4, aValue.Len()-1 ) ); + + xub_StrLen nPos = 0; + USHORT nCol = 0; + + while( nCol < 3 && nPos < aColorStr.Len() ) + { + sal_Unicode c; + while( nPos < aColorStr.Len() && + ((c=aColorStr.GetChar(nPos)) == ' ' || c == '\t' || + c == '\n' || c== '\r' ) ) + nPos++; + + xub_StrLen nEnd = aColorStr.Search( ',', nPos ); + String aNumber; + if( STRING_NOTFOUND==nEnd ) + { + aNumber = aColorStr.Copy(nPos); + nPos = aColorStr.Len(); + } + else + { + aNumber = aColorStr.Copy( nPos, nEnd-nPos ); + nPos = nEnd+1; + } + + USHORT nNumber = (USHORT)aNumber.ToInt32(); + if( aNumber.Search('%') != STRING_NOTFOUND ) + { + if( nNumber > 100 ) + nNumber = 100; + nNumber *= 255; + nNumber /= 100; + } + else if( nNumber > 255 ) + nNumber = 255; + + aColors[nCol] = (BYTE)nNumber; + nCol ++; + } + + rColor.SetRed( aColors[0] ); + rColor.SetGreen( aColors[1] ); + rColor.SetBlue( aColors[2] ); + + bRet = TRUE; // etwas anderes als eine Farbe kann es nicht sein + } + break; + + case CSS1_IDENT: + case CSS1_STRING: + { + String aTmp( aValue ); + aTmp.ToUpperAscii(); + nColor = GetHTMLColor( aTmp ); + bRet = nColor != ULONG_MAX; + } + if( bRet || CSS1_STRING != eType || !aValue.Len() || + aValue.GetChar( 0 )!='#' ) + break; + + case CSS1_HEXCOLOR: + { + // HACK fuer MS-IE: DIe Farbe kann auch in einem String stehen + xub_StrLen nOffset = CSS1_STRING==eType ? 1 : 0; + BOOL bDouble = aValue.Len()-nOffset == 3; + xub_StrLen i = nOffset, nEnd = (bDouble ? 3 : 6) + nOffset; + + nColor = 0; + for( ; i<nEnd; i++ ) + { + sal_Unicode c = (i<aValue.Len() ? aValue.GetChar(i) + : '0' ); + if( c >= '0' && c <= '9' ) + c -= 48; + else if( c >= 'A' && c <= 'F' ) + c -= 55; + else if( c >= 'a' && c <= 'f' ) + c -= 87; + else + c = 16; + + nColor *= 16; + if( c<16 ) + nColor += c; + if( bDouble ) + { + nColor *= 16; + if( c<16 ) + nColor += c; + } + } + // bRet = i==6; + bRet = TRUE; + } + break; + default: + ; + } + + + if( bRet && nColor!=ULONG_MAX ) + { + rColor.SetRed( (BYTE)((nColor & 0x00ff0000UL) >> 16) ); + rColor.SetGreen( (BYTE)((nColor & 0x0000ff00UL) >> 8) ); + rColor.SetBlue( (BYTE)(nColor & 0x000000ffUL) ); + } + + return bRet; +} + |