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