Package cssutils :: Package css :: Module cssvalue
[hide private]
[frames] | no frames]

Source Code for Module cssutils.css.cssvalue

   1  """CSSValue related classes 
   2   
   3  - CSSValue implements DOM Level 2 CSS CSSValue 
   4  - CSSPrimitiveValue implements DOM Level 2 CSS CSSPrimitiveValue 
   5  - CSSValueList implements DOM Level 2 CSS CSSValueList 
   6   
   7  """ 
   8  __all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList'] 
   9  __docformat__ = 'restructuredtext' 
  10  __version__ = '$Id: cssvalue.py 1228 2008-05-19 19:59:50Z cthedot $' 
  11   
  12  import re 
  13  import xml.dom 
  14  import cssutils 
  15  import cssproperties 
  16   
17 -class CSSValue(cssutils.util.Base):
18 """ 19 The CSSValue interface represents a simple or a complex value. 20 A CSSValue object only occurs in a context of a CSS property 21 22 Properties 23 ========== 24 cssText 25 A string representation of the current value. 26 cssValueType 27 A (readonly) code defining the type of the value. 28 29 seq: a list (cssutils) 30 All parts of this style declaration including CSSComments 31 valid: boolean 32 if the value is valid at all, False for e.g. color: #1 33 wellformed 34 if this Property is syntactically ok 35 36 _value (INTERNAL!) 37 value without any comments, used to validate 38 """ 39 40 CSS_INHERIT = 0 41 """ 42 The value is inherited and the cssText contains "inherit". 43 """ 44 CSS_PRIMITIVE_VALUE = 1 45 """ 46 The value is a primitive value and an instance of the 47 CSSPrimitiveValue interface can be obtained by using binding-specific 48 casting methods on this instance of the CSSValue interface. 49 """ 50 CSS_VALUE_LIST = 2 51 """ 52 The value is a CSSValue list and an instance of the CSSValueList 53 interface can be obtained by using binding-specific casting 54 methods on this instance of the CSSValue interface. 55 """ 56 CSS_CUSTOM = 3 57 """ 58 The value is a custom value. 59 """ 60 _typestrings = ['CSS_INHERIT' , 'CSS_PRIMITIVE_VALUE', 'CSS_VALUE_LIST', 61 'CSS_CUSTOM'] 62
63 - def __init__(self, cssText=None, readonly=False, _propertyName=None):
64 """ 65 inits a new CSS Value 66 67 cssText 68 the parsable cssText of the value 69 readonly 70 defaults to False 71 property 72 used to validate this value in the context of a property 73 """ 74 super(CSSValue, self).__init__() 75 76 self.seq = [] 77 self.valid = False 78 self.wellformed = False 79 self._valueValue = u'' 80 self._linetoken = None # used for line report only 81 self._propertyName = _propertyName 82 83 if cssText is not None: # may be 0 84 if type(cssText) in (int, float): 85 cssText = unicode(cssText) # if it is a number 86 self.cssText = cssText 87 88 self._readonly = readonly
89
90 - def _getValue(self):
91 v = [] 92 for x in self.seq: 93 if isinstance(x, cssutils.css.CSSComment): 94 continue 95 elif isinstance(x, basestring): 96 v.append(x) 97 else: # maybe CSSPrimitiveValue 98 v.append(x.cssText) 99 if v and u'' == v[-1].strip(): 100 # simple strip of joined string does not work for escaped spaces 101 del v[-1] 102 return u''.join(v)
103
104 - def _setValue(self, value):
105 "overwritten by CSSValueList!" 106 self._valueValue = value
107 108 _value = property(_getValue, _setValue, 109 doc="Actual cssText value of this CSSValue.") 110
111 - def _getCssText(self):
112 return cssutils.ser.do_css_CSSValue(self)
113
114 - def _setCssText(self, cssText):
115 """ 116 Format 117 ====== 118 :: 119 120 unary_operator 121 : '-' | '+' 122 ; 123 operator 124 : '/' S* | ',' S* | /* empty */ 125 ; 126 expr 127 : term [ operator term ]* 128 ; 129 term 130 : unary_operator? 131 [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | 132 TIME S* | FREQ S* ] 133 | STRING S* | IDENT S* | URI S* | hexcolor | function 134 ; 135 function 136 : FUNCTION S* expr ')' S* 137 ; 138 /* 139 * There is a constraint on the color that it must 140 * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F]) 141 * after the "#"; e.g., "#000" is OK, but "#abcd" is not. 142 */ 143 hexcolor 144 : HASH S* 145 ; 146 147 DOMException on setting 148 149 - SYNTAX_ERR: (self) 150 Raised if the specified CSS string value has a syntax error 151 (according to the attached property) or is unparsable. 152 - TODO: INVALID_MODIFICATION_ERR: 153 Raised if the specified CSS string value represents a different 154 type of values than the values allowed by the CSS property. 155 - NO_MODIFICATION_ALLOWED_ERR: (self) 156 Raised if this value is readonly. 157 """ 158 self._checkReadonly() 159 160 # for closures: must be a mutable 161 new = {'values': [], 162 'commas': 0, 163 'valid': True, 164 'wellformed': True } 165 166 def _S(expected, seq, token, tokenizer=None): 167 val = self._tokenvalue(token) 168 if expected.endswith('operator'): 169 seq.append(u' ') 170 return 'term or operator' 171 elif expected.endswith('S'): 172 return 'term or S' 173 else: 174 return expected
175 176 def _char(expected, seq, token, tokenizer=None): 177 val = self._tokenvalue(token) 178 if 'funcend' == expected and u')' == val: 179 # end of FUNCTION 180 seq[-1] += val 181 new['values'].append(seq[-1]) 182 return 'operator' 183 184 elif expected in (')', ']', '}') and expected == val: 185 # end of any block: (), [], {} 186 seq[-1] += val 187 return 'operator' 188 189 elif expected in ('funcend', ')', ']', '}'): 190 # content of func or block: (), [], {} 191 seq[-1] += val 192 return expected 193 194 elif expected.endswith('operator') and ',' == val: 195 # term , term 196 new['commas'] += 1 197 if seq and seq[-1] == u' ': 198 seq[-1] = val 199 else: 200 seq.append(val) 201 return 'term or S' 202 203 elif expected.endswith('operator') and '/' == val: 204 # term / term 205 if seq and seq[-1] == u' ': 206 seq[-1] = val 207 else: 208 seq.append(val) 209 return 'term or S' 210 211 elif expected.startswith('term') and u'(' == val: 212 # start of ( any* ) block 213 seq.append(val) 214 return ')' 215 elif expected.startswith('term') and u'[' == val: 216 # start of [ any* ] block 217 seq.append(val) 218 return ']' 219 elif expected.startswith('term') and u'{' == val: 220 # start of { any* } block 221 seq.append(val) 222 return '}' 223 elif expected.startswith('term') and u'-' == val or u'+' == 'val': 224 # unary operator 225 seq.append(val) 226 new['values'].append(val) 227 return 'number percentage dimension' 228 elif expected.startswith('term') and u'/' == val: 229 # font-size/line-height separator 230 seq.append(val) 231 new['values'].append(val) 232 return 'number percentage dimension' 233 else: 234 new['wellformed'] = False 235 self._log.error(u'CSSValue: Unexpected char.', token) 236 return expected
237 238 def _number_percentage_dimension(expected, seq, token, tokenizer=None): 239 # NUMBER PERCENTAGE DIMENSION after -/+ or operator 240 if expected.startswith('term') or expected == 'number percentage dimension': 241 # normal value 242 val = self._tokenvalue(token) 243 if new['values'] and new['values'][-1] in (u'-', u'+'): 244 new['values'][-1] += val 245 else: 246 new['values'].append(val) 247 seq.append(val) 248 return 'operator' 249 elif 'operator' == expected: 250 # expected S but token which is ok 251 val = self._tokenvalue(token) 252 if new['values'] and new['values'][-1] in (u'-', u'+'): 253 new['values'][-1] += val 254 else: 255 new['values'].append(u' ') 256 seq.append(u' ') 257 new['values'].append(val) 258 seq.append(val) 259 return 'operator' 260 elif expected in ('funcend', ')', ']', '}'): 261 # a block 262 seq[-1] += self._tokenvalue(token) 263 return expected 264 else: 265 new['wellformed'] = False 266 self._log.error(u'CSSValue: Unexpected token.', token) 267 return expected 268 269 def _string_ident_uri_hexcolor(expected, seq, token, tokenizer=None): 270 # STRING IDENT URI HASH 271 if expected.startswith('term'): 272 # normal value 273 274 # TODO: use actual values, probably needs Base2 for this 275 typ = self._type(token) 276 if self._prods.STRING == typ: 277 val = u'"%s"' % self._stringtokenvalue(token) 278 # elif 'URI' == typ: 279 # val = u'url(%s)' % self._uritokenvalue(token) 280 else: 281 val = self._tokenvalue(token) 282 283 new['values'].append(val) 284 seq.append(val) 285 return 'operator' 286 elif 'operator' == expected: 287 # expected S but still ok 288 289 # TODO: use actual values, probably needs Base2 for this 290 typ = self._type(token) 291 if self._prods.STRING == typ: 292 val = u'"%s"' % self._stringtokenvalue(token) 293 # elif 'URI' == typ: 294 # val = u'url(%s)' % self._uritokenvalue(token) 295 else: 296 val = self._tokenvalue(token) 297 new['values'].append(u' ') 298 new['values'].append(val) 299 seq.append(u' ') 300 seq.append(val) 301 return 'operator' 302 elif expected in ('funcend', ')', ']', '}'): 303 # a block 304 seq[-1] += self._tokenvalue(token) 305 return expected 306 else: 307 new['wellformed'] = False 308 self._log.error(u'CSSValue: Unexpected token.', token) 309 return expected 310 311 def _function(expected, seq, token, tokenizer=None): 312 # FUNCTION 313 if expected.startswith('term'): 314 # normal value but add if funcend if found 315 seq.append(self._tokenvalue(token)) 316 return 'funcend' 317 elif 'operator' == expected: 318 # normal value but add if funcend if found 319 seq.append(u' ') 320 seq.append(self._tokenvalue(token)) 321 return 'funcend' 322 elif expected in ('funcend', ')', ']', '}'): 323 # a block 324 seq[-1] += self._tokenvalue(token) 325 return expected 326 else: 327 new['wellformed'] = False 328 self._log.error(u'CSSValue: Unexpected token.', token) 329 return expected 330 331 tokenizer = self._tokenize2(cssText) 332 333 linetoken = self._nexttoken(tokenizer) 334 if not linetoken: 335 self._log.error(u'CSSValue: Unknown syntax or no value: %r.' % 336 self._valuestr(cssText)) 337 else: 338 # TODO: not very efficient tokenizing twice! 339 tokenizer = self._tokenize2(cssText) 340 newseq = [] 341 wellformed, expected = self._parse(expected='term', 342 seq=newseq, tokenizer=tokenizer, 343 productions={'S': _S, 344 'CHAR': _char, 345 346 'NUMBER': _number_percentage_dimension, 347 'PERCENTAGE': _number_percentage_dimension, 348 'DIMENSION': _number_percentage_dimension, 349 350 'STRING': _string_ident_uri_hexcolor, 351 'IDENT': _string_ident_uri_hexcolor, 352 'URI': _string_ident_uri_hexcolor, 353 'HASH': _string_ident_uri_hexcolor, 354 'UNICODE-RANGE': _string_ident_uri_hexcolor, #? 355 356 'FUNCTION': _function 357 }) 358 359 wellformed = wellformed and new['wellformed'] 360 361 # post conditions 362 if expected.startswith('term') and newseq and newseq[-1] != u' ' or ( 363 expected in ('funcend', ')', ']', '}')): 364 wellformed = False 365 self._log.error(u'CSSValue: Incomplete value: %r.' % 366 self._valuestr(cssText)) 367 368 if not new['values']: 369 wellformed = False 370 self._log.error(u'CSSValue: Unknown syntax or no value: %r.' % 371 self._valuestr(cssText)) 372 373 else: 374 self._linetoken = linetoken # used for line report 375 self.seq = newseq 376 self.valid = False 377 378 self._validate() 379 380 if len(new['values']) == 1 and new['values'][0] == u'inherit': 381 self._value = u'inherit' 382 self._cssValueType = CSSValue.CSS_INHERIT 383 self.__class__ = CSSValue # reset 384 elif len(new['values']) == 1: 385 self.__class__ = CSSPrimitiveValue 386 self._init() #inits CSSPrimitiveValue 387 elif len(new['values']) > 1 and\ 388 len(new['values']) == new['commas'] + 1: 389 # e.g. value for font-family: a, b 390 self.__class__ = CSSPrimitiveValue 391 self._init() #inits CSSPrimitiveValue 392 elif len(new['values']) > 1: 393 # separated by S 394 self.__class__ = CSSValueList 395 self._init() # inits CSSValueList 396 else: 397 self._cssValueType = CSSValue.CSS_CUSTOM 398 self.__class__ = CSSValue # reset 399 400 self.wellformed = wellformed 401 402 cssText = property(_getCssText, _setCssText, 403 doc="A string representation of the current value.") 404
405 - def _getCssValueType(self):
406 if hasattr(self, '_cssValueType'): 407 return self._cssValueType
408 409 cssValueType = property(_getCssValueType, 410 doc="A (readonly) code defining the type of the value as defined above.") 411
412 - def _getCssValueTypeString(self):
413 t = self.cssValueType 414 if t is not None: # may be 0! 415 return CSSValue._typestrings[t] 416 else: 417 return None
418 419 cssValueTypeString = property(_getCssValueTypeString, 420 doc="cssutils: Name of cssValueType of this CSSValue (readonly).") 421
422 - def _validate(self):
423 """ 424 validates value against _propertyName context if given 425 """ 426 if self._value: 427 if self._propertyName in cssproperties.cssvalues: 428 if cssproperties.cssvalues[self._propertyName](self._value): 429 self.valid = True 430 else: 431 self.valid = False 432 self._log.warn( 433 u'CSSValue: Invalid value for CSS2 property %r: %r' % 434 (self._propertyName, self._value), neverraise=True) 435 else: 436 self._log.debug( 437 u'CSSValue: Unable to validate as no or unknown property context set for this value: %r' 438 % self._value, neverraise=True)
439
440 - def _get_propertyName(self):
441 return self.__propertyName
442
443 - def _set_propertyName(self, _propertyName):
444 self.__propertyName = _propertyName 445 self._validate()
446 447 _propertyName = property(_get_propertyName, _set_propertyName, 448 doc="cssutils: Property this values is validated against") 449
450 - def __repr__(self):
451 return "cssutils.css.%s(%r, _propertyName=%r)" % ( 452 self.__class__.__name__, self.cssText, self._propertyName)
453
454 - def __str__(self):
455 return "<cssutils.css.%s object cssValueType=%r cssText=%r propname=%r valid=%r at 0x%x>" % ( 456 self.__class__.__name__, self.cssValueTypeString, 457 self.cssText, self._propertyName, self.valid, id(self))
458 459
460 -class CSSPrimitiveValue(CSSValue):
461 """ 462 represents a single CSS Value. May be used to determine the value of a 463 specific style property currently set in a block or to set a specific 464 style property explicitly within the block. Might be obtained from the 465 getPropertyCSSValue method of CSSStyleDeclaration. 466 467 Conversions are allowed between absolute values (from millimeters to 468 centimeters, from degrees to radians, and so on) but not between 469 relative values. (For example, a pixel value cannot be converted to a 470 centimeter value.) Percentage values can't be converted since they are 471 relative to the parent value (or another property value). There is one 472 exception for color percentage values: since a color percentage value 473 is relative to the range 0-255, a color percentage value can be 474 converted to a number; (see also the RGBColor interface). 475 """ 476 # constant: type of this CSSValue class 477 cssValueType = CSSValue.CSS_PRIMITIVE_VALUE 478 479 # An integer indicating which type of unit applies to the value. 480 CSS_UNKNOWN = 0 # only obtainable via cssText 481 CSS_NUMBER = 1 482 CSS_PERCENTAGE = 2 483 CSS_EMS = 3 484 CSS_EXS = 4 485 CSS_PX = 5 486 CSS_CM = 6 487 CSS_MM = 7 488 CSS_IN = 8 489 CSS_PT = 9 490 CSS_PC = 10 491 CSS_DEG = 11 492 CSS_RAD = 12 493 CSS_GRAD = 13 494 CSS_MS = 14 495 CSS_S = 15 496 CSS_HZ = 16 497 CSS_KHZ = 17 498 CSS_DIMENSION = 18 499 CSS_STRING = 19 500 CSS_URI = 20 501 CSS_IDENT = 21 502 CSS_ATTR = 22 503 CSS_COUNTER = 23 504 CSS_RECT = 24 505 CSS_RGBCOLOR = 25 506 # NOT OFFICIAL: 507 CSS_RGBACOLOR = 26 508 509 _floattypes = [CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS, 510 CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC, 511 CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS, CSS_S, 512 CSS_HZ, CSS_KHZ, CSS_DIMENSION 513 ] 514 _stringtypes = [CSS_ATTR, CSS_IDENT, CSS_STRING, CSS_URI] 515 _countertypes = [CSS_COUNTER] 516 _recttypes = [CSS_RECT] 517 _rbgtypes = [CSS_RGBCOLOR, CSS_RGBACOLOR] 518 519 _reNumDim = re.compile(ur'^(.*?)([a-z]+|%)$', re.I| re.U|re.X) 520 521 # oldtype: newType: converterfunc 522 _converter = { 523 # cm <-> mm <-> in, 1 inch is equal to 2.54 centimeters. 524 # pt <-> pc, the points used by CSS 2.1 are equal to 1/72nd of an inch. 525 # pc: picas - 1 pica is equal to 12 points 526 (CSS_CM, CSS_MM): lambda x: x * 10, 527 (CSS_MM, CSS_CM): lambda x: x / 10, 528 529 (CSS_PT, CSS_PC): lambda x: x * 12, 530 (CSS_PC, CSS_PT): lambda x: x / 12, 531 532 (CSS_CM, CSS_IN): lambda x: x / 2.54, 533 (CSS_IN, CSS_CM): lambda x: x * 2.54, 534 (CSS_MM, CSS_IN): lambda x: x / 25.4, 535 (CSS_IN, CSS_MM): lambda x: x * 25.4, 536 537 (CSS_IN, CSS_PT): lambda x: x / 72, 538 (CSS_PT, CSS_IN): lambda x: x * 72, 539 (CSS_CM, CSS_PT): lambda x: x / 2.54 / 72, 540 (CSS_PT, CSS_CM): lambda x: x * 72 * 2.54, 541 (CSS_MM, CSS_PT): lambda x: x / 25.4 / 72, 542 (CSS_PT, CSS_MM): lambda x: x * 72 * 25.4, 543 544 (CSS_IN, CSS_PC): lambda x: x / 72 / 12, 545 (CSS_PC, CSS_IN): lambda x: x * 12 * 72, 546 (CSS_CM, CSS_PC): lambda x: x / 2.54 / 72 / 12, 547 (CSS_PC, CSS_CM): lambda x: x * 12 * 72 * 2.54, 548 (CSS_MM, CSS_PC): lambda x: x / 25.4 / 72 / 12, 549 (CSS_PC, CSS_MM): lambda x: x * 12 * 72 * 25.4, 550 551 # hz <-> khz 552 (CSS_KHZ, CSS_HZ): lambda x: x * 1000, 553 (CSS_HZ, CSS_KHZ): lambda x: x / 1000, 554 # s <-> ms 555 (CSS_S, CSS_MS): lambda x: x * 1000, 556 (CSS_MS, CSS_S): lambda x: x / 1000 557 558 # TODO: convert deg <-> rad <-> grad 559 } 560
561 - def __init__(self, cssText=None, readonly=False, _propertyName=None):
562 """ 563 see CSSPrimitiveValue.__init__() 564 """ 565 super(CSSPrimitiveValue, self).__init__(cssText=cssText, 566 readonly=readonly, 567 _propertyName=_propertyName) 568 569 #(String representation for unit types, token type of unit type, detail) 570 # used to detect primitiveType and for __repr__ 571 self._init()
572
573 - def _init(self):
574 # _unitinfos must be set here as self._prods is not known before 575 self._unitinfos = [ 576 ('CSS_UNKNOWN', None, None), 577 ('CSS_NUMBER', self._prods.NUMBER, None), 578 ('CSS_PERCENTAGE', self._prods.PERCENTAGE, None), 579 ('CSS_EMS', self._prods.DIMENSION, 'em'), 580 ('CSS_EXS', self._prods.DIMENSION, 'ex'), 581 ('CSS_PX', self._prods.DIMENSION, 'px'), 582 ('CSS_CM', self._prods.DIMENSION, 'cm'), 583 ('CSS_MM', self._prods.DIMENSION, 'mm'), 584 ('CSS_IN', self._prods.DIMENSION, 'in'), 585 ('CSS_PT', self._prods.DIMENSION, 'pt'), 586 ('CSS_PC', self._prods.DIMENSION, 'pc'), 587 ('CSS_DEG', self._prods.DIMENSION, 'deg'), 588 ('CSS_RAD', self._prods.DIMENSION, 'rad'), 589 ('CSS_GRAD', self._prods.DIMENSION, 'grad'), 590 ('CSS_MS', self._prods.DIMENSION, 'ms'), 591 ('CSS_S', self._prods.DIMENSION, 's'), 592 ('CSS_HZ', self._prods.DIMENSION, 'hz'), 593 ('CSS_KHZ', self._prods.DIMENSION, 'khz'), 594 ('CSS_DIMENSION', self._prods.DIMENSION, None), 595 ('CSS_STRING', self._prods.STRING, None), 596 ('CSS_URI', self._prods.URI, None), 597 ('CSS_IDENT', self._prods.IDENT, None), 598 ('CSS_ATTR', self._prods.FUNCTION, 'attr('), 599 ('CSS_COUNTER', self._prods.FUNCTION, 'counter('), 600 ('CSS_RECT', self._prods.FUNCTION, 'rect('), 601 ('CSS_RGBCOLOR', self._prods.FUNCTION, 'rgb('), 602 ('CSS_RGBACOLOR', self._prods.FUNCTION, 'rgba('), 603 ]
604
605 - def __set_primitiveType(self):
606 """ 607 primitiveType is readonly but is set lazy if accessed 608 no value is given as self._value is used 609 """ 610 primitiveType = self.CSS_UNKNOWN 611 _floatType = False # if unary expect NUMBER DIMENSION or PERCENTAGE 612 tokenizer = self._tokenize2(self._value) 613 t = self._nexttoken(tokenizer) 614 if not t: 615 self._log.error(u'CSSPrimitiveValue: No value.') 616 617 # unary operator: 618 if self._tokenvalue(t) in (u'-', u'+'): 619 t = self._nexttoken(tokenizer) 620 if not t: 621 self._log.error(u'CSSPrimitiveValue: No value.') 622 623 _floatType = True 624 625 # check for font1, "font2" etc which is treated as ONE string 626 fontstring = 0 # should be at leayst 2 627 expected = 'ident or string' 628 tokenizer = self._tokenize2(self._value) # add used tokens again 629 for token in tokenizer: 630 val, typ = self._tokenvalue(token, normalize=True), self._type(token) 631 if expected == 'ident or string' and typ in ( 632 self._prods.IDENT, self._prods.STRING): 633 expected = 'comma' 634 fontstring += 1 635 elif expected == 'comma' and val == ',': 636 expected = 'ident or string' 637 fontstring += 1 638 elif typ in (self._prods.S, self._prods.COMMENT): 639 continue 640 else: 641 fontstring = False 642 break 643 644 if fontstring > 2: 645 # special case: e.g. for font-family: a, b; only COMMA IDENT and STRING 646 primitiveType = CSSPrimitiveValue.CSS_STRING 647 elif self._type(t) == self._prods.HASH: 648 # special case, maybe should be converted to rgb in any case? 649 primitiveType = CSSPrimitiveValue.CSS_RGBCOLOR 650 else: 651 for i, (name, tokentype, search) in enumerate(self._unitinfos): 652 val, typ = self._tokenvalue(t, normalize=True), self._type(t) 653 if typ == tokentype: 654 if typ == self._prods.DIMENSION: 655 if not search: 656 primitiveType = i 657 break 658 elif re.match(ur'^[^a-z]*(%s)$' % search, val): 659 primitiveType = i 660 break 661 elif typ == self._prods.FUNCTION: 662 if not search: 663 primitiveType = i 664 break 665 elif val.startswith(search): 666 primitiveType = i 667 break 668 else: 669 primitiveType = i 670 break 671 672 if _floatType and primitiveType not in self._floattypes: 673 # - or + only expected before floattype 674 primitiveType = self.CSS_UNKNOWN 675 676 self._primitiveType = primitiveType
677
678 - def _getPrimitiveType(self):
679 if not hasattr(self, '_primitivetype'): 680 self.__set_primitiveType() 681 return self._primitiveType
682 683 primitiveType = property(_getPrimitiveType, 684 doc="READONLY: The type of the value as defined by the constants specified above.") 685
686 - def _getPrimitiveTypeString(self):
687 return self._unitinfos[self.primitiveType][0]
688 689 primitiveTypeString = property(_getPrimitiveTypeString, 690 doc="Name of primitive type of this value.") 691
692 - def _getCSSPrimitiveTypeString(self, type):
693 "get TypeString by given type which may be unknown, used by setters" 694 try: 695 return self._unitinfos[type][0] 696 except (IndexError, TypeError): 697 return u'%r (UNKNOWN TYPE)' % type
698
699 - def __getValDim(self):
700 "splits self._value in numerical and dimension part" 701 try: 702 val, dim = self._reNumDim.findall(self._value)[0] 703 except IndexError: 704 val, dim = self._value, u'' 705 try: 706 val = float(val) 707 except ValueError: 708 raise xml.dom.InvalidAccessErr( 709 u'CSSPrimitiveValue: No float value %r' 710 % (self._value)) 711 712 return val, dim
713
714 - def getFloatValue(self, unitType):
715 """ 716 (DOM method) This method is used to get a float value in a 717 specified unit. If this CSS value doesn't contain a float value 718 or can't be converted into the specified unit, a DOMException 719 is raised. 720 721 unitType 722 to get the float value. The unit code can only be a float unit type 723 (i.e. CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS, CSS_PX, CSS_CM, 724 CSS_MM, CSS_IN, CSS_PT, CSS_PC, CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS, 725 CSS_S, CSS_HZ, CSS_KHZ, CSS_DIMENSION). 726 727 returns not necessarily a float but some cases just an int 728 e.g. if the value is ``1px`` it return ``1`` and **not** ``1.0`` 729 730 conversions might return strange values like 1.000000000001 731 """ 732 if unitType not in self._floattypes: 733 raise xml.dom.InvalidAccessErr( 734 u'unitType Parameter is not a float type') 735 736 val, dim = self.__getValDim() 737 738 if self.primitiveType != unitType: 739 try: 740 val = self._converter[self.primitiveType, unitType](val) 741 except KeyError: 742 raise xml.dom.InvalidAccessErr( 743 u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r' 744 % (self.primitiveTypeString, 745 self._getCSSPrimitiveTypeString(unitType))) 746 747 if val == int(val): 748 val = int(val) 749 750 return val
751
752 - def setFloatValue(self, unitType, floatValue):
753 """ 754 (DOM method) A method to set the float value with a specified unit. 755 If the property attached with this value can not accept the 756 specified unit or the float value, the value will be unchanged and 757 a DOMException will be raised. 758 759 unitType 760 a unit code as defined above. The unit code can only be a float 761 unit type 762 floatValue 763 the new float value which does not have to be a float value but 764 may simple be an int e.g. if setting:: 765 766 setFloatValue(CSS_PX, 1) 767 768 raises DOMException 769 - INVALID_ACCESS_ERR: Raised if the attached property doesn't 770 support the float value or the unit type. 771 - NO_MODIFICATION_ALLOWED_ERR: Raised if this property is readonly. 772 """ 773 self._checkReadonly() 774 if unitType not in self._floattypes: 775 raise xml.dom.InvalidAccessErr( 776 u'CSSPrimitiveValue: unitType %r is not a float type' % 777 self._getCSSPrimitiveTypeString(unitType)) 778 try: 779 val = float(floatValue) 780 except ValueError, e: 781 raise xml.dom.InvalidAccessErr( 782 u'CSSPrimitiveValue: floatValue %r is not a float' % 783 floatValue) 784 785 oldval, dim = self.__getValDim() 786 787 if self.primitiveType != unitType: 788 # convert if possible 789 try: 790 val = self._converter[ 791 unitType, self.primitiveType](val) 792 except KeyError: 793 raise xml.dom.InvalidAccessErr( 794 u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r' 795 % (self.primitiveTypeString, 796 self._getCSSPrimitiveTypeString(unitType))) 797 798 if val == int(val): 799 val = int(val) 800 801 self.cssText = '%s%s' % (val, dim)
802
803 - def getStringValue(self):
804 """ 805 (DOM method) This method is used to get the string value. If the 806 CSS value doesn't contain a string value, a DOMException is raised. 807 808 Some properties (like 'font-family' or 'voice-family') 809 convert a whitespace separated list of idents to a string. 810 811 Only the actual value is returned so e.g. all the following return the 812 actual value ``a``: url(a), attr(a), "a", 'a' 813 """ 814 if self.primitiveType not in self._stringtypes: 815 raise xml.dom.InvalidAccessErr( 816 u'CSSPrimitiveValue %r is not a string type' 817 % self.primitiveTypeString) 818 819 if CSSPrimitiveValue.CSS_STRING == self.primitiveType: 820 # _stringtokenvalue expects tuple with at least 2 821 return self._stringtokenvalue((None,self._value)) 822 elif CSSPrimitiveValue.CSS_URI == self.primitiveType: 823 # _uritokenvalue expects tuple with at least 2 824 return self._uritokenvalue((None, self._value)) 825 elif CSSPrimitiveValue.CSS_ATTR == self.primitiveType: 826 return self._value[5:-1] 827 else: 828 return self._value
829
830 - def setStringValue(self, stringType, stringValue):
831 """ 832 (DOM method) A method to set the string value with the specified 833 unit. If the property attached to this value can't accept the 834 specified unit or the string value, the value will be unchanged and 835 a DOMException will be raised. 836 837 stringType 838 a string code as defined above. The string code can only be a 839 string unit type (i.e. CSS_STRING, CSS_URI, CSS_IDENT, and 840 CSS_ATTR). 841 stringValue 842 the new string value 843 Only the actual value is expected so for (CSS_URI, "a") the 844 new value will be ``url(a)``. For (CSS_STRING, "'a'") 845 the new value will be ``"\\'a\\'"`` as the surrounding ``'`` are 846 not part of the string value 847 848 raises 849 DOMException 850 851 - INVALID_ACCESS_ERR: Raised if the CSS value doesn't contain a 852 string value or if the string value can't be converted into 853 the specified unit. 854 855 - NO_MODIFICATION_ALLOWED_ERR: Raised if this property is readonly. 856 """ 857 self._checkReadonly() 858 # self not stringType 859 if self.primitiveType not in self._stringtypes: 860 raise xml.dom.InvalidAccessErr( 861 u'CSSPrimitiveValue %r is not a string type' 862 % self.primitiveTypeString) 863 # given stringType is no StringType 864 if stringType not in self._stringtypes: 865 raise xml.dom.InvalidAccessErr( 866 u'CSSPrimitiveValue: stringType %s is not a string type' 867 % self._getCSSPrimitiveTypeString(stringType)) 868 869 if self._primitiveType != stringType: 870 raise xml.dom.InvalidAccessErr( 871 u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r' 872 % (self.primitiveTypeString, 873 self._getCSSPrimitiveTypeString(stringType))) 874 875 if CSSPrimitiveValue.CSS_STRING == self._primitiveType: 876 self.cssText = u'"%s"' % stringValue.replace(u'"', ur'\\"') 877 elif CSSPrimitiveValue.CSS_URI == self._primitiveType: 878 # Some characters appearing in an unquoted URI, such as 879 # parentheses, commas, whitespace characters, single quotes 880 # (') and double quotes ("), must be escaped with a backslash 881 # so that the resulting URI value is a URI token: 882 # '\(', '\)', '\,'. 883 # 884 # Here the URI is set in quotes alltogether! 885 if u'(' in stringValue or\ 886 u')' in stringValue or\ 887 u',' in stringValue or\ 888 u'"' in stringValue or\ 889 u'\'' in stringValue or\ 890 u'\n' in stringValue or\ 891 u'\t' in stringValue or\ 892 u'\r' in stringValue or\ 893 u'\f' in stringValue or\ 894 u' ' in stringValue: 895 stringValue = '"%s"' % stringValue.replace(u'"', ur'\"') 896 self.cssText = u'url(%s)' % stringValue 897 elif CSSPrimitiveValue.CSS_ATTR == self._primitiveType: 898 self.cssText = u'attr(%s)' % stringValue 899 else: 900 self.cssText = stringValue 901 self._primitiveType = stringType
902
903 - def getCounterValue(self):
904 """ 905 (DOM method) This method is used to get the Counter value. If 906 this CSS value doesn't contain a counter value, a DOMException 907 is raised. Modification to the corresponding style property 908 can be achieved using the Counter interface. 909 """ 910 if not self.CSS_COUNTER == self.primitiveType: 911 raise xml.dom.InvalidAccessErr(u'Value is not a counter type') 912 # TODO: use Counter class 913 raise NotImplementedError()
914
915 - def getRGBColorValue(self):
916 """ 917 (DOM method) This method is used to get the RGB color. If this 918 CSS value doesn't contain a RGB color value, a DOMException 919 is raised. Modification to the corresponding style property 920 can be achieved using the RGBColor interface. 921 """ 922 # TODO: what about coercing #000 to RGBColor? 923 if self.primitiveType not in self._rbgtypes: 924 raise xml.dom.InvalidAccessErr(u'Value is not a RGB value') 925 # TODO: use RGBColor class 926 raise NotImplementedError()
927
928 - def getRectValue(self):
929 """ 930 (DOM method) This method is used to get the Rect value. If this CSS 931 value doesn't contain a rect value, a DOMException is raised. 932 Modification to the corresponding style property can be achieved 933 using the Rect interface. 934 """ 935 if self.primitiveType not in self._recttypes: 936 raise xml.dom.InvalidAccessErr(u'value is not a Rect value') 937 # TODO: use Rect class 938 raise NotImplementedError()
939
940 - def __str__(self):
941 return "<cssutils.css.%s object primitiveType=%s cssText=%r _propertyName=%r valid=%r at 0x%x>" % ( 942 self.__class__.__name__, self.primitiveTypeString, 943 self.cssText, self._propertyName, self.valid, id(self))
944 945
946 -class CSSValueList(CSSValue):
947 """ 948 The CSSValueList interface provides the abstraction of an ordered 949 collection of CSS values. 950 951 Some properties allow an empty list into their syntax. In that case, 952 these properties take the none identifier. So, an empty list means 953 that the property has the value none. 954 955 The items in the CSSValueList are accessible via an integral index, 956 starting from 0. 957 """ 958 cssValueType = CSSValue.CSS_VALUE_LIST 959
960 - def __init__(self, cssText=None, readonly=False, _propertyName=None):
961 """ 962 inits a new CSSValueList 963 """ 964 super(CSSValueList, self).__init__(cssText=cssText, 965 readonly=readonly, 966 _propertyName=_propertyName) 967 self._init()
968
969 - def _init(self):
970 "called by CSSValue if newly identified as CSSValueList" 971 # defines which values 972 ivalueseq, valueseq = 0, self._SHORTHANDPROPERTIES.get( 973 self._propertyName, []) 974 self._items = [] 975 newseq = [] 976 i, max = 0, len(self.seq) 977 minus = None 978 while i < max: 979 v = self.seq[i] 980 981 if u'-' == v: 982 if minus: # 2 "-" after another 983 self._log.error( 984 u'CSSValueList: Unknown syntax: %r.' 985 % u''.join(self.seq)) 986 else: 987 minus = v 988 989 elif isinstance(v, basestring) and not v.strip() == u'' and\ 990 not u'/' == v: 991 if minus: 992 v = minus + v 993 minus = None 994 # TODO: complete 995 # if shorthand get new propname 996 if ivalueseq < len(valueseq): 997 propname, mandatory = valueseq[ivalueseq] 998 if mandatory: 999 ivalueseq += 1 1000 else: 1001 propname = None 1002 ivalueseq = len(valueseq) # end 1003 else: 1004 propname = self._propertyName 1005 1006 # TODO: more (do not check individual values for these props) 1007 if propname in self._SHORTHANDPROPERTIES: 1008 propname = None 1009 1010 if i+1 < max and self.seq[i+1] == u',': 1011 # a comma separated list of values as ONE value 1012 # e.g. font-family: a,b 1013 fullvalue = [v] 1014 1015 expected = 'comma' # or 'value' 1016 for j in range(i+1, max): 1017 testv = self.seq[j] 1018 if u' ' == testv: # a single value follows 1019 break 1020 elif testv in ('-', '+') and expected == 'value': 1021 # unary modifier 1022 fullvalue.append(testv) 1023 expected = 'value' 1024 elif u',' == testv and expected == 'comma': 1025 fullvalue.append(testv) 1026 expected = 'value' 1027 elif u',' != testv and expected == 'value': 1028 fullvalue.append(testv) 1029 expected = 'comma' 1030 else: 1031 self._log.error( 1032 u'CSSValueList: Unknown syntax: %r.' 1033 % testv) 1034 return 1035 if expected == 'value': 1036 self._log.error( 1037 u'CSSValueList: Unknown syntax: %r.' 1038 % u''.join(self.seq)) 1039 return 1040 # setting _propertyName this way does not work 1041 # for compound props like font! 1042 i += len(fullvalue) - 1 1043 o = CSSValue(cssText=u''.join(fullvalue), 1044 _propertyName=propname) 1045 else: 1046 # a single value, u' ' or nothing should be following 1047 o = CSSValue(cssText=v, _propertyName=propname) 1048 1049 self._items.append(o) 1050 newseq.append(o) 1051 1052 else: 1053 # S (or TODO: comment?) 1054 newseq.append(v) 1055 1056 i += 1 1057 1058 self.seq = newseq
1059 1060 length = property(lambda self: len(self._items), 1061 doc="(DOM attribute) The number of CSSValues in the list.") 1062
1063 - def item(self, index):
1064 """ 1065 (DOM method) Used to retrieve a CSSValue by ordinal index. The 1066 order in this collection represents the order of the values in the 1067 CSS style property. If index is greater than or equal to the number 1068 of values in the list, this returns None. 1069 """ 1070 try: 1071 return self._items[index] 1072 except IndexError: 1073 return None
1074
1075 - def __iter__(self):
1076 "CSSValueList is iterable" 1077 return CSSValueList.__items(self)
1078
1079 - def __items(self):
1080 "the iterator" 1081 for i in range (0, self.length): 1082 yield self.item(i)
1083
1084 - def __str_(self):
1085 return "<cssutils.css.%s object length=%s at 0x%x>" % ( 1086 self.__class__.__name__, self.length, id(self))
1087