1 /** \file lvstsheet.h 2 \brief style sheet 3 4 Implements CSS compiler for CoolReader Engine. 5 6 Supports only subset of CSS. 7 8 Selectors supported: 9 10 - * { } - universal selector 11 - element-name { } - selector by element name 12 - element1, element2 { } - several selectors delimited by comma 13 14 Properties supported: 15 16 - display 17 - white-space 18 - text-align 19 - vertical-align 20 - font-family 21 - font-size 22 - font-style 23 - font-weight 24 - text-indent 25 - line-height 26 - width 27 - height 28 - margin-left 29 - margin-right 30 - margin-top 31 - margin-bottom 32 - margin 33 34 35 (c) Vadim Lopatin, 2000-2006 36 This source code is distributed under the terms of 37 GNU General Public License. 38 39 See LICENSE file for details. 40 41 */ 42 43 44 #ifndef __LVSTSHEET_H_INCLUDED__ 45 #define __LVSTSHEET_H_INCLUDED__ 46 47 #include "cssdef.h" 48 #include "lvstyles.h" 49 #include "textlang.h" 50 51 class lxmlDocBase; 52 struct ldomNode; 53 54 /** \brief CSS property declaration 55 56 Currently supports only subset of properties. 57 58 Properties supported: 59 60 - display 61 - white-space 62 - text-align 63 - vertical-align 64 - font-family 65 - font-size 66 - font-style 67 - font-weight 68 - text-indent 69 - line-height 70 - width 71 - height 72 - margin-left 73 - margin-right 74 - margin-top 75 - margin-bottom 76 - margin 77 */ 78 class LVCssDeclaration { 79 private: 80 int * _data; 81 public: 82 void apply( css_style_rec_t * style ); empty()83 bool empty() { return _data==NULL; } 84 bool parse( const char * & decl, lUInt32 domVersionRequested, bool higher_importance=false, lxmlDocBase * doc=NULL, lString32 codeBase=lString32::empty_str ); 85 lUInt32 getHash(); LVCssDeclaration()86 LVCssDeclaration() : _data(NULL) { } ~LVCssDeclaration()87 ~LVCssDeclaration() { if (_data) delete[] _data; } 88 }; 89 90 typedef LVRef<LVCssDeclaration> LVCssDeclRef; 91 92 enum LVCssSelectorRuleType 93 { 94 cssrt_universal, // * 95 cssrt_parent, // E > F 96 cssrt_ancessor, // E F 97 cssrt_predecessor, // E + F 98 cssrt_predsibling, // E ~ F 99 cssrt_attrset, // E[foo] 100 cssrt_attreq, // E[foo="value"] 101 cssrt_attreq_i, // E[foo="value i"] (case insensitive) 102 cssrt_attrhas, // E[foo~="value"] 103 cssrt_attrhas_i, // E[foo~="value i"] 104 cssrt_attrstarts_word, // E[foo|="value"] 105 cssrt_attrstarts_word_i, // E[foo|="value i"] 106 cssrt_attrstarts, // E[foo^="value"] 107 cssrt_attrstarts_i, // E[foo^="value i"] 108 cssrt_attrends, // E[foo$="value"] 109 cssrt_attrends_i, // E[foo$="value i"] 110 cssrt_attrcontains, // E[foo*="value"] 111 cssrt_attrcontains_i, // E[foo*="value i"] 112 cssrt_id, // E#id 113 cssrt_class, // E.class 114 cssrt_pseudoclass // E:pseudo-class, E:pseudo-class(value) 115 }; 116 117 class LVCssSelectorRule 118 { 119 // 120 LVCssSelectorRuleType _type; 121 lUInt16 _id; 122 lUInt16 _attrid; 123 LVCssSelectorRule * _next; 124 lString32 _value; 125 public: LVCssSelectorRule(LVCssSelectorRuleType type)126 LVCssSelectorRule(LVCssSelectorRuleType type) 127 : _type(type), _id(0), _attrid(0), _next(NULL) 128 { } 129 LVCssSelectorRule( LVCssSelectorRule & v ); setId(lUInt16 id)130 void setId( lUInt16 id ) { _id = id; } setAttr(lUInt16 id,lString32 value)131 void setAttr( lUInt16 id, lString32 value ) { _attrid = id; _value = value; } getNext()132 LVCssSelectorRule * getNext() { return _next; } setNext(LVCssSelectorRule * next)133 void setNext(LVCssSelectorRule * next) { _next = next; } ~LVCssSelectorRule()134 ~LVCssSelectorRule() { if (_next) delete _next; } 135 /// check condition for node 136 bool check( const ldomNode * & node ); 137 /// check next rules for node 138 bool checkNextRules( const ldomNode * node ); 139 /// Some selector rule types do the full rules chain check themselves isFullChecking()140 bool isFullChecking() { return _type == cssrt_ancessor || _type == cssrt_predsibling; } 141 lUInt32 getHash(); 142 lUInt32 getWeight(); 143 }; 144 145 /** \brief simple CSS selector 146 147 Currently supports only element name and universal selector. 148 149 - * { } - universal selector 150 - element-name { } - selector by element name 151 - element1, element2 { } - several selectors delimited by comma 152 */ 153 class LVCssSelector { 154 private: 155 156 157 lUInt16 _id; 158 LVCssDeclRef _decl; 159 int _specificity; 160 int _pseudo_elem; // from enum LVCssSelectorPseudoElement, or 0 161 LVCssSelector * _next; 162 LVCssSelectorRule * _rules; 163 void insertRuleStart( LVCssSelectorRule * rule ); 164 void insertRuleAfterStart( LVCssSelectorRule * rule ); 165 public: 166 LVCssSelector( LVCssSelector & v ); LVCssSelector()167 LVCssSelector() : _id(0), _specificity(0), _pseudo_elem(0), _next(NULL), _rules(NULL) { } LVCssSelector(int specificity)168 LVCssSelector(int specificity) : _id(0), _specificity(specificity), _pseudo_elem(0), _next(NULL), _rules(NULL) { } ~LVCssSelector()169 ~LVCssSelector() { if (_next) delete _next; if (_rules) delete _rules; } 170 bool parse( const char * &str, lxmlDocBase * doc ); getElementNameId()171 lUInt16 getElementNameId() { return _id; } 172 bool check( const ldomNode * node ) const; 173 void applyToPseudoElement( const ldomNode * node, css_style_rec_t * style ) const; apply(const ldomNode * node,css_style_rec_t * style)174 void apply( const ldomNode * node, css_style_rec_t * style ) const 175 { 176 if (check( node )) { 177 if ( _pseudo_elem > 0 ) { 178 applyToPseudoElement(node, style); 179 } 180 else { 181 _decl->apply(style); 182 } 183 // style->flags |= STYLE_REC_FLAG_MATCHED; 184 // Done in applyToPseudoElement() as currently only needed there. 185 // Uncomment if more generic usage needed. 186 } 187 } setDeclaration(LVCssDeclRef decl)188 void setDeclaration( LVCssDeclRef decl ) { _decl = decl; } getSpecificity()189 int getSpecificity() { return _specificity; } getNext()190 LVCssSelector * getNext() { return _next; } setNext(LVCssSelector * next)191 void setNext(LVCssSelector * next) { _next = next; } 192 lUInt32 getHash(); 193 }; 194 195 196 /** \brief stylesheet 197 198 Can parse stylesheet and apply compiled rules. 199 200 Currently supports only subset of CSS features. 201 202 \sa LVCssSelector 203 \sa LVCssDeclaration 204 */ 205 class LVStyleSheet { 206 lxmlDocBase * _doc; 207 208 int _selector_count; 209 LVArray <int> _selector_count_stack; 210 lString8 _charset; 211 212 LVPtrVector <LVCssSelector> _selectors; 213 LVPtrVector <LVPtrVector <LVCssSelector> > _stack; dup()214 LVPtrVector <LVCssSelector> * dup() 215 { 216 LVPtrVector <LVCssSelector> * res = new LVPtrVector <LVCssSelector>(); 217 res->reserve( _selectors.length() ); 218 for ( int i=0; i<_selectors.length(); i++ ) { 219 LVCssSelector * selector = _selectors[i]; 220 if ( selector ) 221 res->add( new LVCssSelector(*selector) ); 222 else 223 res->add(NULL); 224 } 225 return res; 226 } 227 228 void set(LVPtrVector<LVCssSelector> & v ); 229 public: 230 231 232 // save current state of stylesheet push()233 void push() 234 { 235 _selector_count_stack.add( _selector_count ); 236 _stack.add( dup() ); 237 } 238 // restore previously saved state pop()239 bool pop() 240 { 241 // Restore original counter (so we don't overflow the 19 bits 242 // of _specificity reserved for storing selector order, so up 243 // to 524288, when we meet a book with 600 DocFragments each 244 // including a 1000 selectors stylesheet). 245 if ( !_selector_count_stack.empty() ) 246 _selector_count = _selector_count_stack.remove( _selector_count_stack.length()-1 ); 247 LVPtrVector <LVCssSelector> * v = _stack.pop(); 248 if ( !v ) 249 return false; 250 set( *v ); 251 delete v; 252 return true; 253 } 254 255 /// remove all rules from stylesheet clear()256 void clear() { 257 _selector_count = 0; 258 _selector_count_stack.clear(); 259 _selectors.clear(); 260 _stack.clear(); 261 } 262 /// set document to retrieve ID values from setDocument(lxmlDocBase * doc)263 void setDocument( lxmlDocBase * doc ) { _doc = doc; } 264 /// constructor _doc(doc)265 LVStyleSheet( lxmlDocBase * doc = NULL ) : _doc(doc), _selector_count(0) { } 266 /// copy constructor 267 LVStyleSheet( LVStyleSheet & sheet ); 268 /// parse stylesheet, compile and add found rules to sheet 269 bool parse( const char * str, bool higher_importance=false, lString32 codeBase=lString32::empty_str ); 270 /// parse @charset rule 271 bool parseCharsetRule( const char * &str ); 272 /// apply stylesheet to node style 273 void apply( const ldomNode * node, css_style_rec_t * style ); 274 /// calculate hash 275 lUInt32 getHash(); 276 }; 277 278 /// parse color value like #334455, #345 or red 279 bool parse_color_value( const char * & str, css_length_t & value ); 280 281 /// update (if needed) a style->content (parsed from the CSS declaration) before 282 // applying to a node's style 283 void update_style_content_property( css_style_rec_t * style, ldomNode * node ); 284 /// get the computed final text value for a node from its style->content 285 lString32 get_applied_content_property( ldomNode * node ); 286 287 /// extract @import filename from beginning of CSS 288 bool LVProcessStyleSheetImport( const char * &str, lString8 & import_file ); 289 /// load stylesheet from file, with processing of import 290 bool LVLoadStylesheetFile( lString32 pathName, lString8 & css ); 291 292 #endif // __LVSTSHEET_H_INCLUDED__ 293