From 04d8e251c5bfb9763f75e529a62e75f18551712f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 11 May 2009 07:25:58 -0700 Subject: [PATCH 1/6] Fix for bug in windows detection of BeBook --- src/calibre/devices/scanner.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) 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: From 5ff51fc3f9c97ce1403d4cf51cebd308e81dc5b6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 12 May 2009 12:16:14 -0700 Subject: [PATCH 2/6] New recipes for index.hu and pcworld.hu by Ezmegaz --- src/calibre/trac/plugins/download.py | 2 +- src/calibre/trac/plugins/templates/linux.html | 2 +- src/calibre/web/feeds/recipes/__init__.py | 2 +- src/calibre/web/feeds/recipes/recipe_blic.py | 28 ++---- .../web/feeds/recipes/recipe_index_hu.py | 20 +++++ src/calibre/web/feeds/recipes/recipe_nin.py | 14 +-- .../web/feeds/recipes/recipe_pcworld_hu.py | 22 +++++ .../web/feeds/recipes/recipe_pobjeda.py | 18 ++-- .../recipes/recipe_st_petersburg_times.py | 87 ++++++++++--------- .../web/feeds/recipes/recipe_vijesti.py | 29 ++++--- src/calibre/web/feeds/recipes/recipe_vreme.py | 10 ++- 11 files changed, 144 insertions(+), 90 deletions(-) create mode 100644 src/calibre/web/feeds/recipes/recipe_index_hu.py create mode 100644 src/calibre/web/feeds/recipes/recipe_pcworld_hu.py 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 48e5d9e720..4d2adfb1c0 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', )] 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_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_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): From 1a1cf7f1b98d3fb8a11725ff23b11d7499681294 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 12 May 2009 22:13:29 -0700 Subject: [PATCH 3/6] Update cssutils to latest --- src/cssutils/__init__.py | 9 +- src/cssutils/css/cssmediarule.py | 13 +- src/cssutils/css/cssstyledeclaration.py | 4 +- src/cssutils/css/cssvalue.py | 9 +- src/cssutils/css/property.py | 122 +++-- src/cssutils/css/selector.py | 13 +- src/cssutils/cssproductions.py | 6 +- src/cssutils/errorhandler.py | 8 +- src/cssutils/helper.py | 5 +- src/cssutils/profiles.py | 636 +++++++++++++----------- src/cssutils/serialize.py | 24 +- src/cssutils/stylesheets/mediaquery.py | 10 +- src/cssutils/util.py | 3 +- 13 files changed, 503 insertions(+), 359 deletions(-) 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..aecf3e5fb1 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 @@ -89,9 +89,9 @@ class _ErrorHandler(object): 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: From 1c56b03f95505f13da3612634271d0c729cadc95 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 May 2009 09:42:48 -0700 Subject: [PATCH 4/6] New recipes for HRT and RTS by Darko Miletic --- src/calibre/gui2/images/news/hrt.png | Bin 0 -> 606 bytes src/calibre/gui2/images/news/rts.png | Bin 0 -> 458 bytes src/calibre/web/feeds/recipes/__init__.py | 2 +- src/calibre/web/feeds/recipes/recipe_hrt.py | 66 ++++++++++++++++++++ src/calibre/web/feeds/recipes/recipe_rts.py | 60 ++++++++++++++++++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/calibre/gui2/images/news/hrt.png create mode 100644 src/calibre/gui2/images/news/rts.png create mode 100644 src/calibre/web/feeds/recipes/recipe_hrt.py create mode 100644 src/calibre/web/feeds/recipes/recipe_rts.py diff --git a/src/calibre/gui2/images/news/hrt.png b/src/calibre/gui2/images/news/hrt.png new file mode 100644 index 0000000000000000000000000000000000000000..828819e226825ea2bbdf022b0104aa8f19131d1f GIT binary patch literal 606 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#Q0mB1L(l)>N-JP5IrO9_# zR5obbi2T96Cg`_*kXoqM>@Ar&$*FUdHgbBt=I4mG5z(<>Q%Bp828l_3cYHs$amF1} zfwJ`v8o3YOnc4n%UiyPZRffNJ%dPL*)zs*IwOYi)5hk+3{q5?bXU~dj>&Ms5Vk=>&W=wZ1_8BJOo5%VVN*e<*I*K#v6d&0`ehpM+sb8Bc^scuR{moDkW@%QnNK$6-z*Z`*<(-V4|6)O=dfY$k0ul_}EiQe@W6j5(oMGV{O9 zd;VNpTE)}w=fg>dJ{`LIroPVXN7apMtv?M9KH^oLcqO)c|DOA040X9*87vr1RQ*U5dG+Mjn0LK>{@b?fO$hXV;(a!r`SyQHukhUqW9sWan{|9|TBUHprBiYR&wCx8 z)6at(RrofUEOKCQ$@#>$Y^vnPpp~0!{`Swfd0yxA5w~RC{r??a{S#_T@ZeY{^kCY2 zA-BU(waKz`vyb|*a4@d?$@W)SE@t}zrJ5}(&E`e`<43i`HKHWBC^fMpRW}7lFc=va zSn3*>>KYn{7+6{vTUdd(=2iv<`5bHNVH$GtQ!>*kF*Fz&SeY7InHoVf6ikfz57fZm M>FVdQ&MBb@02*iXy8r+H literal 0 HcmV?d00001 diff --git a/src/calibre/gui2/images/news/rts.png b/src/calibre/gui2/images/news/rts.png new file mode 100644 index 0000000000000000000000000000000000000000..278f45edd7af222703cb4d11cc6711967a97cc13 GIT binary patch literal 458 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*VRoX?&vjv*GOk51m0d)Pq0Ri1h4_9kx8FAQ0C z)vYD+F1HrQS2T;Td2e?!<6t_bEfM7~cW)Eh0&eFck&9Hye_p*i@q4BGch3L3i-q6U zzKJh5H*-11uC(l2tDISB(_Xl$hlVODX>Px!cIZwCbCaRZM3J3s-?zUPkw5fH@%z2w z`sMTHKM=Uq9C3Y}%-N}nJz36K2462N>zXqskW;p^E|-g6=*udXmTkF$6Sqjp>fboR zIy-~mQJwtkw`PKWE6?ZucKmT?X%xrmgFjLuXE8aL$1{BY%(Z;(t}4c`NKV!Ro6c?TrIoRT6^LtYWnhrcv8EoTAvZrIGp!OsgP{RXwUvPdM8mHV^EjXe22WQ% Jmvv4FO#mNWrN#gN literal 0 HcmV?d00001 diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index 4d2adfb1c0..ae1ae24131 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', 'index_hu', 'pcworld_hu', + 'straitstimes', 'index_hu', 'pcworld_hu', 'hrt', 'rts', )] import re, imp, inspect, time, os 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_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) + From 580497d0ee0773e7e7a135fadae76b3649bc38f6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 May 2009 10:08:17 -0700 Subject: [PATCH 5/6] IGN:Change in cssutils profiles API --- src/calibre/ebooks/oeb/stylizer.py | 3 ++- src/cssutils/errorhandler.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index a6803854e8..c4301322e8 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -17,7 +17,8 @@ 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.profiles import Profiles +cssprofiles = Profiles() 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/cssutils/errorhandler.py b/src/cssutils/errorhandler.py index aecf3e5fb1..2d1814b6c8 100644 --- a/src/cssutils/errorhandler.py +++ b/src/cssutils/errorhandler.py @@ -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,7 +86,7 @@ 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, line, col) From d03b6493455224ff15e6947a3bd0f01bb2849e51 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 May 2009 14:04:58 -0700 Subject: [PATCH 6/6] Add a copyright file to comply with the FSF guidelines --- copyright | 355 +++++++++++++++++++++++++++++ src/calibre/ebooks/oeb/stylizer.py | 3 +- 2 files changed, 356 insertions(+), 2 deletions(-) create mode 100644 copyright 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/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index c4301322e8..6eca033bb1 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -17,8 +17,7 @@ from xml.dom import SyntaxErr as CSSSyntaxError import cssutils from cssutils.css import CSSStyleRule, CSSPageRule, CSSStyleDeclaration, \ CSSValueList, cssproperties -from cssutils.profiles import Profiles -cssprofiles = Profiles() +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