From 6175dca68605a152a413e1c72c22e057fba14249 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 09:41:41 -0800 Subject: [PATCH 01/21] IGN:... --- src/calibre/devices/cybookg3/t2b.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/cybookg3/t2b.py b/src/calibre/devices/cybookg3/t2b.py index 0baa7a1060..5bf512f22d 100644 --- a/src/calibre/devices/cybookg3/t2b.py +++ b/src/calibre/devices/cybookg3/t2b.py @@ -4,7 +4,8 @@ __copyright__ = '2009, John Schember ' Write a t2b file to disk. ''' -import Image, StringIO +import StringIO +from PIL import Image DEFAULT_T2B_DATA = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x0f\xff\xff\xff\xf0\xff\x0f\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\xff\xff\xff\xf0\xff\x0f\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\xff\xf0\xff\xff\xff\xf0\xff\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc3\xff\xff\xff\xff\xff\xf0\xff\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x07\xff\xff\xfc\x00?\xf0\xff\x0f\xc3\x00?\xf0\xc0\xfe\x00?\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xf0<\x0f\xf0\xff\x0f\xc0,\x0f\xf0\x0e\xf0,\x0f\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xc3\xf0\xff\x0f\xc0\xff\x0f\xf0\xff\xf0\xff\xc7\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xff\xff\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xc3\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xff\x00\x03\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xc3\xff\xff\xff\xff\xff\xff\xff\x0f\xff\xff\xf0\x1f\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc0\x00\x03\xff\xff\xff\xff\xff\xff\xff\x0b\xff\xff\xf0\xff\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc3\xff\xff\xf3\xff\xc3\xf0\xff\x0f\xc3\xff\xc3\xf0\xff\xc3\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\xff\xfc\xf0\xff\x03\xf0\xff\x0f\xc0\xff\x0f\xf0\xff\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x0f\x00\xf08\x03\xf0\xff\x0f\xc0,\x0f\xf0\xff\xf0\x1f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x00\x0f\xfc\x00\xc3\xf0\xff\x0f\xc3\x00?\xf0\xff\xff\x00\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x03\xfe\x94\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x0f\xff\xff\xff\xff\xff\xff\xfc\x7f\xfe\x94\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x0f\xff\xfe\xa9@\xff\xff\xff\xff\xff\xff\xfc?\xfe\xa4\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xe9P\xff\xff\xff\xff\xff\xff\xfe/\xfe\xa8\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xf9T\xff\xff\xff\xff\xf0@\x00+\xfa\xa8?\xff\xff\xff\xff\xff\xff\xff\xfc\xbf\xff\xff\xf9T\xff\xff\xff\xff\xcb\xe4}*\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfc\xbf\xff\xff\xe9T\xff\xff\xff\xff\xc7\xe4\xfd\x1a\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfc\xaf\xea\xaa\xa6\xa4\xff@\x00\x0f\xc3\xe8\xfe\x1a\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4\x00\x7f\xfe\x90\x03\xe8\xfe\n\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4?\xff\xff\xa5C\xe8\xfe\x06\xaa\xaa?\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4?\xff\xff\xeaC\xe8\xbe\x06\xaa\xaa\x0f\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4/\xff\xff\xea\x82\xe8j\x06\xaa\xaa\x0f\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4/\xff\xff\xaa\x82\xe8*F\xaa\xaa\x8f\xff\xff\xff\xff\xff\xff\xff\xfcj\x95UZ\xa4+\xff\xfe\xaa\x82\xe8*\x86\xaa\xaa\x8f\xff\xff\x80\xff\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x86\xaa\xaa\x8f\xf0\x00T?\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x81\xaa\xaa\x8c\x03\xff\x95?\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x81\xaa\xaa\x80\xbf\xff\x95?\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8*\x81\xaa\xaa\x9b\xff\xff\x95\x0f\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8\x1a\x81\xaa\xaa\x9a\xff\xfe\x95\x0f\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xe8\n\x81\xaa\xaa\xa6\xbf\xfeUO\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xa8\n\x91j\xaa\xa5\xaa\xa9ZO\xff\xff\xff\xfcj\x95UV\xa4\x1a\xfa\xaa\xaa\x82\xa8\n\xa0j\xaa\xa5Z\x95ZO\xff\xff\xff\xfcj\x95UV\xa4*\xfa\xaa\xaa\x82\xa9\n\xa0j\xaa\xa5UUZC\xff\xff\xff\xfcj\x95UV\xa4*\xfa\xaa\xaa\x82\xaa\n\xa0j\xaa\xa4UUZS\xff\xff\xff\xfcZ\x95UV\xa4*\xfa\xaa\xaa\x82\xaa\n\xa0j\xaa\xa4UUZS\xff\xff\xff\xfcZ\x95UU\xa4*\xfa\xaa\xaa\x82\xaa\n\xa0j\xaa\xa8UUVS\xff\xff\xff\xfcZ\x95UU\xa4*\xea\xaa\xaa\x82\xaa\x06\xa0Z\xaa\xa8UUV\x93\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x81\xaa\x02\xa0\x1a\xaa\xa8UUV\x90\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa\x02\xa0\x1a\xaa\xa8\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa"\xa0\x1a\xaa\xa8\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa2\xa4\x16\xaa\xa8\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa2\xa8\x16\xa6\xa9\x15UU\x94\xff\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x80\xaa2\xa8\x16\xa6\xa9\x05UUT?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x84\xaa2\xa8\x16\xaa\xaa\x05UUU?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x88\xaa2\xa8\x06\xaa\xaa\x05UUU?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa1\xa8\xc5\xaa\xaa\x05UUU?\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa0\xa8E\xa9\xaa\x05UUU/\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa<\xa8\x05\xa9\xaaAUUU\x0f\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa<\xa8\x05\xa9\xaaAUUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa<\xa9\x05\xaa\xaaAUUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x1c\xaa\x01\xaa\xaa\x81UUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0c\xaa\x01\xaa\xaa\x81UUUO\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0c\xaa1j\xaa\x80UUUC\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0cj1jj\x90UUUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x0c*1jj\x90UUUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaaL*1jj\xa0UUUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x8f* j\xaa\xa0\x15UUS\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x8f*@j\xaa\xa0\x15UUP\xff\xff\xfcZ\x95UU\xa4*\xaa\xaa\xaa\x8c\xaa\x8f*\x8cZ\xaa\xa1\x15UUT\xff\xff\xfcZ\x95UU\xa4j\xaa\xaa\xaa\x8c\xaa\x8f*\x8cZ\x9a\xa0\x15UUT\xff\xff\xfcZ\x95UU\xa4j\xaa\xaa\xaa\x8c\xaa\x8f*\x8cZ\x9a\xa0\x15UUT\xff\xff\xfcZ\x95UU\xa4j\xaa\xaa\xaa\x8c\xaa\x8f\x1a\x8cZ\x9a\xa4\x15UUT?\xff\xfcZ\x95UU\x94j\xaa\xaa\xaa\x8cj\x8f\n\x8cVj\xa4\x05UU\xa4?\xff\xfcVUUU\xa4j\xaa\xaa\xaa\x8cj\x8fJ\x8c\x16\xaa\xa8\xc5UZ\xa5?\xff\xfcUUUV\xa4j\xaa\xaa\xaa\x8cj\x8f\xca\x8f\x16\xaa\xa8\xc5V\xaa\xa5?\xff\xfcUj\xaa\xaa\xa4j\xaa\xaa\xaa\x8cj\x8f\xca\x8f\x1a\xaa\xa8\x05Z\xaaU?\xff\xfcV\xaa\xaa\xaa\xa5j\xaa\xaa\xaa\x8e*\x8f\xca\x83\x1a\xaa\xa4\x01eUU?\xff\xfcZ\xaa\xaa\xaa\xa5j\xaa\xaa\xaa\x8f*\x8f\xca\x83\x1a\xa5U\x01U\x00\x00\x0f\xff\xfcUUUUUZ\xaa\xaa\xaaO%\x8f\xc6\x93\x15\x00\x001@\x0f\xff\xff\xff\xfcP\x00\x00\x00\x15\x00\x00\x00\x00\x0f\x00\x07\xc0\x03\x00\xff\xff0\x1f\xff\xff\xff\xff\xfc\x00\xff\xff\xf8\x00?\xff\xff\xff\x0f?\xc7\xc3\xf7\x0f\xff\xff\xf1\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xf4\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff' From 1a98b0c266827d6f7b584521cd5494c418b1ca9a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 09:58:44 -0800 Subject: [PATCH 02/21] EPUB viewer:Support quotes in the path to embedded font files --- src/calibre/ebooks/epub/iterator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/epub/iterator.py b/src/calibre/ebooks/epub/iterator.py index 08c275e81f..5601e9b8de 100644 --- a/src/calibre/ebooks/epub/iterator.py +++ b/src/calibre/ebooks/epub/iterator.py @@ -95,7 +95,7 @@ class EbookIterator(object): for match in re.compile(r'@font-face\s*{([^}]+)}').finditer(css): block = match.group(1) family = re.compile(r'font-family\s*:\s*([^;]+)').search(block) - url = re.compile(r'url\s*\((.+?)\)', re.DOTALL).search(block) + url = re.compile(r'url\s*\([\'"]*(.+?)[\'"]*\)', re.DOTALL).search(block) if url: path = url.group(1).split('/') path = os.path.join(os.path.dirname(item.path), *path) From 383d33e6aa5b727724171161b54d202cf06751d3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 10:43:34 -0800 Subject: [PATCH 03/21] Fix #1645 (Picks up thumbnail not cover from some Mobipocket files) --- src/calibre/ebooks/mobi/reader.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 2ed41ac898..5d2edd3fe0 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -124,6 +124,7 @@ class BookHeader(object): sublangid = (langcode >> 10) & 0xFF self.language = main_language.get(langid, 'ENGLISH') self.sublanguage = sub_language.get(sublangid, 'NEUTRAL') + self.first_image_index = struct.unpack('>L', raw[0x6c:0x6c+4])[0] self.exth_flag, = struct.unpack('>L', raw[0x80:0x84]) self.exth = None @@ -441,17 +442,18 @@ class MobiReader(object): os.makedirs(output_dir) image_index = 0 self.image_names = [] - for i in range(self.num_sections): + for i in range(self.book_header.first_image_index, self.num_sections): if i in processed_records: continue processed_records.append(i) data = self.sections[i][0] buf = cStringIO.StringIO(data) + image_index += 1 try: im = PILImage.open(buf) - except IOError: + except IOError, e: continue - image_index += 1 + path = os.path.join(output_dir, '%05d.jpg'%image_index) self.image_names.append(os.path.basename(path)) im.convert('RGB').save(open(path, 'wb'), format='JPEG') @@ -476,6 +478,7 @@ def get_metadata(stream): else: tdir = tempfile.mkdtemp('_mobi_meta', __appname__+'_') atexit.register(shutil.rmtree, tdir) + #print tdir mr.extract_images([], tdir) mi = mr.create_opf('dummy.html') if mi.cover: @@ -491,7 +494,6 @@ def get_metadata(stream): if os.access(candidate, os.R_OK): cover = candidate break - if os.access(cover, os.R_OK): mi.cover_data = ('JPEG', open(os.path.join(tdir, cover), 'rb').read()) else: From e3115f03cffc165ac516f4c4d93055cfbe5a736a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 11:30:47 -0800 Subject: [PATCH 04/21] EPUB output:Set --remove-paragraph-spacing to off by default --- src/calibre/ebooks/epub/__init__.py | 2 +- src/calibre/ebooks/epub/from_feeds.py | 1 + src/calibre/ebooks/html.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/epub/__init__.py b/src/calibre/ebooks/epub/__init__.py index d8d4c9a758..442f5260b1 100644 --- a/src/calibre/ebooks/epub/__init__.py +++ b/src/calibre/ebooks/epub/__init__.py @@ -156,7 +156,7 @@ to auto-generate a Table of Contents. help=_('Set the right margin in pts. Default is %default')) layout('base_font_size2', ['--base-font-size'], default=12.0, help=_('The base font size in pts. Default is %defaultpt. Set to 0 to disable rescaling of fonts.')) - layout('remove_paragraph_spacing', ['--remove-paragraph-spacing'], default=True, + layout('remove_paragraph_spacing', ['--remove-paragraph-spacing'], default=False, help=_('Remove spacing between paragraphs. Will not work if the source file forces inter-paragraph spacing.')) layout('preserve_tag_structure', ['--preserve-tag-structure'], default=False, help=_('Preserve the HTML tag structure while splitting large HTML files. This is only neccessary if the HTML files contain CSS that uses sibling selectors. Enabling this greatly slows down processing of large HTML files.')) diff --git a/src/calibre/ebooks/epub/from_feeds.py b/src/calibre/ebooks/epub/from_feeds.py index fd1759712d..6a12353f50 100644 --- a/src/calibre/ebooks/epub/from_feeds.py +++ b/src/calibre/ebooks/epub/from_feeds.py @@ -52,6 +52,7 @@ def convert(opts, recipe_arg, notification=None): print 'Generating epub...' opts.encoding = 'utf-8' + opts.remove_paragraph_spacing = True html2epub(opf, opts, notification=notification) diff --git a/src/calibre/ebooks/html.py b/src/calibre/ebooks/html.py index 634963e775..c853f62171 100644 --- a/src/calibre/ebooks/html.py +++ b/src/calibre/ebooks/html.py @@ -848,7 +848,7 @@ class Processor(Parser): # Workaround for anchor rendering bug in ADE css += '\n\na { color: inherit; text-decoration: inherit; cursor: default; }\na[href] { color: blue; text-decoration: underline; cursor:pointer; }' if self.opts.remove_paragraph_spacing: - css += '\n\np {text-indent: 2em; margin-top:0pt; margin-bottom:0pt; padding:0pt; border:0pt;}' + css += '\n\np {text-indent: 1.5em; margin-top:0pt; margin-bottom:0pt; padding:0pt; border:0pt;}' if self.opts.override_css: css += '\n\n' + self.opts.override_css self.override_css = self.css_parser.parseString(self.preprocess_css(css)) From 7e63bc95e2f277a31be70ff5356e44ae85e1575f Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 15:03:50 -0500 Subject: [PATCH 05/21] Fix #1649: - Handle empty XML files while manifest trimming - Ignore s with non OPS raster image @src --- src/calibre/ebooks/mobi/writer.py | 3 +-- src/calibre/ebooks/oeb/transforms/trimmanifest.py | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 62d444ee95..d9f3a841ab 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -180,7 +180,6 @@ class Serializer(object): if not isinstance(elem.tag, basestring) \ or namespace(elem.tag) not in nsrmap: return - hrefs = self.oeb.manifest.hrefs tag = prefixname(elem.tag, nsrmap) for attr in ('name', 'id'): if attr in elem.attrib: @@ -203,7 +202,7 @@ class Serializer(object): continue elif attr == 'src': href = item.abshref(val) - if href in hrefs: + if href in self.images: index = self.images[href] buffer.write('recindex="%05d"' % index) continue diff --git a/src/calibre/ebooks/oeb/transforms/trimmanifest.py b/src/calibre/ebooks/oeb/transforms/trimmanifest.py index bd2c388245..b150a12831 100644 --- a/src/calibre/ebooks/oeb/transforms/trimmanifest.py +++ b/src/calibre/ebooks/oeb/transforms/trimmanifest.py @@ -41,8 +41,9 @@ class ManifestTrimmer(object): while unchecked: new = set() for item in unchecked: - if item.media_type in OEB_DOCS or \ - item.media_type[-4:] in ('/xml', '+xml'): + if (item.media_type in OEB_DOCS or + item.media_type[-4:] in ('/xml', '+xml')) and \ + item.data is not None: hrefs = [sel(item.data) for sel in LINK_SELECTORS] for href in chain(*hrefs): href = item.abshref(href) From 50d041712986148de67d4101caa4392dfecdacf8 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 15:13:31 -0500 Subject: [PATCH 06/21] Fix #1650: - Handle a //guide/reference[@cover] which specifies the raster image cover directly --- src/calibre/ebooks/mobi/mobiml.py | 9 ++++++--- src/calibre/ebooks/oeb/base.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py index 7a74bd9401..720bb3df8f 100644 --- a/src/calibre/ebooks/mobi/mobiml.py +++ b/src/calibre/ebooks/mobi/mobiml.py @@ -12,7 +12,7 @@ import copy import re from lxml import etree from calibre.ebooks.oeb.base import namespace, barename -from calibre.ebooks.oeb.base import XHTML, XHTML_NS +from calibre.ebooks.oeb.base import XHTML, XHTML_NS, OEB_DOCS from calibre.ebooks.oeb.stylizer import Stylizer from calibre.ebooks.oeb.transforms.flatcss import KeyMapper @@ -96,8 +96,11 @@ class MobiMLizer(object): href = oeb.guide['cover'].href del oeb.guide['cover'] item = oeb.manifest.hrefs[href] - oeb.manifest.remove(item) - + if item.spine_position is not None: + oeb.spine.remove(item) + if item.media_type in OEB_DOCS: + self.oeb.manifest.remove(item) + def mobimlize_spine(self): for item in self.oeb.spine: stylizer = Stylizer(item.data, item.href, self.oeb, self.profile) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 4248657e23..892d7c507e 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -506,6 +506,7 @@ class Spine(object): self.items.pop(index) for i in xrange(index, len(self.items)): self.items[i].spine_position = i + item.spine_position = None def __iter__(self): for item in self.items: From 2ab9c0229b1c333f6823102faeca28f9da375931 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 15:51:46 -0800 Subject: [PATCH 07/21] Recipe for The Age by Matthew Briggs --- src/calibre/web/feeds/recipes/__init__.py | 2 +- .../web/feeds/recipes/recipe_the_age.py | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/calibre/web/feeds/recipes/recipe_the_age.py diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index f0687ece28..96dec2e032 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -22,7 +22,7 @@ recipe_modules = ['recipe_' + r for r in ( 'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik', 'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet', 'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de', - 'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', + 'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', 'the_age', )] import re, imp, inspect, time, os diff --git a/src/calibre/web/feeds/recipes/recipe_the_age.py b/src/calibre/web/feeds/recipes/recipe_the_age.py new file mode 100644 index 0000000000..8bedc50cd5 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_the_age.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2009, Matthew Briggs ' +__docformat__ = 'restructuredtext en' + +''' +theage.com.au +''' +from calibre import strftime +from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup + + +class TheAge(BasicNewsRecipe): + + title = 'The Age' + description = 'Business News, World News and Breaking News in Melbourne, Australia' + __author__ = 'Matthew Briggs' + + def get_browser(self): + br = BasicNewsRecipe.get_browser() + br.set_handle_refresh(False) + return br + + def parse_index(self): + + soup = BeautifulSoup(self.browser.open('http://www.theage.com.au/text/').read()) + + feeds, articles = [], [] + feed = None + + + for tag in soup.findAll(['h3', 'a']): + if tag.name == 'h3': + if articles: + feeds.append((feed, articles)) + articles = [] + feed = self.tag_to_string(tag) + elif feed is not None and tag.has_key('href') and tag['href'].strip(): + url = tag['href'].strip() + if url.startswith('/'): + url = 'http://www.theage.com.au' + url + title = self.tag_to_string(tag) + articles.append({ + 'title': title, + 'url' : url, + 'date' : strftime('%a, %d %b'), + 'description' : '', + 'content' : '', + }) + + return feeds + + + From b56e85f23d5700beeee5e75079f485cad0e976e5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 16:01:09 -0800 Subject: [PATCH 08/21] Fix #1639 (Calibre can not handle properly URL's with non-ascii characters) --- src/calibre/web/fetch/simple.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/web/fetch/simple.py b/src/calibre/web/fetch/simple.py index 43f7aa0626..2e5c8bae70 100644 --- a/src/calibre/web/fetch/simple.py +++ b/src/calibre/web/fetch/simple.py @@ -398,7 +398,7 @@ class RecursiveFetcher(object, LoggingInterface): _fname = basename(iurl) if not isinstance(_fname, unicode): _fname.decode('latin1', 'replace') - _fname.encode('ascii', 'replace').replace('%', '') + _fname.encode('ascii', 'replace').replace('%', '').replace(os.sep, '') res = os.path.join(linkdiskpath, _fname) self.downloaded_paths.append(res) self.filemap[nurl] = res From f688c8c3b3cb8d6dbcc35895ee5a5ae3c70f6a9e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 16:11:12 -0800 Subject: [PATCH 09/21] Recipe for La Prensa by Darko Miletic --- src/calibre/web/feeds/recipes/__init__.py | 3 +- .../web/feeds/recipes/recipe_laprensa.py | 50 +++++++++++++++++++ src/calibre/web/fetch/simple.py | 2 +- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/calibre/web/feeds/recipes/recipe_laprensa.py diff --git a/src/calibre/web/feeds/recipes/__init__.py b/src/calibre/web/feeds/recipes/__init__.py index 96dec2e032..2b5652260d 100644 --- a/src/calibre/web/feeds/recipes/__init__.py +++ b/src/calibre/web/feeds/recipes/__init__.py @@ -22,7 +22,8 @@ recipe_modules = ['recipe_' + r for r in ( 'time_magazine', 'endgadget', 'fudzilla', 'nspm_int', 'nspm', 'pescanik', 'spiegel_int', 'themarketticker', 'tomshardware', 'xkcd', 'ftd', 'zdnet', 'joelonsoftware', 'telepolis', 'common_dreams', 'nin', 'tomshardware_de', - 'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', 'the_age', + 'pagina12', 'infobae', 'ambito', 'elargentino', 'sueddeutsche', 'the_age', + 'laprensa', )] import re, imp, inspect, time, os diff --git a/src/calibre/web/feeds/recipes/recipe_laprensa.py b/src/calibre/web/feeds/recipes/recipe_laprensa.py new file mode 100644 index 0000000000..55bb8778a3 --- /dev/null +++ b/src/calibre/web/feeds/recipes/recipe_laprensa.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = '2008, Darko Miletic ' +''' +laprensa.com.ar +''' +import urllib + +from calibre.web.feeds.news import BasicNewsRecipe + +class LaPrensa(BasicNewsRecipe): + title = 'La Prensa' + __author__ = 'Darko Miletic' + description = 'Informacion Libre las 24 horas' + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'cp1252' + cover_url = 'http://www.laprensa.com.ar/imgs/logo.gif' + + html2lrf_options = [ + '--comment' , description + , '--category' , 'news, Argentina' + , '--publisher' , title + ] + + feeds = [ + (u'Politica' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=4' ) + ,(u'Economia' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=5' ) + ,(u'Opinion' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=6' ) + ,(u'El Mundo' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=7' ) + ,(u'Actualidad' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=8' ) + ,(u'Deportes' , u'http://www.laprensa.com.ar/Rss.aspx?Rss=9' ) + ,(u'Espectaculos', u'http://www.laprensa.com.ar/Rss.aspx?Rss=10') + ] + + def print_version(self, url): + return url.replace('.note.aspx','.NotePrint.note.aspx') + + def get_article_url(self, article): + raw = article.get('link', None).encode('utf8') + final = urllib.quote(raw,':/') + return final + + def preprocess_html(self, soup): + del soup.body['onload'] + return soup + diff --git a/src/calibre/web/fetch/simple.py b/src/calibre/web/fetch/simple.py index 2e5c8bae70..8e84c5033f 100644 --- a/src/calibre/web/fetch/simple.py +++ b/src/calibre/web/fetch/simple.py @@ -398,7 +398,7 @@ class RecursiveFetcher(object, LoggingInterface): _fname = basename(iurl) if not isinstance(_fname, unicode): _fname.decode('latin1', 'replace') - _fname.encode('ascii', 'replace').replace('%', '').replace(os.sep, '') + _fname = _fname.encode('ascii', 'replace').replace('%', '').replace(os.sep, '') res = os.path.join(linkdiskpath, _fname) self.downloaded_paths.append(res) self.filemap[nurl] = res From 0f56822a070007ffdd5abeb140e81fe7759705b1 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 20:54:34 -0500 Subject: [PATCH 10/21] Fix #1652: - Ignore duplicate entries and attempt to continue. --- src/calibre/ebooks/oeb/base.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 892d7c507e..ac6fb1e5dd 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -803,12 +803,20 @@ class OEBBook(object): def _manifest_from_opf(self, opf): self.manifest = manifest = Manifest(self) for elem in xpath(opf, '/o2:package/o2:manifest/o2:item'): + id = elem.get('id') href = elem.get('href') + media_type = elem.get('media-type') + fallback = elem.get('fallback') + if href in manifest.hrefs: + self.logger.warn(u'Duplicate manifest entry for %r.' % href) + continue if not self.container.exists(href): self.logger.warn(u'Manifest item %r not found.' % href) continue - manifest.add(elem.get('id'), href, elem.get('media-type'), - elem.get('fallback')) + if id in manifest.ids: + self.logger.warn(u'Duplicate manifest id %r.' % id) + id, href = manifest.generate(id, href) + manifest.add(id, href, media_type, fallback) def _spine_from_opf(self, opf): self.spine = spine = Spine(self) From d321b77930eaf7f4c69dc1d79f0496ff7bccb32c Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 21:17:27 -0500 Subject: [PATCH 11/21] Fix #1653: - Fail gracefully on input with fragment-level broken links --- src/calibre/ebooks/mobi/writer.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index d9f3a841ab..4fb080801e 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -95,6 +95,7 @@ class Serializer(object): def __init__(self, oeb, images): self.oeb = oeb self.images = images + self.logger = oeb.logger self.id_offsets = {} self.href_offsets = defaultdict(list) self.breaks = [] @@ -144,8 +145,8 @@ class Serializer(object): item = hrefs[path] if path else None if item and item.spine_position is None: return False - id = item.id if item else base.id - href = '#'.join((id, frag)) if frag else id + path = item.href if item else base.href + href = '#'.join((path, frag)) if frag else path buffer.write('filepos=') self.href_offsets[href].append(buffer.tell()) buffer.write('0000000000') @@ -170,7 +171,7 @@ class Serializer(object): buffer = self.buffer if not item.linear: self.breaks.append(buffer.tell() - 1) - self.id_offsets[item.id] = buffer.tell() + self.id_offsets[item.href] = buffer.tell() for elem in item.data.find(XHTML('body')): self.serialize_elem(elem, item) buffer.write('') @@ -183,8 +184,8 @@ class Serializer(object): tag = prefixname(elem.tag, nsrmap) for attr in ('name', 'id'): if attr in elem.attrib: - id = '#'.join((item.id, elem.attrib[attr])) - self.id_offsets[id] = buffer.tell() + href = '#'.join((item.href, elem.attrib[attr])) + self.id_offsets[href] = buffer.tell() del elem.attrib[attr] if tag == 'a' and not elem.attrib \ and not len(elem) and not elem.text: @@ -232,8 +233,12 @@ class Serializer(object): def fixup_links(self): buffer = self.buffer - for id, hoffs in self.href_offsets.items(): - ioff = self.id_offsets[id] + id_offsets = self.id_offsets + for href, hoffs in self.href_offsets.items(): + if href not in id_offsets: + self.logger.warn('Hyperlink target %r not found' % href) + href, _ = urldefrag(href) + ioff = self.id_offsets[href] for hoff in hoffs: buffer.seek(hoff) buffer.write('%010d' % ioff) From 48340fe6eff473093ea88e0a50ea53acc7b92f55 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 21:32:03 -0500 Subject: [PATCH 12/21] Fix #1654: - Convert image format to full-color when forcing large-pixel area lossless image formats to JPEG --- src/calibre/ebooks/mobi/writer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 4fb080801e..306f097c90 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -364,7 +364,11 @@ class MobiWriter(object): if image.format not in ('JPEG', 'GIF'): width, height = image.size area = width * height - format = 'GIF' if area <= 40000 else 'JPEG' + if area <= 40000: + format = 'GIF' + else: + image = image.convert('RGBA') + format = 'JPEG' changed = True if dimen is not None: image.thumbnail(dimen, Image.ANTIALIAS) From c198458f654ab170b126e22d5dff4c7b6cd61aa0 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 22:33:58 -0500 Subject: [PATCH 13/21] Improve Mobipocket conversion of basic tables --- src/calibre/ebooks/mobi/mobiml.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py index 720bb3df8f..afc2fb63ba 100644 --- a/src/calibre/ebooks/mobi/mobiml.py +++ b/src/calibre/ebooks/mobi/mobiml.py @@ -140,7 +140,7 @@ class MobiMLizer(object): para = bstate.para if tag in SPECIAL_TAGS and not text: para = para if para is not None else bstate.body - elif para is None: + elif para is None or tag in ('td', 'th'): body = bstate.body if bstate.pbreak: etree.SubElement(body, MBP('pagebreak')) @@ -160,7 +160,8 @@ class MobiMLizer(object): elif indent != 0 and abs(indent) < self.profile.fbase: indent = (indent / abs(indent)) * self.profile.fbase if tag in NESTABLE_TAGS: - para = wrapper = etree.SubElement(parent, XHTML(tag)) + para = wrapper = etree.SubElement( + parent, XHTML(tag), attrib=istate.attrib) bstate.nested.append(para) if tag == 'li' and len(istates) > 1: istates[-2].list_num += 1 @@ -340,6 +341,10 @@ class MobiMLizer(object): tag = 'tr' elif display == 'table-cell': tag = 'td' + if tag in TABLE_TAGS: + for attr in ('rowspan', 'colspan'): + if attr in elem.attrib: + istate.attrib[attr] = elem.attrib[attr] text = None if elem.text: if istate.preserve: @@ -377,6 +382,6 @@ class MobiMLizer(object): bstate.vpadding += bstate.vmargin bstate.vmargin = 0 bstate.vpadding += vpadding - if tag in NESTABLE_TAGS and bstate.nested: + if bstate.nested and bstate.nested[-1].tag == elem.tag: bstate.nested.pop() istates.pop() From 50ea39227b25ee58bd700b8d1d17e399777a7770 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 22:57:18 -0500 Subject: [PATCH 14/21] Use lxml to handle HTML entities and -specified encodings --- src/calibre/ebooks/oeb/base.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index ac6fb1e5dd..85d87cf425 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -15,10 +15,10 @@ from urlparse import urldefrag, urlparse, urlunparse from urllib import unquote as urlunquote import logging import re -import htmlentitydefs import uuid import copy from lxml import etree +from lxml import html from calibre import LoggingInterface XML_PARSER = etree.XMLParser(recover=True) @@ -67,14 +67,6 @@ OEB_IMAGES = set([GIF_MIME, JPEG_MIME, PNG_MIME, SVG_MIME]) MS_COVER_TYPE = 'other.ms-coverimage-standard' -recode = lambda s: s.decode('iso-8859-1').encode('ascii', 'xmlcharrefreplace') -ENTITYDEFS = dict((k, recode(v)) for k, v in htmlentitydefs.entitydefs.items()) -del ENTITYDEFS['lt'] -del ENTITYDEFS['gt'] -del ENTITYDEFS['quot'] -del ENTITYDEFS['amp'] -del recode - def element(parent, *args, **kwargs): if parent is not None: @@ -298,7 +290,6 @@ class Metadata(object): class Manifest(object): class Item(object): - ENTITY_RE = re.compile(r'&([a-zA-Z_:][a-zA-Z0-9.-_:]+);') NUM_RE = re.compile('^(.*)([0-9][0-9.]*)(?=[.]|$)') def __init__(self, id, href, media_type, @@ -317,9 +308,12 @@ class Manifest(object): % (self.id, self.href, self.media_type) def _force_xhtml(self, data): - repl = lambda m: ENTITYDEFS.get(m.group(1), m.group(0)) - data = self.ENTITY_RE.sub(repl, data) - data = etree.fromstring(data, parser=XML_PARSER) + try: + data = etree.fromstring(data, parser=XML_PARSER) + except etree.XMLSyntaxError: + data = html.fromstring(data, parser=XML_PARSER) + data = etree.tostring(data, encoding=unicode) + data = etree.fromstring(data, parser=XML_PARSER) if namespace(data.tag) != XHTML_NS: data.attrib['xmlns'] = XHTML_NS data = etree.tostring(data) From 2857ff00fb0dd9da2dfdd4eaab72e15320e4b31e Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Mon, 19 Jan 2009 22:58:27 -0500 Subject: [PATCH 15/21] Fix validity bug in generated OEBBook-generated NCX --- src/calibre/ebooks/oeb/base.py | 35 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index 85d87cf425..c42d363291 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -675,22 +675,22 @@ class TOC(object): node.to_opf1(tour) return tour - def to_ncx(self, parent, playorder=None, depth=1): - if not playorder: playorder = [0] + def to_ncx(self, parent, order=None, depth=1): + if not order: order = [0] for node in self.nodes: - playorder[0] += 1 + order[0] += 1 + playOrder = str(order[0]) + id = self.id or 'np' + playOrder point = etree.SubElement(parent, - NCX('navPoint'), attrib={'playOrder': str(playorder[0])}) + NCX('navPoint'), id=id, playOrder=playOrder) if self.klass: point.attrib['class'] = node.klass - if self.id: - point.attrib['id'] = node.id label = etree.SubElement(point, NCX('navLabel')) etree.SubElement(label, NCX('text')).text = node.title href = node.href if depth > 1 else urldefrag(node.href)[0] child = etree.SubElement(point, NCX('content'), attrib={'src': href}) - node.to_ncx(point, playorder, depth+1) + node.to_ncx(point, order, depth+1) return parent @@ -986,22 +986,11 @@ class OEBBook(object): guide = self.guide.to_opf1(package) return {OPF_MIME: ('content.opf', package)} - def _generate_ncx_item(self): - id = 'ncx' - index = 0 - while id in self.manifest: - id = 'ncx' + str(index) - index = index + 1 - href = 'toc' - index = 0 - while (href + '.ncx') in self.manifest.hrefs: - href = 'toc' + str(index) - href += '.ncx' - return (id, href) - def _to_ncx(self): - ncx = etree.Element(NCX('ncx'), attrib={'version': '2005-1'}, - nsmap={None: NCX_NS}) + lang = unicode(self.metadata.language[0]) + ncx = etree.Element(NCX('ncx'), + attrib={'version': '2005-1', XML('lang'): lang}, + nsmap={None: NCX_NS}) head = etree.SubElement(ncx, NCX('head')) etree.SubElement(head, NCX('meta'), attrib={'name': 'dtb:uid', 'content': unicode(self.uid)}) @@ -1024,7 +1013,7 @@ class OEBBook(object): nsmap={None: OPF2_NS}) metadata = self.metadata.to_opf2(package) manifest = self.manifest.to_opf2(package) - id, href = self._generate_ncx_item() + id, href = self.manifest.generate('ncx', 'toc.ncx') etree.SubElement(manifest, OPF('item'), attrib={'id': id, 'href': href, 'media-type': NCX_MIME}) spine = self.spine.to_opf2(package) From f34034884ac442762c481aff1e52c4a0394ab542 Mon Sep 17 00:00:00 2001 From: "Marshall T. Vandegrift" Date: Tue, 20 Jan 2009 00:32:49 -0500 Subject: [PATCH 16/21] Dynamic translations!: - Dynamic access to any of the extant translation sets - Integration into OEBBook to provide book-language translations - Integration with HTML TOC to provide language-appropriate title - oeb2mobi/any2mobi option to provide HTML TOC from the command-line --- src/calibre/ebooks/mobi/writer.py | 5 +++- src/calibre/ebooks/oeb/base.py | 6 +++++ src/calibre/ebooks/oeb/transforms/htmltoc.py | 10 +++++--- src/calibre/translations/dynamic.py | 27 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 src/calibre/translations/dynamic.py diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index 306f097c90..3be283fa0a 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -515,6 +515,9 @@ def add_mobi_options(parser): group.add_option( '-r', '--rescale-images', default=False, action='store_true', help=_('Modify images to meet Palm device size limitations.')) + group.add_option( + '--toc-title', default=None, action='store', + help=_('Title for any generated in-line table of contents.')) parser.add_option_group(group) group = OptionGroup(parser, _('Profiles'), _('Device renderer profiles. ' 'Affects conversion of default font sizes and rasterization ' @@ -558,7 +561,7 @@ def oeb2mobi(opts, inpath): imagemax = PALM_MAX_IMAGE_SIZE if opts.rescale_images else None context = Context(source, dest) oeb = OEBBook(inpath, logger=logger) - tocadder = HTMLTOCAdder() + tocadder = HTMLTOCAdder(title=opts.toc_title) tocadder.transform(oeb, context) mangler = CaseMangler() mangler.transform(oeb, context) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index c42d363291..c167151a5f 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -20,6 +20,7 @@ import copy from lxml import etree from lxml import html from calibre import LoggingInterface +from calibre.translations.dynamic import translate XML_PARSER = etree.XMLParser(recover=True) XML_NS = 'http://www.w3.org/XML/1998/namespace' @@ -973,6 +974,11 @@ class OEBBook(object): self._toc_from_opf(opf) self._ensure_cover_image() + def translate(self, text): + lang = str(self.metadata.language[0]) + lang = lang.split('-', 1)[0].lower() + return translate(lang, text) + def to_opf1(self): package = etree.Element('package', attrib={'unique-identifier': self.uid.id}) diff --git a/src/calibre/ebooks/oeb/transforms/htmltoc.py b/src/calibre/ebooks/oeb/transforms/htmltoc.py index 9eaa04d41d..7da7df17e9 100644 --- a/src/calibre/ebooks/oeb/transforms/htmltoc.py +++ b/src/calibre/ebooks/oeb/transforms/htmltoc.py @@ -44,13 +44,15 @@ body > .calibre_toc_block { } class HTMLTOCAdder(object): - def __init__(self, style='nested'): + def __init__(self, title=None, style='nested'): + self.title = title self.style = style def transform(self, oeb, context): if 'toc' in oeb.guide: return oeb.logger.info('Generating in-line TOC...') + title = self.title or oeb.translate('Table of Contents') style = self.style if style not in STYLE_CSS: oeb.logger.error('Unknown TOC style %r' % style) @@ -61,15 +63,15 @@ class HTMLTOCAdder(object): contents = element(None, XHTML('html'), nsmap={None: XHTML_NS}, attrib={XML('lang'): language}) head = element(contents, XHTML('head')) - title = element(head, XHTML('title')) - title.text = 'Table of Contents' + htitle = element(head, XHTML('title')) + htitle.text = title element(head, XHTML('link'), rel='stylesheet', type=CSS_MIME, href=css_href) body = element(contents, XHTML('body'), attrib={'class': 'calibre_toc'}) h1 = element(body, XHTML('h1'), attrib={'class': 'calibre_toc_header'}) - h1.text = 'Table of Contents' + h1.text = title self.add_toc_level(body, oeb.toc) id, href = oeb.manifest.generate('contents', 'contents.xhtml') item = oeb.manifest.add(id, href, XHTML_MIME, data=contents) diff --git a/src/calibre/translations/dynamic.py b/src/calibre/translations/dynamic.py new file mode 100644 index 0000000000..1c9f53e960 --- /dev/null +++ b/src/calibre/translations/dynamic.py @@ -0,0 +1,27 @@ +''' +Dynamic language lookup of translations for user-visible strings. +''' + +__license__ = 'GPL v3' +__copyright__ = '2008, Marshall T. Vandegrift ' + +import sys +from cStringIO import StringIO +from gettext import GNUTranslations, NullTranslations +from calibre.translations.compiled import translations + +__all__ = ['translate'] + +_CACHE = {} + +def translate(lang, text): + trans = None + if lang in _CACHE: + trans = _CACHE[lang] + elif lang in translations: + buf = StringIO(translations[lang]) + trans = GNUTranslations(buf) + _CACHE[lang] = trans + if trans is None: + return _(text) + return trans.ugettext(text) From 6a4933c45330a307c9863a1ebb134946c0e20cd8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 19 Jan 2009 22:16:34 -0800 Subject: [PATCH 17/21] Fix #1626 (bad font size on "The New Yorker" recipe) --- src/calibre/web/feeds/news.py | 10 +++-- .../web/feeds/recipes/recipe_new_yorker.py | 8 ++++ src/calibre/web/feeds/templates.py | 38 +++++++++++++------ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 85ed39a16d..6da6383210 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -532,7 +532,9 @@ class BasicNewsRecipe(object, LoggingInterface): if body is not None: templ = self.navbar.generate(False, f, a, feed_len, not self.has_single_feed, - url, __appname__, center=self.center_navbar) + url, __appname__, + center=self.center_navbar, + extra_css=self.extra_css) elem = BeautifulSoup(templ.render(doctype='xhtml').decode('utf-8')).find('div') body.insert(0, elem) if self.remove_javascript: @@ -575,7 +577,8 @@ class BasicNewsRecipe(object, LoggingInterface): def feeds2index(self, feeds): templ = templates.IndexTemplate() - return templ.generate(self.title, self.timefmt, feeds).render(doctype='xhtml') + return templ.generate(self.title, self.timefmt, feeds, + extra_css=self.extra_css).render(doctype='xhtml') @classmethod def description_limiter(cls, src): @@ -626,7 +629,8 @@ class BasicNewsRecipe(object, LoggingInterface): templ = templates.FeedTemplate() - return templ.generate(feed, self.description_limiter).render(doctype='xhtml') + return templ.generate(feed, self.description_limiter, + extra_css=self.extra_css).render(doctype='xhtml') def create_logger(self, feed_number, article_number): diff --git a/src/calibre/web/feeds/recipes/recipe_new_yorker.py b/src/calibre/web/feeds/recipes/recipe_new_yorker.py index f5ce85b351..f8ef5bc8cc 100644 --- a/src/calibre/web/feeds/recipes/recipe_new_yorker.py +++ b/src/calibre/web/feeds/recipes/recipe_new_yorker.py @@ -16,6 +16,14 @@ class NewYorker(BasicNewsRecipe): max_articles_per_feed = 100 no_stylesheets = False use_embedded_content = False + extra_css = ''' + .calibre_feed_list {font-size:xx-small} + .calibre_article_list {font-size:xx-small} + .calibre_feed_title {font-size:normal} + .calibre_recipe_title {font-size:normal} + .calibre_feed_description {font-size:xx-small} + ''' + keep_only_tags = [ dict(name='div' , attrs={'id':'printbody' }) diff --git a/src/calibre/web/feeds/templates.py b/src/calibre/web/feeds/templates.py index b2b96bd9a4..1a6a574129 100644 --- a/src/calibre/web/feeds/templates.py +++ b/src/calibre/web/feeds/templates.py @@ -32,6 +32,11 @@ class NavBarTemplate(Template): xmlns:py="http://genshi.edgewall.org/" > + + +