mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Pull from trunk
This commit is contained in:
commit
0be9db0630
355
copyright
Normal file
355
copyright
Normal file
@ -0,0 +1,355 @@
|
||||
Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=196
|
||||
Upstream-Name: calibre
|
||||
Upstream-Maintainer: Kovid Goyal <kovid@kovidgoyal.net>
|
||||
Upstream-Source: http://calibre.kovidgoyal.net/downloads
|
||||
|
||||
Files: *
|
||||
Copyright: Copyright (C) 2008 Kovid Goyal <kovid@kovidgoyal.net>
|
||||
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 <cogumbreiro@users.sf.net>
|
||||
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 <kulkarni.ashish@gmail.com>
|
||||
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 <llasram@gmail.com>
|
||||
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 <llasram@gmail.com>
|
||||
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 <llasram@gmail.com>
|
||||
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 <norguhtar at gmail.com>
|
||||
Copyright: copyright 2002 Paul Henry Tremblay
|
||||
Copyright: Copyright (C) 2008 B.Scott Wxby [bswxby]
|
||||
Copyright: Copyright (C) 2007 David Chen SonyReader<at>DaveChen<dot>org
|
||||
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 <darko.miletic at gmail.com>
|
||||
Copyright: 2008, Mathieu Godlewski <mathieu at godlewski.fr>
|
||||
Copyright: Copyright (C) 2008 B.Scott Wxby [bswxby]
|
||||
Copyright: Copyright (C) 2007 David Chen SonyReader<at>DaveChen<dot>org
|
||||
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 <kulkarni.ashish@gmail.com>
|
||||
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.
|
||||
|
@ -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:
|
||||
|
@ -18,7 +18,7 @@ from xml.dom import SyntaxErr as CSSSyntaxError
|
||||
import cssutils
|
||||
from cssutils.css import CSSStyleRule, CSSPageRule, CSSStyleDeclaration, \
|
||||
CSSValueList, cssproperties
|
||||
from cssutils.profiles import profiles as cssprofiles
|
||||
from cssutils import profile as cssprofiles
|
||||
from lxml import etree
|
||||
from lxml.cssselect import css_to_xpath, ExpressionError, SelectorSyntaxError
|
||||
from calibre.ebooks.oeb.base import XHTML, XHTML_NS, CSS_MIME, OEB_STYLES
|
||||
|
BIN
src/calibre/gui2/images/news/hrt.png
Normal file
BIN
src/calibre/gui2/images/news/hrt.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 606 B |
BIN
src/calibre/gui2/images/news/rts.png
Normal file
BIN
src/calibre/gui2/images/news/rts.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 458 B |
@ -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'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
||||
</p>
|
||||
<p>
|
||||
${app} is available in the software repositories of the following
|
||||
linux distributions:
|
||||
supported linux distributions:
|
||||
<table id="install_info">
|
||||
<col width="150" /><col width="*" />
|
||||
<tr>
|
||||
|
@ -42,7 +42,7 @@ recipe_modules = ['recipe_' + r for r in (
|
||||
'moneynews', 'der_standard', 'diepresse', 'nzz_ger', 'hna',
|
||||
'seattle_times', 'scott_hanselman', 'coding_horror', 'twitchfilms',
|
||||
'stackoverflow', 'telepolis_artikel', 'zaobao', 'usnews',
|
||||
'straitstimes',
|
||||
'straitstimes', 'index_hu', 'pcworld_hu', 'hrt', 'rts',
|
||||
)]
|
||||
|
||||
import re, imp, inspect, time, os
|
||||
|
@ -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 = '<meta http-equiv="Content-Language" content="sr-Latn-RS"/>'
|
||||
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')
|
||||
|
66
src/calibre/web/feeds/recipes/recipe_hrt.py
Normal file
66
src/calibre/web/feeds/recipes/recipe_hrt.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
|
||||
'''
|
||||
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)
|
20
src/calibre/web/feeds/recipes/recipe_index_hu.py
Normal file
20
src/calibre/web/feeds/recipes/recipe_index_hu.py
Normal file
@ -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/')]
|
||||
|
@ -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 = '<meta http-equiv="Content-Language" content="' + self.lang + '"/>'
|
||||
mtag += '\n<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '"/>'
|
||||
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
|
||||
|
22
src/calibre/web/feeds/recipes/recipe_pcworld_hu.py
Normal file
22
src/calibre/web/feeds/recipes/recipe_pcworld_hu.py
Normal file
@ -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 <kovid@kovidgoyal.net>'
|
||||
__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')]
|
||||
|
@ -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 = '<meta http-equiv="Content-Language" content="sr-Latn-ME"/>'
|
||||
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
|
||||
|
60
src/calibre/web/feeds/recipes/recipe_rts.py
Normal file
60
src/calibre/web/feeds/recipes/recipe_rts.py
Normal file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
|
||||
'''
|
||||
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)
|
||||
|
@ -1,39 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
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 <darko.miletic at gmail.com>'
|
||||
|
||||
'''
|
||||
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
|
||||
|
@ -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 = '<meta http-equiv="Content-Language" content="sr-Latn-ME"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll('img'):
|
||||
if item.has_key('align'):
|
||||
del item['align']
|
||||
item.insert(0,'<br /><br />')
|
||||
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')
|
||||
|
@ -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 = '<meta http-equiv="Content-Language" content="' + self.lang + '"/>'
|
||||
mtag += '\n<meta http-equiv="Content-Type" content="text/html; charset=' + self.encoding + '"/>'
|
||||
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):
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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`` "
|
||||
|
@ -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):
|
||||
|
@ -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'):
|
||||
|
@ -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
|
||||
|
@ -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})*"',
|
||||
|
@ -16,7 +16,7 @@ log
|
||||
"""
|
||||
__all__ = ['ErrorHandler']
|
||||
__docformat__ = 'restructuredtext'
|
||||
__version__ = '$Id: errorhandler.py 1560 2008-12-14 16:13:16Z cthedot $'
|
||||
__version__ = '$Id: errorhandler.py 1728 2009-05-01 20:35:25Z cthedot $'
|
||||
|
||||
from helper import Deprecated
|
||||
import logging
|
||||
@ -27,7 +27,7 @@ class _ErrorHandler(object):
|
||||
"""
|
||||
handles all errors and log messages
|
||||
"""
|
||||
def __init__(self, log, defaultloglevel=logging.INFO,
|
||||
def __init__(self, log, defaultloglevel=logging.INFO,
|
||||
raiseExceptions=True):
|
||||
"""
|
||||
inits log if none given
|
||||
@ -51,7 +51,7 @@ class _ErrorHandler(object):
|
||||
hdlr.setFormatter(formatter)
|
||||
self._log.addHandler(hdlr)
|
||||
self._log.setLevel(defaultloglevel)
|
||||
|
||||
|
||||
self.raiseExceptions = raiseExceptions
|
||||
|
||||
def __getattr__(self, name):
|
||||
@ -86,12 +86,12 @@ class _ErrorHandler(object):
|
||||
if error and self.raiseExceptions and not neverraise:
|
||||
if isinstance(error, urllib2.HTTPError) or isinstance(error, urllib2.URLError):
|
||||
raise
|
||||
elif issubclass(error, xml.dom.DOMException):
|
||||
elif issubclass(error, xml.dom.DOMException):
|
||||
error.line = line
|
||||
error.col = col
|
||||
raise error(msg)
|
||||
else:
|
||||
raise error(msg)
|
||||
# raise error(msg, line, col)
|
||||
# else:
|
||||
raise error(msg)
|
||||
else:
|
||||
self._logcall(msg)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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<macro>[a-z][a-z0-9-]*)}',
|
||||
macro_value, value)
|
||||
dictionary[key] = value
|
||||
return dictionary
|
||||
|
||||
def _compile_regexes(self, dictionary):
|
||||
"""Compile all regular expressions into callable objects"""
|
||||
for key, value in dictionary.items():
|
||||
if not 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<macro>[a-z][a-z0-9-]*)}',
|
||||
macro_value, value)
|
||||
dictionary[key] = value
|
||||
return dictionary
|
||||
|
||||
def _compile_regexes(self, dictionary):
|
||||
"""Compile all regular expressions into callable objects"""
|
||||
for key, value in dictionary.items():
|
||||
if not 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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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]? <media_type> [ and <expression> ]*]
|
||||
| <expression> [ and <expression> ]*
|
||||
expression: ( <media_feature> [: <value>]? )
|
||||
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
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user