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