From fc4805f699510d98705225187d91fc0dc83d4637 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Apr 2014 09:39:57 +0530 Subject: [PATCH 01/11] Edit book: Allow editing SVG files as raw XML --- src/calibre/gui2/tweak_book/editor/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/tweak_book/editor/__init__.py b/src/calibre/gui2/tweak_book/editor/__init__.py index 94215621d4..270f45f2dc 100644 --- a/src/calibre/gui2/tweak_book/editor/__init__.py +++ b/src/calibre/gui2/tweak_book/editor/__init__.py @@ -16,7 +16,7 @@ def syntax_from_mime(name, mime): return 'html' if mime in OEB_STYLES: return 'css' - if mime in {guess_type('a.opf'), guess_type('a.ncx'), guess_type('a.xml'), 'application/oebps-page-map+xml'}: + if mime in {guess_type('a.svg'), guess_type('a.opf'), guess_type('a.ncx'), guess_type('a.xml'), 'application/oebps-page-map+xml'}: return 'xml' if mime.startswith('text/'): return 'text' From b078ab5b511a420e5b68907b61f6bc8de43f8ed6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Apr 2014 10:34:22 +0530 Subject: [PATCH 02/11] Edit Book: Add a button to easily insert HTML tags. Useful if you want to quickly surround selected text with an arbitrary tag. You can right click the button to get a list of recently used tags. --- imgsrc/code.svg | 1 + resources/images/code.png | Bin 0 -> 1330 bytes src/calibre/ebooks/constants.py | 111 ++++++++++++++++++ src/calibre/gui2/tweak_book/__init__.py | 1 + src/calibre/gui2/tweak_book/boss.py | 6 +- .../gui2/tweak_book/editor/smart/html.py | 9 ++ src/calibre/gui2/tweak_book/editor/text.py | 4 + src/calibre/gui2/tweak_book/editor/widget.py | 35 +++++- src/calibre/gui2/tweak_book/widgets.py | 36 +++++- 9 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 imgsrc/code.svg create mode 100644 resources/images/code.png create mode 100644 src/calibre/ebooks/constants.py diff --git a/imgsrc/code.svg b/imgsrc/code.svg new file mode 100644 index 0000000000..c186a40dcb --- /dev/null +++ b/imgsrc/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/code.png b/resources/images/code.png new file mode 100644 index 0000000000000000000000000000000000000000..3980b0e00c5d5cf69ebe3cd4e3f9b63f5eaf4971 GIT binary patch literal 1330 zcmZ9MdsxzE6vshGgePQE%bE^)p7N44OPP5GAxi-Z^HwC~kF2b=(#=?E4uLh)%u6Yf z<_ztkIrEaH`IJIpy3G<>H(VEfSc%JwpE>O34fpfk&ikDAd){--`~LHJ-jf@-J&Z{B zihx3)h^+9?C`84JZexw?8k(RV5$k=Nuu#;(;!<|j6r)gBBP)~3JsKl#iLX|dI1nAFVJqBv zE6X~Asm6M4`Gq+1P}$9(pIje!<$PwXCPLGaAQTy+Lb<6GZ}sUXope|-T2)GQR%Erc zNzKJ_w3FJuNb5f!8B-4m<`rw%ZHMNC0N5n@d#jo}$(?)uL(WmWOx;NsGnG>FBwwav zzvYVjXx;Zn4cXT*_{#l!LJv1t+0g!j-Je*ckA zt!Np@R~{(1Y+8{aQ~feN52I6_AAY)q0}6+<5)(8nh=`-L_1$N6I!Cy70gVu~ZzxtO zn{gH??H)U9h8fl`Qs98m~q1eTDssmZ$l4nz!Da!_p_CdF|P|t$Z9^sQ9=sGJ$MI^aAngr ztV<+pHg{nw4axQ=U`8@h97moHb4D(Vo3COCdam`0Y7Nq535d7SLdjK7O}YZ$3ze*C z(lX1t<4B7t__`v*(7xdk{G?5;0M2xqP9q}LUoqO^Egvr-u*6)~=W5QQyq`x~i@KA0 zTd&mV@o+w%do_$O>I7)~K3Mvx+3ssYrfk_$kat1#zcIyS{KDH!k=?KI&Pu z-j>v`PC_h95^Zl@VmT=|+vvPJ5??+^pyOy4_qt=cPV5eMsqLfAaa8e>FXq`MeBDIG z11+zqu$nzcGeM4f^JAq`tz;3%linVUn>Y|)F9JIz&a$C|#M9b<_Yts!4K}tQEF>2P zvMIILsSJCBp>ATSUg+y1S9ZLTAxX2DHW%Vj>sbzAwE2bm)-jGR!rS*ywg|IGq*yfr zVdP--{%%r?G%H=E|M&WTG15~SQj{m-qXDN)RXILCS!z zW@WK09m?=eL*Gf)0#W7WHmr}Za+T=G}yl`n~H=} zc_u~H!HtiiHR;EP5~OeCAo5?5wziCxB}iiN1;m5RI`&S(Ghm05OB{jg4NGU;=l&_b z;|62Wo&d6EV)}OjN-o=LV>Jxs*10VtYF5&&HFs4`%j#aa`CZl}Y5yuZLdOk$nzr=@ zFn&pvTzZWT{m6R`u*q!hnHkeroS6stRks2^q0+Wa%w)2gUyeUnR-bi~Ki0~h<@xu! i-s>}cHhOYe-%$?eX!*T2daffw5XIWEJ+y%dNdE`rbd@Lo literal 0 HcmV?d00001 diff --git a/src/calibre/ebooks/constants.py b/src/calibre/ebooks/constants.py new file mode 100644 index 0000000000..b01f44fa6d --- /dev/null +++ b/src/calibre/ebooks/constants.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2014, Kovid Goyal ' + +html5_tags = ( # {{{ +frozenset('''\ +html +head +title +base +link +meta +style +script +noscript +body +section +nav +article +aside +h1 +h2 +h3 +h4 +h5 +h6 +header +footer +address +p +hr +br +pre +dialog +blockquote +ol +ul +li +dl +dt +dd +a +q +cite +em +strong +small +mark +dfn +abbr +time +progress +meter +code +var +samp +kbd +sub +sup +span +i +b +bdo +ruby +rt +rp +ins +del +figure +img +iframe +embed +object +param +video +audio +source +canvas +map +area +table +caption +colgroup +col +tbody +thead +tfoot +tr +td +th +form +fieldset +label +input +button +select +datalist +optgroup +option +textarea +output +details +command +bb +menu +legend +div'''.splitlines())) # }}} diff --git a/src/calibre/gui2/tweak_book/__init__.py b/src/calibre/gui2/tweak_book/__init__.py index bfdc16b610..621c45eff2 100644 --- a/src/calibre/gui2/tweak_book/__init__.py +++ b/src/calibre/gui2/tweak_book/__init__.py @@ -42,6 +42,7 @@ d['folders_for_types'] = {'style':'styles', 'image':'images', 'font':'fonts', 'a d['pretty_print_on_open'] = False d['disable_completion_popup_for_search'] = False d['saved_searches'] = [] +d['insert_tag_mru'] = ['p', 'div', 'li', 'h1', 'h2', 'h3', 'h4', 'em', 'strong', 'td', 'tr'] del d diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 9622757207..67c985b6fd 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -39,7 +39,7 @@ from calibre.gui2.tweak_book.preferences import Preferences from calibre.gui2.tweak_book.search import validate_search_request, run_search from calibre.gui2.tweak_book.widgets import ( RationalizeFolders, MultiSplit, ImportForeign, QuickOpen, InsertLink, - InsertSemantics, BusyCursor) + InsertSemantics, BusyCursor, InsertTag) _diff_dialogs = [] @@ -642,6 +642,10 @@ class Boss(QObject): d = InsertLink(current_container(), edname, initial_text=ed.get_smart_selection(), parent=self.gui) if d.exec_() == d.Accepted: ed.insert_hyperlink(d.href, d.text) + elif action[0] == 'insert_tag': + d = InsertTag(parent=self.gui) + if d.exec_() == d.Accepted: + ed.insert_tag(d.tag) else: ed.action_triggered(action) diff --git a/src/calibre/gui2/tweak_book/editor/smart/html.py b/src/calibre/gui2/tweak_book/editor/smart/html.py index a0b2a1ba77..32aa06d587 100644 --- a/src/calibre/gui2/tweak_book/editor/smart/html.py +++ b/src/calibre/gui2/tweak_book/editor/smart/html.py @@ -232,3 +232,12 @@ class HTMLSmarts(NullSmarts): if text: c.insertText(text) editor.setTextCursor(c) + + def insert_tag(self, editor, name): + text = self.get_smart_selection(editor, update=True) + c = editor.textCursor() + pos = min(c.position(), c.anchor()) + c.insertText('<{0}>{1}'.format(name, text)) + c.setPosition(pos + 1 + len(name)) + editor.setTextCursor(c) + diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index fb949f63eb..ed90f821d7 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -612,6 +612,10 @@ class TextEdit(PlainTextEdit): if hasattr(self.smarts, 'insert_hyperlink'): self.smarts.insert_hyperlink(self, target, text) + def insert_tag(self, tag): + if hasattr(self.smarts, 'insert_tag'): + self.smarts.insert_tag(self, tag) + def keyPressEvent(self, ev): if ev.key() == Qt.Key_X and ev.modifiers() == Qt.AltModifier: if self.replace_possible_unicode_sequence(): diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index 81a54d17a9..175eb4d2b0 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -7,13 +7,14 @@ __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' import unicodedata +from functools import partial from PyQt4.Qt import ( QMainWindow, Qt, QApplication, pyqtSignal, QMenu, qDrawShadeRect, QPainter, QImage, QColor, QIcon, QPixmap, QToolButton) from calibre.gui2 import error_dialog -from calibre.gui2.tweak_book import actions, current_container +from calibre.gui2.tweak_book import actions, current_container, tprefs from calibre.gui2.tweak_book.editor.text import TextEdit def create_icon(text, palette=None, sz=32, divider=2): @@ -65,6 +66,10 @@ def register_text_editor_actions(reg, palette): ac = reg(create_icon(name), text, ('rename_block_tag', name), 'rename-block-tag-' + name, 'Ctrl+%d' % (i + 1), desc) ac.setToolTip(desc) + ac = reg('code', _('Insert &tag'), ('insert_tag',), 'insert-tag', ('Ctrl+<'), _('Insert tag')) + ac.setToolTip(_('

Insert tag

Insert a tag, if some text is selected the tag will be inserted around the selected text')) + + class Editor(QMainWindow): has_line_numbers = True @@ -147,6 +152,23 @@ class Editor(QMainWindow): def insert_hyperlink(self, href, text): self.editor.insert_hyperlink(href, text) + def _build_insert_tag_button_menu(self): + m = self.insert_tag_button.menu() + m.clear() + for name in tprefs['insert_tag_mru']: + m.addAction(name, partial(self.insert_tag, name)) + + def insert_tag(self, name): + self.editor.insert_tag(name) + mru = tprefs['insert_tag_mru'] + try: + mru.remove(name) + except ValueError: + pass + mru.insert(0, name) + tprefs['insert_tag_mru'] = mru + self._build_insert_tag_button_menu() + def undo(self): self.editor.undo() @@ -198,6 +220,7 @@ class Editor(QMainWindow): for x in ('cut', 'copy', 'paste'): b.addAction(actions['editor-%s' % x]) self.tools_bar = b = self.addToolBar(_('Editor tools')) + b.setObjectName('tools_bar') if self.syntax == 'html': b.addAction(actions['fix-html-current']) if self.syntax in {'xml', 'html', 'css'}: @@ -206,8 +229,18 @@ class Editor(QMainWindow): b.addAction(actions['insert-image']) if self.syntax == 'html': b.addAction(actions['insert-hyperlink']) + if self.syntax in {'xml', 'html'}: + b.addAction(actions['insert-tag']) + w = self.insert_tag_button = b.widgetForAction(actions['insert-tag']) + w.setPopupMode(QToolButton.MenuButtonPopup) + w.m = m = QMenu() + w.setMenu(m) + w.setContextMenuPolicy(Qt.CustomContextMenu) + w.customContextMenuRequested.connect(self.insert_tag_button.showMenu) + self._build_insert_tag_button_menu() if self.syntax == 'html': self.format_bar = b = self.addToolBar(_('Format text')) + b.setObjectName('html_format_bar') for x in ('bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript', 'color', 'background-color'): b.addAction(actions['format-text-%s' % x]) ac = b.addAction(QIcon(I('format-text-heading.png')), _('Change paragraph to heading')) diff --git a/src/calibre/gui2/tweak_book/widgets.py b/src/calibre/gui2/tweak_book/widgets.py index e3ddc0d13b..fbb021830f 100644 --- a/src/calibre/gui2/tweak_book/widgets.py +++ b/src/calibre/gui2/tweak_book/widgets.py @@ -22,6 +22,7 @@ from calibre.gui2 import error_dialog, choose_files, choose_save_file, NONE, inf from calibre.gui2.tweak_book import tprefs from calibre.utils.icu import primary_sort_key, sort_key from calibre.utils.matcher import get_char, Matcher +from calibre.gui2.complete2 import EditWithComplete ROOT = QModelIndex() @@ -69,6 +70,39 @@ class Dialog(QDialog): def setup_ui(self): raise NotImplementedError('You must implement this method in Dialog subclasses') +class InsertTag(Dialog): # {{{ + + def __init__(self, parent=None): + Dialog.__init__(self, _('Choose tag name'), 'insert-tag', parent=parent) + + def setup_ui(self): + from calibre.ebooks.constants import html5_tags + self.l = l = QVBoxLayout(self) + self.setLayout(l) + + self.la = la = QLabel(_('Specify the name of the &tag to insert:')) + l.addWidget(la) + + self.tag_input = ti = EditWithComplete(self) + ti.set_separator(None) + ti.all_items = html5_tags | frozenset(tprefs['insert_tag_mru']) + la.setBuddy(ti) + l.addWidget(ti) + l.addWidget(self.bb) + ti.setFocus(Qt.OtherFocusReason) + + @property + def tag(self): + return unicode(self.tag_input.text()).strip() + + @classmethod + def test(cls): + d = cls() + if d.exec_() == d.Accepted: + print (d.tag) + +# }}} + class RationalizeFolders(Dialog): # {{{ TYPE_MAP = ( @@ -852,4 +886,4 @@ class InsertSemantics(Dialog): if __name__ == '__main__': app = QApplication([]) - InsertSemantics.test() + InsertTag.test() From 9eaf93e06c4d54b3d4b8da2c9758914699f13b21 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Apr 2014 11:15:54 +0530 Subject: [PATCH 03/11] calibre GUI style: Dont show an arrow for tool buttons set to instant popup mode --- src/qtcurve/style/qtcurve.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qtcurve/style/qtcurve.cpp b/src/qtcurve/style/qtcurve.cpp index 2c012ef196..3b36a95cf1 100644 --- a/src/qtcurve/style/qtcurve.cpp +++ b/src/qtcurve/style/qtcurve.cpp @@ -8148,7 +8148,8 @@ void Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex drawControl(CE_ToolButtonLabel, &label, painter, widget); if (!(toolbutton->subControls&SC_ToolButtonMenu) && - (toolbutton->features&QStyleOptionToolButton::HasMenu)) + (toolbutton->features&QStyleOptionToolButton::HasMenu && + toolbutton->features & QStyleOptionToolButton::PopupDelay)) { QRect arrow(r.right()-(LARGE_ARR_WIDTH+(etched ? 3 : 2)), r.bottom()-(LARGE_ARR_HEIGHT+(etched ? 4 : 3)), From 313d67ce813cc37a07c797d2c7754d2a39ca7900 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 3 Apr 2014 08:25:06 +0200 Subject: [PATCH 04/11] Update the CC version number. --- src/calibre/devices/smart_device_app/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index cfece66235..6678879b8c 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -226,7 +226,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): PURGE_CACHE_ENTRIES_DAYS = 30 - CURRENT_CC_VERSION = 73 + CURRENT_CC_VERSION = 77 ZEROCONF_CLIENT_STRING = b'calibre wireless device client' From 6fa554f2d33977417044c1b052e08cb39da0fde9 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Thu, 3 Apr 2014 11:32:08 +0200 Subject: [PATCH 05/11] Make check_library process cover files even if .jpg is added to ignored extensions. Fix typo in list name. --- src/calibre/library/check_library.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/check_library.py b/src/calibre/library/check_library.py index 79c52cfe4e..024fe9a5bd 100644 --- a/src/calibre/library/check_library.py +++ b/src/calibre/library/check_library.py @@ -30,7 +30,7 @@ CHECKS = [('invalid_titles', _('Invalid titles'), True, False), ('missing_formats', _('Missing book formats'), False, True), ('extra_formats', _('Extra book formats'), True, False), ('extra_files', _('Unknown files in books'), True, False), - ('missing_covers', _('Missing covers files'), False, True), + ('missing_covers', _('Missing cover files'), False, True), ('extra_covers', _('Cover files not in database'), True, True), ('failed_folders', _('Folders raising exception'), False, False) ] @@ -175,7 +175,8 @@ class CheckLibrary(object): def process_book(self, lib, book_info): (db_path, title_dir, book_id) = book_info filenames = frozenset([f for f in os.listdir(os.path.join(lib, db_path)) - if os.path.splitext(f)[1] not in self.ignore_ext]) + if os.path.splitext(f)[1] not in self.ignore_ext or + f == 'cover.jpg']) book_id = int(book_id) formats = frozenset(filter(self.is_ebook_file, filenames)) book_formats = frozenset([x[0]+'.'+x[1].lower() for x in From 664fe0aba7cf00eb479548b17c27d2e072085203 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Apr 2014 23:41:40 +0530 Subject: [PATCH 06/11] DOCX Input: Fix incorrect numbering being generated for numbered lists in some circumstances. Fixes #1301044 [DOCX conversion of numbered list](https://bugs.launchpad.net/calibre/+bug/1301044) Apparently, list counters are associated with abstract numbering definitions and not numbering instances. Also add support for startOverride. --- src/calibre/ebooks/docx/numbering.py | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/calibre/ebooks/docx/numbering.py b/src/calibre/ebooks/docx/numbering.py index df500ee65c..405c19434f 100644 --- a/src/calibre/ebooks/docx/numbering.py +++ b/src/calibre/ebooks/docx/numbering.py @@ -7,7 +7,7 @@ __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' import re -from collections import Counter +from collections import Counter, defaultdict from lxml.html.builder import OL, UL, SPAN @@ -135,8 +135,9 @@ class Level(object): class NumberingDefinition(object): - def __init__(self, parent=None): + def __init__(self, parent=None, an_id=None): self.levels = {} + self.abstract_numbering_definition_id = an_id if parent is not None: for lvl in XPath('./w:lvl')(parent): try: @@ -146,7 +147,7 @@ class NumberingDefinition(object): self.levels[ilvl] = Level(lvl) def copy(self): - ans = NumberingDefinition() + ans = NumberingDefinition(an_id=self.abstract_numbering_definition_id) for l, lvl in self.levels.iteritems(): ans.levels[l] = lvl.copy() return ans @@ -156,7 +157,8 @@ class Numbering(object): def __init__(self): self.definitions = {} self.instances = {} - self.counters = {} + self.counters = defaultdict(Counter) + self.starts = {} self.pic_map = {} def __call__(self, root, styles, rid_map): @@ -174,13 +176,24 @@ class Numbering(object): if nsl: lazy_load[an_id] = get(nsl[0], 'w:val') else: - nd = NumberingDefinition(an) + nd = NumberingDefinition(an, an_id=an_id) self.definitions[an_id] = nd def create_instance(n, definition): nd = definition.copy() + start_overrides = {} for lo in XPath('./w:lvlOverride')(n): - ilvl = get(lo, 'w:ilvl') + try: + ilvl = int(get(lo, 'w:ilvl')) + except (ValueError, TypeError): + ilvl = None + for so in XPath('./w:startOverride[@w:val]')(lo): + try: + start_override = int(get(so, 'w:val')) + except (TypeError, ValueError): + pass + else: + start_overrides[ilvl] = start_override for lvl in XPath('./w:lvl')(lo)[:1]: nilvl = get(lvl, 'w:ilvl') ilvl = nilvl if ilvl is None else ilvl @@ -188,6 +201,11 @@ class Numbering(object): if alvl is None: alvl = Level() alvl.read_from_xml(lvl, override=True) + for ilvl, so in start_overrides.iteritems(): + try: + nd.levels[ilvl].start = start_override + except KeyError: + pass return nd next_pass = {} @@ -213,7 +231,7 @@ class Numbering(object): self.instances[num_id] = create_instance(n, d) for num_id, d in self.instances.iteritems(): - self.counters[num_id] = Counter({lvl:d.levels[lvl].start for lvl in d.levels}) + self.starts[num_id] = {lvl:d.levels[lvl].start for lvl in d.levels} def get_pstyle(self, num_id, style_id): d = self.instances.get(num_id, None) @@ -236,12 +254,17 @@ class Numbering(object): counter[ilvl] = lvl.start def apply_markup(self, items, body, styles, object_map, images): + seen_instances = set() for p, num_id, ilvl in items: d = self.instances.get(num_id, None) if d is not None: lvl = d.levels.get(ilvl, None) if lvl is not None: - counter = self.counters[num_id] + an_id = d.abstract_numbering_definition_id + counter = self.counters[an_id] + if ilvl not in counter or num_id not in seen_instances: + counter[ilvl] = self.starts[num_id][ilvl] + seen_instances.add(num_id) p.tag = 'li' p.set('value', '%s' % counter[ilvl]) p.set('list-lvl', str(ilvl)) From 45bc2213b7132ab1e5eb7c97f5f36de3e851c4b1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Apr 2014 00:20:34 +0530 Subject: [PATCH 07/11] DOCX Input: Fix para styles for numbering blocks applied via pStyle not being applied --- src/calibre/ebooks/docx/styles.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/ebooks/docx/styles.py b/src/calibre/ebooks/docx/styles.py index 17c891423f..80be3ceb0b 100644 --- a/src/calibre/ebooks/docx/styles.py +++ b/src/calibre/ebooks/docx/styles.py @@ -262,6 +262,9 @@ class Styles(object): if num_id is not None: p.set('calibre_num_id', '%s:%s' % (lvl, num_id)) is_numbering = True + ps = self.numbering.get_para_style(num_id, lvl) + if ps is not None: + parent_styles.append(ps) for attr in ans.all_properties: if not (is_numbering and attr == 'text_indent'): # skip text-indent for lists From 9d19b182dee2177b8293e5582e10b0063a83e6d3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Apr 2014 00:26:07 +0530 Subject: [PATCH 08/11] =?UTF-8?q?Driver=20for=20Tolino=20Vision=20(OS=20X/?= =?UTF-8?q?Linux=20only).=20Fixes=20#1301875=20[tolino=20vision=20doesn?= =?UTF-8?q?=C2=B4t=20work=20with=20calibre](https://bugs.launchpad.net/cal?= =?UTF-8?q?ibre/+bug/1301875)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/calibre/devices/eb600/driver.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index 67a8bb5242..73b0bac313 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -53,9 +53,10 @@ class TOLINO(EB600): name = 'Tolino Shine Device Interface' gui_name = 'Tolino Shine' - description = _('Communicate with the Tolino Shine reader.') + description = _('Communicate with the Tolino Shine and Vision readers') FORMATS = ['epub', 'pdf', 'txt'] - BCD = [0x226] + PRODUCT_ID = EB600.PRODUCT_ID + [0x6033] + BCD = [0x226, 0x9999] VENDOR_NAME = ['DEUTSCHE'] WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['_TELEKOMTOLINO'] From 619792025666c9f5efcb6954adbad722be7a1d4d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Apr 2014 00:33:00 +0530 Subject: [PATCH 09/11] A method to add many custom recipes at once --- src/calibre/web/feeds/recipes/collection.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index 3d4ac952fb..1334017840 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -158,6 +158,27 @@ def add_custom_recipe(title, script): with open(os.path.join(bdir, fname), 'wb') as f: f.write(script) +def add_custom_recipes(script_map): + from calibre.web.feeds.recipes import custom_recipes, \ + custom_recipe_filename + id_ = 1000 + keys = tuple(map(int, custom_recipes.iterkeys())) + if keys: + id_ = max(keys)+1 + with custom_recipes: + for title, script in script_map.iteritems(): + fid = str(id_) + bdir = os.path.dirname(custom_recipes.file_path) + + fname = custom_recipe_filename(fid, title) + if isinstance(script, unicode): + script = script.encode('utf-8') + + custom_recipes[fid] = (title, fname) + + with open(os.path.join(bdir, fname), 'wb') as f: + f.write(script) + def remove_custom_recipe(id_): from calibre.web.feeds.recipes import custom_recipes From 3a193a24aa157ce04382d1127a2fa3834e51313c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Apr 2014 01:01:05 +0530 Subject: [PATCH 10/11] ... --- src/calibre/web/feeds/recipes/collection.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index 1334017840..f7d9dd25f0 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -178,6 +178,7 @@ def add_custom_recipes(script_map): with open(os.path.join(bdir, fname), 'wb') as f: f.write(script) + id_ += 1 def remove_custom_recipe(id_): From 49ba0a991bb2c486e4b7ba7ed9b47bb0c08b3d4f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Apr 2014 01:04:53 +0530 Subject: [PATCH 11/11] DRY --- src/calibre/web/feeds/recipes/collection.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index f7d9dd25f0..5cfbb7899c 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -140,23 +140,7 @@ def update_custom_recipe(id_, title, script): def add_custom_recipe(title, script): - from calibre.web.feeds.recipes import custom_recipes, \ - custom_recipe_filename - id_ = 1000 - keys = tuple(map(int, custom_recipes.iterkeys())) - if keys: - id_ = max(keys)+1 - id_ = str(id_) - bdir = os.path.dirname(custom_recipes.file_path) - - fname = custom_recipe_filename(id_, title) - if isinstance(script, unicode): - script = script.encode('utf-8') - - custom_recipes[id_] = (title, fname) - - with open(os.path.join(bdir, fname), 'wb') as f: - f.write(script) + add_custom_recipes({title:script}) def add_custom_recipes(script_map): from calibre.web.feeds.recipes import custom_recipes, \