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