1
2 """serializer classes for CSS classes
3
4 TODO:
5 - indent subrules if selector is subselector
6
7 """
8 __all__ = ['CSSSerializer']
9 __docformat__ = 'restructuredtext'
10 __author__ = '$LastChangedBy: cthedot $'
11 __date__ = '$LastChangedDate: 2007-09-01 18:43:49 +0200 (Sa, 01 Sep 2007) $'
12 __version__ = '$LastChangedRevision: 305 $'
13
14 import cssutils
15
17 """
18 controls output of CSSSerializer
19
20 defaultAtKeyword = True
21 Should the literal @keyword from src CSS be used or the default
22 form, e.g. if ``True``: ``@import`` else: ``@i\mport``
23 defaultPropertyName = True
24 Should the normalized propertyname be used or the one given in
25 the src file, e.g. if ``True``: ``color`` else: ``c\olor``
26
27 Only used if ``keepAllProperties==False``.
28
29 importHrefFormat = None
30 Uses hreftype if ``None`` or explicit ``'string'`` or ``'uri'``
31 indent = 4 * ' '
32 Indentation of e.g Properties inside a CSSStyleDeclaration
33 keepAllProperties = False
34 If ``True`` all properties set in the original CSSStylesheet
35 are kept meaning even properties set twice with the exact same
36 same name are kept!
37 keepComments = True
38 If ``False`` removes all CSSComments
39 lineNumbers = False
40 Only used if a complete CSSStyleSheet is serialized.
41 lineSeparator = u'\n'
42 How to end a line. This may be set to e.g. u'' for serializing
43 of CSSStyleDeclarations usable in HTML style attribute.
44 omitLastSemicolon = True
45 If ``True`` omits ; after last property of CSSStyleDeclaration
46 removeInvalid = True
47 Omits invalid rules, MAY CHANGE!
48 """
49 - def __init__(self, indent=u' ', lineSeparator=u'\n'):
50 """
51 Always use named instead of positional parameters
52 """
53 self.defaultAtKeyword = True
54 self.defaultPropertyName = True
55 self.indent = indent
56 self.lineSeparator = lineSeparator
57 self.importHrefFormat = None
58 self.keepAllProperties = False
59 self.keepComments = True
60 self.omitLastSemicolon = True
61 self.lineNumbers = False
62 self.removeInvalid = True
63
65 """
66 Methods to serialize a CSSStylesheet and its parts
67
68 To use your own serializing method the easiest is to subclass CSS
69 Serializer and overwrite the methods you like to customize.
70 """
72 """
73 prefs
74 instance of Preferences
75 """
76 if not prefs:
77 prefs = Preferences()
78 self.prefs = prefs
79 self.ttypes = cssutils.token.Token
80 self._level = 0
81
83 if self.prefs.lineNumbers:
84 pad = len(str(text.count(self.prefs.lineSeparator)+1))
85 out = []
86 for i, line in enumerate(text.split(self.prefs.lineSeparator)):
87 out.append((u'%*i: %s') % (pad, i+1, line))
88 text = self.prefs.lineSeparator.join(out)
89 return text
90
92 """
93 returns
94 True if x.valid or prefs.removeInvalid == False
95 else False
96 """
97 if self.prefs.removeInvalid and \
98 hasattr(x, 'valid') and not x.valid:
99 return True
100 else:
101 return False
102
104 """
105 escapes delim charaters in string s with \delim
106 """
107 return s.replace(delim, u'\%s' % delim)
108
110 """
111 used by all @rules to get the keyword used
112 dependent on prefs setting defaultAtKeyword
113 """
114 if self.prefs.defaultAtKeyword:
115 return default
116 else:
117 return rule.atkeyword
118
120 """
121 used by all styledeclarations to get the propertyname used
122 dependent on prefs setting defaultPropertyName
123 """
124 if self.prefs.defaultPropertyName and \
125 not self.prefs.keepAllProperties:
126 return property.normalname
127 else:
128 return actual
129
131 """
132 indent a block like a CSSStyleDeclaration to the given level
133 which may be higher than self._level (e.g. for CSSStyleDeclaration)
134 """
135 if not self.prefs.lineSeparator:
136 return text
137 return self.prefs.lineSeparator.join(
138 [u'%s%s' % (level * self.prefs.indent, line)
139 for line in text.split(self.prefs.lineSeparator)]
140 )
141
143
144 raise NotImplementedError(
145 "Serializer not implemented for %s" % rule)
146
184
192
201
203 """
204 serializes CSSCharsetRule
205 encoding: string
206
207 always @charset "encoding";
208 no comments or other things allowed!
209 """
210 if not rule.encoding or self._noinvalids(rule):
211 return u''
212 return u'@charset "%s";' % self._escapestring(rule.encoding)
213
215 """
216 serializes CSSImportRule
217
218 href
219 string
220 hreftype
221 'uri' or 'string'
222 media
223 cssutils.stylesheets.medialist.MediaList
224
225 + CSSComments
226 """
227 if not rule.href or self._noinvalids(rule):
228 return u''
229 out = [u'%s ' % self._getatkeyword(rule, u'@import')]
230 for part in rule.seq:
231 if rule.href == part:
232 if self.prefs.importHrefFormat == 'uri':
233 out.append(u'url(%s)' % part)
234 elif self.prefs.importHrefFormat == 'string' or \
235 rule.hreftype == 'string':
236 out.append(u'"%s"' % self._escapestring(part))
237 else:
238 out.append(u'url(%s)' % part)
239 elif isinstance(
240 part, cssutils.stylesheets.medialist.MediaList):
241 mediaText = self.do_stylesheets_medialist(part).strip()
242 if mediaText and not mediaText == u'all':
243 out.append(u' %s' % mediaText)
244 elif hasattr(part, 'cssText'):
245 out.append(part.cssText)
246 return u'%s;' % u''.join(out)
247
249 """
250 serializes CSSNamespaceRule
251
252 uri
253 string
254 prefix
255 string
256
257 + CSSComments
258 """
259 if not rule.uri or self._noinvalids(rule):
260 return u''
261
262 out = [u'%s' % self._getatkeyword(rule, u'@namespace')]
263 for part in rule.seq:
264 if rule.prefix == part and part != u'':
265 out.append(u' %s' % part)
266 elif rule.uri == part:
267 out.append(u' "%s"' % self._escapestring(part))
268 elif hasattr(part, 'cssText'):
269 out.append(part.cssText)
270 return u'%s;' % u''.join(out)
271
290
292 """
293 serializes CSSPageRule
294
295 selectorText
296 string
297 style
298 CSSStyleDeclaration
299
300 + CSSComments
301 """
302 self._level += 1
303 try:
304 styleText = self.do_css_CSSStyleDeclaration(rule.style)
305 finally:
306 self._level -= 1
307
308 if not styleText or self._noinvalids(rule):
309 return u''
310
311 return u'%s%s {%s%s%s%s}' % (
312 self._getatkeyword(rule, u'@page'),
313 self.do_pageselector(rule.seq),
314 self.prefs.lineSeparator,
315 self._indentblock(styleText, self._level + 1),
316 self.prefs.lineSeparator,
317 (self._level + 1) * self.prefs.indent
318 )
319
320 - def do_pageselector(self, seq):
321 """
322 a selector of a CSSPageRule including comments
323 """
324 if len(seq) == 0 or self._noinvalids(seq):
325 return u''
326 else:
327 out = []
328 for part in seq:
329 if hasattr(part, 'cssText'):
330 out.append(part.cssText)
331 else:
332 out.append(part)
333 return u' %s' % u''.join(out)
334
336 """
337 serializes CSSUnknownRule
338 anything until ";" or "{...}"
339 + CSSComments
340 """
341 if rule.atkeyword and not self._noinvalids(rule):
342 out = [u'@%s' % rule.atkeyword]
343 for part in rule.seq:
344 if isinstance(part, cssutils.css.csscomment.CSSComment):
345 if self.prefs.keepComments:
346 out.append(part.cssText)
347 else:
348 out.append(part)
349 if not (out[-1].endswith(u';') or out[-1].endswith(u'}')):
350 out.append(u';')
351 return u''.join(out)
352 else:
353 return u''
354
386
405
407 """
408 a single selector including comments
409
410 TODO: style combinators like + >
411 """
412 if len(selector.seq) == 0 or self._noinvalids(selector):
413 return u''
414 else:
415 out = []
416 for part in selector.seq:
417 if hasattr(part, 'cssText'):
418 out.append(part.cssText)
419 else:
420 if type(part) == dict:
421 out.append(part['value'])
422 else:
423 out.append(part)
424 return u''.join(out)
425
427 """
428 Style declaration of CSSStyleRule
429 """
430 if len(style.seq) == 0 or self._noinvalids(style):
431 return u''
432 else:
433 if separator is None:
434 separator = self.prefs.lineSeparator
435
436 out = []
437 for (i, part) in enumerate(style.seq):
438
439 if isinstance(part,
440 cssutils.css.csscomment.CSSComment):
441 if self.prefs.keepComments:
442 out.append(self.do_CSSComment(part))
443 out.append(separator)
444
445
446 elif isinstance(part,
447 cssutils.css.cssstyledeclaration.SameNamePropertyList):
448
449 if not self.prefs.keepAllProperties:
450 _index = part._currentIndex()
451 out.append(
452 self.do_css_Property(part[_index],
453 self.prefs.omitLastSemicolon and
454 i==len(style.seq)-1))
455 out.append(separator)
456 else:
457
458 for (j, p) in enumerate(part):
459 out.append(
460 self.do_css_Property(p,
461 self.prefs.omitLastSemicolon and
462 i==len(style.seq)-1 and
463 j==len(part)-1))
464 out.append(separator)
465
466 else:
467 out.append(part)
468
469 if out and out[-1] == separator:
470 del out[-1]
471
472 return u''.join(out)
473
475 """
476 Style declaration of CSSStyleRule
477
478 Property has a seqs attribute which contains seq lists for
479 name, a CSSvalue and a seq list for priority
480 """
481 if not property.seqs[0] or self._noinvalids(property):
482 return u''
483 else:
484 out = []
485 nameseq, cssvalue, priorityseq = property.seqs
486
487 for part in nameseq:
488 if hasattr(part, 'cssText'):
489 out.append(part.cssText)
490 elif property.name == part:
491 out.append(self._getpropertyname(property, part))
492 else:
493 out.append(part)
494 if out:
495 out.append(u': ')
496
497 out.append(self.do_css_CSSvalue(cssvalue))
498
499 if out and priorityseq:
500 out.append(u' ')
501 for part in priorityseq:
502 if hasattr(part, 'cssText'):
503 out.append(part.cssText)
504 else:
505 out.append(part)
506 return u'%s%s' % (u''.join(out), (";", "")[bool(omitSemicolon)])
507
509 """
510 serializes a CSSValue
511 """
512 if not cssvalue:
513 return u''
514 else:
515 out = []
516 for part in cssvalue.seq:
517 if hasattr(part, 'cssText'):
518
519 out.append(part.cssText)
520 else:
521 out.append(part)
522 return (u''.join(out)).strip()
523