diff --git a/copyright b/copyright
new file mode 100644
index 0000000000..d4aa70052d
--- /dev/null
+++ b/copyright
@@ -0,0 +1,355 @@
+Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=196
+Upstream-Name: calibre
+Upstream-Maintainer: Kovid Goyal
+Upstream-Source: http://calibre.kovidgoyal.net/downloads
+
+Files: *
+Copyright: Copyright (C) 2008 Kovid Goyal
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/calibre/ebooks/BeautifulSoup.py
+Copyright: Copyright (c) 2004-2007, Leonard Richardson
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/ebooks/chardet/*
+Copyright: Copyright (C) 1998-2001 Netscape Communications Corporation
+License: LGPL-2.1+
+ The full text of the LGPL is distributed as in
+ /usr/share/common-licenses/LGPL-2.1 on Debian systems.
+
+Files: src/calibre/ebooks/hyphenate.py
+Copyright: Copyright (C) 1990, 2004, 2005 Gerard D.C. Kuiken.
+License: other
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved.
+
+Files: /src/cherrypy/*
+Copyright: Copyright (c) 2004-2007, CherryPy Team (team@cherrypy.org)
+Copyright: Copyright (C) 2005, Tiago Cogumbreiro
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/odf/*
+Copyright: Copyright (C) 2006-2008 Søren Roug, European Environment Agency
+License: LGPL2.1+
+ The full text of the LGPL is distributed as in
+ /usr/share/common-licenses/LGPL-2.1 on Debian systems.
+
+Files: src/odf/teletype.py
+Files: src/odf/easyliststyle.py
+Copyright: Copyright (C) 2008, J. David Eisenberg
+License: GPL2+
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-2 on Debian systems.
+
+Files: src/pyPdf/*
+Copyright: Copyright (c) 2006, Mathieu Fenniak
+Copyright: Copyright (c) 2007, Ashish Kulkarni
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/utils/genshi/*
+Copyright: Copyright (C) 2006-2008 Edgewall Software
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/utils/lzx/*
+Copyright: Copyright (C) 2002, Matthew T. Russotto
+Copyright: Copyright (C) 2008, Marshall T. Vandegrift
+Copyright: Copyright (C) 2006-2008, Alexander Chemeris
+License: LGPL-2.1
+ The full text of the LGPL is distributed as in
+ /usr/share/common-licenses/LGPL-2.1 on Debian systems.
+
+Files: src/calibre/utils/lzx/msstdint.h
+Copyright: Copyright (C) 2006-2008, Alexander Chemeris
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/utils/pyparsing.py
+Copyright: Copyright (c) 2003-2008, Paul T. McGuire
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Files: src/calibre/utils/PythonMagickWand.py
+Copyright: (c) 2007 - Achim Domma - domma@procoders.net
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+Files: src/calibre/utils/msdes/d3des.h:
+Files: src/calibre/utils/msdes/des.c:
+Copyright: Copyright (C) 1988,1989,1990,1991,1992, Richard Outerbridge
+License: Other
+ THIS SOFTWARE PLACED IN THE PUBLIC DOMAIN BY THE AUTHOUR
+
+Files: src/calibre/utils/msdes/msdesmodule.c
+Copyright: Copyright (C) 2008, Marshall T. Vandegrift
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/calibre/utils/msdes/spr.h
+Copyright: Copyright (C) 2002, Dan A. Jackson
+License: GPL2+
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-2 on Debian systems.
+
+Files: src/calibre/gui2/pictureflow/*
+Copyright: (C) Copyright 2007 Trolltech ASA
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/ebooks/lit/*
+Copyright: 2008, Marshall T. Vandegrift
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/calibre/ebooks/lrf/*
+Copyright: 2008, Anatoly Shipitsin
+Copyright: copyright 2002 Paul Henry Tremblay
+Copyright: Copyright (C) 2008 B.Scott Wxby [bswxby]
+Copyright: Copyright (C) 2007 David Chen SonyReaderDaveChenorg
+Copyright: Copyright (c) 2007 Mike Higgins (Falstaff)
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/calibre/ebooks/BeautifulSoup.py
+Copyright: Copyright (c) 2004-2007, Leonard Richardson
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/ebooks/rtf2xml/*
+Copyright: copyright 2002 Paul Henry Tremblay
+License: GPL
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL on Debian systems.
+
+Files: src/calibre/web/feeds/feedparser.py
+Copyright: Copyright (c) 2002-2006, Mark Pilgrim
+License: BSD
+ The full text of the BSD license is distributed as in
+ /usr/share/common-licenses/BSD on Debian systems.
+
+Files: src/calibre/web/feeds/recipes/*
+Copyright: 2008, Darko Miletic
+Copyright: 2008, Mathieu Godlewski
+Copyright: Copyright (C) 2008 B.Scott Wxby [bswxby]
+Copyright: Copyright (C) 2007 David Chen SonyReaderDaveChenorg
+Copyright: 2008, Derry FitzGerald
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/calibre/ebooks/metadata/*
+Copyright: 2008, Ashish Kulkarni
+Copyright: Copyright (C) 2006 Søren Roug, European Environment Agency
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/encutils/__init__.py
+Copyright: 2005-2008: Christof Hoeke
+License: LGPL-3+, CC-BY-3.0
+ The full text of the LGPL is distributed as in
+ /usr/share/common-licenses/LGPL-3 on Debian systems.
+
+Files: src/calibre/translations/*
+Copyright: Copyright (C) 2007, Kovid Goyal
+Copyright: Copyright (C) 2008, Rosetta Contributors and Canonical Ltd.
+License: GPL-3
+ The full text of the GPL is distributed as in
+ /usr/share/common-licenses/GPL-3 on Debian systems.
+
+Files: src/calibre/gui2/viewer/jquery.js
+Files: src/calibre/gui2/viewer/jquery_scrollTo.js
+Files: src/calibre/library/static/date.js
+Copyright: Copyright (C) 2008, John Resig (jquery.com)
+Copyright: Copyright (C) 2007-2008, Ariel Flesler - aflesler@gmail.com | http://flesler.blogspot.com
+Copyright: Copyright (C) 2006-2007, Coolite Inc. (http://www.coolite.com/)
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Files: src/calibre/ebooks/lrf/fonts/liberation/*
+Copyright: Copyright (C) 2007, Red Hat, Inc. All rights reserved.
+License: Other
+ Copyright (C) 2007, Red Hat, Inc. All rights reserved.
+ LIBERATION is a trademark of Red Hat, Inc.
+
+ This agreement governs the use of the Software and any updates to the Software,
+ regardless of the delivery mechanism. Subject to the following terms, Red Hat, Inc.
+ ("Red Hat") grants to the user ("Client") a license to this work pursuant to
+ the GNU General Public License v.2 with the exceptions set forth below and such
+ other terms as our set forth in this End User License Agreement.
+
+ 1. The Software and License Exception. LIBERATION font software (the "Software")
+ consists of TrueType-OpenType formatted font software for rendering LIBERATION
+ typefaces in sans serif, serif, and monospaced character styles. You are licensed
+ to use, modify, copy, and distribute the Software pursuant to the GNU General
+ Public License v.2 with the following exceptions:
+
+ (a) As a special exception, if you create a document which uses this font, and
+ embed this font or unaltered portions of this font into the document, this
+ font does not by itself cause the resulting document to be covered by the
+ GNU General Public License. This exception does not however invalidate any
+ other reasons why the document might be covered by the GNU General Public
+ License. If you modify this font, you may extend this exception to your
+ version of the font, but you are not obligated to do so. If you do not
+ wish to do so, delete this exception statement from your version.
+
+ (b) As a further exception, any distribution of the object code of the Software
+ in a physical product must provide you the right to access and modify the
+ source code for the Software and to reinstall that modified version of the
+ Software in object code form on the same physical product on which you
+ received it.
+
+ 2. Intellectual Property Rights. The Software and each of its components, including
+ the source code, documentation, appearance, structure and organization are owned
+ by Red Hat and others and are protected under copyright and other laws. Title to
+ the Software and any component, or to any copy, modification, or merged portion
+ shall remain with the aforementioned, subject to the applicable license.
+ The "LIBERATION" trademark is a trademark of Red Hat, Inc. in the U.S. and other
+ countries. This agreement does not permit Client to distribute modified versions
+ of the Software using Red Hat's trademarks. If Client makes a redistribution of
+ a modified version of the Software, then Client must modify the files names to
+ remove any reference to the Red Hat trademarks and must not use the Red Hat
+ trademarks in any way to reference or promote the modified Software.
+
+ 3. Limited Warranty. To the maximum extent permitted under applicable law, the
+ Software is provided and licensed "as is" without warranty of any kind,
+ expressed or implied, including the implied warranties of merchantability,
+ non-infringement or fitness for a particular purpose. Red Hat does not warrant
+ that the functions contained in the Software will meet Client's requirements or
+ that the operation of the Software will be entirely error free or appear precisely
+ as described in the accompanying documentation.
+
+ 4. Limitation of Remedies and Liability. To the maximum extent permitted by applicable
+ law, Red Hat or any Red Hat authorized dealer will not be liable to Client for any
+ incidental or consequential damages, including lost profits or lost savings arising
+ out of the use or inability to use the Software, even if Red Hat or such dealer has
+ been advised of the possibility of such damages.
+
+ 5. General. If any provision of this agreement is held to be unenforceable, that shall
+ not affect the enforceability of the remaining provisions. This agreement shall be
+ governed by the laws of the State of North Carolina and of the United States, without
+ regard to any conflict of laws provisions, except that the United Nations Convention
+ on the International Sale of Goods shall not apply.
+
+
+Files: installer/cx_Freeze/*
+Copyright: Copyright © 2007-2008, Colt Engineering, Edmonton, Alberta, Canada.
+Copyright: Copyright © 2001-2006, Computronix (Canada) Ltd., Edmonton, Alberta, Canada.
+License: other
+ All rights reserved.
+
+ NOTE: this license is derived from the Python Software Foundation License
+ which can be found at http://www.python.org/psf/license
+
+ License for cx_Freeze 4.0.1
+ ---------------------------
+
+ 1. This LICENSE AGREEMENT is between the copyright holders and the Individual
+ or Organization ("Licensee") accessing and otherwise using cx_Freeze
+ software in source or binary form and its associated documentation.
+
+ 2. Subject to the terms and conditions of this License Agreement, the
+ copyright holders hereby grant Licensee a nonexclusive, royalty-free,
+ world-wide license to reproduce, analyze, test, perform and/or display
+ publicly, prepare derivative works, distribute, and otherwise use cx_Freeze
+ alone or in any derivative version, provided, however, that this License
+ Agreement and this notice of copyright are retained in cx_Freeze alone or in
+ any derivative version prepared by Licensee.
+
+ 3. In the event Licensee prepares a derivative work that is based on or
+ incorporates cx_Freeze or any part thereof, and wants to make the derivative
+ work available to others as provided herein, then Licensee hereby agrees to
+ include in any such work a brief summary of the changes made to cx_Freeze.
+
+ 4. The copyright holders are making cx_Freeze available to Licensee on an
+ "AS IS" basis. THE COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES,
+ EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, THE COPYRIGHT
+ HOLDERS MAKE NO AND DISCLAIM ANY REPRESENTATION OR WARRANTY OF
+ MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
+ CX_FREEZE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
+
+ 5. THE COPYRIGHT HOLDERS SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF
+ CX_FREEZE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+ A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING CX_FREEZE, OR ANY
+ DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+ 6. This License Agreement will automatically terminate upon a material breach
+ of its terms and conditions.
+
+ 7. Nothing in this License Agreement shall be deemed to create any relationship
+ of agency, partnership, or joint venture between the copyright holders and
+ Licensee. This License Agreement does not grant permission to use
+ copyright holder's trademarks or trade name in a trademark sense to endorse
+ or promote products or services of Licensee, or any third party.
+
+ 8. By copying, installing or otherwise using cx_Freeze, Licensee agrees to be
+ bound by the terms and conditions of this License Agreement.
+
+ Computronix® is a registered trademark of Computronix (Canada) Ltd.
+
diff --git a/src/calibre/devices/scanner.py b/src/calibre/devices/scanner.py
index a93ef54c32..c73b1cd2f6 100644
--- a/src/calibre/devices/scanner.py
+++ b/src/calibre/devices/scanner.py
@@ -47,8 +47,8 @@ class DeviceScanner(object):
rev = ('rev_%4.4x'%c).replace('a', ':')
if rev in device_id:
return True
- return False
-
+ return False
+
def test_bcd(self, bcdDevice, bcd):
if bcd is None or len(bcd) == 0:
return True
@@ -56,19 +56,20 @@ class DeviceScanner(object):
if c == bcdDevice:
return True
return False
-
+
def is_device_connected(self, device):
vendor_ids = device.VENDOR_ID if hasattr(device.VENDOR_ID, '__len__') else [device.VENDOR_ID]
product_ids = device.PRODUCT_ID if hasattr(device.PRODUCT_ID, '__len__') else [device.PRODUCT_ID]
if iswindows:
- for vendor_id, product_id in zip(vendor_ids, product_ids):
- vid, pid = 'vid_%4.4x'%vendor_id, 'pid_%4.4x'%product_id
- vidd, pidd = 'vid_%i'%vendor_id, 'pid_%i'%product_id
- for device_id in self.devices:
- if (vid in device_id or vidd in device_id) and (pid in device_id or pidd in device_id):
- if self.test_bcd_windows(device_id, getattr(device, 'BCD', None)):
- if device.can_handle(device_id):
- return True
+ for vendor_id in vendor_ids:
+ for product_id in product_ids:
+ vid, pid = 'vid_%4.4x'%vendor_id, 'pid_%4.4x'%product_id
+ vidd, pidd = 'vid_%i'%vendor_id, 'pid_%i'%product_id
+ for device_id in self.devices:
+ if (vid in device_id or vidd in device_id) and (pid in device_id or pidd in device_id):
+ if self.test_bcd_windows(device_id, getattr(device, 'BCD', None)):
+ if device.can_handle(device_id):
+ return True
else:
for vendor, product, bcdDevice in self.devices:
if vendor in vendor_ids and product in product_ids:
diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py
index a6873fac2a..8cac967890 100644
--- a/src/calibre/ebooks/oeb/stylizer.py
+++ b/src/calibre/ebooks/oeb/stylizer.py
@@ -18,7 +18,7 @@ from xml.dom import SyntaxErr as CSSSyntaxError
import cssutils
from cssutils.css import CSSStyleRule, CSSPageRule, CSSStyleDeclaration, \
CSSValueList, cssproperties
-from cssutils.profiles import profiles as cssprofiles
+from cssutils import profile as cssprofiles
from lxml import etree
from lxml.cssselect import css_to_xpath, ExpressionError, SelectorSyntaxError
from calibre.ebooks.oeb.base import XHTML, XHTML_NS, CSS_MIME, OEB_STYLES
diff --git a/src/calibre/gui2/images/news/hrt.png b/src/calibre/gui2/images/news/hrt.png
new file mode 100644
index 0000000000..828819e226
Binary files /dev/null and b/src/calibre/gui2/images/news/hrt.png differ
diff --git a/src/calibre/gui2/images/news/rts.png b/src/calibre/gui2/images/news/rts.png
new file mode 100644
index 0000000000..278f45edd7
Binary files /dev/null and b/src/calibre/gui2/images/news/rts.png differ
diff --git a/src/calibre/trac/plugins/download.py b/src/calibre/trac/plugins/download.py
index 03a6676e7b..dd25279071 100644
--- a/src/calibre/trac/plugins/download.py
+++ b/src/calibre/trac/plugins/download.py
@@ -20,7 +20,7 @@ DEPENDENCIES = [
('BeautifulSoup', '3.0.5', 'beautifulsoup', 'python-beautifulsoup', 'python-BeautifulSoup'),
('dnspython', '1.6.0', 'dnspython', 'dnspython', 'dnspython', 'dnspython'),
('poppler', '0.10.5', 'poppler', 'poppler', 'poppler', 'poppler'),
- ('pdftk', '1.12', 'pdftk', 'pdftk', 'pdftk', 'pdftk'),
+ ('podofo', '0.7', 'podofo', 'podofo', 'podofo', 'podofo'),
]
diff --git a/src/calibre/trac/plugins/templates/linux.html b/src/calibre/trac/plugins/templates/linux.html
index 96881aa108..ffaa1d8394 100644
--- a/src/calibre/trac/plugins/templates/linux.html
+++ b/src/calibre/trac/plugins/templates/linux.html
@@ -49,7 +49,7 @@
${app} is available in the software repositories of the following
- linux distributions:
+ supported linux distributions:
diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py
index 6b7bc6d2ac..bda81ddc89 100644
--- a/src/calibre/web/feeds/recipes/__init__.py
+++ b/src/calibre/web/feeds/recipes/__init__.py
@@ -42,7 +42,7 @@ recipe_modules = ['recipe_' + r for r in (
'moneynews', 'der_standard', 'diepresse', 'nzz_ger', 'hna',
'seattle_times', 'scott_hanselman', 'coding_horror', 'twitchfilms',
'stackoverflow', 'telepolis_artikel', 'zaobao', 'usnews',
- 'straitstimes',
+ 'straitstimes', 'index_hu', 'pcworld_hu', 'hrt', 'rts',
)]
import re, imp, inspect, time, os
diff --git a/src/calibre/web/feeds/recipes/recipe_blic.py b/src/calibre/web/feeds/recipes/recipe_blic.py
index e4e4987dec..e212e73218 100644
--- a/src/calibre/web/feeds/recipes/recipe_blic.py
+++ b/src/calibre/web/feeds/recipes/recipe_blic.py
@@ -16,12 +16,14 @@ class Blic(BasicNewsRecipe):
description = 'Blic.co.yu online verzija najtiraznije novine u Srbiji donosi najnovije vesti iz Srbije i sveta, komentare, politicke analize, poslovne i ekonomske vesti, vesti iz regiona, intervjue, informacije iz kulture, reportaze, pokriva sve sportske dogadjaje, detaljan tv program, nagradne igre, zabavu, fenomenalni Blic strip, dnevni horoskop, arhivu svih dogadjaja'
publisher = 'RINGIER d.o.o.'
category = 'news, politics, Serbia'
+ delay = 1
oldest_article = 2
max_articles_per_feed = 100
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
language = _('Serbian')
+ lang = 'sr-Latn-RS'
extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif} '
html2lrf_options = [
@@ -45,26 +47,14 @@ class Blic(BasicNewsRecipe):
start_url, question, rest_url = url.partition('?')
return u'http://www.blic.rs/_print.php?' + rest_url
- def cleanup_image_tags(self,soup):
- for item in soup.findAll('img'):
- for attrib in ['height','width','border','align']:
- if item.has_key(attrib):
- del item[attrib]
- oldParent = item.parent
- myIndex = oldParent.contents.index(item)
- item.extract()
- divtag = Tag(soup,'div')
- brtag = Tag(soup,'br')
- oldParent.insert(myIndex,divtag)
- divtag.append(item)
- divtag.append(brtag)
- return soup
-
-
def preprocess_html(self, soup):
- mtag = ''
- soup.head.insert(0,mtag)
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ soup.head.insert(0,mlang)
for item in soup.findAll(style=True):
del item['style']
- return self.cleanup_image_tags(soup)
+ return self.adeify_images(soup)
+
+ def get_article_url(self, article):
+ raw = article.get('link', None)
+ return raw.replace('.co.yu','.rs')
\ No newline at end of file
diff --git a/src/calibre/web/feeds/recipes/recipe_hrt.py b/src/calibre/web/feeds/recipes/recipe_hrt.py
new file mode 100644
index 0000000000..d07b214e02
--- /dev/null
+++ b/src/calibre/web/feeds/recipes/recipe_hrt.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+__license__ = 'GPL v3'
+__copyright__ = '2009, Darko Miletic '
+
+'''
+www.hrt.hr
+'''
+
+import re
+from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
+
+class HRT(BasicNewsRecipe):
+ title = 'HRT: Vesti'
+ __author__ = 'Darko Miletic'
+ description = 'News from Croatia'
+ publisher = 'HRT'
+ category = 'news, politics, Croatia, HRT'
+ no_stylesheets = True
+ encoding = 'utf-8'
+ use_embedded_content = False
+ language = _("Croatian")
+ lang = 'hr-HR'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}'
+
+ html2lrf_options = [
+ '--comment', description
+ , '--category', category
+ , '--publisher', publisher
+ ]
+
+ html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
+
+
+ preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
+
+ keep_only_tags = [dict(name='div', attrs={'class':'bigVijest'})]
+
+ remove_tags = [dict(name=['object','link','embed'])]
+
+ remove_tags_after = dict(name='div', attrs={'class':'nsAuthor'})
+
+ feeds = [
+ (u'Vijesti' , u'http://www.hrt.hr/?id=316&type=100&rss=vijesti' )
+ ,(u'Sport' , u'http://www.hrt.hr/?id=316&type=100&rss=sport' )
+ ,(u'Zabava' , u'http://www.hrt.hr/?id=316&type=100&rss=zabava' )
+ ,(u'Filmovi i serije' , u'http://www.hrt.hr/?id=316&type=100&rss=filmovi' )
+ ,(u'Dokumentarni program', u'http://www.hrt.hr/?id=316&type=100&rss=dokumentarci')
+ ,(u'Glazba' , u'http://www.hrt.hr/?id=316&type=100&rss=glazba' )
+ ,(u'Kultura' , u'http://www.hrt.hr/?id=316&type=100&rss=kultura' )
+ ,(u'Mladi' , u'http://www.hrt.hr/?id=316&type=100&rss=mladi' )
+ ,(u'Manjine' , u'http://www.hrt.hr/?id=316&type=100&rss=manjine' )
+ ,(u'Radio' , u'http://www.hrt.hr/?id=316&type=100&rss=radio' )
+ ]
+
+ def preprocess_html(self, soup):
+ soup.html['xml:lang'] = self.lang
+ soup.html['lang'] = self.lang
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
+ soup.head.insert(0,mlang)
+ soup.head.insert(1,mcharset)
+ for item in soup.findAll(style=True):
+ del item['style']
+ return self.adeify_images(soup)
diff --git a/src/calibre/web/feeds/recipes/recipe_index_hu.py b/src/calibre/web/feeds/recipes/recipe_index_hu.py
new file mode 100644
index 0000000000..8b36500e5c
--- /dev/null
+++ b/src/calibre/web/feeds/recipes/recipe_index_hu.py
@@ -0,0 +1,20 @@
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class Index(BasicNewsRecipe):
+
+ title = u'INDEX.HU'
+ oldest_article = 3
+ max_articles_per_feed = 50
+ language = _('Hungarian')
+ __author__ = 'Ezmegaz'
+
+ feeds = [(u'ALL', u'http://index.hu/24ora/rss/'),
+ (u'BELF\xd6LD', u'http://index.hu/belfold/rss/default/'),
+ (u'K\xdcLF\xd6LD', u'http://index.hu/kulfold/rss/default/'),
+ (u'BULV\xc1R', u'http://index.hu/bulvar/rss/default/'),
+ (u'GAZDAS\xc1G', u'http://index.hu/gazdasag/rss/default/'),
+ (u'TECH', u'http://index.hu/tech/rss/main/'),
+ (u'KULT\xdaRA', u'http://index.hu/kultur/rss/main/'),
+ (u'TUDOM\xc1NY', u'http://index.hu/tudomany/rss/main/'),
+ (u'V\xc9LEM\xc9NY', u'http://index.hu/velemeny/rss/default/')]
+
diff --git a/src/calibre/web/feeds/recipes/recipe_nin.py b/src/calibre/web/feeds/recipes/recipe_nin.py
index fe1e97e8b8..4de53a1049 100644
--- a/src/calibre/web/feeds/recipes/recipe_nin.py
+++ b/src/calibre/web/feeds/recipes/recipe_nin.py
@@ -8,12 +8,13 @@ nin.co.rs
import re, urllib
from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
class Nin(BasicNewsRecipe):
title = 'NIN online'
__author__ = 'Darko Miletic'
description = 'Nedeljne informativne novine'
- publisher = 'NIN'
+ publisher = 'NIN D.O.O.'
category = 'news, politics, Serbia'
no_stylesheets = True
oldest_article = 15
@@ -28,9 +29,9 @@ class Nin(BasicNewsRecipe):
remove_javascript = True
use_embedded_content = False
language = _('Serbian')
- lang = 'sr-RS'
+ lang = 'sr-Latn-RS'
direction = 'ltr'
- extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif} .artTitle{font-size: x-large; font-weight: bold} .columnhead{font-size: small; font-weight: bold}'
html2lrf_options = [
'--comment' , description
@@ -70,9 +71,10 @@ class Nin(BasicNewsRecipe):
def preprocess_html(self, soup):
soup.html['lang'] = self.lang
soup.html['dir' ] = self.direction
- mtag = ''
- mtag += '\n'
- soup.head.insert(0,mtag)
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
+ soup.head.insert(0,mlang)
+ soup.head.insert(1,mcharset)
for item in soup.findAll(style=True):
del item['style']
return soup
diff --git a/src/calibre/web/feeds/recipes/recipe_pcworld_hu.py b/src/calibre/web/feeds/recipes/recipe_pcworld_hu.py
new file mode 100644
index 0000000000..ad1f1df72a
--- /dev/null
+++ b/src/calibre/web/feeds/recipes/recipe_pcworld_hu.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
+from __future__ import with_statement
+
+__license__ = 'GPL v3'
+__copyright__ = '2009, Kovid Goyal '
+__docformat__ = 'restructuredtext en'
+
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class Index(BasicNewsRecipe):
+
+
+ title = u'PCWORLD.HU'
+ oldest_article = 3
+ max_articles_per_feed = 50
+ language = _('Hungarian')
+ __author__ = 'Ezmegaz'
+
+
+ feeds = [(u'H\xedrek', u'http://pcworld.hu/rss/rss.xml'), (u'Hardver h\xedrek', u'http://www.pcworld.hu/rss/rss_hardverhirek.xml'), (u'Szoftver h\xedrek', u'http://www.pcworld.hu/rss/rss_szoftverhirek.xml'), (u'Hardver cikkek', u'http://www.pcworld.hu/rss/rss_hardvercikkek.xml'), (u'Szoftver cikkek', u'http://www.pcworld.hu/rss/rss_szoftvercikkek.xml'), (u'Mobil h\xedrek', u'http://www.pcworld.hu/rss/rss_mobil.xml'), (u'\xdczleti h\xedrek', u'http://www.pcworld.hu/rss/rss_uzlet.xml'), (u'Let\xf6lt\xe9sek', u'http://www.pcworld.hu/rss/rss_letoltes.xml'), (u'PC World TV', u'http://tv.pcworld.hu/rss/rss_hun_pcw.xml'), (u'Tudta-e...?', u'http://pcworld.hu/rss/rss_tudtae.xml')]
+
diff --git a/src/calibre/web/feeds/recipes/recipe_pobjeda.py b/src/calibre/web/feeds/recipes/recipe_pobjeda.py
index 5afb2b3f6a..6078e6ba0a 100644
--- a/src/calibre/web/feeds/recipes/recipe_pobjeda.py
+++ b/src/calibre/web/feeds/recipes/recipe_pobjeda.py
@@ -10,6 +10,7 @@ pobjeda.co.me
import re
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
class Pobjeda(BasicNewsRecipe):
title = 'Pobjeda Online'
@@ -22,12 +23,13 @@ class Pobjeda(BasicNewsRecipe):
encoding = 'utf8'
remove_javascript = True
use_embedded_content = False
+ language = _('Serbian')
+ lang = 'sr-Latn-Me'
INDEX = u'http://www.pobjeda.co.me'
- extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: serif1, serif}'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}'
html2lrf_options = [
'--comment', description
- , '--base-font-size', '10'
, '--category', category
, '--publisher', publisher
]
@@ -59,11 +61,13 @@ class Pobjeda(BasicNewsRecipe):
]
def preprocess_html(self, soup):
- soup.html['xml:lang'] = 'sr-Latn-ME'
- soup.html['lang'] = 'sr-Latn-ME'
- mtag = ''
- soup.head.insert(0,mtag)
- return soup
+ soup.html['xml:lang'] = self.lang
+ soup.html['lang'] = self.lang
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
+ soup.head.insert(0,mlang)
+ soup.head.insert(1,mcharset)
+ return self.adeify_images(soup)
def get_cover_url(self):
cover_url = None
diff --git a/src/calibre/web/feeds/recipes/recipe_rts.py b/src/calibre/web/feeds/recipes/recipe_rts.py
new file mode 100644
index 0000000000..57ee346d62
--- /dev/null
+++ b/src/calibre/web/feeds/recipes/recipe_rts.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+__license__ = 'GPL v3'
+__copyright__ = '2009, Darko Miletic '
+
+'''
+www.rts.rs
+'''
+
+import re
+from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
+
+class RTS(BasicNewsRecipe):
+ title = 'RTS: Vesti'
+ __author__ = 'Darko Miletic'
+ description = 'News from Serbia'
+ publisher = 'RTS'
+ category = 'news, politics, Serbia, RTS'
+ no_stylesheets = True
+ encoding = 'utf-8'
+ use_embedded_content = True
+ language = _("Serbian")
+ lang = 'sr-Latn-RS'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif}'
+
+ html2lrf_options = [
+ '--comment', description
+ , '--category', category
+ , '--publisher', publisher
+ ]
+
+ html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
+
+
+ preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
+
+ feeds = [
+ (u'Vesti' , u'http://www.rts.rs/page/stories/sr/rss.html' )
+ ,(u'Srbija' , u'http://www.rts.rs/page/stories/sr/rss/9/Srbija.html' )
+ ,(u'Region' , u'http://www.rts.rs/page/stories/sr/rss/11/Region.html' )
+ ,(u'Svet' , u'http://www.rts.rs/page/stories/sr/rss/10/Svet.html' )
+ ,(u'Hronika' , u'http://www.rts.rs/page/stories/sr/rss/135/Hronika.html' )
+ ,(u'Drustvo' , u'http://www.rts.rs/page/stories/sr/rss/125/Dru%C5%A1tvo.html')
+ ,(u'Ekonomija' , u'http://www.rts.rs/page/stories/sr/rss/13/Ekonomija.html' )
+ ,(u'Nauka' , u'http://www.rts.rs/page/stories/sr/rss/14/Nauka.html' )
+ ,(u'Kultura' , u'http://www.rts.rs/page/stories/sr/rss/16/Kultura.html' )
+ ,(u'Zanimljivosti' , u'http://www.rts.rs/page/stories/sr/rss/15/Zanimljivosti.html')
+ ,(u'Sport' , u'http://www.rts.rs/page/sport/sr/rss.html' )
+ ]
+
+ def preprocess_html(self, soup):
+ soup.html['xml:lang'] = self.lang
+ soup.html['lang'] = self.lang
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
+ soup.head.insert(0,mlang)
+ soup.head.insert(1,mcharset)
+ return self.adeify_images(soup)
+
diff --git a/src/calibre/web/feeds/recipes/recipe_st_petersburg_times.py b/src/calibre/web/feeds/recipes/recipe_st_petersburg_times.py
index 8c22262904..cc023448c7 100644
--- a/src/calibre/web/feeds/recipes/recipe_st_petersburg_times.py
+++ b/src/calibre/web/feeds/recipes/recipe_st_petersburg_times.py
@@ -1,39 +1,48 @@
-#!/usr/bin/env python
-
-__license__ = 'GPL v3'
-__copyright__ = '2008, Darko Miletic '
-'''
-sptimes.ru
-'''
-
-from calibre import strftime
-from calibre.web.feeds.news import BasicNewsRecipe
-
-class PetersburgTimes(BasicNewsRecipe):
- title = u'The St. Petersburg Times'
- __author__ = 'Darko Miletic'
- description = 'News from Russia'
- oldest_article = 7
- max_articles_per_feed = 100
- no_stylesheets = True
- use_embedded_content = False
- language = _('English')
- INDEX = 'http://www.sptimes.ru'
-
- def parse_index(self):
- articles = []
- soup = self.index_to_soup(self.INDEX)
-
- for item in soup.findAll('a', attrs={'class':'story_link_o'}):
- if item.has_key('href'):
- url = self.INDEX + item['href'].replace('action_id=2','action_id=100')
- title = self.tag_to_string(item)
- c_date = strftime('%A, %d %B, %Y')
- description = ''
- articles.append({
- 'title':title,
- 'date':c_date,
- 'url':url,
- 'description':description
- })
- return [(soup.head.title.string, articles)]
+#!/usr/bin/env python
+
+__license__ = 'GPL v3'
+__copyright__ = '2009, Darko Miletic '
+
+'''
+sptimes.ru
+'''
+
+from calibre.web.feeds.news import BasicNewsRecipe
+
+class PetersburgTimes(BasicNewsRecipe):
+ title = 'The St. Petersburg Times'
+ __author__ = 'Darko Miletic'
+ description = 'News from Russia'
+ publisher = 'sptimes.ru'
+ category = 'news, politics, Russia'
+ max_articles_per_feed = 100
+ no_stylesheets = True
+ remove_javascript = True
+ encoding = 'cp1251'
+ use_embedded_content = False
+ language = _('English')
+
+ html2lrf_options = [
+ '--comment', description
+ , '--category', category
+ , '--publisher', publisher
+ , '--ignore-tables'
+ ]
+
+ html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
+
+ remove_tags = [dict(name=['object','link','embed'])]
+
+ feeds = [(u'Headlines', u'http://sptimes.ru/headlines.php' )]
+
+ def preprocess_html(self, soup):
+ return self.adeify_images(soup)
+
+ def get_article_url(self, article):
+ raw = article.get('guid', None)
+ return raw
+
+ def print_version(self, url):
+ start_url, question, article_id = url.rpartition('/')
+ return u'http://www.sptimes.ru/index.php?action_id=100&story_id=' + article_id
+
\ No newline at end of file
diff --git a/src/calibre/web/feeds/recipes/recipe_vijesti.py b/src/calibre/web/feeds/recipes/recipe_vijesti.py
index 9923193d7b..9ef32e636c 100644
--- a/src/calibre/web/feeds/recipes/recipe_vijesti.py
+++ b/src/calibre/web/feeds/recipes/recipe_vijesti.py
@@ -9,6 +9,7 @@ vijesti.me
import re
from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
class Vijesti(BasicNewsRecipe):
title = 'Vijesti'
@@ -16,8 +17,8 @@ class Vijesti(BasicNewsRecipe):
description = 'News from Montenegro'
publisher = 'Daily Press Vijesti'
category = 'news, politics, Montenegro'
- oldest_article = 1
- max_articles_per_feed = 100
+ oldest_article = 2
+ max_articles_per_feed = 150
no_stylesheets = True
remove_javascript = True
encoding = 'cp1250'
@@ -25,7 +26,8 @@ class Vijesti(BasicNewsRecipe):
remove_javascript = True
use_embedded_content = False
language = _('Serbian')
- extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
+ lang ='sr-Latn-Me'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: sans1, sans-serif}'
html2lrf_options = [
'--comment', description
@@ -44,12 +46,15 @@ class Vijesti(BasicNewsRecipe):
feeds = [(u'Sve vijesti', u'http://www.vijesti.me/rss.php' )]
def preprocess_html(self, soup):
- soup.html['xml:lang'] = 'sr-Latn-ME'
- soup.html['lang'] = 'sr-Latn-ME'
- mtag = ''
- soup.head.insert(0,mtag)
- for item in soup.findAll('img'):
- if item.has_key('align'):
- del item['align']
- item.insert(0,'
')
- return soup
+ soup.html['xml:lang'] = self.lang
+ soup.html['lang'] = self.lang
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
+ soup.head.insert(0,mlang)
+ soup.head.insert(1,mcharset)
+ return self.adeify_images(soup)
+
+ def get_article_url(self, article):
+ raw = article.get('link', None)
+ return raw.replace('.cg.yu','.me')
+
\ No newline at end of file
diff --git a/src/calibre/web/feeds/recipes/recipe_vreme.py b/src/calibre/web/feeds/recipes/recipe_vreme.py
index 1df953cae3..bcc7a14407 100644
--- a/src/calibre/web/feeds/recipes/recipe_vreme.py
+++ b/src/calibre/web/feeds/recipes/recipe_vreme.py
@@ -9,6 +9,7 @@ vreme.com
import re
from calibre import strftime
from calibre.web.feeds.news import BasicNewsRecipe
+from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
class Vreme(BasicNewsRecipe):
title = 'Vreme'
@@ -27,7 +28,7 @@ class Vreme(BasicNewsRecipe):
language = _('Serbian')
lang = 'sr-Latn-RS'
direction = 'ltr'
- extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{text-align: justify; font-family: serif1, serif} .article_description{font-family: serif1, serif}'
+ extra_css = '@font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} body{font-family: serif1, serif} .article_description{font-family: serif1, serif} .heading1{font-size: x-large; font-weight: bold} .heading2{font-size: large; font-weight: bold} .toc-heading{font-size: small}'
html2lrf_options = [
'--comment' , description
@@ -89,9 +90,10 @@ class Vreme(BasicNewsRecipe):
del item['size']
soup.html['lang'] = self.lang
soup.html['dir' ] = self.direction
- mtag = ''
- mtag += '\n'
- soup.head.insert(0,mtag)
+ mlang = Tag(soup,'meta',[("http-equiv","Content-Language"),("content",self.lang)])
+ mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=UTF-8")])
+ soup.head.insert(0,mlang)
+ soup.head.insert(1,mcharset)
return soup
def get_cover_url(self):
diff --git a/src/cssutils/__init__.py b/src/cssutils/__init__.py
index 846ab3b397..467d17238b 100644
--- a/src/cssutils/__init__.py
+++ b/src/cssutils/__init__.py
@@ -70,11 +70,11 @@ Usage may be::
__all__ = ['css', 'stylesheets', 'CSSParser', 'CSSSerializer']
__docformat__ = 'restructuredtext'
__author__ = 'Christof Hoeke with contributions by Walter Doerwald'
-__date__ = '$LastChangedDate:: 2009-02-16 12:05:02 -0800 #$:'
+__date__ = '$LastChangedDate:: 2009-05-09 13:59:54 -0700 #$:'
-VERSION = '0.9.6a1'
+VERSION = '0.9.6a5'
-__version__ = '%s $Id: __init__.py 1669 2009-02-16 20:05:02Z cthedot $' % VERSION
+__version__ = '%s $Id: __init__.py 1747 2009-05-09 20:59:54Z cthedot $' % VERSION
import codec
import xml.dom
@@ -92,6 +92,9 @@ from parse import CSSParser
from serialize import CSSSerializer
ser = CSSSerializer()
+from profiles import Profiles
+profile = Profiles(log=log)
+
# used by Selector defining namespace prefix '*'
_ANYNS = -1
diff --git a/src/cssutils/css/cssmediarule.py b/src/cssutils/css/cssmediarule.py
index 3867f99c16..d4b82af600 100644
--- a/src/cssutils/css/cssmediarule.py
+++ b/src/cssutils/css/cssmediarule.py
@@ -1,7 +1,7 @@
"""CSSMediaRule implements DOM Level 2 CSS CSSMediaRule."""
__all__ = ['CSSMediaRule']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: cssmediarule.py 1641 2009-01-13 21:05:37Z cthedot $'
+__version__ = '$Id: cssmediarule.py 1743 2009-05-09 20:33:15Z cthedot $'
import cssrule
import cssutils
@@ -131,8 +131,15 @@ class CSSMediaRule(cssrule.CSSRule):
mediaendonly=True,
separateEnd=True)
nonetoken = self._nexttoken(tokenizer, None)
- if (u'}' != self._tokenvalue(braceOrEOF) and
- 'EOF' != self._type(braceOrEOF)):
+ if 'EOF' == self._type(braceOrEOF):
+ # HACK!!!
+ # TODO: Not complete, add EOF to rule and } to @media
+ cssrulestokens.append(braceOrEOF)
+ braceOrEOF = ('CHAR', '}', 0, 0)
+ self._log.debug(u'CSSMediaRule: Incomplete, adding "}".',
+ token=braceOrEOF, neverraise=True)
+
+ if u'}' != self._tokenvalue(braceOrEOF):
self._log.error(u'CSSMediaRule: No "}" found.',
token=braceOrEOF)
elif nonetoken:
diff --git a/src/cssutils/css/cssstyledeclaration.py b/src/cssutils/css/cssstyledeclaration.py
index c766151116..ae106341bb 100644
--- a/src/cssutils/css/cssstyledeclaration.py
+++ b/src/cssutils/css/cssstyledeclaration.py
@@ -51,7 +51,7 @@ TODO:
"""
__all__ = ['CSSStyleDeclaration', 'Property']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: cssstyledeclaration.py 1658 2009-02-07 18:24:40Z cthedot $'
+__version__ = '$Id: cssstyledeclaration.py 1710 2009-04-18 15:46:20Z cthedot $'
from cssproperties import CSS2Properties
from property import Property
@@ -613,7 +613,7 @@ class CSSStyleDeclaration(CSS2Properties, cssutils.util.Base2):
except IndexError:
return u''
- length = property(lambda self: len(self.__nnames()),
+ length = property(lambda self: len(list(self.__nnames())),
doc="(DOM) The number of distinct properties that have been explicitly "
"in this declaration block. The range of valid indices is 0 to "
"length-1 inclusive. These are properties with a different ``name`` "
diff --git a/src/cssutils/css/cssvalue.py b/src/cssutils/css/cssvalue.py
index 856e42e5c1..9b5a8a1aef 100644
--- a/src/cssutils/css/cssvalue.py
+++ b/src/cssutils/css/cssvalue.py
@@ -7,10 +7,9 @@
"""
__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList', 'RGBColor']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: cssvalue.py 1638 2009-01-13 20:39:33Z cthedot $'
+__version__ = '$Id: cssvalue.py 1684 2009-03-01 18:26:21Z cthedot $'
from cssutils.prodparser import *
-from cssutils.profiles import profiles
import cssutils
import cssutils.helper
import re
@@ -121,7 +120,8 @@ class CSSValue(cssutils.util._NewBase):
# special case IE only expression
Prod(name='expression',
match=lambda t, v: t == self._prods.FUNCTION and
- cssutils.helper.normalize(v) == 'expression(',
+ cssutils.helper.normalize(v) in (u'expression(',
+ u'alpha('),
nextSor=nextSor,
toSeq=lambda t, tokens: (ExpressionValue.name,
ExpressionValue(cssutils.helper.pushtoken(t,
@@ -968,7 +968,8 @@ class RGBColor(CSSPrimitiveValue):
class ExpressionValue(CSSFunction):
- """Special IE only CSSFunction which may contain *anything*."""
+ """Special IE only CSSFunction which may contain *anything*.
+ Used for expressions and ``alpha(opacity=100)`` currently"""
name = u'Expression (IE only)'
def _productiondefinition(self):
diff --git a/src/cssutils/css/property.py b/src/cssutils/css/property.py
index 04a5e3c0eb..c096fa767d 100644
--- a/src/cssutils/css/property.py
+++ b/src/cssutils/css/property.py
@@ -1,10 +1,9 @@
"""Property is a single CSS property in a CSSStyleDeclaration."""
__all__ = ['Property']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: property.py 1664 2009-02-07 22:47:09Z cthedot $'
+__version__ = '$Id: property.py 1685 2009-03-01 18:26:48Z cthedot $'
from cssutils.helper import Deprecated
-from cssutils.profiles import profiles
from cssvalue import CSSValue
import cssutils
import xml.dom
@@ -67,6 +66,7 @@ class Property(cssutils.util.Base):
self._mediaQuery = _mediaQuery
self._parent = _parent
+ self.__nametoken = None
self._name = u''
self._literalname = u''
if name:
@@ -193,6 +193,7 @@ class Property(cssutils.util.Base):
# define a token for error logging
if isinstance(name, list):
token = name[0]
+ self.__nametoken = token
else:
token = None
@@ -208,9 +209,9 @@ class Property(cssutils.util.Base):
self.seqs[0] = newseq
# # validate
- if self._name not in profiles.knownnames:
+ if self._name not in cssutils.profile.knownNames:
# self.valid = False
- self._log.warn(u'Property: Unknown Property.',
+ self._log.warn(u'Property: Unknown Property name.',
token=token, neverraise=True)
else:
pass
@@ -354,7 +355,7 @@ class Property(cssutils.util.Base):
# validate priority
if self._priority not in (u'', u'important'):
self._log.error(u'Property: No CSS priority value: %r.' %
- self._priority)
+ self._priority)
priority = property(lambda self: self._priority, _setPriority,
doc="Priority of this property.")
@@ -362,42 +363,101 @@ class Property(cssutils.util.Base):
literalpriority = property(lambda self: self._literalpriority,
doc="Readonly literal (not normalized) priority of this property")
- def validate(self, profile=None):
- """Validate value against `profile`.
+ def validate(self, profiles=None):
+ """Validate value against `profiles`.
- :param profile:
- A profile name used for validating. If no `profile` is given
- ``Property.profiles
+ :param profiles:
+ A list of profile names used for validating. If no `profiles`
+ is given ``cssutils.profile.defaultProfiles`` is used
+
+ For each of the following cases a message is reported:
+
+ - INVALID (so the property is known but not valid)
+ ``ERROR Property: Invalid value for "{PROFILE-1[/PROFILE-2...]"
+ property: ...``
+
+ - VALID but not in given profiles or defaultProfiles
+ ``WARNING Property: Not valid for profile "{PROFILE-X}" but valid
+ "{PROFILE-Y}" property: ...``
+
+ - VALID in current profile
+ ``DEBUG Found valid "{PROFILE-1[/PROFILE-2...]" property...``
+
+ - UNKNOWN property
+ ``WARNING Unknown Property name...`` is issued
+
+ so for example::
+
+ cssutils.log.setLevel(logging.DEBUG)
+ parser = cssutils.CSSParser()
+ s = parser.parseString('''body {
+ unknown-property: x;
+ color: 4;
+ color: rgba(1,2,3,4);
+ color: red
+ }''')
+
+ # Log output:
+
+ WARNING Property: Unknown Property name. [2:9: unknown-property]
+ ERROR Property: Invalid value for "CSS Color Module Level 3/CSS Level 2.1" property: 4 [3:9: color]
+ DEBUG Property: Found valid "CSS Color Module Level 3" value: rgba(1, 2, 3, 4) [4:9: color]
+ DEBUG Property: Found valid "CSS Level 2.1" value: red [5:9: color]
+
+
+ and when setting an explicit default profile::
+
+ cssutils.profile.defaultProfiles = cssutils.profile.CSS_LEVEL_2
+ s = parser.parseString('''body {
+ unknown-property: x;
+ color: 4;
+ color: rgba(1,2,3,4);
+ color: red
+ }''')
+
+ # Log output:
+
+ WARNING Property: Unknown Property name. [2:9: unknown-property]
+ ERROR Property: Invalid value for "CSS Color Module Level 3/CSS Level 2.1" property: 4 [3:9: color]
+ WARNING Property: Not valid for profile "CSS Level 2.1" but valid "CSS Color Module Level 3" value: rgba(1, 2, 3, 4) [4:9: color]
+ DEBUG Property: Found valid "CSS Level 2.1" value: red [5:9: color]
"""
valid = False
if self.name and self.value:
- if profile is None:
- usedprofile = cssutils.profiles.defaultprofile
- else:
- usedprofile = profile
-
- if self.name in profiles.knownnames:
- valid, validprofiles = profiles.validateWithProfile(self.name,
- self.value,
- usedprofile)
+ if self.name in cssutils.profile.knownNames:
+ # add valid, matching, validprofiles...
+ valid, matching, validprofiles = \
+ cssutils.profile.validateWithProfile(self.name,
+ self.value,
+ profiles)
+
if not valid:
- self._log.error(u'Property: Invalid value for "%s" property: %s: %s'
- % (u'/'.join(validprofiles),
- self.name,
+ self._log.error(u'Property: Invalid value for '
+ u'"%s" property: %s'
+ % (u'/'.join(validprofiles), self.value),
+ token=self.__nametoken,
+ neverraise=True)
+
+ # TODO: remove logic to profiles!
+ elif valid and not matching:#(profiles and profiles not in validprofiles):
+ if not profiles:
+ notvalidprofiles = u'/'.join(cssutils.profile.defaultProfiles)
+ else:
+ notvalidprofiles = profiles
+ self._log.warn(u'Property: Not valid for profile "%s" '
+ u'but valid "%s" value: %s '
+ % (notvalidprofiles, u'/'.join(validprofiles),
self.value),
+ token = self.__nametoken,
neverraise=True)
- elif valid and (usedprofile and usedprofile not in validprofiles):
- self._log.warn(u'Property: Not valid for profile "%s": %s: %s'
- % (usedprofile, self.name, self.value),
- neverraise=True)
+ valid = False
- if valid:
- self._log.info(u'Property: Found valid "%s" property: %s: %s'
- % (u'/'.join(validprofiles),
- self.name,
- self.value),
+ elif valid:
+ self._log.debug(u'Property: Found valid "%s" value: %s'
+ % (u'/'.join(validprofiles), self.value),
+ token = self.__nametoken,
neverraise=True)
if self._priority not in (u'', u'important'):
diff --git a/src/cssutils/css/selector.py b/src/cssutils/css/selector.py
index c3120f29d2..a2191e548d 100644
--- a/src/cssutils/css/selector.py
+++ b/src/cssutils/css/selector.py
@@ -7,7 +7,7 @@ TODO
"""
__all__ = ['Selector']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: selector.py 1638 2009-01-13 20:39:33Z cthedot $'
+__version__ = '$Id: selector.py 1741 2009-05-09 18:20:20Z cthedot $'
from cssutils.util import _SimpleNamespaces
import cssutils
@@ -701,6 +701,14 @@ class Selector(cssutils.util.Base2):
u'Selector: Unexpected negation.', token=token)
return expected
+ def _atkeyword(expected, seq, token, tokenizer=None):
+ "invalidates selector"
+ new['wellformed'] = False
+ self._log.error(
+ u'Selector: Unexpected ATKEYWORD.', token=token)
+ return expected
+
+
# expected: only|not or mediatype, mediatype, feature, and
newseq = self._tempSeq()
@@ -727,7 +735,8 @@ class Selector(cssutils.util.Base2):
'INCLUDES': _attcombinator,
'S': _S,
- 'COMMENT': _COMMENT})
+ 'COMMENT': _COMMENT,
+ 'ATKEYWORD': _atkeyword})
wellformed = wellformed and new['wellformed']
# post condition
diff --git a/src/cssutils/cssproductions.py b/src/cssutils/cssproductions.py
index 53cb0e0b31..90155539a0 100644
--- a/src/cssutils/cssproductions.py
+++ b/src/cssutils/cssproductions.py
@@ -12,14 +12,14 @@ open issues
"""
__all__ = ['CSSProductions', 'MACROS', 'PRODUCTIONS']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: cssproductions.py 1537 2008-12-03 14:37:10Z cthedot $'
+__version__ = '$Id: cssproductions.py 1738 2009-05-02 13:03:28Z cthedot $'
# a complete list of css3 macros
MACROS = {
'nonascii': r'[^\0-\177]',
'unicode': r'\\[0-9a-f]{1,6}(?:{nl}|{s})?',
- # 'escape': r'{unicode}|\\[ -~\200-\4177777]',
- 'escape': r'{unicode}|\\[ -~\200-\777]',
+ #'escape': r'{unicode}|\\[ -~\200-\777]',
+ 'escape': r'{unicode}|\\[^\n\r\f0-9a-f]',
'nmstart': r'[_a-zA-Z]|{nonascii}|{escape}',
'nmchar': r'[-_a-zA-Z0-9]|{nonascii}|{escape}',
'string1': r'"([^\n\r\f\\"]|\\{nl}|{escape})*"',
diff --git a/src/cssutils/errorhandler.py b/src/cssutils/errorhandler.py
index 5e7a2f83f7..2d1814b6c8 100644
--- a/src/cssutils/errorhandler.py
+++ b/src/cssutils/errorhandler.py
@@ -16,7 +16,7 @@ log
"""
__all__ = ['ErrorHandler']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: errorhandler.py 1560 2008-12-14 16:13:16Z cthedot $'
+__version__ = '$Id: errorhandler.py 1728 2009-05-01 20:35:25Z cthedot $'
from helper import Deprecated
import logging
@@ -27,7 +27,7 @@ class _ErrorHandler(object):
"""
handles all errors and log messages
"""
- def __init__(self, log, defaultloglevel=logging.INFO,
+ def __init__(self, log, defaultloglevel=logging.INFO,
raiseExceptions=True):
"""
inits log if none given
@@ -51,7 +51,7 @@ class _ErrorHandler(object):
hdlr.setFormatter(formatter)
self._log.addHandler(hdlr)
self._log.setLevel(defaultloglevel)
-
+
self.raiseExceptions = raiseExceptions
def __getattr__(self, name):
@@ -86,12 +86,12 @@ class _ErrorHandler(object):
if error and self.raiseExceptions and not neverraise:
if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError):
raise
- elif issubclass(error, xml.dom.DOMException):
+ elif issubclass(error, xml.dom.DOMException):
error.line = line
error.col = col
- raise error(msg)
- else:
- raise error(msg)
+# raise error(msg, line, col)
+# else:
+ raise error(msg)
else:
self._logcall(msg)
diff --git a/src/cssutils/helper.py b/src/cssutils/helper.py
index 19d77ed27a..912d65d5e9 100644
--- a/src/cssutils/helper.py
+++ b/src/cssutils/helper.py
@@ -68,6 +68,9 @@ def string(value):
u'\f', u'\\c ').replace(
u'"', u'\\"')
+ if value.endswith(u'\\'):
+ value = value[:-1] + u'\\\\'
+
return u'"%s"' % value
def stringvalue(string):
@@ -77,7 +80,7 @@ def stringvalue(string):
``'a \'string'`` => ``a 'string``
"""
- return string.replace('\\'+string[0], string[0])[1:-1]
+ return string.replace(u'\\'+string[0], string[0])[1:-1]
_match_forbidden_in_uri = re.compile(ur'''.*?[\(\)\s\;,'"]''', re.U).match
def uri(value):
diff --git a/src/cssutils/profiles.py b/src/cssutils/profiles.py
index 2392da6161..78fb43468d 100644
--- a/src/cssutils/profiles.py
+++ b/src/cssutils/profiles.py
@@ -1,41 +1,340 @@
-"""CSS profiles.
-
-css2 is based on cssvalues
- contributed by Kevin D. Smith, thanks!
-
- "cssvalues" is used as a property validator.
- it is an importable object that contains a dictionary of compiled regular
- expressions. The keys of this dictionary are all of the valid CSS property
- names. The values are compiled regular expressions that can be used to
- validate the values for that property. (Actually, the values are references
- to the 'match' method of a compiled regular expression, so that they are
- simply called like functions.)
+"""CSS profiles.
+Profiles is based on code by Kevin D. Smith, orginally used as cssvalues,
+thanks!
"""
-__all__ = ['profiles']
+__all__ = ['Profiles']
__docformat__ = 'restructuredtext'
__version__ = '$Id: cssproperties.py 1116 2008-03-05 13:52:23Z cthedot $'
-import cssutils
import re
+class NoSuchProfileException(Exception):
+ """Raised if no profile with given name is found"""
+ pass
+
+
+class Profiles(object):
+ """
+ All profiles used for validation. ``cssutils.profile`` is a
+ preset object of this class and used by all properties for validation.
+
+ Predefined profiles are (use
+ :meth:`~cssutils.profiles.Profiles.propertiesByProfile` to
+ get a list of defined properties):
+
+ :attr:`~cssutils.profiles.Profiles.CSS_LEVEL_2`
+ Properties defined by CSS2.1
+ :attr:`~cssutils.profiles.Profiles.CSS3_COLOR`
+ CSS 3 color properties
+ :attr:`~cssutils.profiles.Profiles.CSS3_BOX`
+ Currently overflow related properties only
+ :attr:`~cssutils.profiles.Profiles.CSS3_PAGED_MEDIA`
+ As defined at http://www.w3.org/TR/css3-page/ (at 090307)
+
+ Predefined macros are:
+
+ :attr:`~cssutils.profiles.Profiles._TOKEN_MACROS`
+ Macros containing the token values as defined to CSS2
+ :attr:`~cssutils.profiles.Profiles._MACROS`
+ Additional general macros.
+
+ If you want to redefine any of these macros do this in your custom
+ macros.
+ """
+ CSS_LEVEL_2 = 'CSS Level 2.1'
+ CSS3_COLOR = CSS_COLOR_LEVEL_3 = 'CSS Color Module Level 3'
+ CSS3_BOX = CSS_BOX_LEVEL_3 = 'CSS Box Module Level 3'
+ CSS3_PAGED_MEDIA = 'CSS3 Paged Media Module'
+
+ _TOKEN_MACROS = {
+ '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*',
+ }
+ _MACROS = {
+ '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}%',
+ }
+
+ def __init__(self, log=None):
+ """A few known profiles are predefined."""
+ self._log = log
+ self._profileNames = [] # to keep order, REFACTOR!
+ self._profiles = {}
+ self._defaultProfiles = None
+
+ self.addProfile(self.CSS_LEVEL_2,
+ properties[self.CSS_LEVEL_2],
+ macros[self.CSS_LEVEL_2])
+ self.addProfile(self.CSS3_BOX,
+ properties[self.CSS3_BOX],
+ macros[self.CSS3_BOX])
+ self.addProfile(self.CSS3_COLOR,
+ properties[self.CSS3_COLOR],
+ macros[self.CSS3_COLOR])
+ self.addProfile(self.CSS3_PAGED_MEDIA,
+ properties[self.CSS3_PAGED_MEDIA],
+ macros[self.CSS3_PAGED_MEDIA])
+
+ self.__update_knownNames()
+
+ 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 hasattr(value, '__call__'):
+ while re.search(r'{[a-z][a-z0-9-]*}', value):
+ value = re.sub(r'{(?P[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 hasattr(value, '__call__'):
+ value = re.compile('^(?:%s)$' % value, re.I).match
+ dictionary[key] = value
+
+ return dictionary
+
+ def __update_knownNames(self):
+ self._knownNames = []
+ for properties in self._profiles.values():
+ self._knownNames.extend(properties.keys())
+
+ def _getDefaultProfiles(self):
+ "If not explicitly set same as Profiles.profiles but in reverse order."
+ if not self._defaultProfiles:
+ return self.profiles#list(reversed(self.profiles))
+ else:
+ return self._defaultProfiles
+
+ def _setDefaultProfiles(self, profiles):
+ "profiles may be a single or a list of profile names"
+ if isinstance(profiles, basestring):
+ self._defaultProfiles = (profiles,)
+ else:
+ self._defaultProfiles = profiles
+
+ defaultProfiles = property(_getDefaultProfiles,
+ _setDefaultProfiles,
+ doc=u"Names of profiles to use for validation."
+ u"To use e.g. the CSS2 profile set "
+ u"``cssutils.profile.defaultProfiles = "
+ u"cssutils.profile.CSS_LEVEL_2``")
+
+ profiles = property(lambda self: self._profileNames,
+ doc=u'Names of all profiles in order as defined.')
+
+ knownNames = property(lambda self: self._knownNames,
+ doc="All known property 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`.
+
+ :param profile:
+ the new `profile`'s name
+ :param 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.
+ :param macros:
+ may be used in the given properties definitions. There are some
+ predefined basic macros which may always be used in
+ :attr:`Profiles._TOKEN_MACROS` and :attr:`Profiles._MACROS`.
+ """
+ if not macros:
+ macros = {}
+ m = Profiles._TOKEN_MACROS.copy()
+ m.update(Profiles._MACROS)
+ m.update(macros)
+ properties = self._expand_macros(properties, m)
+ self._profileNames.append(profile)
+ self._profiles[profile] = self._compile_regexes(properties)
+
+ self.__update_knownNames()
+
+ def removeProfile(self, profile=None, all=False):
+ """Remove `profile` or remove `all` profiles.
+
+ :param profile:
+ profile name to remove
+ :param all:
+ if ``True`` removes all profiles to start with a clean state
+ :exceptions:
+ - :exc:`cssutils.profiles.NoSuchProfileException`:
+ If given `profile` cannot be found.
+ """
+ if all:
+ self._profiles.clear()
+ del self._profileNames[:]
+ else:
+ try:
+ del self._profiles[profile]
+ del self._profileNames[self._profileNames.index(profile)]
+ except KeyError:
+ raise NoSuchProfileException(u'No profile %r.' % profile)
+
+ self.__update_knownNames()
+
+ def propertiesByProfile(self, profiles=None):
+ """Generator: Yield property names, if no `profiles` is given all
+ profile's properties are used.
+
+ :param profiles:
+ a single profile name or a list of names.
+ """
+ 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.
+
+ :param name:
+ a property name
+ :param value:
+ a CSS value (string)
+ :returns:
+ if the `value` is valid for the given property `name` in 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, profiles=None):
+ """Check if `value` is valid for given property `name` returning
+ ``(valid, profile)``.
+
+ :param name:
+ a property name
+ :param value:
+ a CSS value (string)
+ :param profiles:
+ internal parameter used by Property.validate only
+ :returns:
+ ``valid, matching, profiles`` where ``valid`` is if the `value`
+ is valid for the given property `name` in any profile,
+ ``matching==True`` if it is valid in the given `profiles`
+ and ``profiles`` the profile names for which the value is valid
+ (or ``[]`` if not valid at all)
+
+ Example::
+
+ >>> cssutils.profile.defaultProfiles = cssutils.profile.CSS_LEVEL_2
+ >>> print cssutils.profile.validateWithProfile('color', 'rgba(1,1,1,1)')
+ (True, False, Profiles.CSS3_COLOR)
+ """
+ if name not in self.knownNames:
+ return False, False, []
+ else:
+ if not profiles:
+ profiles = self.defaultProfiles
+ elif isinstance(profiles, basestring):
+ profiles = (profiles, )
+
+ for profilename in profiles:
+ # check given profiles
+ if name in self._profiles[profilename]:
+ validate = self._profiles[profilename][name]
+ try:
+ if validate(value):
+ return True, True, [profilename]
+ except Exception, e:
+ self._log.error(e, error=Exception)
+
+ for profilename in (p for p in self._profileNames
+ if p not in profiles):
+ # check remaining profiles as well
+ if name in self._profiles[profilename]:
+ validate = self._profiles[profilename][name]
+ try:
+ if validate(value):
+ return True, False, [profilename]
+ except Exception, e:
+ self._log.error(e, error=Exception)
+
+ names = []
+ for profilename, properties in self._profiles.items():
+ # return profile to which name belongs
+ if name in properties.keys():
+ names.append(profilename)
+ names.sort()
+ return False, False, names
+
+
properties = {}
+macros = {}
"""
Define some regular expression fragments that will be used as
macros within the CSS property value regular expressions.
"""
-css2macros = {
+macros[Profiles.CSS_LEVEL_2] = {
'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-position': r'({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?)|inherit',
+ 'background-position': r'({percentage}|{length}|left|center|right)(\s*({percentage}|{length}|top|center|bottom))?|((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}',
@@ -72,7 +371,7 @@ css2macros = {
"""
Define the regular expressions for validation all CSS values
"""
-properties['css2'] = {
+properties[Profiles.CSS_LEVEL_2] = {
'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}',
@@ -108,7 +407,7 @@ properties['css2'] = {
'clear': r'none|left|right|both|inherit',
'clip': r'{shape}|auto|inherit',
'color': r'{color}|inherit',
- 'content': r'normal|{content}(\s+{content})*|inherit',
+ 'content': r'none|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',
@@ -191,288 +490,47 @@ properties['css2'] = {
'z-index': r'auto|{integer}|inherit',
}
+# CSS Box Module Level 3
+macros[Profiles.CSS3_BOX] = {
+ 'overflow': macros[Profiles.CSS_LEVEL_2]['overflow']
+ }
+properties[Profiles.CSS3_BOX] = {
+ 'overflow': '{overflow}\s?{overflow}?|inherit',
+ 'overflow-x': '{overflow}|inherit',
+ 'overflow-y': '{overflow}|inherit'
+ }
+
# CSS Color Module Level 3
-css3colormacros = {
+macros[Profiles.CSS3_COLOR] = {
# 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)',
+ 'namedcolor': r'(currentcolor|transparent|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)',
# 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}\)',
'x11color': r'aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen',
'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)',
-
}
-properties['css3color'] = {
+properties[Profiles.CSS3_COLOR] = {
'color': r'{namedcolor}|{hexcolor}|{rgbcolor}|{rgbacolor}|{hslcolor}|inherit',
'opacity': r'{num}|inherit'
}
-# CSS Box Module Level 3
-properties['css3box'] = {
- 'overflow': '{overflow}\s?{overflow}?',
- 'overflow-x': '{overflow}',
- 'overflow-y': '{overflow}'
+# CSS3 Paged Media
+macros[Profiles.CSS3_PAGED_MEDIA] = {
+ 'pagesize': 'a5|a4|a3|b5|b4|letter|legal|ledger',
+ 'pagebreak': 'auto|always|avoid|left|right'
+ }
+properties[Profiles.CSS3_PAGED_MEDIA] = {
+ 'fit': 'fill|hidden|meet|slice',
+ 'fit-position': r'auto|(({percentage}|{length})(\s*({percentage}|{length}))?|((top|center|bottom)\s*(left|center|right)?)|((left|center|right)\s*(top|center|bottom)?))',
+ 'image-orientation': 'auto|{angle}',
+ 'orphans': r'{integer}|inherit',
+ 'page': 'auto|{ident}',
+ 'page-break-before': '{pagebreak}|inherit',
+ 'page-break-after': '{pagebreak}|inherit',
+ 'page-break-inside': 'auto|avoid|inherit',
+ 'size': '({length}{w}){1,2}|auto|{pagesize}{w}(?:portrait|landscape)',
+ 'widows': r'{integer}|inherit'
}
-class NoSuchProfileException(Exception):
- """Raised if no profile with given name is found"""
- pass
-
-
-class Profiles(object):
- """
- All profiles used for validation. ``cssutils.profiles.profiles`` is a
- preset object of this class and used by all properties for validation.
-
- Predefined profiles are (use
- :meth:`~cssutils.profiles.Profiles.propertiesByProfile` to
- get a list of defined properties):
-
- :attr:`~cssutils.profiles.Profiles.Profiles.CSS_LEVEL_2`
- Properties defined by CSS2.1
- :attr:`~cssutils.profiles.Profiles.Profiles.CSS_COLOR_LEVEL_3`
- CSS 3 color properties
- :attr:`~cssutils.profiles.Profiles.Profiles.CSS_BOX_LEVEL_3`
- Currently overflow related properties only
-
- """
- CSS_LEVEL_2 = 'CSS Level 2.1'
- CSS_COLOR_LEVEL_3 = 'CSS Color Module Level 3'
- CSS_BOX_LEVEL_3 = 'CSS Box Module Level 3'
-
- 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}%',
- }
-
- def __init__(self):
- """A few known profiles are predefined."""
- self._log = cssutils.log
- self._profilenames = [] # to keep order, REFACTOR!
- self._profiles = {}
-
- self.addProfile(self.CSS_LEVEL_2, properties['css2'], css2macros)
- self.addProfile(self.CSS_COLOR_LEVEL_3, properties['css3color'], css3colormacros)
- self.addProfile(self.CSS_BOX_LEVEL_3, properties['css3box'])
-
- self.__update_knownnames()
-
- 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 hasattr(value, '__call__'):
- while re.search(r'{[a-z][a-z0-9-]*}', value):
- value = re.sub(r'{(?P[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 hasattr(value, '__call__'):
- value = re.compile('^(?:%s)$' % value, re.I).match
- dictionary[key] = value
-
- return dictionary
-
- def __update_knownnames(self):
- self._knownnames = []
- for properties in self._profiles.values():
- self._knownnames.extend(properties.keys())
-
- profiles = property(lambda self: sorted(self._profiles.keys()),
- doc=u'Names of all profiles.')
-
- knownnames = property(lambda self: self._knownnames,
- doc="All known property 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`.
-
- :param profile:
- the new `profile`'s name
- :param 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.
- :param macros:
- may be used in the given properties definitions. There are some
- predefined basic macros which may always be used in
- :attr:`Profiles.basicmacros` and :attr:`Profiles.generalmacros`.
- """
- 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)
-
- self.__update_knownnames()
-
- def removeProfile(self, profile=None, all=False):
- """Remove `profile` or remove `all` profiles.
-
- :param profile:
- profile name to remove
- :param all:
- if ``True`` removes all profiles to start with a clean state
- :exceptions:
- - :exc:`cssutils.profiles.NoSuchProfileException`:
- If given `profile` cannot be found.
- """
- if all:
- self._profiles.clear()
- else:
- try:
- del self._profiles[profile]
- except KeyError:
- raise NoSuchProfileException(u'No profile %r.' % profile)
-
- self.__update_knownnames()
-
- def propertiesByProfile(self, profiles=None):
- """Generator: Yield property names, if no `profiles` is given all
- profile's properties are used.
-
- :param profiles:
- a single profile name or a list of names.
- """
- 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.
-
- :param name:
- a property name
- :param value:
- a CSS value (string)
- :returns:
- if the `value` is valid for the given property `name` in 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, profiles=None):
- """Check if `value` is valid for given property `name` returning
- ``(valid, profile)``.
-
- :param name:
- a property name
- :param value:
- a CSS value (string)
- :returns:
- ``valid, profiles`` where ``valid`` is if the `value` is valid for
- the given property `name` in any profile of given `profiles`
- and ``profiles`` the profile names for which the value is valid
- (or ``[]`` if not valid at all)
-
- 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)
- """
- if name not in self.knownnames:
- return False, []
- else:
- if not profiles:
- profiles = self._profilenames
- elif isinstance(profiles, basestring):
- profiles = (profiles, )
-
- for profilename in profiles:
- # check given profiles
- if name in self._profiles[profilename]:
- validate = self._profiles[profilename][name]
- try:
- if validate(value):
- return True, [profilename]
- except Exception, e:
- self._log.error(e, error=Exception)
-
- for profilename in (p for p in self._profilenames if p not in profiles):
- # check remaining profiles as well
- if name in self._profiles[profilename]:
- validate = self._profiles[profilename][name]
- try:
- if validate(value):
- return True, [profilename]
- except Exception, e:
- self._log.error(e, error=Exception)
-
- names = []
- for profilename, properties in self._profiles.items():
- # return profile to which name belongs
- if name in properties.keys():
- names.append(profilename)
- names.sort()
- return False, names
-
-# used by
-profiles = Profiles()
-
-# set for validation to e.g.``Profiles.CSS_LEVEL_2``
-defaultprofile = None
diff --git a/src/cssutils/serialize.py b/src/cssutils/serialize.py
index 0533901a05..ac6539ed29 100644
--- a/src/cssutils/serialize.py
+++ b/src/cssutils/serialize.py
@@ -3,7 +3,7 @@
"""cssutils serializer"""
__all__ = ['CSSSerializer', 'Preferences']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: serialize.py 1606 2009-01-03 20:32:17Z cthedot $'
+__version__ = '$Id: serialize.py 1741 2009-05-09 18:20:20Z cthedot $'
import codecs
import cssutils
@@ -58,6 +58,9 @@ class Preferences(object):
keepEmptyRules = False
defines if empty rules like e.g. ``a {}`` are kept in the resulting
serialized sheet
+ keepUnkownAtRules = True
+ defines if unknown @rules like e.g. ``@three-dee {}`` are kept in the
+ serialized sheet
keepUsedNamespaceRulesOnly = False
if True only namespace rules which are actually used are kept
@@ -82,12 +85,10 @@ class Preferences(object):
spacer = u' '
general spacer, used e.g. by CSSUnknownRule
- validOnly = False **DO NOT CHANGE YET**
- if True only valid (currently Properties) are kept
+ validOnly = False
+ if True only valid (Properties) are output
A Property is valid if it is a known Property with a valid value.
- Currently CSS 2.1 values as defined in cssproperties.py would be
- valid.
"""
def __init__(self, **initials):
"""Always use named instead of positional parameters."""
@@ -118,6 +119,7 @@ class Preferences(object):
self.keepAllProperties = True
self.keepComments = True
self.keepEmptyRules = False
+ self.keepUnkownAtRules = True
self.keepUsedNamespaceRulesOnly = False
self.lineNumbers = False
self.lineSeparator = u'\n'
@@ -139,6 +141,7 @@ class Preferences(object):
self.indent = u''
self.keepComments = False
self.keepEmptyRules = False
+ self.keepUnkownAtRules = False
self.keepUsedNamespaceRulesOnly = True
self.lineNumbers = False
self.lineSeparator = u''
@@ -563,7 +566,7 @@ class CSSSerializer(object):
anything until ";" or "{...}"
+ CSSComments
"""
- if rule.wellformed:
+ if rule.wellformed and self.prefs.keepUnkownAtRules:
out = Out(self)
out.append(rule.atkeyword)
@@ -741,10 +744,11 @@ class CSSSerializer(object):
out.append(separator)
elif isinstance(val, cssutils.css.Property):
# PropertySimilarNameList
- out.append(val.cssText)
- if not (self.prefs.omitLastSemicolon and i==len(seq)-1):
- out.append(u';')
- out.append(separator)
+ if val.cssText:
+ out.append(val.cssText)
+ if not (self.prefs.omitLastSemicolon and i==len(seq)-1):
+ out.append(u';')
+ out.append(separator)
elif isinstance(val, cssutils.css.CSSUnknownRule):
# @rule
out.append(val.cssText)
diff --git a/src/cssutils/stylesheets/mediaquery.py b/src/cssutils/stylesheets/mediaquery.py
index b75ec285cf..347ae8b0b0 100644
--- a/src/cssutils/stylesheets/mediaquery.py
+++ b/src/cssutils/stylesheets/mediaquery.py
@@ -5,7 +5,7 @@ A cssutils implementation, not defined in official DOM.
"""
__all__ = ['MediaQuery']
__docformat__ = 'restructuredtext'
-__version__ = '$Id: mediaquery.py 1638 2009-01-13 20:39:33Z cthedot $'
+__version__ = '$Id: mediaquery.py 1738 2009-05-02 13:03:28Z cthedot $'
import cssutils
import re
@@ -21,8 +21,8 @@ class MediaQuery(cssutils.util.Base):
media_query: [[only | not]? [ and ]*]
| [ and ]*
expression: ( [: ]? )
- media_type: all | aural | braille | handheld | print |
- projection | screen | tty | tv | embossed
+ media_type: all | braille | handheld | print |
+ projection | speech | screen | tty | tv | embossed
media_feature: width | min-width | max-width
| height | min-height | max-height
| device-width | min-device-width | max-device-width
@@ -35,8 +35,8 @@ class MediaQuery(cssutils.util.Base):
| scan | grid
"""
- MEDIA_TYPES = [u'all', u'aural', u'braille', u'embossed', u'handheld',
- u'print', u'projection', u'screen', u'tty', u'tv']
+ MEDIA_TYPES = [u'all', u'braille', u'embossed', u'handheld',
+ u'print', u'projection', u'screen', u'speech', u'tty', u'tv']
# From the HTML spec (see MediaQuery):
# "[...] character that isn't a US ASCII letter [a-zA-Z] (Unicode
diff --git a/src/cssutils/util.py b/src/cssutils/util.py
index 81335910ac..6f8ba3bc5e 100644
--- a/src/cssutils/util.py
+++ b/src/cssutils/util.py
@@ -2,7 +2,7 @@
"""
__all__ = []
__docformat__ = 'restructuredtext'
-__version__ = '$Id: util.py 1654 2009-02-03 20:16:20Z cthedot $'
+__version__ = '$Id: util.py 1743 2009-05-09 20:33:15Z cthedot $'
from helper import normalize
from itertools import ifilter
@@ -307,7 +307,6 @@ class Base(_BaseClass):
bracket == parant == 0) and typ in endtypes:
# mediaqueryendonly with STRING
break
-
if separateEnd:
# TODO: use this method as generator, then this makes sense
if resulttokens: