diff --git a/src/calibre/devices/kindle/apnx.py b/src/calibre/devices/kindle/apnx.py index a051c84be6..75b0804e6a 100644 --- a/src/calibre/devices/kindle/apnx.py +++ b/src/calibre/devices/kindle/apnx.py @@ -19,7 +19,12 @@ class APNXBuilder(object): Create an APNX file using a pseudo page mapping. ''' - def write_apnx(self, mobi_file_path, apnx_path, accurate=True): + def write_apnx(self, mobi_file_path, apnx_path, accurate=True, page_count=0): + ''' + If you want a fixed number of pages (such as from a custom column) then + pass in a value to page_count, otherwise a count will be estimated + using either the fast or accurate algorithm. + ''' # Check that this is really a MOBI file. with open(mobi_file_path, 'rb') as mf: ident = PdbHeaderReader(mf).identity() @@ -28,16 +33,19 @@ class APNXBuilder(object): # Get the pages depending on the chosen parser pages = [] - if accurate: - try: - pages = self.get_pages_accurate(mobi_file_path) - except: - # Fall back to the fast parser if we can't - # use the accurate one. Typically this is - # due to the file having DRM. - pages = self.get_pages_fast(mobi_file_path) + if page_count: + pages = self.get_pages_exact(mobi_file_path, page_count) else: - pages = self.get_pages_fast(mobi_file_path) + if accurate: + try: + pages = self.get_pages_accurate(mobi_file_path) + except: + # Fall back to the fast parser if we can't + # use the accurate one. Typically this is + # due to the file having DRM. + pages = self.get_pages_fast(mobi_file_path) + else: + pages = self.get_pages_fast(mobi_file_path) if not pages: raise Exception(_('Could not generate page mapping.')) @@ -77,6 +85,31 @@ class APNXBuilder(object): return apnx + def get_pages_exact(self, mobi_file_path, page_count): + ''' + Given a specified page count (such as from a custom column), + create our array of pages for the apnx file by dividing by + the content size of the book. + ''' + pages = [] + count = 0 + + with open(mobi_file_path, 'rb') as mf: + phead = PdbHeaderReader(mf) + r0 = phead.section_data(0) + text_length = struct.unpack('>I', r0[4:8])[0] + + chars_per_page = int(text_length / page_count) + while count < text_length: + pages.append(count) + count += chars_per_page + + if len(pages) > page_count: + # Rounding created extra page entries + pages = pages[:page_count] + + return pages + def get_pages_fast(self, mobi_file_path): ''' 2300 characters of uncompressed text per page. This is diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index 1b10ce3050..c71eb67985 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -302,19 +302,28 @@ class KINDLE2(KINDLE): ' this information to the Kindle when uploading MOBI files by' ' USB. Note that the page numbers do not correspond to any paper' ' book.'), - _('Use slower but more accurate page number generation') + + _('Use slower but more accurate page number calculation') + ':::' + _('There are two ways to generate the page number information. Using the more accurate ' 'generator will produce pages that correspond better to a printed book. ' 'However, this method is slower and will slow down sending files ' 'to the Kindle.'), + _('Custom column name to retrieve page counts from') + + ':::' + + _('If you have a custom column in your library that you use to ' + 'store the page count of books, you can have calibre use that ' + 'information, instead of calculating a page count. Specify the ' + 'name of the custom column here, for example, #pages. '), + ] EXTRA_CUSTOMIZATION_DEFAULT = [ True, False, + '', ] OPT_APNX = 0 OPT_APNX_ACCURATE = 1 + OPT_APNX_CUST_COL = 2 def books(self, oncard=None, end_session=True): bl = USBMS.books(self, oncard=oncard, end_session=end_session) @@ -380,10 +389,20 @@ class KINDLE2(KINDLE): if not os.path.exists(path): os.makedirs(path) + cust_col_name = opts.extra_customization[self.OPT_APNX_CUST_COL] + custom_page_count = 0 + if cust_col_name: + try: + custom_page_count = int(metadata.get(cust_col_name, 0)) + except: + pass + apnx_path = '%s.apnx' % os.path.join(path, filename) apnx_builder = APNXBuilder() try: - apnx_builder.write_apnx(filepath, apnx_path, accurate=opts.extra_customization[self.OPT_APNX_ACCURATE]) + apnx_builder.write_apnx(filepath, apnx_path, + accurate=opts.extra_customization[self.OPT_APNX_ACCURATE], + page_count=custom_page_count) except: print 'Failed to generate APNX' import traceback diff --git a/src/calibre/gui2/device_drivers/configwidget.py b/src/calibre/gui2/device_drivers/configwidget.py index 2e5b77e5bc..94843f90e3 100644 --- a/src/calibre/gui2/device_drivers/configwidget.py +++ b/src/calibre/gui2/device_drivers/configwidget.py @@ -82,6 +82,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) l.setToolTip(tt) + self.opt_extra_customization[i].setToolTip(tt) l.setBuddy(self.opt_extra_customization[i]) l.setWordWrap(True) self.opt_extra_customization[i].setText(settings.extra_customization[i])