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