mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
IGN:...
This commit is contained in:
parent
308b128089
commit
fc934f1f66
@ -23,3 +23,8 @@ installer/windows/calibre/build.log
|
||||
src/calibre/translations/.errors
|
||||
src/calibre/plugins/*
|
||||
src/calibre/gui2/pictureflow/.build
|
||||
src/cssutils/.svn/
|
||||
src/cssutils/_todo/
|
||||
src/cssutils/scripts/
|
||||
src/cssutils/css/.svn/
|
||||
src/cssutils/stylesheets/.svn/
|
||||
|
@ -11,6 +11,20 @@ from calibre.utils.config import Config, StringConfig
|
||||
from calibre.utils.zipfile import ZipFile, ZIP_STORED
|
||||
from calibre.ebooks.html import config as common_config
|
||||
|
||||
class DefaultProfile(object):
|
||||
|
||||
flow_size = sys.maxint
|
||||
|
||||
class PRS505(DefaultProfile):
|
||||
|
||||
flow_size = 300000
|
||||
|
||||
|
||||
PROFILES = {
|
||||
'PRS505' : PRS505,
|
||||
'None' : DefaultProfile,
|
||||
}
|
||||
|
||||
def initialize_container(path_to_container, opf_name='metadata.opf'):
|
||||
'''
|
||||
Create an empty EPUB document, with a default skeleton.
|
||||
@ -43,7 +57,8 @@ def config(defaults=None):
|
||||
|
||||
c.add_opt('output', ['-o', '--output'], default=None,
|
||||
help=_('The output EPUB file. If not specified, it is derived from the input file name.'))
|
||||
|
||||
c.add_opt('profile', ['--profile'], default='PRS505', choices=list(PROFILES.keys()),
|
||||
help=_('Profile of the target device this EPUB is meant for. Set to None to create a device independent EPUB. The profile is used for device specific restrictions on the EPUB. Choices are: ')+str(list(PROFILES.keys())))
|
||||
|
||||
structure = c.add_group('structure detection', _('Control auto-detection of document structure.'))
|
||||
structure('chapter', ['--chapter'], default="//*[re:match(name(), 'h[1-2]') and re:test(., 'chapter|book|section', 'i')]",
|
||||
|
@ -17,7 +17,7 @@ from calibre.ebooks.epub import config as common_config
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.ebooks.metadata.toc import TOC
|
||||
from calibre.ebooks.epub import initialize_container
|
||||
from calibre.ebooks.epub import initialize_container, PROFILES
|
||||
|
||||
|
||||
class HTMLProcessor(Processor):
|
||||
@ -38,6 +38,13 @@ class HTMLProcessor(Processor):
|
||||
|
||||
self.split()
|
||||
|
||||
def save(self):
|
||||
file = Processor.save(self)
|
||||
with open(file, 'rb') as f:
|
||||
f.seek(0, 2)
|
||||
size = f.tell()
|
||||
if size > self.opts.profile.flow_size:
|
||||
self.split()
|
||||
|
||||
|
||||
def collect_font_statistics(self):
|
||||
@ -87,6 +94,7 @@ def convert(htmlfile, opts, notification=None):
|
||||
htmlfile = os.path.abspath(htmlfile)
|
||||
if opts.output is None:
|
||||
opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub'
|
||||
opts.profile = PROFILES[opts.profile]
|
||||
opts.output = os.path.abspath(opts.output)
|
||||
if htmlfile.lower().endswith('.opf'):
|
||||
opf = OPFReader(htmlfile, os.path.dirname(os.path.abspath(htmlfile)))
|
||||
|
@ -438,7 +438,7 @@ class Processor(Parser):
|
||||
style = etree.SubElement(head, 'style', attrib={'type':'text/css'})
|
||||
style.text='\n'+self.css
|
||||
style.tail = '\n\n'
|
||||
Parser.save(self)
|
||||
return Parser.save(self)
|
||||
|
||||
def populate_toc(self, toc):
|
||||
if self.level >= self.opts.max_toc_recursion:
|
||||
|
@ -1,4 +0,0 @@
|
||||
from csscombine import csscombine
|
||||
__all__ = ["csscapture", "csscombine", "cssparse"]
|
||||
|
||||
|
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Retrieve all CSS stylesheets including embedded for a given URL.
|
||||
Retrieve as StyleSheetList or save to disk - raw, parsed or minified version.
|
||||
|
||||
TODO:
|
||||
- maybe use DOM 3 load/save?
|
||||
- logger class which handles all cases when no log is given...
|
||||
- saveto: why does urllib2 hang?
|
||||
"""
|
||||
__all__ = ['CSSCapture']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csscapture.py 1332 2008-07-09 13:12:56Z cthedot $'
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
from cssutils.script import CSSCapture
|
||||
|
||||
def main(args=None):
|
||||
usage = "usage: %prog [options] URL"
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('-d', '--debug', action='store_true', dest='debug',
|
||||
help='show debug messages during capturing')
|
||||
parser.add_option('-m', '--minified', action='store_true', dest='minified',
|
||||
help='saves minified version of captured files')
|
||||
parser.add_option('-n', '--notsave', action='store_true', dest='notsave',
|
||||
help='if given files are NOT saved, only log is written')
|
||||
# parser.add_option('-r', '--saveraw', action='store_true', dest='saveraw',
|
||||
# help='if given saves raw css otherwise cssutils\' parsed files')
|
||||
parser.add_option('-s', '--saveto', action='store', dest='saveto',
|
||||
help='saving retrieved files to "saveto", defaults to "_CSSCapture_SAVED"')
|
||||
parser.add_option('-u', '--useragent', action='store', dest='ua',
|
||||
help='useragent to use for request of URL, default is urllib2s default')
|
||||
options, url = parser.parse_args()
|
||||
|
||||
# TODO:
|
||||
options.saveraw = False
|
||||
|
||||
if not url:
|
||||
parser.error('no URL given')
|
||||
else:
|
||||
url = url[0]
|
||||
|
||||
if options.debug:
|
||||
level = logging.DEBUG
|
||||
else:
|
||||
level = logging.INFO
|
||||
|
||||
# START
|
||||
c = CSSCapture(ua=options.ua, defaultloglevel=level)
|
||||
|
||||
stylesheetlist = c.capture(url)
|
||||
|
||||
if options.notsave is None or not options.notsave:
|
||||
if options.saveto:
|
||||
saveto = options.saveto
|
||||
else:
|
||||
saveto = u'_CSSCapture_SAVED'
|
||||
c.saveto(saveto, saveraw=options.saveraw, minified=options.minified)
|
||||
else:
|
||||
for i, s in enumerate(stylesheetlist):
|
||||
print u'''%s.
|
||||
encoding: %r
|
||||
title: %r
|
||||
href: %r''' % (i + 1, s.encoding, s.title, s.href)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Combine sheets referred to by @import rules in a given CSS proxy sheet
|
||||
into a single new sheet.
|
||||
|
||||
- proxy currently is a path (no URI!)
|
||||
- in @import rules only relative paths do work for now but should be used
|
||||
anyway
|
||||
- currently no nested @imports are resolved
|
||||
- messages are send to stderr
|
||||
- output to stdout.
|
||||
|
||||
Example::
|
||||
|
||||
csscombine sheets\csscombine-proxy.css -m -t ascii -s utf-8
|
||||
1>combined.css 2>log.txt
|
||||
|
||||
results in log.txt::
|
||||
|
||||
COMBINING sheets/csscombine-proxy.css
|
||||
USING SOURCE ENCODING: css
|
||||
* PROCESSING @import sheets\csscombine-1.css
|
||||
* PROCESSING @import sheets\csscombine-2.css
|
||||
INFO Nested @imports are not combined: @import "1.css";
|
||||
SETTING TARGET ENCODING: ascii
|
||||
|
||||
and combined.css::
|
||||
|
||||
@charset "ascii";@import"1.css";@namespaces2"uri";s2|sheet-1{top:1px}s2|sheet-2{top:2px}proxy{top:3px}
|
||||
|
||||
or without option -m::
|
||||
|
||||
@charset "ascii";
|
||||
@import "1.css";
|
||||
@namespace s2 "uri";
|
||||
@namespace other "other";
|
||||
/* proxy sheet were imported sheets should be combined */
|
||||
/* non-ascii chars: \F6 \E4 \FC */
|
||||
/* @import "csscombine-1.css"; */
|
||||
/* combined sheet 1 */
|
||||
s2|sheet-1 {
|
||||
top: 1px
|
||||
}
|
||||
/* @import url(csscombine-2.css); */
|
||||
/* combined sheet 2 */
|
||||
s2|sheet-2 {
|
||||
top: 2px
|
||||
}
|
||||
proxy {
|
||||
top: 3px
|
||||
}
|
||||
|
||||
TODO
|
||||
- URL or file hrefs? URI should be default
|
||||
- no nested @imports are resolved yet
|
||||
- maybe add a config file which is used?
|
||||
|
||||
"""
|
||||
__all__ = ['csscombine']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: csscombine.py 1332 2008-07-09 13:12:56Z cthedot $'
|
||||
|
||||
import optparse
|
||||
import sys
|
||||
from cssutils.script import csscombine
|
||||
|
||||
def main(args=None):
|
||||
usage = "usage: %prog [options] path"
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('-s', '--sourceencoding', action='store',
|
||||
dest='sourceencoding',
|
||||
help='encoding of input, defaulting to "css". If given overwrites other encoding information like @charset declarations')
|
||||
parser.add_option('-t', '--targetencoding', action='store',
|
||||
dest='targetencoding',
|
||||
help='encoding of output, defaulting to "UTF-8"', default='utf-8')
|
||||
parser.add_option('-m', '--minify', action='store_true', dest='minify',
|
||||
default=False,
|
||||
help='saves minified version of combined files, defaults to False')
|
||||
options, path = parser.parse_args()
|
||||
|
||||
if not path:
|
||||
parser.error('no path given')
|
||||
else:
|
||||
path = path[0]
|
||||
|
||||
print csscombine(path, options.sourceencoding, options.targetencoding,
|
||||
options.minify)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""utility script to parse given filenames or string
|
||||
"""
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssparse.py 1327 2008-07-08 21:17:12Z cthedot $'
|
||||
|
||||
import cssutils
|
||||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
|
||||
def main(args=None):
|
||||
"""
|
||||
Parses given filename(s) or string (using optional encoding) and prints
|
||||
the parsed style sheet to stdout.
|
||||
|
||||
Redirect stdout to save CSS. Redirect stderr to save parser log infos.
|
||||
"""
|
||||
usage = """usage: %prog [options] filename1.css [filename2.css ...]
|
||||
[>filename_combined.css] [2>parserinfo.log] """
|
||||
p = optparse.OptionParser(usage=usage)
|
||||
p.add_option('-e', '--encoding', action='store', dest='encoding',
|
||||
help='encoding of the file')
|
||||
p.add_option('-d', '--debug', action='store_true', dest='debug',
|
||||
help='activate debugging output')
|
||||
p.add_option('-m', '--minify', action='store_true', dest='minify',
|
||||
help='minify parsed CSS', default=False)
|
||||
p.add_option('-s', '--string', action='store_true', dest='string',
|
||||
help='parse given string')
|
||||
|
||||
(options, params) = p.parse_args(args)
|
||||
|
||||
if not params:
|
||||
p.error("no filename given")
|
||||
|
||||
if options.debug:
|
||||
p = cssutils.CSSParser(loglevel=logging.DEBUG)
|
||||
else:
|
||||
p = cssutils.CSSParser()
|
||||
|
||||
if options.minify:
|
||||
cssutils.ser.prefs.useMinified()
|
||||
|
||||
if options.string:
|
||||
sheet = p.parseString(u''.join(params), encoding=options.encoding)
|
||||
print sheet.cssText
|
||||
print
|
||||
sys.stderr.write('\n')
|
||||
else:
|
||||
for filename in params:
|
||||
sys.stderr.write('=== CSS FILE: "%s" ===\n' % filename)
|
||||
sheet = p.parseFile(filename, encoding=options.encoding)
|
||||
print sheet.cssText
|
||||
print
|
||||
sys.stderr.write('\n')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -70,7 +70,7 @@ Usage may be::
|
||||
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__author__ = 'Christof Hoeke with contributions by Walter Doerwald'
|
||||
__date__ = '$LastChangedDate:: 2008-08-11 20:11:23 +0200 #$:'
|
||||
__date__ = '$LastChangedDate:: 2008-08-11 11:11:23 -0700 #$:'
|
||||
|
||||
VERSION = '0.9.5.1'
|
||||
|
@ -61,7 +61,7 @@ simply called like functions.)
|
||||
"""
|
||||
__all__ = ['CSS2Properties', 'cssvalues']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
__version__ = '$Id: cssproperties.py 1469 2008-09-15 19:06:00Z cthedot $'
|
||||
|
||||
import re
|
||||
|
||||
@ -168,7 +168,7 @@ cssvalues = {
|
||||
'border-top-width': r'{border-width}|inherit',
|
||||
'border-right-width': r'{border-width}|inherit',
|
||||
'border-bottom-width': r'{border-width}|inherit',
|
||||
'border-right-width': r'{border-width}|inherit',
|
||||
'border-left-width': r'{border-width}|inherit',
|
||||
'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
|
||||
'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'bottom': r'{length}|{percentage}|auto|inherit',
|
@ -5,16 +5,18 @@
|
||||
- CSSValueList implements DOM Level 2 CSS CSSValueList
|
||||
|
||||
"""
|
||||
__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList']
|
||||
__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList', 'CSSColor']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssvalue.py 1228 2008-05-19 19:59:50Z cthedot $'
|
||||
__version__ = '$Id: cssvalue.py 1473 2008-09-15 21:15:54Z cthedot $'
|
||||
|
||||
import re
|
||||
import xml.dom
|
||||
import cssutils
|
||||
import cssproperties
|
||||
from cssutils.profiles import profiles
|
||||
from cssutils.prodparser import *
|
||||
|
||||
class CSSValue(cssutils.util.Base):
|
||||
|
||||
class CSSValue(cssutils.util.Base2):
|
||||
"""
|
||||
The CSSValue interface represents a simple or a complex value.
|
||||
A CSSValue object only occurs in a context of a CSS property
|
||||
@ -73,7 +75,7 @@ class CSSValue(cssutils.util.Base):
|
||||
"""
|
||||
super(CSSValue, self).__init__()
|
||||
|
||||
self.seq = []
|
||||
#self.seq = []
|
||||
self.valid = False
|
||||
self.wellformed = False
|
||||
self._valueValue = u''
|
||||
@ -88,14 +90,26 @@ class CSSValue(cssutils.util.Base):
|
||||
self._readonly = readonly
|
||||
|
||||
def _getValue(self):
|
||||
# TODO:
|
||||
v = []
|
||||
for x in self.seq:
|
||||
if isinstance(x, cssutils.css.CSSComment):
|
||||
for item in self.seq:
|
||||
type_, val = item.type, item.value
|
||||
if isinstance(val, cssutils.css.CSSComment):
|
||||
# only value here
|
||||
continue
|
||||
elif isinstance(x, basestring):
|
||||
v.append(x)
|
||||
else: # maybe CSSPrimitiveValue
|
||||
v.append(x.cssText)
|
||||
elif 'STRING' == type_:
|
||||
v.append(cssutils.ser._string(val))
|
||||
elif 'URI' == type_:
|
||||
v.append(cssutils.ser._uri(val))
|
||||
elif u',' == val:
|
||||
# list of items
|
||||
v.append(u' ')
|
||||
v.append(val)
|
||||
elif isinstance(val, basestring):
|
||||
v.append(val)
|
||||
else:
|
||||
# maybe CSSPrimitiveValue
|
||||
v.append(val.cssText)
|
||||
if v and u'' == v[-1].strip():
|
||||
# simple strip of joined string does not work for escaped spaces
|
||||
del v[-1]
|
||||
@ -158,15 +172,17 @@ class CSSValue(cssutils.util.Base):
|
||||
self._checkReadonly()
|
||||
|
||||
# for closures: must be a mutable
|
||||
new = {'values': [],
|
||||
new = {'rawvalues': [], # used for validation
|
||||
'values': [],
|
||||
'commas': 0,
|
||||
'valid': True,
|
||||
'wellformed': True }
|
||||
|
||||
def _S(expected, seq, token, tokenizer=None):
|
||||
val = self._tokenvalue(token)
|
||||
if expected.endswith('operator'):
|
||||
seq.append(u' ')
|
||||
type_, val, line, col = token
|
||||
new['rawvalues'].append(u' ')
|
||||
if expected == 'operator': #expected.endswith('operator'):
|
||||
seq.append(u' ', 'separator', line=line, col=col)
|
||||
return 'term or operator'
|
||||
elif expected.endswith('S'):
|
||||
return 'term or S'
|
||||
@ -174,60 +190,69 @@ class CSSValue(cssutils.util.Base):
|
||||
return expected
|
||||
|
||||
def _char(expected, seq, token, tokenizer=None):
|
||||
val = self._tokenvalue(token)
|
||||
type_, val, line, col = token
|
||||
new['rawvalues'].append(val)
|
||||
|
||||
if 'funcend' == expected and u')' == val:
|
||||
# end of FUNCTION
|
||||
seq[-1] += val
|
||||
seq.appendToVal(val)
|
||||
new['values'].append(seq[-1])
|
||||
return 'operator'
|
||||
|
||||
elif expected in (')', ']', '}') and expected == val:
|
||||
# end of any block: (), [], {}
|
||||
seq[-1] += val
|
||||
seq.appendToVal(val)
|
||||
return 'operator'
|
||||
|
||||
elif expected in ('funcend', ')', ']', '}'):
|
||||
# content of func or block: (), [], {}
|
||||
seq[-1] += val
|
||||
seq.appendToVal(val)
|
||||
return expected
|
||||
|
||||
elif expected.endswith('operator') and ',' == val:
|
||||
# term , term
|
||||
# term, term; remove all WS between terms!!!
|
||||
new['commas'] += 1
|
||||
if seq and seq[-1] == u' ':
|
||||
seq[-1] = val
|
||||
if seq and seq[-1].type == 'separator':
|
||||
seq.replace(-1, val, type_, line=line, col=col)
|
||||
else:
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'term or S'
|
||||
|
||||
elif expected.endswith('operator') and '/' == val:
|
||||
# term / term
|
||||
if seq and seq[-1] == u' ':
|
||||
seq[-1] = val
|
||||
if seq and seq[-1].value == u' ':
|
||||
old = seq[-1]
|
||||
seq.replace(-1, val, old.type, old.line, old.col)
|
||||
#seq[-1] = val
|
||||
else:
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'term or S'
|
||||
|
||||
elif expected.startswith('term') and u'(' == val:
|
||||
# start of ( any* ) block
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return ')'
|
||||
elif expected.startswith('term') and u'[' == val:
|
||||
# start of [ any* ] block
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return ']'
|
||||
elif expected.startswith('term') and u'{' == val:
|
||||
# start of { any* } block
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return '}'
|
||||
elif expected.startswith('term') and u'-' == val or u'+' == 'val':
|
||||
# unary operator
|
||||
seq.append(val)
|
||||
elif expected.startswith('term') and u'+' == val:
|
||||
# unary operator "+"
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
new['values'].append(val)
|
||||
return 'number percentage dimension'
|
||||
elif expected.startswith('term') and u'-' == val:
|
||||
# unary "-" operator
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
new['values'].append(val)
|
||||
return 'number percentage dimension'
|
||||
elif expected.startswith('term') and u'/' == val:
|
||||
# font-size/line-height separator
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
new['values'].append(val)
|
||||
return 'number percentage dimension'
|
||||
else:
|
||||
@ -237,71 +262,91 @@ class CSSValue(cssutils.util.Base):
|
||||
|
||||
def _number_percentage_dimension(expected, seq, token, tokenizer=None):
|
||||
# NUMBER PERCENTAGE DIMENSION after -/+ or operator
|
||||
type_, val, line, col = token
|
||||
new['rawvalues'].append(val)
|
||||
if expected.startswith('term') or expected == 'number percentage dimension':
|
||||
# normal value
|
||||
val = self._tokenvalue(token)
|
||||
if new['values'] and new['values'][-1] in (u'-', u'+'):
|
||||
new['values'][-1] += val
|
||||
else:
|
||||
new['values'].append(val)
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'operator'
|
||||
elif 'operator' == expected:
|
||||
# expected S but token which is ok
|
||||
val = self._tokenvalue(token)
|
||||
if new['values'] and new['values'][-1] in (u'-', u'+'):
|
||||
new['values'][-1] += val
|
||||
else:
|
||||
new['values'].append(u' ')
|
||||
seq.append(u' ')
|
||||
seq.append(u' ', 'separator') # self._prods.S
|
||||
new['values'].append(val)
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'operator'
|
||||
elif expected in ('funcend', ')', ']', '}'):
|
||||
# a block
|
||||
seq[-1] += self._tokenvalue(token)
|
||||
seq.appendToVal(val)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSValue: Unexpected token.', token)
|
||||
return expected
|
||||
|
||||
def _string_ident_uri_hexcolor(expected, seq, token, tokenizer=None):
|
||||
# STRING IDENT URI HASH
|
||||
def _string_ident_uri(expected, seq, token, tokenizer=None):
|
||||
# STRING IDENT URI
|
||||
type_, val, line, col = token
|
||||
|
||||
new['rawvalues'].append(val)
|
||||
if expected.startswith('term'):
|
||||
# normal value
|
||||
|
||||
# TODO: use actual values, probably needs Base2 for this
|
||||
typ = self._type(token)
|
||||
if self._prods.STRING == typ:
|
||||
val = u'"%s"' % self._stringtokenvalue(token)
|
||||
# elif 'URI' == typ:
|
||||
# val = u'url(%s)' % self._uritokenvalue(token)
|
||||
else:
|
||||
val = self._tokenvalue(token)
|
||||
|
||||
if self._prods.STRING == type_:
|
||||
val = self._stringtokenvalue(token)
|
||||
elif self._prods.URI == type_:
|
||||
val = self._uritokenvalue(token)
|
||||
new['values'].append(val)
|
||||
seq.append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'operator'
|
||||
elif 'operator' == expected:
|
||||
# expected S but still ok
|
||||
|
||||
# TODO: use actual values, probably needs Base2 for this
|
||||
typ = self._type(token)
|
||||
if self._prods.STRING == typ:
|
||||
val = u'"%s"' % self._stringtokenvalue(token)
|
||||
# elif 'URI' == typ:
|
||||
# val = u'url(%s)' % self._uritokenvalue(token)
|
||||
else:
|
||||
val = self._tokenvalue(token)
|
||||
if self._prods.STRING == type_:
|
||||
val = self._stringtokenvalue(token)
|
||||
elif self._prods.URI == type_:
|
||||
val = self._uritokenvalue(token)
|
||||
new['values'].append(u' ')
|
||||
new['values'].append(val)
|
||||
seq.append(u' ')
|
||||
seq.append(val)
|
||||
seq.append(u' ', 'separator') # self._prods.S
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'operator'
|
||||
elif expected in ('funcend', ')', ']', '}'):
|
||||
# a block
|
||||
seq[-1] += self._tokenvalue(token)
|
||||
seq.appendToVal(val)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
self._log.error(u'CSSValue: Unexpected token.', token)
|
||||
return expected
|
||||
|
||||
def _hash(expected, seq, token, tokenizer=None):
|
||||
# HASH
|
||||
type_, val, line, col = token
|
||||
new['rawvalues'].append(val)
|
||||
|
||||
val = CSSColor(cssText=token)
|
||||
type_ = type(val)
|
||||
if expected.startswith('term'):
|
||||
# normal value
|
||||
new['values'].append(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'operator'
|
||||
elif 'operator' == expected:
|
||||
# expected S but still ok
|
||||
new['values'].append(u' ')
|
||||
new['values'].append(val)
|
||||
seq.append(u' ', 'separator') # self._prods.S
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'operator'
|
||||
elif expected in ('funcend', ')', ']', '}'):
|
||||
# a block
|
||||
seq.appendToVal(val)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
@ -309,19 +354,32 @@ class CSSValue(cssutils.util.Base):
|
||||
return expected
|
||||
|
||||
def _function(expected, seq, token, tokenizer=None):
|
||||
# FUNCTION
|
||||
# FUNCTION incl colors
|
||||
type_, val, line, col = token
|
||||
|
||||
if self._normalize(val) in ('rgb(', 'rgba(', 'hsl(', 'hsla('):
|
||||
# a CSSColor
|
||||
val = CSSColor(cssText=(token, tokenizer))
|
||||
type_ = type(val)
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
new['values'].append(val)
|
||||
new['rawvalues'].append(val.cssText)
|
||||
return 'operator'
|
||||
|
||||
new['rawvalues'].append(val)
|
||||
|
||||
if expected.startswith('term'):
|
||||
# normal value but add if funcend if found
|
||||
seq.append(self._tokenvalue(token))
|
||||
# normal value but add if funcend is found
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'funcend'
|
||||
elif 'operator' == expected:
|
||||
# normal value but add if funcend if found
|
||||
seq.append(u' ')
|
||||
seq.append(self._tokenvalue(token))
|
||||
# normal value but add if funcend is found
|
||||
seq.append(u' ', 'separator') # self._prods.S
|
||||
seq.append(val, type_, line=line, col=col)
|
||||
return 'funcend'
|
||||
elif expected in ('funcend', ')', ']', '}'):
|
||||
# a block
|
||||
seq[-1] += self._tokenvalue(token)
|
||||
seq.appendToVal(val)
|
||||
return expected
|
||||
else:
|
||||
new['wellformed'] = False
|
||||
@ -335,11 +393,9 @@ class CSSValue(cssutils.util.Base):
|
||||
self._log.error(u'CSSValue: Unknown syntax or no value: %r.' %
|
||||
self._valuestr(cssText))
|
||||
else:
|
||||
# TODO: not very efficient tokenizing twice!
|
||||
tokenizer = self._tokenize2(cssText)
|
||||
newseq = []
|
||||
newseq = self._tempSeq() # []
|
||||
wellformed, expected = self._parse(expected='term',
|
||||
seq=newseq, tokenizer=tokenizer,
|
||||
seq=newseq, tokenizer=tokenizer,initialtoken=linetoken,
|
||||
productions={'S': _S,
|
||||
'CHAR': _char,
|
||||
|
||||
@ -347,11 +403,11 @@ class CSSValue(cssutils.util.Base):
|
||||
'PERCENTAGE': _number_percentage_dimension,
|
||||
'DIMENSION': _number_percentage_dimension,
|
||||
|
||||
'STRING': _string_ident_uri_hexcolor,
|
||||
'IDENT': _string_ident_uri_hexcolor,
|
||||
'URI': _string_ident_uri_hexcolor,
|
||||
'HASH': _string_ident_uri_hexcolor,
|
||||
'UNICODE-RANGE': _string_ident_uri_hexcolor, #?
|
||||
'STRING': _string_ident_uri,
|
||||
'IDENT': _string_ident_uri,
|
||||
'URI': _string_ident_uri,
|
||||
'HASH': _hash,
|
||||
'UNICODE-RANGE': _string_ident_uri, #?
|
||||
|
||||
'FUNCTION': _function
|
||||
})
|
||||
@ -359,7 +415,16 @@ class CSSValue(cssutils.util.Base):
|
||||
wellformed = wellformed and new['wellformed']
|
||||
|
||||
# post conditions
|
||||
if expected.startswith('term') and newseq and newseq[-1] != u' ' or (
|
||||
def lastseqvalue(seq):
|
||||
"""find last actual value in seq, not COMMENT!"""
|
||||
for i, item in enumerate(reversed(seq)):
|
||||
if 'COMMENT' != item.type:
|
||||
return len(seq)-1-i, item.value
|
||||
else:
|
||||
return 0, None
|
||||
lastpos, lastval = lastseqvalue(newseq)
|
||||
|
||||
if expected.startswith('term') and lastval != u' ' or (
|
||||
expected in ('funcend', ')', ']', '}')):
|
||||
wellformed = False
|
||||
self._log.error(u'CSSValue: Incomplete value: %r.' %
|
||||
@ -371,11 +436,14 @@ class CSSValue(cssutils.util.Base):
|
||||
self._valuestr(cssText))
|
||||
|
||||
else:
|
||||
self._linetoken = linetoken # used for line report
|
||||
self.seq = newseq
|
||||
self.valid = False
|
||||
# remove last token if 'separator'
|
||||
if lastval == u' ':
|
||||
del newseq[lastpos]
|
||||
|
||||
self._validate()
|
||||
self._linetoken = linetoken # used for line report
|
||||
self._setSeq(newseq)
|
||||
|
||||
self.valid = self._validate(u''.join(new['rawvalues']))
|
||||
|
||||
if len(new['values']) == 1 and new['values'][0] == u'inherit':
|
||||
self._value = u'inherit'
|
||||
@ -419,24 +487,43 @@ class CSSValue(cssutils.util.Base):
|
||||
cssValueTypeString = property(_getCssValueTypeString,
|
||||
doc="cssutils: Name of cssValueType of this CSSValue (readonly).")
|
||||
|
||||
def _validate(self):
|
||||
def _validate(self, value=None, profile=None):
|
||||
"""
|
||||
validates value against _propertyName context if given
|
||||
"""
|
||||
valid = False
|
||||
if self._value:
|
||||
if self._propertyName in cssproperties.cssvalues:
|
||||
if cssproperties.cssvalues[self._propertyName](self._value):
|
||||
self.valid = True
|
||||
else:
|
||||
self.valid = False
|
||||
if self._propertyName and self._propertyName in profiles.propertiesByProfile():
|
||||
valid, validprofile = \
|
||||
profiles.validateWithProfile(self._propertyName,
|
||||
self._normalize(self._value))
|
||||
if not validprofile:
|
||||
validprofile = u''
|
||||
|
||||
if not valid:
|
||||
self._log.warn(
|
||||
u'CSSValue: Invalid value for CSS2 property %r: %r' %
|
||||
(self._propertyName, self._value), neverraise=True)
|
||||
u'CSSValue: Invalid value for %s property "%s: %s".' %
|
||||
(validprofile, self._propertyName,
|
||||
self._value), neverraise=True)
|
||||
elif profile and validprofile != profile:
|
||||
self._log.warn(
|
||||
u'CSSValue: Invalid value for %s property "%s: %s" but valid %s property.' %
|
||||
(profile, self._propertyName, self._value,
|
||||
validprofile), neverraise=True)
|
||||
else:
|
||||
self._log.debug(
|
||||
u'CSSValue: Unable to validate as no or unknown property context set for this value: %r'
|
||||
u'CSSValue: Found valid %s property "%s: %s".' %
|
||||
(validprofile, self._propertyName, self._value),
|
||||
neverraise=True)
|
||||
else:
|
||||
self._log.debug(u'CSSValue: Unable to validate as no or unknown property context set for value: %r'
|
||||
% self._value, neverraise=True)
|
||||
|
||||
if not value:
|
||||
# if value is given this should not be saved
|
||||
self.valid = valid
|
||||
return valid
|
||||
|
||||
def _get_propertyName(self):
|
||||
return self.__propertyName
|
||||
|
||||
@ -608,6 +695,16 @@ class CSSPrimitiveValue(CSSValue):
|
||||
no value is given as self._value is used
|
||||
"""
|
||||
primitiveType = self.CSS_UNKNOWN
|
||||
|
||||
for item in self.seq:
|
||||
if item.type == self._prods.URI:
|
||||
primitiveType = self.CSS_URI
|
||||
break
|
||||
elif item.type == self._prods.STRING:
|
||||
primitiveType = self.CSS_STRING
|
||||
break
|
||||
else:
|
||||
|
||||
_floatType = False # if unary expect NUMBER DIMENSION or PERCENTAGE
|
||||
tokenizer = self._tokenize2(self._value)
|
||||
t = self._nexttoken(tokenizer)
|
||||
@ -635,7 +732,7 @@ class CSSPrimitiveValue(CSSValue):
|
||||
elif expected == 'comma' and val == ',':
|
||||
expected = 'ident or string'
|
||||
fontstring += 1
|
||||
elif typ in (self._prods.S, self._prods.COMMENT):
|
||||
elif typ in ('separator', self._prods.S, self._prods.COMMENT):
|
||||
continue
|
||||
else:
|
||||
fontstring = False
|
||||
@ -711,7 +808,7 @@ class CSSPrimitiveValue(CSSValue):
|
||||
|
||||
return val, dim
|
||||
|
||||
def getFloatValue(self, unitType):
|
||||
def getFloatValue(self, unitType=None):
|
||||
"""
|
||||
(DOM method) This method is used to get a float value in a
|
||||
specified unit. If this CSS value doesn't contain a float value
|
||||
@ -722,20 +819,22 @@ class CSSPrimitiveValue(CSSValue):
|
||||
to get the float value. The unit code can only be a float unit type
|
||||
(i.e. CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS, CSS_PX, CSS_CM,
|
||||
CSS_MM, CSS_IN, CSS_PT, CSS_PC, CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS,
|
||||
CSS_S, CSS_HZ, CSS_KHZ, CSS_DIMENSION).
|
||||
CSS_S, CSS_HZ, CSS_KHZ, CSS_DIMENSION) or None in which case
|
||||
the current dimension is used.
|
||||
|
||||
returns not necessarily a float but some cases just an int
|
||||
returns not necessarily a float but some cases just an integer
|
||||
e.g. if the value is ``1px`` it return ``1`` and **not** ``1.0``
|
||||
|
||||
conversions might return strange values like 1.000000000001
|
||||
"""
|
||||
if unitType not in self._floattypes:
|
||||
if unitType is not None and unitType not in self._floattypes:
|
||||
raise xml.dom.InvalidAccessErr(
|
||||
u'unitType Parameter is not a float type')
|
||||
|
||||
val, dim = self.__getValDim()
|
||||
|
||||
if self.primitiveType != unitType:
|
||||
if unitType is not None and self.primitiveType != unitType:
|
||||
# convert if needed
|
||||
try:
|
||||
val = self._converter[self.primitiveType, unitType](val)
|
||||
except KeyError:
|
||||
@ -937,6 +1036,17 @@ class CSSPrimitiveValue(CSSValue):
|
||||
# TODO: use Rect class
|
||||
raise NotImplementedError()
|
||||
|
||||
def _getCssText(self):
|
||||
"""overwritten from CSSValue"""
|
||||
return cssutils.ser.do_css_CSSPrimitiveValue(self)
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
"""use CSSValue's implementation"""
|
||||
return super(CSSPrimitiveValue, self)._setCssText(cssText)
|
||||
|
||||
cssText = property(_getCssText, _setCssText,
|
||||
doc="A string representation of the current value.")
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object primitiveType=%s cssText=%r _propertyName=%r valid=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.primitiveTypeString,
|
||||
@ -972,24 +1082,24 @@ class CSSValueList(CSSValue):
|
||||
ivalueseq, valueseq = 0, self._SHORTHANDPROPERTIES.get(
|
||||
self._propertyName, [])
|
||||
self._items = []
|
||||
newseq = []
|
||||
newseq = self._tempSeq(False)
|
||||
i, max = 0, len(self.seq)
|
||||
minus = None
|
||||
while i < max:
|
||||
v = self.seq[i]
|
||||
|
||||
if u'-' == v:
|
||||
item = self.seq[i]
|
||||
type_, val, line, col = item.type, item.value, item.line, item.col
|
||||
if u'-' == val:
|
||||
if minus: # 2 "-" after another
|
||||
self._log.error(
|
||||
self._log.error( # TODO:
|
||||
u'CSSValueList: Unknown syntax: %r.'
|
||||
% u''.join(self.seq))
|
||||
else:
|
||||
minus = v
|
||||
minus = val
|
||||
|
||||
elif isinstance(v, basestring) and not v.strip() == u'' and\
|
||||
not u'/' == v:
|
||||
elif isinstance(val, basestring) and not type_ == 'separator' and\
|
||||
not u'/' == val:
|
||||
if minus:
|
||||
v = minus + v
|
||||
val = minus + val
|
||||
minus = None
|
||||
# TODO: complete
|
||||
# if shorthand get new propname
|
||||
@ -1007,55 +1117,70 @@ class CSSValueList(CSSValue):
|
||||
if propname in self._SHORTHANDPROPERTIES:
|
||||
propname = None
|
||||
|
||||
if i+1 < max and self.seq[i+1] == u',':
|
||||
if i+1 < max and self.seq[i+1].value == u',':
|
||||
# a comma separated list of values as ONE value
|
||||
# e.g. font-family: a,b
|
||||
fullvalue = [v]
|
||||
# CSSValue already has removed extra S tokens!
|
||||
fullvalue = [val]
|
||||
|
||||
expected = 'comma' # or 'value'
|
||||
for j in range(i+1, max):
|
||||
testv = self.seq[j]
|
||||
if u' ' == testv: # a single value follows
|
||||
item2 = self.seq[j]
|
||||
typ2, val2, line2, col2 = (item2.type, item2.value,
|
||||
item2.line, item2.col)
|
||||
if u' ' == val2:
|
||||
# end or a single value follows
|
||||
break
|
||||
elif testv in ('-', '+') and expected == 'value':
|
||||
elif 'value' == expected and val2 in u'-+':
|
||||
# unary modifier
|
||||
fullvalue.append(testv)
|
||||
fullvalue.append(val2)
|
||||
expected = 'value'
|
||||
elif u',' == testv and expected == 'comma':
|
||||
fullvalue.append(testv)
|
||||
elif 'comma' == expected and u',' == val2:
|
||||
fullvalue.append(val2)
|
||||
expected = 'value'
|
||||
elif u',' != testv and expected == 'value':
|
||||
fullvalue.append(testv)
|
||||
elif 'value' == expected and u',' != val2:
|
||||
if 'STRING' == typ2:
|
||||
val2 = cssutils.ser._string(val2)
|
||||
fullvalue.append(val2)
|
||||
expected = 'comma'
|
||||
else:
|
||||
self._log.error(
|
||||
u'CSSValueList: Unknown syntax: %r.'
|
||||
% testv)
|
||||
% val2)
|
||||
return
|
||||
if expected == 'value':
|
||||
self._log.error(
|
||||
self._log.error( # TODO:
|
||||
u'CSSValueList: Unknown syntax: %r.'
|
||||
% u''.join(self.seq))
|
||||
return
|
||||
# setting _propertyName this way does not work
|
||||
# for compound props like font!
|
||||
i += len(fullvalue) - 1
|
||||
o = CSSValue(cssText=u''.join(fullvalue),
|
||||
obj = CSSValue(cssText=u''.join(fullvalue),
|
||||
_propertyName=propname)
|
||||
else:
|
||||
# a single value, u' ' or nothing should be following
|
||||
o = CSSValue(cssText=v, _propertyName=propname)
|
||||
if 'STRING' == type_:
|
||||
val = cssutils.ser._string(val)
|
||||
elif 'URI' == type_:
|
||||
val = cssutils.ser._uri(val)
|
||||
|
||||
self._items.append(o)
|
||||
newseq.append(o)
|
||||
obj = CSSValue(cssText=val, _propertyName=propname)
|
||||
|
||||
self._items.append(obj)
|
||||
newseq.append(obj, CSSValue)
|
||||
|
||||
elif CSSColor == type_:
|
||||
self._items.append(val)
|
||||
newseq.append(val, CSSColor)
|
||||
|
||||
else:
|
||||
# S (or TODO: comment?)
|
||||
newseq.append(v)
|
||||
newseq.append(val, type_)
|
||||
|
||||
i += 1
|
||||
|
||||
self.seq = newseq
|
||||
self._setSeq(newseq)
|
||||
|
||||
length = property(lambda self: len(self._items),
|
||||
doc="(DOM attribute) The number of CSSValues in the list.")
|
||||
@ -1081,6 +1206,174 @@ class CSSValueList(CSSValue):
|
||||
for i in range (0, self.length):
|
||||
yield self.item(i)
|
||||
|
||||
def __str_(self):
|
||||
return "<cssutils.css.%s object length=%s at 0x%x>" % (
|
||||
self.__class__.__name__, self.length, id(self))
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object cssValueType=%r cssText=%r length=%r propname=%r valid=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.cssValueTypeString,
|
||||
self.cssText, self.length, self._propertyName,
|
||||
self.valid, id(self))
|
||||
|
||||
|
||||
class CSSFunction(CSSPrimitiveValue):
|
||||
"""A CSS function value like rect() etc."""
|
||||
|
||||
def __init__(self, cssText=None, readonly=False):
|
||||
"""
|
||||
Init a new CSSFunction
|
||||
|
||||
cssText
|
||||
the parsable cssText of the value
|
||||
readonly
|
||||
defaults to False
|
||||
"""
|
||||
super(CSSColor, self).__init__()
|
||||
self.valid = False
|
||||
self.wellformed = False
|
||||
if cssText is not None:
|
||||
self.cssText = cssText
|
||||
|
||||
self._funcType = None
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
self._checkReadonly()
|
||||
if False:
|
||||
pass
|
||||
else:
|
||||
types = self._prods # rename!
|
||||
valueProd = Prod(name='value',
|
||||
match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE),
|
||||
toSeq=CSSPrimitiveValue,
|
||||
toStore='parts'
|
||||
)
|
||||
# COLOR PRODUCTION
|
||||
funcProds = Sequence([
|
||||
Prod(name='FUNC',
|
||||
match=lambda t, v: t == types.FUNCTION,
|
||||
toStore='funcType' ),
|
||||
Prod(**PreDef.sign),
|
||||
valueProd,
|
||||
# more values starting with Comma
|
||||
# should use store where colorType is saved to
|
||||
# define min and may, closure?
|
||||
Sequence([Prod(**PreDef.comma),
|
||||
Prod(**PreDef.sign),
|
||||
valueProd],
|
||||
minmax=lambda: (2, 2)),
|
||||
Prod(**PreDef.funcEnd)
|
||||
])
|
||||
# store: colorType, parts
|
||||
wellformed, seq, store, unusedtokens = ProdsParser().parse(cssText,
|
||||
u'CSSFunction',
|
||||
funcProds,
|
||||
{'parts': []})
|
||||
|
||||
if wellformed:
|
||||
self.wellformed = True
|
||||
self._setSeq(seq)
|
||||
self._funcType = self._normalize(store['colorType'].value[:-1])
|
||||
|
||||
cssText = property(lambda self: cssutils.ser.do_css_CSSColor(self),
|
||||
_setCssText)
|
||||
|
||||
funcType = property(lambda self: self._funcType)
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(%r)" % (self.__class__.__name__, self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object colorType=%r cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.colorType, self.cssText,
|
||||
id(self))
|
||||
|
||||
|
||||
|
||||
|
||||
class CSSColor(CSSPrimitiveValue):
|
||||
"""A CSS color like RGB, RGBA or a simple value like `#000` or `red`."""
|
||||
|
||||
def __init__(self, cssText=None, readonly=False):
|
||||
"""
|
||||
Init a new CSSColor
|
||||
|
||||
cssText
|
||||
the parsable cssText of the value
|
||||
readonly
|
||||
defaults to False
|
||||
"""
|
||||
super(CSSColor, self).__init__()
|
||||
self._colorType = None
|
||||
self.valid = False
|
||||
self.wellformed = False
|
||||
if cssText is not None:
|
||||
self.cssText = cssText
|
||||
|
||||
self._readonly = readonly
|
||||
|
||||
def _setCssText(self, cssText):
|
||||
self._checkReadonly()
|
||||
if False:
|
||||
pass
|
||||
else:
|
||||
types = self._prods # rename!
|
||||
valueProd = Prod(name='value',
|
||||
match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE),
|
||||
toSeq=CSSPrimitiveValue,
|
||||
toStore='parts'
|
||||
)
|
||||
# COLOR PRODUCTION
|
||||
funccolor = Sequence([Prod(name='FUNC',
|
||||
match=lambda t, v: self._normalize(v) in ('rgb(', 'rgba(', 'hsl(', 'hsla(') and t == types.FUNCTION,
|
||||
toSeq=lambda v: self._normalize(v),
|
||||
toStore='colorType' ),
|
||||
PreDef.unary(),
|
||||
valueProd,
|
||||
# 2 or 3 more values starting with Comma
|
||||
Sequence([PreDef.comma(),
|
||||
PreDef.unary(),
|
||||
valueProd],
|
||||
minmax=lambda: (2,3)),
|
||||
PreDef.funcEnd()
|
||||
]
|
||||
)
|
||||
colorprods = Choice([funccolor,
|
||||
Prod(name='HEX color',
|
||||
match=lambda t, v: t == types.HASH and
|
||||
len(v) == 4 or len(v) == 7,
|
||||
toStore='colorType'
|
||||
),
|
||||
Prod(name='named color',
|
||||
match=lambda t, v: t == types.IDENT,
|
||||
toStore='colorType'
|
||||
),
|
||||
]
|
||||
)
|
||||
# store: colorType, parts
|
||||
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
||||
u'CSSColor',
|
||||
colorprods,
|
||||
{'parts': []})
|
||||
|
||||
if wellformed:
|
||||
self.wellformed = True
|
||||
if store['colorType'].type == self._prods.HASH:
|
||||
self._colorType = 'HEX'
|
||||
elif store['colorType'].type == self._prods.IDENT:
|
||||
self._colorType = 'Named Color'
|
||||
else:
|
||||
self._colorType = self._normalize(store['colorType'].value)[:-1]
|
||||
|
||||
self._setSeq(seq)
|
||||
|
||||
cssText = property(lambda self: cssutils.ser.do_css_CSSColor(self),
|
||||
_setCssText)
|
||||
|
||||
colorType = property(lambda self: self._colorType)
|
||||
|
||||
def __repr__(self):
|
||||
return "cssutils.css.%s(%r)" % (self.__class__.__name__, self.cssText)
|
||||
|
||||
def __str__(self):
|
||||
return "<cssutils.css.%s object colorType=%r cssText=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.colorType, self.cssText,
|
||||
id(self))
|
@ -4,11 +4,12 @@ Internal use only, may be removed in the future!
|
||||
"""
|
||||
__all__ = ['Property']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: property.py 1305 2008-06-22 18:42:51Z cthedot $'
|
||||
__version__ = '$Id: property.py 1444 2008-08-31 18:45:35Z cthedot $'
|
||||
|
||||
import xml.dom
|
||||
import cssutils
|
||||
import cssproperties
|
||||
#import cssproperties
|
||||
from cssutils.profiles import profiles
|
||||
from cssvalue import CSSValue
|
||||
from cssutils.helper import Deprecated
|
||||
|
||||
@ -236,10 +237,10 @@ class Property(cssutils.util.Base):
|
||||
self.seqs[0] = newseq
|
||||
|
||||
# validate
|
||||
if self._name not in cssproperties.cssvalues:
|
||||
if self._name not in profiles.propertiesByProfile():
|
||||
self.valid = False
|
||||
tokenizer=self._tokenize2(name)
|
||||
self._log.info(u'Property: No CSS2 Property: %r.' %
|
||||
self._log.warn(u'Property: Unknown Property: %r.' %
|
||||
new['literalname'], token=token, neverraise=True)
|
||||
else:
|
||||
self.valid = True
|
402
src/cssutils/prodparser.py
Normal file
402
src/cssutils/prodparser.py
Normal file
@ -0,0 +1,402 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Productions parser used by css and stylesheets classes to parse
|
||||
test into a cssutils.util.Seq and at the same time retrieving
|
||||
additional specific cssutils.util.Item objects for later use.
|
||||
|
||||
TODO:
|
||||
- ProdsParser
|
||||
- handle EOF or STOP?
|
||||
- handle unknown @rules
|
||||
- handle S: maybe save to Seq? parameterized?
|
||||
- store['_raw']: always?
|
||||
|
||||
- Sequence:
|
||||
- opt first(), naive impl for now
|
||||
|
||||
"""
|
||||
__all__ = ['ProdParser', 'Sequence', 'Choice', 'Prod', 'PreDef']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: parse.py 1418 2008-08-09 19:27:50Z cthedot $'
|
||||
|
||||
import cssutils
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Base Exception class for ProdParser (used internally)."""
|
||||
pass
|
||||
|
||||
class Exhausted(ParseError):
|
||||
"""Raised if Sequence or Choice is done."""
|
||||
pass
|
||||
|
||||
class NoMatch(ParseError):
|
||||
"""Raised if Sequence or Choice do not match."""
|
||||
pass
|
||||
|
||||
class MissingToken(ParseError):
|
||||
"""Raised if Sequence or Choice are not exhausted."""
|
||||
pass
|
||||
|
||||
|
||||
class Choice(object):
|
||||
"""A Choice of productions (Sequence or single Prod)."""
|
||||
def __init__(self, prods):
|
||||
"""
|
||||
prods
|
||||
Prod or Sequence objects
|
||||
"""
|
||||
self._prods = prods
|
||||
self._exhausted = False
|
||||
|
||||
def nextProd(self, token):
|
||||
"""
|
||||
Return:
|
||||
|
||||
- next matching Prod or Sequence
|
||||
- raises ParseError if nothing matches
|
||||
- raises Exhausted if choice already done
|
||||
|
||||
``token`` may be None but this occurs when no tokens left."""
|
||||
if not self._exhausted:
|
||||
for x in self._prods:
|
||||
if isinstance(x, Prod):
|
||||
test = x
|
||||
else:
|
||||
# nested Sequence matches if 1st prod matches
|
||||
test = x.first()
|
||||
try:
|
||||
if test.matches(token):
|
||||
self._exhausted = True
|
||||
return x
|
||||
except ParseError, e:
|
||||
# do not raise if other my match
|
||||
continue
|
||||
else:
|
||||
# None matched
|
||||
raise ParseError(u'No match in choice')
|
||||
else:
|
||||
raise Exhausted(u'Extra token')
|
||||
|
||||
|
||||
class Sequence(object):
|
||||
"""A Sequence of productions (Choice or single Prod)."""
|
||||
def __init__(self, prods, minmax=None):
|
||||
"""
|
||||
prods
|
||||
Prod or Sequence objects
|
||||
minmax = lambda: (1, 1)
|
||||
callback returning number of times this sequence may run
|
||||
"""
|
||||
self._prods = prods
|
||||
if not minmax:
|
||||
minmax = lambda: (1, 1)
|
||||
self._min, self._max = minmax()
|
||||
|
||||
self._number = len(self._prods)
|
||||
self._round = 1 # 1 based!
|
||||
self._pos = 0
|
||||
|
||||
def first(self):
|
||||
"""Return 1st element of Sequence, used by Choice"""
|
||||
# TODO: current impl first only if 1st if an prod!
|
||||
for prod in self._prods:
|
||||
if not prod.optional:
|
||||
return prod
|
||||
|
||||
def _currentName(self):
|
||||
"""Return current element of Sequence, used by name"""
|
||||
# TODO: current impl first only if 1st if an prod!
|
||||
for prod in self._prods[self._pos:]:
|
||||
if not prod.optional:
|
||||
return prod.name
|
||||
else:
|
||||
return 'Unknown'
|
||||
|
||||
name = property(_currentName, doc='Used for Error reporting')
|
||||
|
||||
def nextProd(self, token):
|
||||
"""Return
|
||||
|
||||
- next matching Prod or Choice
|
||||
- raises ParseError if nothing matches
|
||||
- raises Exhausted if sequence already done
|
||||
"""
|
||||
while self._pos < self._number:
|
||||
x = self._prods[self._pos]
|
||||
thisround = self._round
|
||||
|
||||
self._pos += 1
|
||||
if self._pos == self._number:
|
||||
if self._round < self._max:
|
||||
# new round?
|
||||
self._pos = 0
|
||||
self._round += 1
|
||||
|
||||
if isinstance(x, Prod):
|
||||
if not token and (x.optional or thisround > self._min):
|
||||
# token is None if nothing expected
|
||||
raise Exhausted()
|
||||
elif not token and not x.optional:
|
||||
raise MissingToken(u'Missing token for production %s'
|
||||
% x.name)
|
||||
elif x.matches(token):
|
||||
return x
|
||||
elif x.optional:
|
||||
# try next
|
||||
continue
|
||||
# elif thisround > self._min:
|
||||
# # minimum done
|
||||
# self._round = self._max
|
||||
# self._pos = self._number
|
||||
# return None
|
||||
else:
|
||||
# should have matched
|
||||
raise NoMatch(u'No matching production for token')
|
||||
|
||||
else:
|
||||
# nested Sequence or Choice
|
||||
return x
|
||||
|
||||
# Sequence is exhausted
|
||||
if self._round >= self._max:
|
||||
raise Exhausted(u'Extra token')
|
||||
|
||||
|
||||
class Prod(object):
|
||||
"""Single Prod in Sequence or Choice."""
|
||||
def __init__(self, name, match, toSeq=None, toStore=None,
|
||||
optional=False):
|
||||
"""
|
||||
name
|
||||
name used for error reporting
|
||||
match callback
|
||||
function called with parameters tokentype and tokenvalue
|
||||
returning True, False or raising ParseError
|
||||
toSeq callback (optional)
|
||||
if given calling toSeq(token) will be appended to seq
|
||||
else simply seq
|
||||
toStore (optional)
|
||||
key to save util.Item to store or callback(store, util.Item)
|
||||
optional = False
|
||||
wether Prod is optional or not
|
||||
"""
|
||||
self.name = name
|
||||
self.match = match
|
||||
self.optional=optional
|
||||
|
||||
def makeToStore(key):
|
||||
"Return a function used by toStore."
|
||||
def toStore(store, item):
|
||||
"Set or append store item."
|
||||
if key in store:
|
||||
store[key].append(item)
|
||||
else:
|
||||
store[key] = item
|
||||
return toStore
|
||||
|
||||
if toSeq:
|
||||
# called: seq.append(toSeq(value))
|
||||
self.toSeq = toSeq
|
||||
else:
|
||||
self.toSeq = lambda val: val
|
||||
|
||||
if callable(toStore):
|
||||
self.toStore = toStore
|
||||
elif toStore:
|
||||
self.toStore = makeToStore(toStore)
|
||||
else:
|
||||
# always set!
|
||||
self.toStore = None
|
||||
|
||||
def matches(self, token):
|
||||
"""Return if token matches."""
|
||||
type_, val, line, col = token
|
||||
return self.match(type_, val)
|
||||
|
||||
def __repr__(self):
|
||||
return "<cssutils.prodsparser.%s object name=%r at 0x%x>" % (
|
||||
self.__class__.__name__, self.name, id(self))
|
||||
|
||||
|
||||
class ProdParser(object):
|
||||
"""Productions parser."""
|
||||
def __init__(self):
|
||||
self.types = cssutils.cssproductions.CSSProductions
|
||||
self._log = cssutils.log
|
||||
self._tokenizer = cssutils.tokenize2.Tokenizer()
|
||||
|
||||
def parse(self, text, name, productions, store=None):
|
||||
"""
|
||||
text (or token generator)
|
||||
to parse, will be tokenized if not a generator yet
|
||||
|
||||
may be:
|
||||
- a string to be tokenized
|
||||
- a single token, a tuple
|
||||
- a tuple of (token, tokensGenerator)
|
||||
- already tokenized so a tokens generator
|
||||
|
||||
name
|
||||
used for logging
|
||||
productions
|
||||
used to parse tokens
|
||||
store UPDATED
|
||||
If a Prod defines ``toStore`` the key defined there
|
||||
is a key in store to be set or if store[key] is a list
|
||||
the next Item is appended here.
|
||||
|
||||
TODO: NEEDED? :
|
||||
Key ``raw`` is always added and holds all unprocessed
|
||||
values found
|
||||
|
||||
returns
|
||||
:wellformed: True or False
|
||||
:seq: a filled cssutils.util.Seq object which is NOT readonly yet
|
||||
:store: filled keys defined by Prod.toStore
|
||||
:unusedtokens: token generator containing tokens not used yet
|
||||
"""
|
||||
if isinstance(text, basestring):
|
||||
# to tokenize
|
||||
tokens = self._tokenizer.tokenize(text)
|
||||
elif isinstance(text, tuple):
|
||||
# (token, tokens) or a single token
|
||||
if len(text) == 2:
|
||||
# (token, tokens)
|
||||
def gen(token, tokens):
|
||||
"new generator appending token and tokens"
|
||||
yield token
|
||||
for t in tokens:
|
||||
yield t
|
||||
|
||||
tokens = (t for t in gen(*text))
|
||||
|
||||
else:
|
||||
# single token
|
||||
tokens = [text]
|
||||
else:
|
||||
# already tokenized, assume generator
|
||||
tokens = text
|
||||
|
||||
# a new seq to append all Items to
|
||||
seq = cssutils.util.Seq(readonly=False)
|
||||
|
||||
# store for specific values
|
||||
if not store:
|
||||
store = {}
|
||||
# store['_raw'] = []
|
||||
|
||||
# stack of productions
|
||||
prods = [productions]
|
||||
|
||||
wellformed = True
|
||||
for token in tokens:
|
||||
type_, val, line, col = token
|
||||
# store['_raw'].append(val)
|
||||
|
||||
# default productions
|
||||
if type_ == self.types.S:
|
||||
# always append S?
|
||||
seq.append(val, type_, line, col)
|
||||
elif type_ == self.types.COMMENT:
|
||||
# always append COMMENT
|
||||
seq.append(val, type_, line, col)
|
||||
# elif type_ == self.types.ATKEYWORD:
|
||||
# # @rule
|
||||
# r = cssutils.css.CSSUnknownRule(cssText=val)
|
||||
# seq.append(r, type(r), line, col)
|
||||
elif type_ == self.types.EOF:
|
||||
# do nothing
|
||||
pass
|
||||
# next = 'EOF'
|
||||
else:
|
||||
# check prods
|
||||
try:
|
||||
while True:
|
||||
# find next matching production
|
||||
try:
|
||||
prod = prods[-1].nextProd(token)
|
||||
except (NoMatch, Exhausted), e:
|
||||
# try next
|
||||
prod = None
|
||||
|
||||
if isinstance(prod, Prod):
|
||||
break
|
||||
elif not prod:
|
||||
if len(prods) > 1:
|
||||
# nested exhausted, next in parent
|
||||
prods.pop()
|
||||
else:
|
||||
raise Exhausted('Extra token')
|
||||
else:
|
||||
# nested Sequence, Choice
|
||||
prods.append(prod)
|
||||
|
||||
except ParseError, e:
|
||||
wellformed = False
|
||||
self._log.error(u'%s: %s: %r' % (name, e, token))
|
||||
|
||||
else:
|
||||
# process prod
|
||||
if prod.toSeq:
|
||||
seq.append(prod.toSeq(val), type_, line, col)
|
||||
else:
|
||||
seq.append(val, type_, line, col)
|
||||
|
||||
if prod.toStore:
|
||||
prod.toStore(store, seq[-1])
|
||||
|
||||
# if 'STOP' == next: # EOF?
|
||||
# # stop here and ignore following tokens
|
||||
# break
|
||||
|
||||
while True:
|
||||
# all productions exhausted?
|
||||
try:
|
||||
prod = prods[-1].nextProd(token=None)
|
||||
except Exhausted, e:
|
||||
prod = None # ok
|
||||
except (MissingToken, NoMatch), e:
|
||||
wellformed = False
|
||||
self._log.error(u'%s: %s'
|
||||
% (name, e))
|
||||
else:
|
||||
try:
|
||||
if prod.optional:
|
||||
# ignore optional ones
|
||||
continue
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if prod:
|
||||
wellformed = False
|
||||
self._log.error(u'%s: Missing token for production %r'
|
||||
% (name, prod.name))
|
||||
elif len(prods) > 1:
|
||||
# nested exhausted, next in parent
|
||||
prods.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
# bool, Seq, None or generator
|
||||
return wellformed, seq, store, tokens
|
||||
|
||||
|
||||
class PreDef(object):
|
||||
"""Predefined Prod definition for use in productions definition
|
||||
for ProdParser instances.
|
||||
"""
|
||||
@staticmethod
|
||||
def comma():
|
||||
","
|
||||
return Prod(name=u'comma', match=lambda t, v: v == u',')
|
||||
|
||||
@staticmethod
|
||||
def funcEnd():
|
||||
")"
|
||||
return Prod(name=u'end FUNC ")"', match=lambda t, v: v == u')')
|
||||
|
||||
@staticmethod
|
||||
def unary():
|
||||
"+ or -"
|
||||
return Prod(name=u'unary +-', match=lambda t, v: v in u'+-',
|
||||
optional=True)
|
367
src/cssutils/profiles.py
Normal file
367
src/cssutils/profiles.py
Normal file
@ -0,0 +1,367 @@
|
||||
"""CSS profiles. Predefined are:
|
||||
|
||||
- 'CSS level 2'
|
||||
|
||||
"""
|
||||
__all__ = ['profiles']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
|
||||
|
||||
import cssutils
|
||||
import re
|
||||
|
||||
"""
|
||||
Define some regular expression fragments that will be used as
|
||||
macros within the CSS property value regular expressions.
|
||||
"""
|
||||
css2macros = {
|
||||
'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
|
||||
'border-color': '{color}',
|
||||
'border-width': '{length}|thin|medium|thick',
|
||||
|
||||
'background-color': r'{color}|transparent|inherit',
|
||||
'background-image': r'{uri}|none|inherit',
|
||||
'background-position': r'({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right))|((left|center|right)\s*(top|center|bottom))|inherit',
|
||||
'background-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
|
||||
'background-attachment': r'scroll|fixed|inherit',
|
||||
|
||||
|
||||
'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
|
||||
'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
|
||||
'identifier': r'{ident}',
|
||||
'family-name': r'{string}|{identifier}',
|
||||
'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
|
||||
'absolute-size': r'(x?x-)?(small|large)|medium',
|
||||
'relative-size': r'smaller|larger',
|
||||
'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
|
||||
'font-size': r'{absolute-size}|{relative-size}|{length}|{percentage}|inherit',
|
||||
'font-style': r'normal|italic|oblique|inherit',
|
||||
'font-variant': r'normal|small-caps|inherit',
|
||||
'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
|
||||
'line-height': r'normal|{number}|{length}|{percentage}|inherit',
|
||||
'list-style-image': r'{uri}|none|inherit',
|
||||
'list-style-position': r'inside|outside|inherit',
|
||||
'list-style-type': r'disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-greek|lower-(latin|alpha)|upper-(latin|alpha)|armenian|georgian|none|inherit',
|
||||
'margin-width': r'{length}|{percentage}|auto',
|
||||
'outline-color': r'{color}|invert|inherit',
|
||||
'outline-style': r'{border-style}|inherit',
|
||||
'outline-width': r'{border-width}|inherit',
|
||||
'padding-width': r'{length}|{percentage}',
|
||||
'specific-voice': r'{identifier}',
|
||||
'generic-voice': r'male|female|child',
|
||||
'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
|
||||
'border-attrs': r'{border-width}|{border-style}|{border-color}',
|
||||
'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}',
|
||||
'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
|
||||
'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
|
||||
'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
|
||||
'text-attrs': r'underline|overline|line-through|blink',
|
||||
}
|
||||
|
||||
"""
|
||||
Define the regular expressions for validation all CSS values
|
||||
"""
|
||||
css2 = {
|
||||
'azimuth': r'{angle}|(behind\s+)?(left-side|far-left|left|center-left|center|center-right|right|far-right|right-side)(\s+behind)?|behind|leftwards|rightwards|inherit',
|
||||
'background-attachment': r'{background-attachment}',
|
||||
'background-color': r'{background-color}',
|
||||
'background-image': r'{background-image}',
|
||||
'background-position': r'{background-position}',
|
||||
'background-repeat': r'{background-repeat}',
|
||||
# Each piece should only be allowed one time
|
||||
'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
|
||||
'border-collapse': r'collapse|separate|inherit',
|
||||
'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
|
||||
'border-spacing': r'{length}(\s+{length})?|inherit',
|
||||
'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
|
||||
'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'border-top-color': r'{border-color}|transparent|inherit',
|
||||
'border-right-color': r'{border-color}|transparent|inherit',
|
||||
'border-bottom-color': r'{border-color}|transparent|inherit',
|
||||
'border-left-color': r'{border-color}|transparent|inherit',
|
||||
'border-top-style': r'{border-style}|inherit',
|
||||
'border-right-style': r'{border-style}|inherit',
|
||||
'border-bottom-style': r'{border-style}|inherit',
|
||||
'border-left-style': r'{border-style}|inherit',
|
||||
'border-top-width': r'{border-width}|inherit',
|
||||
'border-right-width': r'{border-width}|inherit',
|
||||
'border-bottom-width': r'{border-width}|inherit',
|
||||
'border-left-width': r'{border-width}|inherit',
|
||||
'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
|
||||
'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
|
||||
'bottom': r'{length}|{percentage}|auto|inherit',
|
||||
'caption-side': r'top|bottom|inherit',
|
||||
'clear': r'none|left|right|both|inherit',
|
||||
'clip': r'{shape}|auto|inherit',
|
||||
'color': r'{color}|inherit',
|
||||
'content': r'normal|{content}(\s+{content})*|inherit',
|
||||
'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
||||
'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
|
||||
'cue-after': r'{uri}|none|inherit',
|
||||
'cue-before': r'{uri}|none|inherit',
|
||||
'cue': r'({uri}|none|inherit){1,2}|inherit',
|
||||
'cursor': r'((({uri}{w},{w})*)?(auto|crosshair|default|pointer|move|(e|ne|nw|n|se|sw|s|w)-resize|text|wait|help|progress))|inherit',
|
||||
'direction': r'ltr|rtl|inherit',
|
||||
'display': r'inline|block|list-item|run-in|inline-block|table|inline-table|table-row-group|table-header-group|table-footer-group|table-row|table-column-group|table-column|table-cell|table-caption|none|inherit',
|
||||
'elevation': r'{angle}|below|level|above|higher|lower|inherit',
|
||||
'empty-cells': r'show|hide|inherit',
|
||||
'float': r'left|right|none|inherit',
|
||||
'font-family': r'{font-family}',
|
||||
'font-size': r'{font-size}',
|
||||
'font-style': r'{font-style}',
|
||||
'font-variant': r'{font-variant}',
|
||||
'font-weight': r'{font-weight}',
|
||||
'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|caption|icon|menu|message-box|small-caption|status-bar|inherit',
|
||||
'height': r'{length}|{percentage}|auto|inherit',
|
||||
'left': r'{length}|{percentage}|auto|inherit',
|
||||
'letter-spacing': r'normal|{length}|inherit',
|
||||
'line-height': r'{line-height}',
|
||||
'list-style-image': r'{list-style-image}',
|
||||
'list-style-position': r'{list-style-position}',
|
||||
'list-style-type': r'{list-style-type}',
|
||||
'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
|
||||
'margin-right': r'{margin-width}|inherit',
|
||||
'margin-left': r'{margin-width}|inherit',
|
||||
'margin-top': r'{margin-width}|inherit',
|
||||
'margin-bottom': r'{margin-width}|inherit',
|
||||
'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
|
||||
'max-height': r'{length}|{percentage}|none|inherit',
|
||||
'max-width': r'{length}|{percentage}|none|inherit',
|
||||
'min-height': r'{length}|{percentage}|none|inherit',
|
||||
'min-width': r'{length}|{percentage}|none|inherit',
|
||||
'orphans': r'{integer}|inherit',
|
||||
'outline-color': r'{outline-color}',
|
||||
'outline-style': r'{outline-style}',
|
||||
'outline-width': r'{outline-width}',
|
||||
'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
|
||||
'overflow': r'visible|hidden|scroll|auto|inherit',
|
||||
'padding-top': r'{padding-width}|inherit',
|
||||
'padding-right': r'{padding-width}|inherit',
|
||||
'padding-bottom': r'{padding-width}|inherit',
|
||||
'padding-left': r'{padding-width}|inherit',
|
||||
'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
|
||||
'page-break-after': r'auto|always|avoid|left|right|inherit',
|
||||
'page-break-before': r'auto|always|avoid|left|right|inherit',
|
||||
'page-break-inside': r'avoid|auto|inherit',
|
||||
'pause-after': r'{time}|{percentage}|inherit',
|
||||
'pause-before': r'{time}|{percentage}|inherit',
|
||||
'pause': r'({time}|{percentage}){1,2}|inherit',
|
||||
'pitch-range': r'{number}|inherit',
|
||||
'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
|
||||
'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
|
||||
'position': r'static|relative|absolute|fixed|inherit',
|
||||
'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
|
||||
'richness': r'{number}|inherit',
|
||||
'right': r'{length}|{percentage}|auto|inherit',
|
||||
'speak-header': r'once|always|inherit',
|
||||
'speak-numeral': r'digits|continuous|inherit',
|
||||
'speak-punctuation': r'code|none|inherit',
|
||||
'speak': r'normal|none|spell-out|inherit',
|
||||
'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
|
||||
'stress': r'{number}|inherit',
|
||||
'table-layout': r'auto|fixed|inherit',
|
||||
'text-align': r'left|right|center|justify|inherit',
|
||||
'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
|
||||
'text-indent': r'{length}|{percentage}|inherit',
|
||||
'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
|
||||
'top': r'{length}|{percentage}|auto|inherit',
|
||||
'unicode-bidi': r'normal|embed|bidi-override|inherit',
|
||||
'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
|
||||
'visibility': r'visible|hidden|collapse|inherit',
|
||||
'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|inherit',
|
||||
'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
|
||||
'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
|
||||
'widows': r'{integer}|inherit',
|
||||
'width': r'{length}|{percentage}|auto|inherit',
|
||||
'word-spacing': r'normal|{length}|inherit',
|
||||
'z-index': r'auto|{integer}|inherit',
|
||||
}
|
||||
|
||||
# CSS Color Module Level 3
|
||||
css3colormacros = {
|
||||
# orange and transparent in CSS 2.1
|
||||
'namedcolor': r'(currentcolor|transparent|orange|black|green|silver|lime|gray|olive|white|yellow|maroon|navy|red|blue|purple|teal|fuchsia|aqua)',
|
||||
# orange?
|
||||
'rgbacolor': r'rgba\({w}{int}{w},{w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgba\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w},{w}{num}{w}\)',
|
||||
'hslcolor': r'hsl\({w}{int}{w},{w}{num}%{w},{w}{num}%{w}\)|hsla\({w}{int}{w},{w}{num}%{w},{w}{num}%{w},{w}{num}{w}\)',
|
||||
}
|
||||
|
||||
|
||||
css3color = {
|
||||
'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{rgbacolor}|{hslcolor}|inherit',
|
||||
'opacity': r'{num}|inherit'
|
||||
}
|
||||
|
||||
class NoSuchProfileException(Exception):
|
||||
"""Raised if no profile with given name is found"""
|
||||
pass
|
||||
|
||||
|
||||
class Profiles(object):
|
||||
"""
|
||||
A dictionary of::
|
||||
|
||||
profilename: {
|
||||
propname: propvalue_regex*
|
||||
}
|
||||
|
||||
Predefined profiles are:
|
||||
|
||||
- 'CSS level 2': Properties defined by CSS2
|
||||
|
||||
"""
|
||||
basicmacros = {
|
||||
'ident': r'[-]?{nmstart}{nmchar}*',
|
||||
'name': r'{nmchar}+',
|
||||
'nmstart': r'[_a-z]|{nonascii}|{escape}',
|
||||
'nonascii': r'[^\0-\177]',
|
||||
'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
|
||||
'escape': r'{unicode}|\\[ -~\200-\777]',
|
||||
# 'escape': r'{unicode}|\\[ -~\200-\4177777]',
|
||||
'int': r'[-]?\d+',
|
||||
'nmchar': r'[\w-]|{nonascii}|{escape}',
|
||||
'num': r'[-]?\d+|[-]?\d*\.\d+',
|
||||
'number': r'{num}',
|
||||
'string': r'{string1}|{string2}',
|
||||
'string1': r'"(\\\"|[^\"])*"',
|
||||
'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
|
||||
'string2': r"'(\\\'|[^\'])*'",
|
||||
'nl': r'\n|\r\n|\r|\f',
|
||||
'w': r'\s*',
|
||||
}
|
||||
generalmacros = {
|
||||
'hexcolor': r'#[0-9a-f]{3}|#[0-9a-f]{6}',
|
||||
'rgbcolor': r'rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
|
||||
'namedcolor': r'(transparent|orange|maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray)',
|
||||
'uicolor': r'(ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)',
|
||||
'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{uicolor}',
|
||||
#'color': r'(maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray|ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)|#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
|
||||
'integer': r'{int}',
|
||||
'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
|
||||
'angle': r'0|{num}(deg|grad|rad)',
|
||||
'time': r'0|{num}m?s',
|
||||
'frequency': r'0|{num}k?Hz',
|
||||
'percentage': r'{num}%',
|
||||
}
|
||||
|
||||
CSS_LEVEL_2 = 'CSS Level 2.1'
|
||||
CSS_COLOR_LEVEL_3 = 'CSS Color Module Level 3'
|
||||
|
||||
def __init__(self):
|
||||
self._log = cssutils.log
|
||||
self._profilenames = [] # to keep order, REFACTOR!
|
||||
self._profiles = {}
|
||||
self.addProfile(self.CSS_LEVEL_2, css2, css2macros)
|
||||
self.addProfile(self.CSS_COLOR_LEVEL_3, css3color, css3colormacros)
|
||||
|
||||
def _expand_macros(self, dictionary, macros):
|
||||
"""Expand macros in token dictionary"""
|
||||
def macro_value(m):
|
||||
return '(?:%s)' % macros[m.groupdict()['macro']]
|
||||
for key, value in dictionary.items():
|
||||
if not callable(value):
|
||||
while re.search(r'{[a-z][a-z0-9-]*}', value):
|
||||
value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
|
||||
macro_value, value)
|
||||
dictionary[key] = value
|
||||
return dictionary
|
||||
|
||||
def _compile_regexes(self, dictionary):
|
||||
"""Compile all regular expressions into callable objects"""
|
||||
for key, value in dictionary.items():
|
||||
if not callable(value):
|
||||
value = re.compile('^(?:%s)$' % value, re.I).match
|
||||
dictionary[key] = value
|
||||
|
||||
return dictionary
|
||||
|
||||
profiles = property(lambda self: sorted(self._profiles.keys()),
|
||||
doc=u'Names of all profiles.')
|
||||
|
||||
def addProfile(self, profile, properties, macros=None):
|
||||
"""Add a new profile with name ``profile`` (e.g. 'CSS level 2')
|
||||
and the given ``properties``. ``macros`` are
|
||||
|
||||
``profile``
|
||||
The new profile's name
|
||||
``properties``
|
||||
A dictionary of ``{ property-name: propery-value }`` items where
|
||||
property-value is a regex which may use macros defined in given
|
||||
``macros`` or the standard macros Profiles.tokens and
|
||||
Profiles.generalvalues.
|
||||
|
||||
``propery-value`` may also be a function which takes a single
|
||||
argument which is the value to validate and which should return
|
||||
True or False.
|
||||
Any exceptions which may be raised during this custom validation
|
||||
are reported or raised as all other cssutils exceptions depending
|
||||
on cssutils.log.raiseExceptions which e.g during parsing normally
|
||||
is False so the exceptions would be logged only.
|
||||
"""
|
||||
if not macros:
|
||||
macros = {}
|
||||
m = self.basicmacros
|
||||
m.update(self.generalmacros)
|
||||
m.update(macros)
|
||||
properties = self._expand_macros(properties, m)
|
||||
self._profilenames.append(profile)
|
||||
self._profiles[profile] = self._compile_regexes(properties)
|
||||
|
||||
def propertiesByProfile(self, profiles=None):
|
||||
"""Generator: Yield property names, if no profile(s) is given all
|
||||
profile's properties are used."""
|
||||
if not profiles:
|
||||
profiles = self.profiles
|
||||
elif isinstance(profiles, basestring):
|
||||
profiles = (profiles, )
|
||||
|
||||
try:
|
||||
for profile in sorted(profiles):
|
||||
for name in sorted(self._profiles[profile].keys()):
|
||||
yield name
|
||||
except KeyError, e:
|
||||
raise NoSuchProfileException(e)
|
||||
|
||||
def validate(self, name, value):
|
||||
"""Check if value is valid for given property name using any profile."""
|
||||
for profile in self.profiles:
|
||||
if name in self._profiles[profile]:
|
||||
try:
|
||||
# custom validation errors are caught
|
||||
r = bool(self._profiles[profile][name](value))
|
||||
except Exception, e:
|
||||
self._log.error(e, error=Exception)
|
||||
return False
|
||||
if r:
|
||||
return r
|
||||
return False
|
||||
|
||||
def validateWithProfile(self, name, value):
|
||||
"""Check if value is valid for given property name returning
|
||||
(valid, valid_in_profile).
|
||||
|
||||
You may want to check if valid_in_profile is what you expected.
|
||||
|
||||
Example: You might expect a valid Profiles.CSS_LEVEL_2 value but
|
||||
e.g. ``validateWithProfile('color', 'rgba(1,1,1,1)')`` returns
|
||||
(True, Profiles.CSS_COLOR_LEVEL_3)
|
||||
"""
|
||||
for profilename in self._profilenames:
|
||||
if name in self._profiles[profilename]:
|
||||
try:
|
||||
# custom validation errors are caught
|
||||
r = (bool(self._profiles[profilename][name](value)),
|
||||
profilename)
|
||||
except Exception, e:
|
||||
self._log.error(e, error=Exception)
|
||||
r = False, None
|
||||
if r[0]:
|
||||
return r
|
||||
return False, None
|
||||
|
||||
|
||||
profiles = Profiles()
|
||||
|
@ -5,9 +5,10 @@
|
||||
"""
|
||||
__all__ = ['CSSSerializer', 'Preferences']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: serialize.py 1419 2008-08-09 19:28:06Z cthedot $'
|
||||
__version__ = '$Id: serialize.py 1472 2008-09-15 21:14:54Z cthedot $'
|
||||
import codecs
|
||||
import re
|
||||
import xml.dom
|
||||
import cssutils
|
||||
|
||||
def _escapecss(e):
|
||||
@ -193,17 +194,24 @@ class Out(object):
|
||||
- some other vals
|
||||
add ``*spacer`` except ``space=False``
|
||||
"""
|
||||
if val or 'STRING' == typ:
|
||||
if val or typ in ('STRING', 'URI'):
|
||||
# PRE
|
||||
if 'COMMENT' == typ:
|
||||
if self.ser.prefs.keepComments:
|
||||
val = val.cssText
|
||||
else:
|
||||
return
|
||||
elif typ in ('Property', cssutils.css.CSSRule.UNKNOWN_RULE):
|
||||
elif hasattr(val, 'cssText'):
|
||||
val = val.cssText
|
||||
# elif typ in ('Property', cssutils.css.CSSRule.UNKNOWN_RULE):
|
||||
# val = val.cssText
|
||||
elif 'S' == typ and not keepS:
|
||||
return
|
||||
elif typ in ('NUMBER', 'DIMENSION', 'PERCENTAGE') and val == '0':
|
||||
# remove sign + or - if value is zero
|
||||
# TODO: only for lenghts!
|
||||
if self.out and self.out[-1] in u'+-':
|
||||
del self.out[-1]
|
||||
elif 'STRING' == typ:
|
||||
# may be empty but MUST not be None
|
||||
if val is None:
|
||||
@ -211,7 +219,7 @@ class Out(object):
|
||||
val = self.ser._string(val)
|
||||
elif 'URI' == typ:
|
||||
val = self.ser._uri(val)
|
||||
elif val in u'+>~,:{;)]':
|
||||
elif val in u'+>~,:{;)]/':
|
||||
self._remove_last_if_S()
|
||||
|
||||
# APPEND
|
||||
@ -235,11 +243,12 @@ class Out(object):
|
||||
self.out.append(self.ser.prefs.lineSeparator)
|
||||
elif u';' == val: # end or prop or block
|
||||
self.out.append(self.ser.prefs.lineSeparator)
|
||||
elif val not in u'}[]()' and space:
|
||||
elif val not in u'}[]()/' and typ not in ('FUNCTION',) and space:
|
||||
self.out.append(self.ser.prefs.spacer)
|
||||
|
||||
def value(self, delim=u'', end=None):
|
||||
def value(self, delim=u'', end=None, keepS=False):
|
||||
"returns all items joined by delim"
|
||||
if not keepS:
|
||||
self._remove_last_if_S()
|
||||
if end:
|
||||
self.out.append(end)
|
||||
@ -253,8 +262,9 @@ class CSSSerializer(object):
|
||||
To use your own serializing method the easiest is to subclass CSS
|
||||
Serializer and overwrite the methods you like to customize.
|
||||
"""
|
||||
# chars not in URI without quotes around
|
||||
__forbidden_in_uri_matcher = re.compile(ur'''.*?[\)\s\;]''', re.U).match
|
||||
# chars not in URI without quotes around as problematic with other stuff
|
||||
# really ","?
|
||||
__forbidden_in_uri_matcher = re.compile(ur'''.*?[\(\)\s\;,]''', re.U).match
|
||||
|
||||
def __init__(self, prefs=None):
|
||||
"""
|
||||
@ -825,30 +835,100 @@ class CSSSerializer(object):
|
||||
return u''.join(out).strip()
|
||||
|
||||
def do_css_CSSValue(self, cssvalue):
|
||||
"""
|
||||
serializes a CSSValue
|
||||
"""
|
||||
# TODO: use Out()
|
||||
"""Serializes a CSSValue"""
|
||||
# TODO: use self._valid(cssvalue)?
|
||||
|
||||
if not cssvalue:
|
||||
return u''
|
||||
else:
|
||||
sep = u',%s' % self.prefs.listItemSpacer
|
||||
out = []
|
||||
for part in cssvalue.seq:
|
||||
if hasattr(part, 'cssText'):
|
||||
# comments or CSSValue if a CSSValueList
|
||||
out.append(part.cssText)
|
||||
elif isinstance(part, basestring) and part == u',':
|
||||
out.append(sep)
|
||||
out = Out(self)
|
||||
for item in cssvalue.seq:
|
||||
type_, val = item.type, item.value
|
||||
if type_ in (cssutils.css.CSSColor,
|
||||
cssutils.css.CSSValue):
|
||||
# CSSColor or CSSValue if a CSSValueList
|
||||
out.append(val.cssText, type_, space=False, keepS=True)
|
||||
else:
|
||||
# TODO: escape func parameter if STRING!
|
||||
if part and part[0] == part[-1] and part[0] in '\'"':
|
||||
# string has " " around it in CSSValue!
|
||||
part = self._string(part[1:-1])
|
||||
out.append(part)
|
||||
return (u''.join(out)).strip()
|
||||
if val and val[0] == val[-1] and val[0] in '\'"':
|
||||
val = self._string(val[1:-1])
|
||||
# S must be kept! in between values but no extra space
|
||||
out.append(val, type_, space=False, keepS=True)
|
||||
return out.value()
|
||||
|
||||
def do_css_CSSPrimitiveValue(self, cssvalue):
|
||||
"""Serialize a CSSPrimitiveValue"""
|
||||
# TODO: use self._valid(cssvalue)?
|
||||
if not cssvalue:
|
||||
return u''
|
||||
else:
|
||||
out = Out(self)
|
||||
|
||||
unary = None
|
||||
for item in cssvalue.seq:
|
||||
type_, val = item.type, item.value
|
||||
if 'CHAR' == type_ and val in u'+-':
|
||||
# save for next round
|
||||
unary = val
|
||||
continue
|
||||
if cssutils.css.CSSColor == type_:
|
||||
# Comment or CSSColor
|
||||
val = val.cssText
|
||||
elif type_ in ('DIMENSION', 'NUMBER', 'PERCENTAGE'):
|
||||
# handle saved unary and add to number
|
||||
try:
|
||||
# NUMBER or DIMENSION and is it 0?
|
||||
if 0 == cssvalue.getFloatValue():
|
||||
val = u'0'
|
||||
else:
|
||||
# add unary to val if not 0
|
||||
# TODO: only for lengths!
|
||||
if u'-' == unary:
|
||||
val = unary + val
|
||||
except xml.dom.InvalidAccessErr, e:
|
||||
pass
|
||||
unary = None
|
||||
elif unary:
|
||||
# or simple add
|
||||
out.append(unary, 'CHAR', space=False, keepS=True)
|
||||
unary = None
|
||||
|
||||
out.append(val, type_)
|
||||
# if hasattr(val, 'cssText'):
|
||||
# # comments or CSSValue if a CSSValueList
|
||||
# out.append(val.cssText, type_)
|
||||
# else:
|
||||
# out.append(val, type_) #?
|
||||
|
||||
return out.value()
|
||||
|
||||
def do_css_CSSColor(self, cssvalue):
|
||||
"""Serialize a CSSColor value"""
|
||||
if not cssvalue:
|
||||
return u''
|
||||
else:
|
||||
out = Out(self)
|
||||
unary = None
|
||||
for item in cssvalue.seq:
|
||||
type_, val = item.type, item.value
|
||||
|
||||
# prepare
|
||||
if 'HASH' == type_:
|
||||
# TODO: add pref for this!
|
||||
if len(val) == 7 and val[1] == val[2] and \
|
||||
val[3] == val[4] and val[5] == val[6]:
|
||||
val = u'#%s%s%s' % (val[1], val[3], val[5])
|
||||
elif 'CHAR' == type_ and val in u'+-':
|
||||
# save - for next round
|
||||
if u'-' == val:
|
||||
# omit +
|
||||
unary = val
|
||||
continue
|
||||
elif unary:
|
||||
val = unary + val.cssText
|
||||
unary = None
|
||||
|
||||
out.append(val, type_)
|
||||
|
||||
return out.value()
|
||||
|
||||
def do_stylesheets_medialist(self, medialist):
|
||||
"""
|
@ -2,7 +2,7 @@
|
||||
"""
|
||||
__all__ = []
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: util.py 1429 2008-08-11 19:01:52Z cthedot $'
|
||||
__version__ = '$Id: util.py 1453 2008-09-08 20:57:19Z cthedot $'
|
||||
|
||||
import codecs
|
||||
from itertools import ifilter
|
||||
@ -13,6 +13,7 @@ import xml.dom
|
||||
from helper import normalize
|
||||
import tokenize2
|
||||
import cssutils
|
||||
import encutils
|
||||
|
||||
class Base(object):
|
||||
"""
|
||||
@ -153,7 +154,7 @@ class Base(object):
|
||||
"""
|
||||
if token:
|
||||
value = token[1][4:-1].strip()
|
||||
if (value[0] in '\'"') and (value[0] == value[-1]):
|
||||
if value and (value[0] in '\'"') and (value[0] == value[-1]):
|
||||
# a string "..." or '...'
|
||||
value = value.replace('\\'+value[0], value[0])[1:-1]
|
||||
return value
|
||||
@ -326,7 +327,7 @@ class Base(object):
|
||||
return p
|
||||
|
||||
def _parse(self, expected, seq, tokenizer, productions, default=None,
|
||||
new=None):
|
||||
new=None, initialtoken=None):
|
||||
"""
|
||||
puts parsed tokens in seq by calling a production with
|
||||
(seq, tokenizer, token)
|
||||
@ -343,13 +344,28 @@ class Base(object):
|
||||
default callback if tokentype not in productions
|
||||
new
|
||||
used to init default productions
|
||||
initialtoken
|
||||
will be used together with tokenizer running 1st this token
|
||||
and then all tokens in tokenizer
|
||||
|
||||
returns (wellformed, expected) which the last prod might have set
|
||||
"""
|
||||
wellformed = True
|
||||
if tokenizer:
|
||||
|
||||
if initialtoken:
|
||||
# add initialtoken to tokenizer
|
||||
def tokens():
|
||||
"Build new tokenizer including initialtoken"
|
||||
yield initialtoken
|
||||
for item in tokenizer:
|
||||
yield item
|
||||
fulltokenizer = (t for t in tokens())
|
||||
else:
|
||||
fulltokenizer = tokenizer
|
||||
|
||||
if fulltokenizer:
|
||||
prods = self._adddefaultproductions(productions, new)
|
||||
for token in tokenizer:
|
||||
for token in fulltokenizer:
|
||||
p = prods.get(token[0], default)
|
||||
if p:
|
||||
expected = p(expected, seq, token, tokenizer)
|
||||
@ -487,16 +503,36 @@ class Seq(object):
|
||||
else:
|
||||
self._seq[index] = Item(val, typ, line, col)
|
||||
|
||||
def appendToVal(self, val=None, index=-1):
|
||||
"""
|
||||
if not readonly append to Item's value at index
|
||||
"""
|
||||
if self._readonly:
|
||||
raise AttributeError('Seq is readonly.')
|
||||
else:
|
||||
old = self._seq[index]
|
||||
self._seq[index] = Item(old.value + val, old.type,
|
||||
old.line, old.col)
|
||||
|
||||
def __repr__(self):
|
||||
"returns a repr same as a list of tuples of (value, type)"
|
||||
return u'cssutils.%s.%s([\n %s])' % (self.__module__,
|
||||
self.__class__.__name__,
|
||||
u',\n '.join([u'(%r, %r)' % (item.type, item.value)
|
||||
for item in self._seq]
|
||||
u',\n '.join([u'%r' % item for item in self._seq]
|
||||
))
|
||||
def __str__(self):
|
||||
return "<cssutils.%s.%s object length=%r at 0x%x>" % (
|
||||
self.__module__, self.__class__.__name__, len(self), id(self))
|
||||
vals = []
|
||||
for v in self:
|
||||
if isinstance(v.value, basestring):
|
||||
vals.append(v.value)
|
||||
elif type(v) == tuple:
|
||||
vals.append(v.value[1])
|
||||
else:
|
||||
vals.append(str(v))
|
||||
|
||||
return "<cssutils.%s.%s object length=%r valuestring=%r at 0x%x>" % (
|
||||
self.__module__, self.__class__.__name__, len(self),
|
||||
u''.join(vals), id(self))
|
||||
|
||||
class Item(object):
|
||||
"""
|
||||
@ -740,7 +776,7 @@ def _defaultFetcher(url):
|
||||
if res:
|
||||
mimeType, encoding = encutils.getHTTPInfo(res)
|
||||
if mimeType != u'text/css':
|
||||
cssutils.log.error(u'Expected "text/css" mime type for url=%s but found: %r' %
|
||||
cssutils.log.error(u'Expected "text/css" mime type for url=%r but found: %r' %
|
||||
(url, mimeType), error=ValueError)
|
||||
return encoding, res.read()
|
||||
|
Loading…
x
Reference in New Issue
Block a user