diff --git a/resources/images/news/alo_novine.png b/resources/images/news/alo_novine.png new file mode 100644 index 0000000000..569ea16fce Binary files /dev/null and b/resources/images/news/alo_novine.png differ diff --git a/resources/recipes/akter.recipe b/resources/recipes/akter.recipe index 3959fff717..0f2fb05640 100644 --- a/resources/recipes/akter.recipe +++ b/resources/recipes/akter.recipe @@ -15,7 +15,7 @@ class Akter(BasicNewsRecipe): category = 'vesti, online vesti, najnovije vesti, politika, sport, ekonomija, biznis, finansije, berza, kultura, zivot, putovanja, auto, automobili, tehnologija, politicki magazin, dogadjaji, desavanja, lifestyle, zdravlje, zdravstvo, vest, novine, nedeljnik, srbija, novi sad, vojvodina, svet, drustvo, zabava, republika srpska, beograd, intervju, komentar, reportaza, arhiva vesti, news, serbia, politics' oldest_article = 8 max_articles_per_feed = 100 - no_stylesheets = False + no_stylesheets = True use_embedded_content = False encoding = 'utf-8' masthead_url = 'http://www.akter.co.rs/templates/gk_thenews2/images/style2/logo.png' @@ -23,9 +23,9 @@ class Akter(BasicNewsRecipe): publication_type = 'magazine' remove_empty_feeds = True PREFIX = 'http://www.akter.co.rs' - extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)} + extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} - .article_description,body,.lokacija{font-family: Arial,Helvetica,sans1,sans-serif} + .article_description,body{font-family: Arial,Helvetica,sans1,sans-serif} .color-2{display:block; margin-bottom: 10px; padding: 5px, 10px; border-left: 1px solid #D00000; color: #D00000} img{margin-bottom: 0.8em} """ diff --git a/resources/recipes/alo_novine.recipe b/resources/recipes/alo_novine.recipe new file mode 100644 index 0000000000..45e53e99e8 --- /dev/null +++ b/resources/recipes/alo_novine.recipe @@ -0,0 +1,65 @@ +__license__ = 'GPL v3' +__copyright__ = '2010, Darko Miletic ' + +''' +www.alo.rs +''' + +import re +from calibre.web.feeds.recipes import BasicNewsRecipe + +class Alo_Novine(BasicNewsRecipe): + title = 'Alo!' + __author__ = 'Darko Miletic' + description = "News Portal from Serbia" + publisher = 'Alo novine d.o.o.' + category = 'news, politics, Serbia' + oldest_article = 2 + max_articles_per_feed = 100 + delay = 4 + no_stylesheets = True + encoding = 'utf-8' + use_embedded_content = False + language = 'sr' + extra_css = """ + @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} + .article_description,body{font-family: Arial,Helvetica,sans1,sans-serif} + .lead {font-size: 1.3em} + h1{color: #DB0700} + .article_uvod{font-style: italic; font-size: 1.2em} + img{margin-bottom: 0.8em} """ + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher': publisher + , 'language' : language + } + + preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')] + + remove_tags = [dict(name=['object','link','embed'])] + remove_attributes = ['height','width'] + + feeds = [ + (u'Najnovije Vijesti', u'http://www.alo.rs/rss/danasnje_vesti') + ,(u'Politika' , u'http://www.alo.rs/rss/politika') + ,(u'Vesti' , u'http://www.alo.rs/rss/vesti') + ,(u'Sport' , u'http://www.alo.rs/rss/sport') + ,(u'Ljudi' , u'http://www.alo.rs/rss/ljudi') + ,(u'Saveti' , u'http://www.alo.rs/rss/saveti') + ] + + def preprocess_html(self, soup): + for item in soup.findAll(style=True): + del item['style'] + return soup + + def print_version(self, url): + artl = url.rpartition('/')[0] + artid = artl.rpartition('/')[2] + return 'http://www.alo.rs/resources/templates/tools/print.php?id=' + artid + + def image_url_processor(self, baseurl, url): + return url.replace('alo.rs//','alo.rs/') + diff --git a/resources/recipes/nytimes.recipe b/resources/recipes/nytimes.recipe index 3e02363f68..a2d5135045 100644 --- a/resources/recipes/nytimes.recipe +++ b/resources/recipes/nytimes.recipe @@ -89,6 +89,7 @@ class NYTimes(BasicNewsRecipe): 'relatedSearchesModule', 'side_tool', 'singleAd', + 'subNavigation clearfix', 'subNavigation tabContent active', 'subNavigation tabContent active clearfix', ]}), @@ -460,8 +461,10 @@ class NYTimes(BasicNewsRecipe): if mp_off >= 0: c = c[:mp_off] emTag.insert(0, c) - hrTag = Tag(soup, 'hr') - #hrTag['style'] = "margin-top:0em;margin-bottom:0em" + #hrTag = Tag(soup, 'hr') + #hrTag['class'] = 'caption_divider' + hrTag = Tag(soup, 'div') + hrTag['class'] = 'divider' emTag.insert(1, hrTag) caption.replaceWith(emTag) diff --git a/resources/recipes/nytimes_sub.recipe b/resources/recipes/nytimes_sub.recipe index f8ad12afe9..f4101ca299 100644 --- a/resources/recipes/nytimes_sub.recipe +++ b/resources/recipes/nytimes_sub.recipe @@ -76,6 +76,7 @@ class NYTimes(BasicNewsRecipe): 'relatedSearchesModule', 'side_tool', 'singleAd', + 'subNavigation clearfix', 'subNavigation tabContent active', 'subNavigation tabContent active clearfix', ]}), @@ -350,8 +351,10 @@ class NYTimes(BasicNewsRecipe): if mp_off >= 0: c = c[:mp_off] emTag.insert(0, c) - hrTag = Tag(soup, 'hr') - #hrTag['style'] = "margin-top:0em;margin-bottom:0em" + #hrTag = Tag(soup, 'hr') + #hrTag['class'] = 'caption_divider' + hrTag = Tag(soup, 'div') + hrTag['class'] = 'divider' emTag.insert(1, hrTag) caption.replaceWith(emTag) diff --git a/setup/installer/linux/freeze.py b/setup/installer/linux/freeze.py index 7353134393..08237b83fa 100644 --- a/setup/installer/linux/freeze.py +++ b/setup/installer/linux/freeze.py @@ -46,7 +46,7 @@ class LinuxFreeze(Command): '/usr/lib/libsqlite3.so.0', '/usr/lib/libsqlite3.so.0', '/usr/lib/libmng.so.1', - '/usr/lib/libpodofo.so.0.6.99', + '/usr/lib/libpodofo.so.0.8.1', '/lib/libz.so.1', '/lib/libuuid.so.1', '/usr/lib/libtiff.so.3', diff --git a/setup/installer/osx/app/main.py b/setup/installer/osx/app/main.py index 5b01c8503c..c95be6752c 100644 --- a/setup/installer/osx/app/main.py +++ b/setup/installer/osx/app/main.py @@ -265,6 +265,9 @@ class Py2App(object): @flush def get_local_dependencies(self, path_to_lib): for x in self.get_dependencies(path_to_lib): + if x.startswith('libpodofo'): + yield x, x + continue for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/', '/opt/local/lib/', '/Library/Frameworks/Python.framework/', SW+'/freetype/lib/'): @@ -397,7 +400,7 @@ class Py2App(object): @flush def add_podofo(self): info('\nAdding PoDoFo') - pdf = join(SW, 'lib', 'libpodofo.0.6.99.dylib') + pdf = join(SW, 'lib', 'libpodofo.0.8.1.dylib') self.install_dylib(pdf) @flush diff --git a/setup/installer/windows/notes.rst b/setup/installer/windows/notes.rst index 0fdecda27c..cd02c80d2e 100644 --- a/setup/installer/windows/notes.rst +++ b/setup/installer/windows/notes.rst @@ -162,9 +162,50 @@ SET(WANT_LIB64 FALSE) SET(PODOFO_BUILD_SHARED TRUE) SET(PODOFO_BUILD_STATIC FALSE) -cp build/podofo-0.7.0/build/src/Release/podofo.dll bin/ -cp build/podofo-0.7.0/build/src/Release/podofo.lib lib/ -cp build/podofo-0.7.0/build/src/Release/podofo.exp lib/ +cp build/podofo/build/src/Release/podofo.dll bin/ +cp build/podofo/build/src/Release/podofo.lib lib/ +cp build/podofo/build/src/Release/podofo.exp lib/ + +cp build/podofo/build/podofo_config.h include/podofo/ +cp -r build/podofo/src/* include/podofo/ + +The following patch was required to get it to compile: + +Index: src/PdfImage.cpp +=================================================================== +--- src/PdfImage.cpp (revision 1261) ++++ src/PdfImage.cpp (working copy) +@@ -627,7 +627,7 @@ + + long lLen = static_cast(pInfo->rowbytes * height); + char* pBuffer = static_cast(malloc(sizeof(char) * lLen)); +- png_bytep pRows[height]; ++ png_bytepp pRows = static_cast(malloc(sizeof(png_bytep)*height)); + for(int y=0; y(pBuffer + (y * pInfo->rowbytes)); +@@ -672,6 +672,7 @@ + this->SetImageData( width, height, pInfo->bit_depth, &stream ); + + free(pBuffer); ++ free(pRows); + } + #endif // PODOFO_HAVE_PNG_LIB + +Index: src/PdfFiltersPrivate.cpp +=================================================================== +--- src/PdfFiltersPrivate.cpp (revision 1261) ++++ src/PdfFiltersPrivate.cpp (working copy) +@@ -1019,7 +1019,7 @@ + /* + * Prepare for input from a memory buffer. + */ +-GLOBAL(void) ++void + jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET * buffer, size_t bufsize) + { + my_src_ptr src; + ImageMagick -------------- diff --git a/setup/installer/windows/wix-template.xml b/setup/installer/windows/wix-template.xml index 17976783aa..37dd8b25a8 100644 --- a/setup/installer/windows/wix-template.xml +++ b/setup/installer/windows/wix-template.xml @@ -153,18 +153,10 @@ - - - + - - - - - - diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py index d352ce5de7..0db3ce6a15 100644 --- a/src/calibre/customize/profiles.py +++ b/src/calibre/customize/profiles.py @@ -275,13 +275,44 @@ class iPadOutput(OutputProfile): # touchscreen_news_css {{{ touchscreen_news_css = u''' /* hr used in articles */ + .article_articles_list { + width:18%; + } + .article_link { + color: #593f29; + font-style: italic; + } + .article_next { + -webkit-border-top-right-radius:4px; + -webkit-border-bottom-right-radius:4px; + font-style: italic; + width:32%; + } + + .article_prev { + -webkit-border-top-left-radius:4px; + -webkit-border-bottom-left-radius:4px; + font-style: italic; + width:32%; + } + .article_sections_list { + width:18%; + } + .articles_link { + font-weight: bold; + } + .sections_link { + font-weight: bold; + } + + .caption_divider { border:#ccc 1px solid; } .touchscreen_navbar { - background:#ccc; - border:#ccc 1px solid; + background:#c3bab2; + border:#ccc 0px solid; border-collapse:separate; border-spacing:1px; margin-left: 5%; @@ -292,22 +323,16 @@ class iPadOutput(OutputProfile): .touchscreen_navbar td { background:#fff; font-family:Helvetica; - font-size:90%; - padding: 5px; + font-size:80%; + /* UI touchboxes use 8px padding */ + padding: 6px; text-align:center; } - .touchscreen_navbar td:first-child { - -webkit-border-top-left-radius:4px; - -webkit-border-bottom-left-radius:4px; - } - .touchscreen_navbar td:last-child { - -webkit-border-top-right-radius:4px; - -webkit-border-bottom-right-radius:4px; - } - .feed_link { - font-style: italic; - } + .touchscreen_navbar td a:link { + color: #593f29; + text-decoration: none; + } /* Index formatting */ .publish_date { @@ -318,12 +343,50 @@ class iPadOutput(OutputProfile): border-top:1px solid gray; } + hr.caption_divider { + border-color:black; + border-style:solid; + border-width:1px; + } + /* Feed summary formatting */ + .article_summary { + display:inline-block; + } + .feed { + font-family:sans-serif; + font-weight:bold; + font-size:larger; + } + + .feed_link { + font-style: italic; + } + + .feed_next { + -webkit-border-top-right-radius:4px; + -webkit-border-bottom-right-radius:4px; + font-style: italic; + width:40%; + } + + .feed_prev { + -webkit-border-top-left-radius:4px; + -webkit-border-bottom-left-radius:4px; + font-style: italic; + width:40%; + } + .feed_title { text-align: center; font-size: 160%; } + .feed_up { + font-weight: bold; + width:20%; + } + .summary_headline { font-weight:bold; text-align:left; @@ -338,12 +401,6 @@ class iPadOutput(OutputProfile): text-align:left; } - .feed { - font-family:sans-serif; - font-weight:bold; - font-size:larger; - } - ''' # }}} diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 59bef5b334..6c9235c0d4 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -20,7 +20,7 @@ from calibre.utils.config import config_dir from calibre.utils.date import isoformat, now, parse_date from calibre.utils.localization import get_lang from calibre.utils.logging import Log -from calibre.utils.zipfile import ZipFile +from calibre.utils.zipfile import ZipFile, safe_replace from PIL import Image as PILImage @@ -38,6 +38,7 @@ if iswindows: class DriverBase(DeviceConfig, DevicePlugin): # Needed for config_widget to work FORMATS = ['epub', 'pdf'] + SUPPORTS_SUB_DIRS = True # To enable second checkbox in customize widget @classmethod def _config_base_name(cls): @@ -87,7 +88,7 @@ class ITUNES(DriverBase): supported_platforms = ['osx','windows'] author = 'GRiker' #: The version of this plugin as a 3-tuple (major, minor, revision) - version = (0,8,0) + version = (0,9,0) OPEN_FEEDBACK_MESSAGE = _( 'Apple device detected, launching iTunes, please wait ...') @@ -106,53 +107,55 @@ class ITUNES(DriverBase): BCD = [0x01] # iTunes enumerations - Sources = [ - 'Unknown', - 'Library', - 'iPod', - 'AudioCD', - 'MP3CD', - 'Device', - 'RadioTuner', - 'SharedLibrary'] - + Audiobooks = [ + 'Audible file', + 'MPEG audio file', + 'Protected AAC audio file' + ] ArtworkFormat = [ - 'Unknown', - 'JPEG', - 'PNG', - 'BMP' - ] - + 'Unknown', + 'JPEG', + 'PNG', + 'BMP' + ] PlaylistKind = [ - 'Unknown', - 'Library', - 'User', - 'CD', - 'Device', - 'Radio Tuner' - ] - + 'Unknown', + 'Library', + 'User', + 'CD', + 'Device', + 'Radio Tuner' + ] PlaylistSpecialKind = [ - 'Unknown', - 'Purchased Music', - 'Party Shuffle', - 'Podcasts', - 'Folder', - 'Video', - 'Music', - 'Movies', - 'TV Shows', - 'Books', - ] - + 'Unknown', + 'Purchased Music', + 'Party Shuffle', + 'Podcasts', + 'Folder', + 'Video', + 'Music', + 'Movies', + 'TV Shows', + 'Books', + ] SearchField = [ - 'All', - 'Visible', - 'Artists', - 'Albums', - 'Composers', - 'SongNames', - ] + 'All', + 'Visible', + 'Artists', + 'Albums', + 'Composers', + 'SongNames', + ] + Sources = [ + 'Unknown', + 'Library', + 'iPod', + 'AudioCD', + 'MP3CD', + 'Device', + 'RadioTuner', + 'SharedLibrary' + ] # Cover art size limits MAX_COVER_WIDTH = 510 @@ -161,6 +164,7 @@ class ITUNES(DriverBase): # Properties cached_books = {} cache_dir = os.path.join(config_dir, 'caches', 'itunes') + archive_path = os.path.join(cache_dir, "thumbs.zip") description_prefix = "added by calibre" ejected = False iTunes= None @@ -168,7 +172,7 @@ class ITUNES(DriverBase): library_orphans = None log = Log() manual_sync_mode = False - path_template = 'iTunes/%s - %s.epub' + path_template = 'iTunes/%s - %s.%s' problem_titles = [] problem_msg = None report_progress = None @@ -252,7 +256,7 @@ class ITUNES(DriverBase): (new_book.title, new_book.author)) booklists[0].append(new_book) - if DEBUG: + if False: self._dump_booklist(booklists[0],header='after',indent=2) self._dump_cached_books(header='after',indent=2) @@ -273,10 +277,13 @@ class ITUNES(DriverBase): """ if not oncard: if DEBUG: - self.log.info("ITUNES:books(oncard=%s)" % oncard) + self.log.info("ITUNES:books():") + if self.settings().use_subdirs: + self.log.info(" Cover fetching/caching enabled") + else: + self.log.info(" Cover fetching/caching disabled") # Fetch a list of books from iPod device connected to iTunes - if 'iPod' in self.sources: booklist = BookList(self.log) cached_books = {} @@ -287,11 +294,12 @@ class ITUNES(DriverBase): book_count = float(len(device_books)) for (i,book) in enumerate(device_books): this_book = Book(book.name(), book.artist()) - this_book.path = self.path_template % (book.name(), book.artist()) + format = 'pdf' if book.kind().startswith('PDF') else 'epub' + this_book.path = self.path_template % (book.name(), book.artist(),format) try: this_book.datetime = parse_date(str(book.date_added())).timetuple() except: - pass + this_book.datetime = time.gmtime() this_book.db_id = None this_book.device_collections = [] this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None @@ -325,11 +333,12 @@ class ITUNES(DriverBase): book_count = float(len(device_books)) for (i,book) in enumerate(device_books): this_book = Book(book.Name, book.Artist) - this_book.path = self.path_template % (book.Name, book.Artist) + format = 'pdf' if book.KindAsString.startswith('PDF') else 'epub' + this_book.path = self.path_template % (book.Name, book.Artist,format) try: this_book.datetime = parse_date(str(book.DateAdded)).timetuple() except: - pass + this_book.datetime = time.gmtime() this_book.db_id = None this_book.device_collections = [] this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None @@ -532,8 +541,10 @@ class ITUNES(DriverBase): # Turn off the Save template cw.opt_save_template.setVisible(False) cw.label.setVisible(False) - # Repurpose the checkbox + # Repurpose the metadata checkbox cw.opt_read_metadata.setText(_("Use Series as Category in iTunes/iBooks")) + # Repurpose the use_subdirs checkbox + cw.opt_use_subdirs.setText(_("Cache covers from iTunes/iBooks")) return cw def delete_books(self, paths, end_session=True): @@ -691,21 +702,19 @@ class ITUNES(DriverBase): self.log.info("ITUNES.open()") # Confirm/create thumbs archive - archive_path = os.path.join(self.cache_dir, "thumbs.zip") - if not os.path.exists(self.cache_dir): if DEBUG: self.log.info(" creating thumb cache '%s'" % self.cache_dir) os.makedirs(self.cache_dir) - if not os.path.exists(archive_path): + if not os.path.exists(self.archive_path): self.log.info(" creating zip archive") - zfw = ZipFile(archive_path, mode='w') + zfw = ZipFile(self.archive_path, mode='w') zfw.writestr("iTunes Thumbs Archive",'') zfw.close() else: if DEBUG: - self.log.info(" existing thumb cache at '%s'" % archive_path) + self.log.info(" existing thumb cache at '%s'" % self.archive_path) def remove_books_from_metadata(self, paths, booklists): ''' @@ -722,21 +731,61 @@ class ITUNES(DriverBase): if DEBUG: self.log.info("ITUNES.remove_books_from_metadata()") for path in paths: - self._dump_cached_book(self.cached_books[path], indent=2) + if DEBUG: + self._dump_cached_book(self.cached_books[path], indent=2) + self.log.info(" looking for '%s' by '%s' (%s)" % + (self.cached_books[path]['title'], + self.cached_books[path]['author'], + self.cached_books[path]['uuid'])) - # Purge the booklist, self.cached_books + # Purge the booklist, self.cached_books, thumb cache for i,bl_book in enumerate(booklists[0]): if False: - self.log.info(" evaluating '%s'" % bl_book.uuid) - if bl_book.uuid == self.cached_books[path]['uuid']: - # Remove from booklists[0] - booklists[0].pop(i) + self.log.info(" evaluating '%s' by '%s' (%s)" % + (bl_book.title, bl_book.author,bl_book.uuid)) + found = False + if bl_book.uuid == self.cached_books[path]['uuid']: + if False: + self.log.info(" matched with uuid") + booklists[0].pop(i) + found = True + elif bl_book.title == self.cached_books[path]['title'] and \ + bl_book.author[0] == self.cached_books[path]['author']: + if False: + self.log.info(" matched with title + author") + booklists[0].pop(i) + found = True + + if found: + # Remove from self.cached_books for cb in self.cached_books: if self.cached_books[cb]['uuid'] == self.cached_books[path]['uuid']: self.cached_books.pop(cb) break + + # Remove from thumb from thumb cache + thumb_path = path.rpartition('.')[0] + '.jpg' + zf = ZipFile(self.archive_path,'a') + fnames = zf.namelist() + try: + thumb = [x for x in fnames if thumb_path in x][0] + except: + thumb = None + if thumb: + if DEBUG: + self.log.info(" deleting '%s' from cover cache" % (thumb_path)) + zf.delete(thumb_path) + else: + if DEBUG: + self.log.info(" '%s' not found in cover cache" % thumb_path) + zf.close() + break +# else: +# if DEBUG: +# self.log.error(" unable to find '%s' by '%s' (%s)" % +# (bl_book.title, bl_book.author,bl_book.uuid)) if False: self._dump_booklist(booklists[0], indent = 2) @@ -842,12 +891,11 @@ class ITUNES(DriverBase): self.log.info("ITUNES.upload_books()") self._dump_files(files, header='upload_books()',indent=2) self._dump_update_list(header='upload_books()',indent=2) - #self.log.info(" self.settings().format_map: %s" % self.settings().format_map) if isosx: for (i,file) in enumerate(files): format = file.rpartition('.')[2].lower() - path = self.path_template % (metadata[i].title, metadata[i].author[0]) + path = self.path_template % (metadata[i].title, metadata[i].author[0],format) self._remove_existing_copy(path, metadata[i]) fpath = self._get_fpath(file, metadata[i], format, update_md=True) db_added, lb_added = self._add_new_copy(fpath, metadata[i]) @@ -856,7 +904,10 @@ class ITUNES(DriverBase): new_booklist.append(this_book) self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book) - # Add new_book to self.cached_paths + # Add new_book to self.cached_books + if DEBUG: + self.log.info(" adding '%s' by '%s' ['%s'] to self.cached_books" % + ( metadata[i].title, metadata[i].author, metadata[i].uuid)) self.cached_books[this_book.path] = { 'author': metadata[i].author, 'dev_book': db_added, @@ -877,7 +928,7 @@ class ITUNES(DriverBase): for (i,file) in enumerate(files): format = file.rpartition('.')[2].lower() - path = self.path_template % (metadata[i].title, metadata[i].author[0]) + path = self.path_template % (metadata[i].title, metadata[i].author[0],format) self._remove_existing_copy(path, metadata[i]) fpath = self._get_fpath(file, metadata[i],format, update_md=True) db_added, lb_added = self._add_new_copy(fpath, metadata[i]) @@ -1091,7 +1142,7 @@ class ITUNES(DriverBase): thumb = None if metadata.cover: - if (format == 'epub'): + if format == 'epub': # Pre-shrink cover # self.MAX_COVER_WIDTH, self.MAX_COVER_HEIGHT try: @@ -1171,17 +1222,18 @@ class ITUNES(DriverBase): # Refresh the thumbnail cache if DEBUG: - self.log.info( " refreshing cached thumb for '%s'" % metadata.title) - archive_path = os.path.join(self.cache_dir, "thumbs.zip") - zfw = ZipFile(archive_path, mode='a') + self.log.info( " refreshing cached thumb for '%s'" % metadata.title) + zfw = ZipFile(self.archive_path, mode='a') thumb_path = path.rpartition('.')[0] + '.jpg' zfw.writestr(thumb_path, thumb) except: self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) - self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title)) + self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title)) finally: zfw.close() - + else: + if DEBUG: + self.log.info(" no cover defined in metadata for '%s'" % metadata.title) return thumb def _create_new_book(self,fpath, metadata, path, db_added, lb_added, thumb, format): @@ -1190,8 +1242,9 @@ class ITUNES(DriverBase): if DEBUG: self.log.info(" ITUNES._create_new_book()") - this_book = Book(metadata.title, metadata.author[0]) - + #this_book = Book(metadata.title, metadata.author[0]) + this_book = Book(metadata.title, ' & '.join(metadata.author)) + this_book.datetime = time.gmtime() this_book.db_id = None this_book.device_collections = [] this_book.format = format @@ -1207,13 +1260,13 @@ class ITUNES(DriverBase): try: this_book.datetime = parse_date(str(lb_added.date_added())).timetuple() except: - this_book.datetime = time.gmtime() + pass elif db_added: this_book.size = self._get_device_book_size(fpath, db_added.size()) try: this_book.datetime = parse_date(str(db_added.date_added())).timetuple() except: - this_book.datetime = time.gmtime() + pass elif iswindows: if lb_added: @@ -1221,13 +1274,13 @@ class ITUNES(DriverBase): try: this_book.datetime = parse_date(str(lb_added.DateAdded)).timetuple() except: - this_book.datetime = time.gmtime() + pass elif db_added: this_book.size = self._get_device_book_size(fpath, db_added.Size) try: this_book.datetime = parse_date(str(db_added.DateAdded)).timetuple() except: - this_book.datetime = time.gmtime() + pass return this_book @@ -1244,7 +1297,8 @@ class ITUNES(DriverBase): plist = None if plist: if DEBUG: - self.log.info(" deleting %s from %s" % (pl_name,fpath)) + self.log.info(" _delete_iTunesMetadata_plist():") + self.log.info(" deleting '%s'\n from '%s'" % (pl_name,fpath)) zf.delete(pl_name) zf.close() @@ -1494,27 +1548,45 @@ class ITUNES(DriverBase): if iswindows: dev_books = self._get_device_books_playlist() if DEBUG: - self.log.info(" ITUNES._find_device_book(uuid)") - self.log.info(" searching for %s ('%s' by %s)" % - (search['uuid'], search['title'], search['author'])) + self.log.info(" ITUNES._find_device_book()") + self.log.info(" searching for '%s' by '%s' (%s)" % + (search['title'], search['author'],search['uuid'])) attempts = 9 while attempts: # Try by uuid - only one hit - hits = dev_books.Search(search['uuid'],self.SearchField.index('All')) - if hits: - hit = hits[0] - self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) - return hit + if 'uuid' in search and search['uuid']: + if DEBUG: + self.log.info(" searching by uuid '%s' ..." % search['uuid']) + hits = dev_books.Search(search['uuid'],self.SearchField.index('All')) + if hits: + hit = hits[0] + self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) + return hit # Try by author - there could be multiple hits - hits = dev_books.Search(search['author'],self.SearchField.index('Artists')) + if search['author']: + if DEBUG: + self.log.info(" searching by author '%s' ..." % search['author']) + hits = dev_books.Search(search['author'],self.SearchField.index('Artists')) + if hits: + for hit in hits: + if hit.Name == search['title']: + if DEBUG: + self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) + return hit + + # Search by title if no author available + if DEBUG: + self.log.info(" searching by title '%s' ..." % search['title']) + hits = dev_books.Search(search['title'],self.SearchField.index('All')) if hits: for hit in hits: if hit.Name == search['title']: if DEBUG: - self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) + self.log.info(" found '%s'" % (hit.Name)) return hit + # PDF just sent, title not updated yet, look for export pattern # PDF metadata was rewritten at export as 'safe(title) - safe(author)' if search['format'] == 'pdf': title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title']) @@ -1547,12 +1619,14 @@ class ITUNES(DriverBase): if iswindows: if DEBUG: self.log.info(" ITUNES._find_library_book()") + ''' if 'uuid' in search: self.log.info(" looking for '%s' by %s (%s)" % (search['title'], search['author'], search['uuid'])) else: self.log.info(" looking for '%s' by %s" % (search['title'], search['author'])) + ''' for source in self.iTunes.sources: if source.Kind == self.Sources.index('Library'): @@ -1577,10 +1651,11 @@ class ITUNES(DriverBase): if DEBUG: self.log.error(" no Books playlist found") + attempts = 9 while attempts: # Find book whose Album field = search['uuid'] - if 'uuid' in search: + if 'uuid' in search and search['uuid']: if DEBUG: self.log.info(" searching by uuid '%s' ..." % search['uuid']) hits = lib_books.Search(search['uuid'],self.SearchField.index('All')) @@ -1590,16 +1665,30 @@ class ITUNES(DriverBase): self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) return hit + # Search by author if known + if search['author']: + if DEBUG: + self.log.info(" searching by author '%s' ..." % search['author']) + hits = lib_books.Search(search['author'],self.SearchField.index('Artists')) + if hits: + for hit in hits: + if hit.Name == search['title']: + if DEBUG: + self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) + return hit + + # Search by title if no author available if DEBUG: - self.log.info(" searching by author '%s' ..." % search['author']) - hits = lib_books.Search(search['author'],self.SearchField.index('Artists')) + self.log.info(" searching by title '%s' ..." % search['title']) + hits = lib_books.Search(search['title'],self.SearchField.index('All')) if hits: for hit in hits: if hit.Name == search['title']: if DEBUG: - self.log.info(" found '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Composer)) + self.log.info(" found '%s'" % (hit.Name)) return hit + # PDF just sent, title not updated yet, look for export pattern # PDF metadata was rewritten at export as 'safe(title) - safe(author)' if search['format'] == 'pdf': title = re.sub(r'[^0-9a-zA-Z ]', '_', search['title']) @@ -1633,96 +1722,116 @@ class ITUNES(DriverBase): as of iTunes 9.2, iBooks 1.1, can't set artwork for PDF files via automation ''' - archive_path = os.path.join(self.cache_dir, "thumbs.zip") - thumb_path = book_path.rpartition('.')[0] + '.jpg' - format = book_path.rpartition('.')[2].lower() - - try: - zfr = ZipFile(archive_path) - thumb_data = zfr.read(thumb_path) - zfr.close() - except: - zfw = ZipFile(archive_path, mode='a') - else: + # self.settings().use_subdirs is a repurposed DeviceConfig field + # We're using it to skip fetching/caching covers to speed things up + if not self.settings().use_subdirs: + thumb_data = None return thumb_data - self.log.info(" ITUNES._generate_thumbnail()") + thumb_path = book_path.rpartition('.')[0] + '.jpg' + format = book_path.rpartition('.')[2].lower() if isosx: - if format == 'epub': - try: - if False: - self.log.info(" fetching artwork from %s\n %s" % (book_path,book)) - # Resize the cover - data = book.artworks[1].raw_data().data - #self._dump_hex(data[:256]) - img_data = cStringIO.StringIO(data) - im = PILImage.open(img_data) - scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80) - im = im.resize((int(width),int(height)), PILImage.ANTIALIAS) - img_data.close() + title = book.name() + elif iswindows: + title = book.Name - thumb = cStringIO.StringIO() - im.convert('RGB').save(thumb,'JPEG') - thumb_data = thumb.getvalue() - thumb.close() + try: + zfr = ZipFile(self.archive_path) + thumb_data = zfr.read(thumb_path) + if thumb_data == 'None': + if False: + self.log.info(" ITUNES._generate_thumbnail()\n returning None from cover cache for '%s'" % title) + zfr.close() + return None + except: + zfw = ZipFile(self.archive_path, mode='a') + else: + if False: + self.log.info(" returning thumb from cache for '%s'" % title) + return thumb_data - # Cache the tagged thumb - if DEBUG: - self.log.info(" generated thumb for '%s', caching" % book.name()) - zfw.writestr(thumb_path, thumb_data) - zfw.close() - return thumb_data - except: - self.log.error(" error generating thumb for '%s'" % book.name()) - try: - zfw.close() - except: - pass - return None - else: + if DEBUG: + self.log.info(" ITUNES._generate_thumbnail():") + if isosx: + + # Fetch the artwork from iTunes + try: + data = book.artworks[1].raw_data().data + except: + # If no artwork, write an empty marker to cache if DEBUG: - self.log.info(" unable to generate PDF thumbs") + self.log.error(" error fetching iTunes artwork for '%s'" % title) + zfw.writestr(thumb_path, 'None') + zfw.close() return None - elif iswindows: + # Generate a thumb + try: + img_data = cStringIO.StringIO(data) + im = PILImage.open(img_data) + scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80) + im = im.resize((int(width),int(height)), PILImage.ANTIALIAS) + thumb = cStringIO.StringIO() + im.convert('RGB').save(thumb,'JPEG') + thumb_data = thumb.getvalue() + thumb.close() + if False: + self.log.info(" generated thumb for '%s', caching" % title) + # Cache the tagged thumb + zfw.writestr(thumb_path, thumb_data) + except: + if DEBUG: + self.log.error(" error generating thumb for '%s', caching empty marker" % book.name()) + self._dump_hex(data[:32]) + thumb_data = None + # Cache the empty cover + zfw.writestr(thumb_path, 'None') + finally: + img_data.close() + zfw.close() + + return thumb_data + + + elif iswindows: if not book.Artwork.Count: if DEBUG: self.log.info(" no artwork available for '%s'" % book.Name) + zfw.writestr(thumb_path, 'None') + zfw.close() return None - if format == 'epub': - # Save the cover from iTunes - try: - tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format]) - book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb) - # Resize the cover - im = PILImage.open(tmp_thumb) - scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80) - im = im.resize((int(width),int(height)), PILImage.ANTIALIAS) - thumb = cStringIO.StringIO() - im.convert('RGB').save(thumb,'JPEG') - thumb_data = thumb.getvalue() - os.remove(tmp_thumb) - thumb.close() + # Fetch the artwork from iTunes - # Cache the tagged thumb - if DEBUG: - self.log.info(" generated thumb for '%s', caching" % book.Name) - zfw.writestr(thumb_path, thumb_data) - zfw.close() - return thumb_data - except: - self.log.error(" error generating thumb for '%s'" % book.Name) - try: - zfw.close() - except: - pass - return None - else: + try: + tmp_thumb = os.path.join(tempfile.gettempdir(), "thumb.%s" % self.ArtworkFormat[book.Artwork.Item(1).Format]) + book.Artwork.Item(1).SaveArtworkToFile(tmp_thumb) + # Resize the cover + im = PILImage.open(tmp_thumb) + scaled, width, height = fit_image(im.size[0],im.size[1], 60, 80) + im = im.resize((int(width),int(height)), PILImage.ANTIALIAS) + thumb = cStringIO.StringIO() + im.convert('RGB').save(thumb,'JPEG') + thumb_data = thumb.getvalue() + os.remove(tmp_thumb) + thumb.close() + if False: + self.log.info(" generated thumb for '%s', caching" % book.Name) + # Cache the tagged thumb + zfw.writestr(thumb_path, thumb_data) + except: if DEBUG: - self.log.info(" unable to generate PDF thumbs") - return None + self.log.error(" error generating thumb for '%s', caching empty marker" % book.Name) + self._dump_hex(data[:32]) + thumb_data = None + # Cache the empty cover + zfw.writestr(thumb_path,'None') + + finally: + zfw.close() + + return thumb_data def _get_device_book_size(self, file, compressed_size): ''' @@ -1766,7 +1875,7 @@ class ITUNES(DriverBase): for book in books: # This may need additional entries for international iTunes users - if book.kind() in ['MPEG audio file']: + if book.kind() in self.Audiobooks: if DEBUG: self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) else: @@ -1798,7 +1907,7 @@ class ITUNES(DriverBase): for book in dev_books: # This may need additional entries for international iTunes users - if book.KindAsString in ['MPEG audio file']: + if book.KindAsString in self.Audiobooks: if DEBUG: self.log.info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString)) else: @@ -1899,12 +2008,13 @@ class ITUNES(DriverBase): lib_books = pl.file_tracks() for book in lib_books: # This may need additional entries for international iTunes users - if book.kind() in ['MPEG audio file']: + if book.kind() in self.Audiobooks: if DEBUG: self.log.info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) else: # Collect calibre orphans - remnants of recipe uploads - path = self.path_template % (book.name(), book.artist()) + format = 'pdf' if book.kind().startswith('PDF') else 'epub' + path = self.path_template % (book.name(), book.artist(),format) if str(book.description()).startswith(self.description_prefix): try: if book.location() == appscript.k.missing_value: @@ -1917,7 +2027,8 @@ class ITUNES(DriverBase): library_books[path] = book if DEBUG: - self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % (book.name(), book.artist(), book.album(), book.kind())) + self.log.info(" %-30.30s %-30.30s %-40.40s [%s]" % + (book.name(), book.artist(), book.album(), book.kind())) else: if DEBUG: self.log.info(' no Library playlists') @@ -1955,11 +2066,12 @@ class ITUNES(DriverBase): try: for book in lib_books: # This may need additional entries for international iTunes users - if book.KindAsString in ['MPEG audio file']: + if book.KindAsString in self.Audiobooks: if DEBUG: self.log.info(" ignoring %-30.30s of type '%s'" % (book.Name, book.KindAsString)) else: - path = self.path_template % (book.Name, book.Artist) + format = 'pdf' if book.KindAsString.startswith('PDF') else 'epub' + path = self.path_template % (book.Name, book.Artist,format) # Collect calibre orphans if book.Description.startswith(self.description_prefix): @@ -2174,7 +2286,9 @@ class ITUNES(DriverBase): # Delete existing from Device|Books, add to self.update_list # for deletion from booklist[0] during add_books_to_metadata for book in self.cached_books: - if self.cached_books[book]['uuid'] == metadata.uuid: + if self.cached_books[book]['uuid'] == metadata.uuid and \ + self.cached_books[book]['title'] == metadata.title and \ + self.cached_books[book]['author'] == metadata.authors[0]: self.update_list.append(self.cached_books[book]) self._remove_from_device(self.cached_books[book]) if DEBUG: @@ -2191,17 +2305,17 @@ class ITUNES(DriverBase): # Delete existing from Library|Books, add to self.update_list # for deletion from booklist[0] during add_books_to_metadata for book in self.cached_books: - if (self.cached_books[book]['uuid'] == metadata.uuid) or \ - (self.cached_books[book]['title'] == metadata.title and \ - self.cached_books[book]['author'] == metadata.authors[0]): + if self.cached_books[book]['uuid'] == metadata.uuid and \ + self.cached_books[book]['title'] == metadata.title and \ + self.cached_books[book]['author'] == metadata.authors[0]: self.update_list.append(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book]) if DEBUG: self.log.info( " deleting library book '%s'" % metadata.title) break - else: - if DEBUG: - self.log.info(" '%s' not found in cached_books" % metadata.title) + else: + if DEBUG: + self.log.info(" '%s' not found in cached_books" % metadata.title) def _remove_from_device(self, cached_book): ''' @@ -2209,18 +2323,20 @@ class ITUNES(DriverBase): ''' self.log.info(" ITUNES._remove_from_device()") if isosx: - if False: - self.log.info(" deleting %s" % cached_book['dev_book']) + if DEBUG: + self.log.info(" deleting '%s' from iDevice" % cached_book['title']) cached_book['dev_book'].delete() elif iswindows: - dev_pl = self._get_device_books_playlist() - hits = dev_pl.Search(cached_book['uuid'],self.SearchField.index('All')) - if hits: - hit = hits[0] - if False: - self.log.info(" deleting '%s' by %s (%s)" % (hit.Name, hit.Artist, hit.Album)) + hit = self._find_device_book(cached_book) + if hit: + if DEBUG: + self.log.info(" deleting '%s' from iDevice" % cached_book['title']) hit.Delete() + else: + if DEBUG: + self.log.warning(" unable to remove '%s' by '%s' (%s) from device" % + (cached_book['title'],cached_book['author'],cached_book['uuid'])) def _remove_from_iTunes(self, cached_book): ''' @@ -2263,7 +2379,7 @@ class ITUNES(DriverBase): except: # We get here if there was an error with .location().path if DEBUG: - self.log.info(" '%s' not found in iTunes" % cached_book['title']) + self.log.info(" '%s' not in iTunes storage" % cached_book['title']) try: self.iTunes.delete(cached_book['lib_book']) @@ -2281,7 +2397,8 @@ class ITUNES(DriverBase): path = book.Location except: book = self._find_library_book(cached_book) - path = book.Location + if book: + path = book.Location if book: if self.iTunes_media and path.startswith(self.iTunes_media): @@ -2292,7 +2409,7 @@ class ITUNES(DriverBase): try: os.remove(path) except: - self.log.warning(" could not find '%s' in iTunes storage" % path) + self.log.warning(" '%s' not in iTunes storage" % path) try: os.rmdir(storage_path[0]) self.log.info(" removed folder '%s'" % storage_path[0]) @@ -2431,7 +2548,8 @@ class ITUNES(DriverBase): if isosx: if lb_added: lb_added.album.set(metadata.title) - lb_added.artist.set(metadata.authors[0]) + #lb_added.artist.set(metadata.authors[0]) + lb_added.artist.set(' & '.join(metadata.authors)) lb_added.composer.set(metadata.uuid) lb_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.enabled.set(True) @@ -2442,7 +2560,8 @@ class ITUNES(DriverBase): if db_added: db_added.album.set(metadata.title) - db_added.artist.set(metadata.authors[0]) + #db_added.artist.set(metadata.authors[0]) + db_added.artist.set(' & '.join(metadata.authors)) db_added.composer.set(metadata.uuid) db_added.description.set("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.enabled.set(True) @@ -2499,7 +2618,8 @@ class ITUNES(DriverBase): elif iswindows: if lb_added: lb_added.Album = metadata.title - lb_added.Artist = metadata.authors[0] + #lb_added.Artist = metadata.authors[0] + lb_added.Artist = ' & '.join(metadata.authors) lb_added.Composer = metadata.uuid lb_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) lb_added.Enabled = True @@ -2510,7 +2630,8 @@ class ITUNES(DriverBase): if db_added: db_added.Album = metadata.title - db_added.Artist = metadata.authors[0] + #db_added.Artist = metadata.authors[0] + db_added.Artist = ' & '.join(metadata.authors) db_added.Composer = metadata.uuid db_added.Description = ("%s %s" % (self.description_prefix,strftime('%Y-%m-%d %H:%M:%S'))) db_added.Enabled = True @@ -2627,7 +2748,11 @@ class ITUNES_ASYNC(ITUNES): """ if not oncard: if DEBUG: - self.log.info("ITUNES_ASYNC:books(oncard=%s)" % oncard) + self.log.info("ITUNES_ASYNC:books()") + if self.settings().use_subdirs: + self.log.info(" Cover fetching/caching enabled") + else: + self.log.info(" Cover fetching/caching disabled") # Fetch a list of books from iTunes @@ -2638,13 +2763,15 @@ class ITUNES_ASYNC(ITUNES): library_books = self._get_library_books() book_count = float(len(library_books)) for (i,book) in enumerate(library_books): + format = 'pdf' if library_books[book].kind().startswith('PDF') else 'epub' this_book = Book(library_books[book].name(), library_books[book].artist()) this_book.path = self.path_template % (library_books[book].name(), - library_books[book].artist()) + library_books[book].artist(), + format) try: this_book.datetime = parse_date(str(library_books[book].date_added())).timetuple() except: - pass + this_book.datetime = time.gmtime() this_book.db_id = None this_book.device_collections = [] #this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None @@ -2664,7 +2791,7 @@ class ITUNES_ASYNC(ITUNES): 'lib_book':library_books[book], 'dev_book':None, 'uuid': library_books[book].composer(), - #'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub' + 'format': format } if self.report_progress is not None: @@ -2678,12 +2805,14 @@ class ITUNES_ASYNC(ITUNES): book_count = float(len(library_books)) for (i,book) in enumerate(library_books): this_book = Book(library_books[book].Name, library_books[book].Artist) + format = 'pdf' if library_books[book].KindAsString.startswith('PDF') else 'epub' this_book.path = self.path_template % (library_books[book].Name, - library_books[book].Artist) + library_books[book].Artist, + format) try: this_book.datetime = parse_date(str(library_books[book].DateAdded)).timetuple() except: - pass + this_book.datetime = time.gmtime() this_book.db_id = None this_book.device_collections = [] this_book.library_id = library_books[book] @@ -2700,7 +2829,7 @@ class ITUNES_ASYNC(ITUNES): 'author':library_books[book].Artist, 'lib_book':library_books[book], 'uuid': library_books[book].Composer, - 'format': 'pdf' if library_books[book].KindAsString.startswith('PDF') else 'epub' + 'format': format } if self.report_progress is not None: @@ -2721,13 +2850,6 @@ class ITUNES_ASYNC(ITUNES): else: return BookList(self.log) - def unmount_device(self): - ''' - ''' - if DEBUG: - self.log.info("ITUNES_ASYNC:unmount_device()") - self.connected = False - def eject(self): ''' Un-mount / eject the device from the OS. This does not check if there @@ -2793,6 +2915,13 @@ class ITUNES_ASYNC(ITUNES): self.problem_msg = None self.update_list = [] + def unmount_device(self): + ''' + ''' + if DEBUG: + self.log.info("ITUNES_ASYNC:unmount_device()") + self.connected = False + class BookList(list): ''' A list of books. Each Book object must have the fields: diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 584796231a..c5e042d3e3 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -495,7 +495,7 @@ class DeviceMenu(QMenu): # {{{ self.connect_to_folder_action = mitem mitem = self.addAction(QIcon(I('devices/itunes.png')), - _('Connect to iTunes (EXPERIMENTAL)')) + _('Connect to iTunes')) mitem.setEnabled(True) mitem.triggered.connect(lambda x : self.connect_to_itunes.emit()) self.connect_to_itunes_action = mitem diff --git a/src/calibre/utils/podofo/podofo.cpp b/src/calibre/utils/podofo/podofo.cpp index 8766b709c9..d88e71628d 100644 --- a/src/calibre/utils/podofo/podofo.cpp +++ b/src/calibre/utils/podofo/podofo.cpp @@ -164,7 +164,7 @@ podofo_convert_pystring(PyObject *py) { Py_UNICODE* u = PyUnicode_AS_UNICODE(py); PyObject *u8 = PyUnicode_EncodeUTF8(u, PyUnicode_GET_SIZE(py), "replace"); if (u8 == NULL) { PyErr_NoMemory(); return NULL; } - pdf_utf8 *s8 = (pdf_utf8 *)PyString_AS_STRING(u8); + pdf_utf8 *s8 = reinterpret_cast(PyString_AS_STRING(u8)); PdfString *ans = new PdfString(s8); Py_DECREF(u8); if (ans == NULL) PyErr_NoMemory(); @@ -219,9 +219,10 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) { PyErr_SetString(PyExc_Exception, "You must first load a PDF Document"); return -1; } - PdfString *s = podofo_convert_pystring(val); if (s == NULL) return -1; + + switch (field) { case 0: info->SetTitle(*s); break; @@ -240,7 +241,9 @@ podofo_PDFDoc_setter(podofo_PDFDoc *self, PyObject *val, int field) { return -1; } + self->doc->set_info(info); + return 0; } diff --git a/src/calibre/web/feeds/templates.py b/src/calibre/web/feeds/templates.py index df414e2e52..4f26a35a02 100644 --- a/src/calibre/web/feeds/templates.py +++ b/src/calibre/web/feeds/templates.py @@ -269,11 +269,11 @@ class TouchscreenFeedTemplate(Template): link = A(CLASS('feed_link'), trim_title(feeds[f-1].title), href = '../feed_%d/index.html' % int(f-1)) - navbar_tr.append(TD(link, width="40%", align="center")) + navbar_tr.append(TD(CLASS('feed_prev'),link)) # Up to Sections - link = A(STRONG('Sections'), href="../index.html") - navbar_tr.append(TD(link,width="20%",align="center")) + link = A('Sections', href="../index.html") + navbar_tr.append(TD(CLASS('feed_up'),link)) # Next Section link = '' @@ -281,7 +281,7 @@ class TouchscreenFeedTemplate(Template): link = A(CLASS('feed_link'), trim_title(feeds[f+1].title), href = '../feed_%d/index.html' % int(f+1)) - navbar_tr.append(TD(link, width="40%", align="center", )) + navbar_tr.append(TD(CLASS('feed_next'),link)) navbar_t.append(navbar_tr) top_navbar = navbar_t bottom_navbar = copy.copy(navbar_t) @@ -319,10 +319,9 @@ class TouchscreenFeedTemplate(Template): continue tr = TR() - div_td = DIV( + div_td = DIV(CLASS('article_summary'), A(article.title, CLASS('summary_headline','calibre_rescale_120', - href=article.url)), - style="display:inline-block") + href=article.url))) if article.author: div_td.append(DIV(article.author, CLASS('summary_byline', 'calibre_rescale_100'))) @@ -354,27 +353,25 @@ class TouchscreenNavBarTemplate(Template): # | Previous if art > 0: - href = '%s../article_%d/index.html'%(prefix, art-1) - navbar_tr.append(TD(A(EM('Previous'),href=href), - width="32%")) + link = A(CLASS('article_link'),'Previous',href='%s../article_%d/index.html'%(prefix, art-1)) + navbar_tr.append(TD(CLASS('article_prev'),link)) else: - navbar_tr.append(TD('', width="32%")) + navbar_tr.append(TD(CLASS('article_prev'),'')) # | Articles | Sections | - href = '%s../index.html#article_%d'%(prefix, art) - navbar_tr.append(TD(A(STRONG('Articles'), href=href),width="18%")) + link = A(CLASS('articles_link'),'Articles', href='%s../index.html#article_%d'%(prefix, art)) + navbar_tr.append(TD(CLASS('article_articles_list'),link)) - href = '%s../../index.html#feed_%d'%(prefix, feed) - navbar_tr.append(TD(A(STRONG('Sections'), href=href),width="18%")) + link = A(CLASS('sections_link'),'Sections', href='%s../../index.html#feed_%d'%(prefix, feed)) + navbar_tr.append(TD(CLASS('article_sections_list'),link)) # | Next next = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \ else 'article_%d'%(art+1) up = '../..' if art == number_of_articles_in_feed - 1 else '..' - href = '%s%s/%s/index.html'%(prefix, up, next) - navbar_tr.append(TD(A(EM('Next'),href=href), - width="32%")) + link = A(CLASS('article_link'),'Next', href='%s%s/%s/index.html'%(prefix, up, next)) + navbar_tr.append(TD(CLASS('article_next'),link)) navbar_t.append(navbar_tr) navbar.append(navbar_t) #print "\n%s\n" % etree.tostring(navbar, pretty_print=True)