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-24 23:32:10 +0100 (Sa, 24 Nov 2007) $' 
  12  __version__ = '$LastChangedRevision: 678 $' 
  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, aslist=True) 574 try: 575 t = tokenizer[0] #self._nexttoken(tokenizer) 576 except IndexError: 577 self._log.error(u'CSSPrimitiveValue: No value.') 578 579 # unary operator: 580 if self._tokenvalue(t) in (u'-', u'+'): 581 try: 582 t = tokenizer[1] #self._nexttoken(tokenizer) 583 except IndexError: 584 self._log.error(u'CSSPrimitiveValue: No value.') 585 586 _floatType = True 587 588 589 #if self.valid == False: 590 # primitiveType = CSSPrimitiveValue.CSS_UNKNOWN 591 592 # check for font1, "font2" etc which is treated a ONE string 593 fontstring = 0 # should be at leayst 2 594 expected = 'ident or string' 595 for x in tokenizer: 596 val, typ = self._tokenvalue(x, normalize=True), self._type(x) 597 if expected == 'ident or string' and typ in ( 598 self._prods.IDENT, self._prods.STRING): 599 expected = 'comma' 600 fontstring += 1 601 elif expected == 'comma' and typ == self._prods.CHAR and val == ',': 602 expected = 'ident or string' 603 fontstring += 1 604 elif typ in (self._prods.S, self._prods.COMMENT): 605 continue 606 else: 607 fontstring = False 608 break 609 610 if fontstring > 2: 611 # special case: e.g. for font-family: a, b; only COMMA IDENT and STRING 612 primitiveType = CSSPrimitiveValue.CSS_STRING 613 elif self._type(t) == self._prods.HASH: 614 # special case, maybe should be converted to rgb in any case? 615 primitiveType = CSSPrimitiveValue.CSS_RGBCOLOR 616 else: 617 for i, (name, tokentype, search) in enumerate(self._unitinfos): 618 val, typ = self._tokenvalue(t, normalize=True), self._type(t) 619 if typ == tokentype: 620 if typ == self._prods.DIMENSION: 621 if not search: 622 primitiveType = i 623 break 624 elif re.match(ur'^[^a-z]*(%s)$' % search, val): 625 primitiveType = i 626 break 627 elif typ == self._prods.FUNCTION: 628 if not search: 629 primitiveType = i 630 break 631 elif val.startswith(search): 632 primitiveType = i 633 break 634 else: 635 primitiveType = i 636 break 637 638 if _floatType and primitiveType not in self._floattypes: 639 # - or + only expected before floattype 640 primitiveType = self.CSS_UNKNOWN 641 642 self._primitiveType = primitiveType
643
644 - def _getPrimitiveType(self):
645 if not hasattr(self, '_primitivetype'): 646 self.__set_primitiveType() 647 return self._primitiveType
648 649 primitiveType = property(_getPrimitiveType, 650 doc="READONLY: The type of the value as defined by the constants specified above.") 651
652 - def _getPrimitiveTypeString(self):
653 return self._unitinfos[self.primitiveType][0]
654 655 primitiveTypeString = property(_getPrimitiveTypeString, 656 doc="Name of primitive type of this value.") 657
658 - def _getCSSPrimitiveTypeString(self, type):
659 "get TypeString by given type which may be unknown, used by setters" 660 try: 661 return self._unitinfos[type][0] 662 except (IndexError, TypeError): 663 return u'%r (UNKNOWN TYPE)' % type
664
665 - def __getValDim(self):
666 "splits self._value in numerical and dimension part" 667 try: 668 val, dim = self._reNumDim.findall(self._value)[0] 669 except IndexError: 670 val, dim = self._value, u'' 671 try: 672 val = float(val) 673 except ValueError: 674 raise xml.dom.InvalidAccessErr( 675 u'CSSPrimitiveValue: No float value %r' 676 % (self._value)) 677 678 return val, dim
679
680 - def getFloatValue(self, unitType):
681 """ 682 (DOM method) This method is used to get a float value in a 683 specified unit. If this CSS value doesn't contain a float value 684 or can't be converted into the specified unit, a DOMException 685 is raised. 686 687 unitType 688 to get the float value. The unit code can only be a float unit type 689 (i.e. CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS, CSS_PX, CSS_CM, 690 CSS_MM, CSS_IN, CSS_PT, CSS_PC, CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS, 691 CSS_S, CSS_HZ, CSS_KHZ, CSS_DIMENSION). 692 693 returns not necessarily a float but some cases just an int 694 e.g. if the value is ``1px`` it return ``1`` and **not** ``1.0`` 695 696 conversions might return strange values like 1.000000000001 697 """ 698 if unitType not in self._floattypes: 699 raise xml.dom.InvalidAccessErr( 700 u'unitType Parameter is not a float type') 701 702 val, dim = self.__getValDim() 703 704 if self.primitiveType != unitType: 705 try: 706 val = self._converter[self.primitiveType, unitType](val) 707 except KeyError: 708 raise xml.dom.InvalidAccessErr( 709 u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r' 710 % (self.primitiveTypeString, 711 self._getCSSPrimitiveTypeString(unitType))) 712 713 if val == int(val): 714 val = int(val) 715 716 return val
717
718 - def setFloatValue(self, unitType, floatValue):
719 """ 720 (DOM method) A method to set the float value with a specified unit. 721 If the property attached with this value can not accept the 722 specified unit or the float value, the value will be unchanged and 723 a DOMException will be raised. 724 725 unitType 726 a unit code as defined above. The unit code can only be a float 727 unit type 728 floatValue 729 the new float value which does not have to be a float value but 730 may simple be an int e.g. if setting:: 731 732 setFloatValue(CSS_PX, 1) 733 734 raises DOMException 735 - INVALID_ACCESS_ERR: Raised if the attached property doesn't 736 support the float value or the unit type. 737 - NO_MODIFICATION_ALLOWED_ERR: Raised if this property is readonly. 738 """ 739 self._checkReadonly() 740 if unitType not in self._floattypes: 741 raise xml.dom.InvalidAccessErr( 742 u'CSSPrimitiveValue: unitType %r is not a float type' % 743 self._getCSSPrimitiveTypeString(unitType)) 744 try: 745 val = float(floatValue) 746 except ValueError, e: 747 raise xml.dom.InvalidAccessErr( 748 u'CSSPrimitiveValue: floatValue %r is not a float' % 749 floatValue) 750 751 oldval, dim = self.__getValDim() 752 753 if self.primitiveType != unitType: 754 # convert if possible 755 try: 756 val = self._converter[ 757 unitType, self.primitiveType](val) 758 except KeyError: 759 raise xml.dom.InvalidAccessErr( 760 u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r' 761 % (self.primitiveTypeString, 762 self._getCSSPrimitiveTypeString(unitType))) 763 764 if val == int(val): 765 val = int(val) 766 767 self.cssText = '%s%s' % (val, dim)
768
769 - def getStringValue(self):
770 """ 771 (DOM method) This method is used to get the string value. If the 772 CSS value doesn't contain a string value, a DOMException is raised. 773 774 Some properties (like 'font-family' or 'voice-family') 775 convert a whitespace separated list of idents to a string. 776 777 Only the actual value is returned so e.g. all the following return the 778 actual value ``a``: url(a), attr(a), "a", 'a' 779 """ 780 if self.primitiveType not in self._stringtypes: 781 raise xml.dom.InvalidAccessErr( 782 u'CSSPrimitiveValue %r is not a string type' 783 % self.primitiveTypeString) 784 785 if CSSPrimitiveValue.CSS_STRING == self.primitiveType: 786 return self._value[1:-1] 787 elif CSSPrimitiveValue.CSS_URI == self.primitiveType: 788 url = self._value[4:-1] 789 if url and url[0] in ('"', "'") and url[0] == url[-1]: 790 return url[1:-1] 791 else: 792 return url 793 elif CSSPrimitiveValue.CSS_ATTR == self.primitiveType: 794 return self._value[5:-1] 795 else: 796 return self._value
797
798 - def setStringValue(self, stringType, stringValue):
799 """ 800 (DOM method) A method to set the string value with the specified 801 unit. If the property attached to this value can't accept the 802 specified unit or the string value, the value will be unchanged and 803 a DOMException will be raised. 804 805 stringType 806 a string code as defined above. The string code can only be a 807 string unit type (i.e. CSS_STRING, CSS_URI, CSS_IDENT, and 808 CSS_ATTR). 809 stringValue 810 the new string value 811 Only the actual value is expected so for (CSS_URI, "a") the 812 new value will be ``url(a)``. For (CSS_STRING, "'a'") 813 the new value will be ``"\\'a\\'"`` as the surrounding ``'`` are 814 not part of the string value 815 816 raises 817 DOMException 818 819 - INVALID_ACCESS_ERR: Raised if the CSS value doesn't contain a 820 string value or if the string value can't be converted into 821 the specified unit. 822 823 - NO_MODIFICATION_ALLOWED_ERR: Raised if this property is readonly. 824 """ 825 self._checkReadonly() 826 # self not stringType 827 if self.primitiveType not in self._stringtypes: 828 raise xml.dom.InvalidAccessErr( 829 u'CSSPrimitiveValue %r is not a string type' 830 % self.primitiveTypeString) 831 # given stringType is no StringType 832 if stringType not in self._stringtypes: 833 raise xml.dom.InvalidAccessErr( 834 u'CSSPrimitiveValue: stringType %s is not a string type' 835 % self._getCSSPrimitiveTypeString(stringType)) 836 837 if self._primitiveType != stringType: 838 raise xml.dom.InvalidAccessErr( 839 u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r' 840 % (self.primitiveTypeString, 841 self._getCSSPrimitiveTypeString(stringType))) 842 843 if CSSPrimitiveValue.CSS_STRING == self._primitiveType: 844 self.cssText = u'"%s"' % stringValue.replace(u'"', ur'\\"') 845 elif CSSPrimitiveValue.CSS_URI == self._primitiveType: 846 # Some characters appearing in an unquoted URI, such as 847 # parentheses, commas, whitespace characters, single quotes 848 # (') and double quotes ("), must be escaped with a backslash 849 # so that the resulting URI value is a URI token: 850 # '\(', '\)', '\,'. 851 # 852 # Here the URI is set in quotes alltogether! 853 if u'(' in stringValue or\ 854 u')' in stringValue or\ 855 u',' in stringValue or\ 856 u'"' in stringValue or\ 857 u'\'' in stringValue or\ 858 u'\n' in stringValue or\ 859 u'\t' in stringValue or\ 860 u'\r' in stringValue or\ 861 u'\f' in stringValue or\ 862 u' ' in stringValue: 863 stringValue = '"%s"' % stringValue.replace(u'"', ur'\"') 864 self.cssText = u'url(%s)' % stringValue 865 elif CSSPrimitiveValue.CSS_ATTR == self._primitiveType: 866 self.cssText = u'attr(%s)' % stringValue 867 else: 868 self.cssText = stringValue 869 self._primitiveType = stringType
870
871 - def getCounterValue(self):
872 """ 873 (DOM method) This method is used to get the Counter value. If 874 this CSS value doesn't contain a counter value, a DOMException 875 is raised. Modification to the corresponding style property 876 can be achieved using the Counter interface. 877 """ 878 if not self.CSS_COUNTER == self.primitiveType: 879 raise xml.dom.InvalidAccessErr(u'Value is not a counter type') 880 # TODO: use Counter class 881 raise NotImplementedError()
882
883 - def getRGBColorValue(self):
884 """ 885 (DOM method) This method is used to get the RGB color. If this 886 CSS value doesn't contain a RGB color value, a DOMException 887 is raised. Modification to the corresponding style property 888 can be achieved using the RGBColor interface. 889 """ 890 # TODO: what about coercing #000 to RGBColor? 891 if self.primitiveType not in self._rbgtypes: 892 raise xml.dom.InvalidAccessErr(u'Value is not a RGB value') 893 # TODO: use RGBColor class 894 raise NotImplementedError()
895
896 - def getRectValue(self):
897 """ 898 (DOM method) This method is used to get the Rect value. If this CSS 899 value doesn't contain a rect value, a DOMException is raised. 900 Modification to the corresponding style property can be achieved 901 using the Rect interface. 902 """ 903 if self.primitiveType not in self._recttypes: 904 raise xml.dom.InvalidAccessErr(u'value is not a Rect value') 905 # TODO: use Rect class 906 raise NotImplementedError()
907
908 - def __str__(self):
909 return "<cssutils.css.%s object primitiveType=%s cssText=%r _propertyName=%r valid=%r at 0x%x>" % ( 910 self.__class__.__name__, self.primitiveTypeString, 911 self.cssText, self._propertyName, self.valid, id(self))
912 913
914 -class CSSValueList(CSSValue):
915 """ 916 The CSSValueList interface provides the abstraction of an ordered 917 collection of CSS values. 918 919 Some properties allow an empty list into their syntax. In that case, 920 these properties take the none identifier. So, an empty list means 921 that the property has the value none. 922 923 The items in the CSSValueList are accessible via an integral index, 924 starting from 0. 925 """ 926 cssValueType = CSSValue.CSS_VALUE_LIST 927
928 - def __init__(self, cssText=None, readonly=False, _propertyName=None):
929 """ 930 inits a new CSSValueList 931 """ 932 super(CSSValueList, self).__init__(cssText=cssText, 933 readonly=readonly, 934 _propertyName=_propertyName) 935 self._init()
936
937 - def _init(self):
938 "called by CSSValue if newly identified as CSSValueList" 939 # defines which values 940 ivalueseq, valueseq = 0, self._SHORTHANDPROPERTIES.get( 941 self._propertyName, []) 942 self._items = [] 943 newseq = [] 944 i, max = 0, len(self.seq) 945 minus = None 946 while i < max: 947 v = self.seq[i] 948 949 if u'-' == v: 950 if minus: # 2 "-" after another 951 self._log.error( 952 u'CSSValueList: Unknown syntax: %r.' 953 % u''.join(self.seq)) 954 else: 955 minus = v 956 957 elif isinstance(v, basestring) and not v.strip() == u'' and\ 958 not u'/' == v: 959 if minus: 960 v = minus + v 961 minus = None 962 # TODO: complete 963 # if shorthand get new propname 964 if ivalueseq < len(valueseq): 965 propname, mandatory = valueseq[ivalueseq] 966 if mandatory: 967 ivalueseq += 1 968 else: 969 propname = None 970 ivalueseq = len(valueseq) # end 971 else: 972 propname = self._propertyName 973 974 # TODO: more (do not check individual values for these props) 975 if propname in ('background', 'background-position',): 976 propname = None 977 978 if i+1 < max and self.seq[i+1] == u',': 979 # a comma separated list of values as ONE value 980 # e.g. font-family: a,b 981 fullvalue = [v] 982 983 expected = 'comma' # or 'value' 984 for j in range(i+1, max): 985 testv = self.seq[j] 986 if u' ' == testv: # a single value follows 987 break 988 elif testv in ('-', '+') and expected == 'value': 989 # unary modifier 990 fullvalue.append(testv) 991 expected = 'value' 992 elif u',' == testv and expected == 'comma': 993 fullvalue.append(testv) 994 expected = 'value' 995 elif u',' != testv and expected == 'value': 996 fullvalue.append(testv) 997 expected = 'comma' 998 else: 999 self._log.error( 1000 u'CSSValueList: Unknown syntax: %r.' 1001 % testv) 1002 return 1003 if expected == 'value': 1004 self._log.error( 1005 u'CSSValueList: Unknown syntax: %r.' 1006 % u''.join(self.seq)) 1007 return 1008 # setting _propertyName this way does not work 1009 # for compound props like font! 1010 i += len(fullvalue) - 1 1011 o = CSSValue(cssText=u''.join(fullvalue), 1012 _propertyName=propname) 1013 else: 1014 # a single value, u' ' or nothing should be following 1015 o = CSSValue(cssText=v, _propertyName=propname) 1016 1017 self._items.append(o) 1018 newseq.append(o) 1019 1020 else: 1021 # S (or TODO: comment?) 1022 newseq.append(v) 1023 1024 i += 1 1025 1026 self.seq = newseq
1027
1028 - def _getLength(self):
1029 return len(self._items)
1030 1031 length = property(_getLength, 1032 doc="(DOM attribute) The number of CSSValues in the list.") 1033
1034 - def item(self, index):
1035 """ 1036 (DOM method) Used to retrieve a CSSValue by ordinal index. The 1037 order in this collection represents the order of the values in the 1038 CSS style property. If index is greater than or equal to the number 1039 of values in the list, this returns None. 1040 """ 1041 try: 1042 return self._items[index] 1043 except IndexError: 1044 return None
1045
1046 - def __iter__(self):
1047 "CSSValueList is iterable" 1048 return CSSValueList.__items(self)
1049
1050 - def __items(self):
1051 "the iterator" 1052 for i in range (0, self.length): 1053 yield self.item(i)
1054
1055 - def __str_(self):
1056 return "<cssutils.css.%s object length=%s at 0x%x>" % ( 1057 self.__class__.__name__, self.length, id(self))
1058