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