From 24bfcbd7e6a89aade50cdacb5b1d7ca16d757ff6 Mon Sep 17 00:00:00 2001 From: Translators <> Date: Fri, 28 Dec 2012 05:10:20 +0000 Subject: [PATCH 01/67] Launchpad automatic translations update. --- src/calibre/translations/af.po | 516 +++++++++++++----------- src/calibre/translations/ar.po | 516 +++++++++++++----------- src/calibre/translations/ast.po | 516 +++++++++++++----------- src/calibre/translations/az.po | 516 +++++++++++++----------- src/calibre/translations/ber.po | 516 +++++++++++++----------- src/calibre/translations/bg.po | 519 +++++++++++++----------- src/calibre/translations/bn.po | 516 +++++++++++++----------- src/calibre/translations/br.po | 516 +++++++++++++----------- src/calibre/translations/bs.po | 516 +++++++++++++----------- src/calibre/translations/ca.po | 568 ++++++++++++++------------ src/calibre/translations/cs.po | 551 +++++++++++++++----------- src/calibre/translations/cy.po | 516 +++++++++++++----------- src/calibre/translations/da.po | 541 ++++++++++++++----------- src/calibre/translations/de.po | 568 ++++++++++++++------------ src/calibre/translations/el.po | 526 +++++++++++++----------- src/calibre/translations/en_AU.po | 516 +++++++++++++----------- src/calibre/translations/en_CA.po | 516 +++++++++++++----------- src/calibre/translations/en_GB.po | 551 +++++++++++++++----------- src/calibre/translations/eo.po | 516 +++++++++++++----------- src/calibre/translations/es.po | 572 +++++++++++++++------------ src/calibre/translations/et.po | 516 +++++++++++++----------- src/calibre/translations/eu.po | 531 ++++++++++++++----------- src/calibre/translations/fa.po | 516 +++++++++++++----------- src/calibre/translations/fi.po | 516 +++++++++++++----------- src/calibre/translations/fo.po | 516 +++++++++++++----------- src/calibre/translations/fr.po | 568 ++++++++++++++------------ src/calibre/translations/fr_CA.po | 516 +++++++++++++----------- src/calibre/translations/fur.po | 516 +++++++++++++----------- src/calibre/translations/gl.po | 533 ++++++++++++++----------- src/calibre/translations/gu.po | 516 +++++++++++++----------- src/calibre/translations/he.po | 516 +++++++++++++----------- src/calibre/translations/hi.po | 516 +++++++++++++----------- src/calibre/translations/him.po | 516 +++++++++++++----------- src/calibre/translations/hr.po | 531 ++++++++++++++----------- src/calibre/translations/hu.po | 551 +++++++++++++++----------- src/calibre/translations/id.po | 516 +++++++++++++----------- src/calibre/translations/is.po | 516 +++++++++++++----------- src/calibre/translations/it.po | 550 +++++++++++++++----------- src/calibre/translations/ja.po | 555 ++++++++++++++------------ src/calibre/translations/jv.po | 516 +++++++++++++----------- src/calibre/translations/ka.po | 516 +++++++++++++----------- src/calibre/translations/kn.po | 516 +++++++++++++----------- src/calibre/translations/ko.po | 529 ++++++++++++++----------- src/calibre/translations/ku.po | 516 +++++++++++++----------- src/calibre/translations/lt.po | 516 +++++++++++++----------- src/calibre/translations/ltg.po | 516 +++++++++++++----------- src/calibre/translations/lv.po | 524 +++++++++++++----------- src/calibre/translations/mk.po | 516 +++++++++++++----------- src/calibre/translations/ml.po | 516 +++++++++++++----------- src/calibre/translations/mr.po | 516 +++++++++++++----------- src/calibre/translations/ms.po | 516 +++++++++++++----------- src/calibre/translations/nb.po | 531 ++++++++++++++----------- src/calibre/translations/nds.po | 531 ++++++++++++++----------- src/calibre/translations/nl.po | 572 +++++++++++++++------------ src/calibre/translations/nn.po | 516 +++++++++++++----------- src/calibre/translations/oc.po | 516 +++++++++++++----------- src/calibre/translations/pa.po | 516 +++++++++++++----------- src/calibre/translations/pl.po | 551 +++++++++++++++----------- src/calibre/translations/pt.po | 533 ++++++++++++++----------- src/calibre/translations/pt_BR.po | 531 ++++++++++++++----------- src/calibre/translations/ro.po | 551 +++++++++++++++----------- src/calibre/translations/ru.po | 568 ++++++++++++++------------ src/calibre/translations/sc.po | 516 +++++++++++++----------- src/calibre/translations/si.po | 516 +++++++++++++----------- src/calibre/translations/sk.po | 551 +++++++++++++++----------- src/calibre/translations/sl.po | 531 ++++++++++++++----------- src/calibre/translations/sq.po | 516 +++++++++++++----------- src/calibre/translations/sr.po | 551 +++++++++++++++----------- src/calibre/translations/sr@latin.po | 516 +++++++++++++----------- src/calibre/translations/sv.po | 559 ++++++++++++++------------ src/calibre/translations/ta.po | 516 +++++++++++++----------- src/calibre/translations/te.po | 516 +++++++++++++----------- src/calibre/translations/th.po | 516 +++++++++++++----------- src/calibre/translations/tr.po | 528 ++++++++++++++----------- src/calibre/translations/uk.po | 570 ++++++++++++++------------ src/calibre/translations/ur.po | 516 +++++++++++++----------- src/calibre/translations/vi.po | 524 +++++++++++++----------- src/calibre/translations/wa.po | 516 +++++++++++++----------- src/calibre/translations/yi.po | 516 +++++++++++++----------- src/calibre/translations/zh_CN.po | 549 ++++++++++++++----------- src/calibre/translations/zh_HK.po | 516 +++++++++++++----------- src/calibre/translations/zh_TW.po | 554 +++++++++++++++----------- 82 files changed, 23899 insertions(+), 19422 deletions(-) diff --git a/src/calibre/translations/af.po b/src/calibre/translations/af.po index fdb8a349bf..b313494f52 100644 --- a/src/calibre/translations/af.po +++ b/src/calibre/translations/af.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: calibre\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-21 05:12+0000\n" +"POT-Creation-Date: 2012-12-28 04:12+0000\n" "PO-Revision-Date: 2012-08-14 16:03+0000\n" "Last-Translator: Albé Theunissen \n" "Language-Team: Afrikaans \n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Launchpad-Export-Date: 2012-12-22 04:36+0000\n" +"X-Launchpad-Export-Date: 2012-12-28 04:49+0000\n" "X-Generator: Launchpad (build 16378)\n" #: /home/kovid/work/calibre/src/calibre/customize/__init__.py:56 @@ -28,8 +28,8 @@ msgstr "Doen absolute niks" #: /home/kovid/work/calibre/src/calibre/db/cache.py:120 #: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:376 #: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:377 -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:100 -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:101 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:114 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:115 #: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:74 #: /home/kovid/work/calibre/src/calibre/devices/kindle/driver.py:77 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:667 @@ -53,6 +53,8 @@ msgstr "Doen absolute niks" #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/html_input.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/lrf_output.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdb_input.py:27 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:28 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/rtf_input.py:289 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/rtf_input.py:291 #: /home/kovid/work/calibre/src/calibre/ebooks/epub/periodical.py:140 @@ -131,11 +133,9 @@ msgstr "Doen absolute niks" #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:174 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/palmdoc/writer.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ztxt/writer.py:27 -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:108 -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/writer.py:109 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:447 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:455 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:166 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:411 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:414 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:167 @@ -147,20 +147,20 @@ msgstr "Doen absolute niks" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:145 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:1416 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:1419 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:55 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:60 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:71 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:79 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:128 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:47 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:825 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:380 #: /home/kovid/work/calibre/src/calibre/gui2/email.py:193 #: /home/kovid/work/calibre/src/calibre/gui2/email.py:208 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:440 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1106 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:439 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1103 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1319 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1322 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1325 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1328 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1416 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1413 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:85 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:250 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:261 @@ -1248,8 +1248,8 @@ msgstr "Voeg boeke toe tot toestel se metadatalys…" #: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:352 #: /home/kovid/work/calibre/src/calibre/devices/bambook/driver.py:354 -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:115 -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:126 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:129 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:140 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:440 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:472 #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:615 @@ -1393,27 +1393,31 @@ msgstr "" msgid "Communicate with the Hanvon N520 eBook reader." msgstr "Kommunikeer met die Hanvon N520 eBoek-leser." -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:47 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:48 +msgid "Communicate with the Kibano eBook reader." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:61 msgid "Communicate with The Book reader." msgstr "Kommunikeer met The Book-leser." -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:59 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:73 msgid "Communicate with the Libre Air reader." msgstr "Kommunikeer met die Libre Air-leser." -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:72 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:86 msgid "Communicate with the SpringDesign Alex eBook reader." msgstr "Kommunikeer met die SpringDesign Alex eBoek-leser" -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:132 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:146 msgid "Communicate with the Azbooka" msgstr "Kommunikeer met die Azbooka" -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:151 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:165 msgid "Communicate with the Elonex EB 511 eBook reader." msgstr "Kommunikeer met die Elonex EB 511 eBoek-leser." -#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:171 +#: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:185 msgid "Communicate with the Cybook Odyssey eBook reader." msgstr "Kommunikeer met die Cybook Odyssey eBoek-leser." @@ -1647,7 +1651,7 @@ msgid "" msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/kobo/driver.py:646 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:393 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:404 msgid "Not Implemented" msgstr "Nie geïmplementeer nie" @@ -2200,35 +2204,35 @@ msgstr "Kommunikeer met die Samsung SNE eBoek-leser." msgid "Communicate with the Teclast K3/K5 reader." msgstr "Kommunikeer met die Teclast K3/K5-leser." -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:37 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:38 msgid "Communicate with the Newsmy reader." msgstr "Kommunikeer met die Newsmy-leser." -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:48 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:49 msgid "Communicate with the Archos reader." msgstr "Kommunikeer met die Archos-leser." -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:58 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:59 msgid "Communicate with the Pico reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:70 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:71 msgid "Communicate with the iPapyrus reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:81 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:82 msgid "Communicate with the Sovos reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:91 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:92 msgid "Communicate with the Sunstech EB700 reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:102 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:103 msgid "Communicate with the Stash W950 reader." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:114 +#: /home/kovid/work/calibre/src/calibre/devices/teclast/driver.py:115 msgid "Communicate with the Wexler reader." msgstr "" @@ -2427,6 +2431,7 @@ msgid "There is insufficient free space on the storage card" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:210 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/render/from_html.py:229 #, python-format msgid "Rendered %s" msgstr "" @@ -2964,55 +2969,57 @@ msgstr "" msgid "Use the new PDF conversion engine." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:71 -#, python-format +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:52 msgid "" -"The unit of measure. Default is inch. Choices are %s Note: This does not " -"override the unit for margins!" +"Normally, the PDF page size is set by the output profile chosen under page " +"options. This option will cause the page size settings under PDF Output to " +"override the size specified by the output profile." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:76 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:58 +#, python-format +msgid "" +"The unit of measure for page sizes. Default is inch. Choices are %s Note: " +"This does not override the unit for margins!" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:63 #, python-format msgid "" "The size of the paper. This size will be overridden when a non default " "output profile is used. Default is letter. Choices are %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:80 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:67 msgid "" "Custom size of the document. Use the form widthxheight EG. `123x321` to " "specify the width and height. This overrides any specified paper-size." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:85 -#, python-format -msgid "The orientation of the page. Default is portrait. Choices are %s" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:89 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:72 msgid "" "Preserve the aspect ratio of the cover, instead of stretching it to fill the " "full first page of the generated pdf." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:94 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:77 msgid "The font family used to render serif fonts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:97 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:80 msgid "The font family used to render sans-serif fonts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:100 -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:104 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:83 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:87 msgid "The font family used to render monospaced fonts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:107 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:90 msgid "The default font size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:110 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:93 msgid "The default font size for monospaced text" msgstr "" @@ -4041,8 +4048,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:76 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/quickview.py:85 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:222 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:84 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1111 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:83 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1108 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:150 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:162 #: /home/kovid/work/calibre/src/calibre/gui2/store/search/models.py:39 @@ -4054,14 +4061,14 @@ msgid "Title" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:770 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:86 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1112 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:85 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1109 #: /home/kovid/work/calibre/src/calibre/gui2/store/stores/mobileread/models.py:23 msgid "Author(s)" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:771 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:91 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:90 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:159 msgid "Publisher" msgstr "" @@ -4094,13 +4101,13 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:535 #: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:842 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:60 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:92 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:91 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:70 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:161 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:982 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1228 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:201 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:780 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:792 msgid "Tags" msgstr "" @@ -4109,7 +4116,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/quickview.py:89 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:60 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/template_dialog.py:224 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:93 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:92 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:70 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:163 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:307 @@ -4121,7 +4128,7 @@ msgstr[0] "" msgstr[1] "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:778 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:95 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:94 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:164 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:127 msgid "Languages" @@ -4133,7 +4140,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:782 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:183 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:89 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:88 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:150 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:305 msgid "Published" @@ -4253,48 +4260,52 @@ msgstr "" msgid "Cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:491 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:508 msgid "Downloads metadata and covers from Amazon" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:501 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:518 msgid "US" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:502 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:519 msgid "France" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:503 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:520 msgid "Germany" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:504 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:521 msgid "UK" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:505 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:522 msgid "Italy" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:506 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:523 msgid "Japan" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:507 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:524 msgid "Spain" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:511 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:525 +msgid "Brazil" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:529 msgid "Amazon website to use:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:512 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:530 msgid "" "Metadata from Amazon will be fetched using this country's Amazon website." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:753 +#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:775 msgid "Amazon timed out. Try again later." msgstr "" @@ -4485,11 +4496,11 @@ msgid "HTML TOC generation options." msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/jacket.py:185 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:90 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:89 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:71 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:160 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:176 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:778 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:790 msgid "Rating" msgstr "" @@ -4808,7 +4819,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/annotate.py:120 #: /home/kovid/work/calibre/src/calibre/gui2/actions/catalog.py:38 #: /home/kovid/work/calibre/src/calibre/gui2/actions/convert.py:107 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:175 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:228 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:75 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:192 #: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:256 @@ -4838,32 +4849,32 @@ msgstr "" msgid "Select book files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:178 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:189 msgid "Adding" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:179 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:190 msgid "Creating book records from ISBNs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:270 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:319 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:281 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:330 msgid "Uploading books to device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:290 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:301 msgid "Supported books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:293 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:304 msgid "Select books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:331 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:342 msgid "Merged some books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:332 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:343 #, python-format msgid "" "The following %d duplicate books were found and incoming book formats were " @@ -4871,21 +4882,21 @@ msgid "" "settings:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:354 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:365 msgid "Failed to read metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:355 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:366 msgid "Failed to read metadata from the following" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:376 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:381 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:400 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:387 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:392 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:411 msgid "Add to library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:381 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:392 #: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:137 #: /home/kovid/work/calibre/src/calibre/gui2/actions/store.py:87 #: /home/kovid/work/calibre/src/calibre/gui2/actions/store.py:106 @@ -4896,32 +4907,32 @@ msgstr "" msgid "No book selected" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:394 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:405 msgid "" "The following books are virtual and cannot be added to the calibre library:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:400 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:411 msgid "No book files found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:406 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:417 msgid "Downloading books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:407 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:418 msgid "Downloading books from device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:437 msgid "Could not download files from the device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:429 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:440 msgid "Could not download some files from the device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:433 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:444 msgid "Could not download files" msgstr "" @@ -5038,6 +5049,7 @@ msgid "No existing calibre library found at %s" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:152 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:160 msgid "Choose Library" msgstr "" @@ -5208,7 +5220,7 @@ msgid "" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:423 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:197 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:250 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:975 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:1007 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:114 @@ -5236,7 +5248,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:534 #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:539 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:225 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:278 #: /home/kovid/work/calibre/src/calibre/gui2/actions/save_to_disk.py:91 #: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:1016 msgid "Not allowed" @@ -5286,7 +5298,7 @@ msgstr "" msgid "Empty output file, probably the conversion process crashed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:84 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:86 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:401 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:405 #: /home/kovid/work/calibre/src/calibre/gui2/auto_add.py:221 @@ -5294,57 +5306,83 @@ msgstr "" msgid "%(title)s by %(author)s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:137 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/toolbar.py:59 +msgid "Choose library" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:138 +msgid "Library &path:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:146 +msgid "Browse for library" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:149 +msgid "&Delete after copy" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:172 msgid "Copy to library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:173 msgid "Copy selected books to the specified library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:165 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:206 msgid "(delete after copy)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:174 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:210 +msgid "Choose library by path..." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:220 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:227 msgid "Cannot copy" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:179 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:221 +msgid "Cannot copy to current library." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:232 msgid "No library" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:180 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:233 #, python-format msgid "No library found at %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:182 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:186 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:235 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:239 msgid "Copying" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:197 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:250 msgid "Could not copy books: " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:201 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:254 #, python-format msgid "Copied %(num)d books to %(loc)s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:258 msgid "Auto merged" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:206 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:259 msgid "" "Some books were automatically merged into existing records in the target " "library. Click Show details to see which ones. This behavior is controlled " "by the Auto merge option in Preferences->Adding books." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:226 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:279 msgid "" "You cannot use other libraries while using the environment variable " "CALIBRE_OVERRIDE_DATABASE_PATH." @@ -5949,7 +5987,7 @@ msgid "Click the show details button to see which ones." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:785 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:797 msgid "Show book details" msgstr "" @@ -6491,9 +6529,9 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_editor_ui.py:140 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor_ui.py:78 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor_ui.py:80 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:277 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:279 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:280 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:283 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:285 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:286 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/adding_ui.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:166 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:167 @@ -6554,7 +6592,7 @@ msgid "Click to open" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:180 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:834 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:846 msgid "Ids" msgstr "" @@ -6564,7 +6602,7 @@ msgid "Book %(sidx)s of %(series)s" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:233 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1115 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1112 msgid "Collections" msgstr "" @@ -6675,7 +6713,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/page_setup_ui.py:124 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdb_output_ui.py:47 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_input_ui.py:43 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:100 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:114 #: /home/kovid/work/calibre/src/calibre/gui2/convert/pmlz_output_ui.py:46 #: /home/kovid/work/calibre/src/calibre/gui2/convert/rb_output_ui.py:33 #: /home/kovid/work/calibre/src/calibre/gui2/convert/search_and_replace_ui.py:145 @@ -6833,7 +6871,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/check_library.py:342 #: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/models.py:21 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/bookmarkmanager.py:90 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:258 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:260 msgid "Name" msgstr "" @@ -8046,48 +8084,59 @@ msgstr "" msgid "PDF Output" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:101 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:115 +msgid "" +"Note: The paper size settings below only take effect if you enable " +"the \"Override\" checkbox below. Otherwise the size from the output profile " +"will be used." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:116 +msgid "&Override paper size set in output profile" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:117 msgid "&Paper Size:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:102 -msgid "&Orientation:" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:103 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:118 msgid "&Custom size:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:104 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:119 +msgid "&Unit:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:120 msgid "Preserve &aspect ratio of cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:105 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:121 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:374 msgid "Se&rif family:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:106 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:122 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:375 msgid "&Sans family:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:107 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:123 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:376 msgid "&Monospace family:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:108 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:124 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:381 msgid "S&tandard font:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:109 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:125 msgid "Default font si&ze:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:110 -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:112 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:126 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:128 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:378 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:380 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:395 @@ -8097,18 +8146,11 @@ msgstr "" msgid " px" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:111 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:127 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:379 msgid "Monospace &font size:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/pdf_output_ui.py:113 -msgid "" -"Note: The paper size settings below only take effect if you have set " -"the output profile to the default output profile. Otherwise the output " -"profile will override these settings." -msgstr "" - #: /home/kovid/work/calibre/src/calibre/gui2/convert/pml_output.py:14 msgid "PMLZ Output" msgstr "" @@ -9245,6 +9287,14 @@ msgstr "" msgid "Reset author to Unknown" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:48 +msgid "Set the series of the new books to:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_empty_book.py:60 +msgid "Reset series" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/add_from_isbn.py:72 msgid "Some invalid ISBNs" msgstr "" @@ -9706,8 +9756,8 @@ msgid "Location" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:77 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:88 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1113 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:87 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1110 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:35 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:76 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:365 @@ -10896,7 +10946,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/store/config/chooser/chooser_widget_ui.py:80 #: /home/kovid/work/calibre/src/calibre/gui2/store/stores/mobileread/store_dialog_ui.py:76 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:652 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:282 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:284 msgid "Search" msgstr "" @@ -11234,7 +11284,7 @@ msgid "never delete" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:230 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:273 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:279 msgid " days" msgstr "" @@ -11819,7 +11869,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:169 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:180 -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:265 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:271 msgid "Switch to Advanced mode" msgstr "" @@ -11893,39 +11943,39 @@ msgid "" "Add/Update recipe button. Continue?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:257 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:263 msgid "Add custom news source" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:258 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:264 msgid "Available user recipes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:259 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:265 msgid "Add/Update &recipe" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:260 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:266 msgid "&Remove recipe" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:261 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:267 msgid "&Share recipe" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:262 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:268 msgid "S&how recipe files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:263 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:269 msgid "Customize &builtin recipe" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:264 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:270 msgid "&Load recipe from file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:266 +#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:272 msgid "" "\n" "

Alap szintű " -"hírösszeállítás létrehozása hírforrások hozzáadásával.
Legtöbb esetben " -"a „Haladó mód”-ot is használnia kell a letöltés " -"testreszabásához.

" +"right:0px; -qt-block-indent:0; text-indent:0px;\">Alapszintű hírösszeállítás " +"létrehozása hírcsatornák hozzáadásával.
Legtöbb esetben a „Haladó mód”-" +"ot is használnia kell a letöltés testreszabásához.

" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:276 msgid "Recipe &title:" @@ -13230,36 +13259,36 @@ msgstr "A legrégebbi letöltendő cikk" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:280 msgid "&Max. number of articles per feed:" -msgstr "A hírforrásban szereplő cikkek &maximális száma:" +msgstr "A hírcsatorna cikkeinek &maximális száma:" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:281 msgid "Maximum number of articles to download per feed." -msgstr "A hírforrásban letöltendő cikkek maximális száma:" +msgstr "A hírcsatornából letöltendő cikkek maximális száma:" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:282 msgid "Feeds in recipe" -msgstr "Hírforrások a hírösszeállításban" +msgstr "Hírcsatornák a hírösszeállításban" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:284 msgid "Remove feed from recipe" -msgstr "Hírforrás eltávolítása a hírösszeállításból" +msgstr "Hírcsatorna eltávolítása a hírösszeállításból" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:287 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:290 msgid "Add feed to recipe" -msgstr "Hírforrás hozzáadása a hírösszeállításhoz" +msgstr "Hírcsatorna hozzáadása a hírösszeállításhoz" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:288 msgid "&Feed title:" -msgstr "&Hírforrás címe:" +msgstr "&Hírcsatorna címe:" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:289 msgid "Feed &URL:" -msgstr "Hírforrás URL-je:" +msgstr "Hírcsatorna URL-je:" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:291 msgid "&Add feed" -msgstr "Hírforrás hozzácadása" +msgstr "&Hírcsatorna hozzácadása" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles_ui.py:292 msgid "" @@ -13314,7 +13343,7 @@ msgstr "Az e-book letöltése nem sikerült" #: /home/kovid/work/calibre/src/calibre/gui2/email.py:125 #, python-format msgid "Email %(name)s to %(to)s" -msgstr "%(name)s elküldése e-mailben ide: %(to)s" +msgstr "%(name)s elküldése emailben ide: %(to)s" #: /home/kovid/work/calibre/src/calibre/gui2/email.py:144 msgid "News:" @@ -13345,24 +13374,24 @@ msgstr "%s formátumban." #: /home/kovid/work/calibre/src/calibre/gui2/email.py:225 msgid "Sending email to" -msgstr "E-mail küldése ide:" +msgstr "Email küldése ide:" #: /home/kovid/work/calibre/src/calibre/gui2/email.py:256 msgid "Auto convert the following books before sending via email?" msgstr "" -"Az e-mailben történő küldés előtt kívánja automatikusan konvertálni a " -"kijelölt könyveket?" +"Az eailben történő küldés előtt kívánja automatikusan konvertálni a kijelölt " +"könyveket?" #: /home/kovid/work/calibre/src/calibre/gui2/email.py:263 msgid "" "Could not email the following books as no suitable formats were found:" msgstr "" -"Nem lehet elküldeni e-mailben a következő könyveket, mert nem léteznek a " +"Nem lehet elküldeni emailben a következő könyveket, mert nem léteznek a " "megadott formátumokban:" #: /home/kovid/work/calibre/src/calibre/gui2/email.py:269 msgid "Failed to email book" -msgstr "A könyv e-mailben történő elküldése meghiúsult" +msgstr "A könyv emailben történő elküldése meghiúsult" #: /home/kovid/work/calibre/src/calibre/gui2/email.py:272 msgid "sent" @@ -13477,7 +13506,7 @@ msgstr "Reguláris kifejezés (?P)" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:123 msgid "Choose a font family" -msgstr "" +msgstr "Betűtípus kiválasztása" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:136 #, python-format @@ -13490,7 +13519,7 @@ msgstr "Betűtípus kiválasztása" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:195 msgid "Add &fonts" -msgstr "" +msgstr "&Hozzáadás" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:199 msgid "Choose a font family from the list below:" @@ -13498,11 +13527,11 @@ msgstr "Válasszon betűtípust az alábbi listából:" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:205 msgid "Find Next" -msgstr "" +msgstr "Következő találat" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:208 msgid "Find Previous" -msgstr "" +msgstr "Előző találat" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:258 #: /home/kovid/work/calibre/src/calibre/gui2/keyboard.py:377 @@ -13521,15 +13550,15 @@ msgstr "Nincs" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:264 msgid "Select font files" -msgstr "" +msgstr "Betűfájlok kiválasztása" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:264 msgid "TrueType/OpenType Fonts" -msgstr "" +msgstr "TrueType/OpenType betűtípusok" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:274 msgid "Corrupt font" -msgstr "" +msgstr "Hibás betűkészlet" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:275 #, python-format @@ -13538,12 +13567,12 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:294 msgid "Added fonts" -msgstr "" +msgstr "Hozzáadott betűtípusok" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:295 #, python-format msgid "Added font families: %s" -msgstr "" +msgstr "Hozzáadott betűtípusok: %s" #: /home/kovid/work/calibre/src/calibre/gui2/font_family_chooser.py:320 msgid "Choose &font family" @@ -14066,7 +14095,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/main.py:75 msgid "Path too long" -msgstr "" +msgstr "Elérési út túl hosszú" #: /home/kovid/work/calibre/src/calibre/gui2/main.py:76 #, python-format @@ -14519,7 +14548,7 @@ msgstr "%s feldolgozva" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/config.py:61 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:124 msgid "Downloaded metadata fields" -msgstr "Letöltendő metaadat mezők" +msgstr "Letöltött metaadat mezők" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:31 msgid "Edit Metadata" @@ -14598,7 +14627,7 @@ msgstr "Sorozatok törlése" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:192 msgid "Clear all tags" -msgstr "" +msgstr "Minden címke törlése" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:201 msgid "Clear Ids" @@ -14950,6 +14979,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/adding_ui.py:159 msgid "Automatically &convert added books to the current output format" msgstr "" +"A hozzáadott &könyvek automatikus konvertálása a jelenlegi kimeneti " +"formátumba" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/adding_ui.py:160 msgid "The Add &Process" @@ -15116,11 +15147,11 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:165 msgid "Preferred &input format order:" -msgstr "&Bemeneti formátumok előnyberészesítési sorrendje:" +msgstr "&Bemeneti formátumok előnyben részesítési sorrendje:" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:168 msgid "Use internal &viewer for:" -msgstr "A beépített ol&vasó program használata a következőkhöz:" +msgstr "A beépített ol&vasóprogram használata a következőkhöz:" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:169 msgid "Reset all disabled &confirmation dialogs" @@ -15128,7 +15159,7 @@ msgstr "&Minden letiltott megerősítő párbeszédablak engedélyezése" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/coloring.py:26 msgid "All Columns" -msgstr "" +msgstr "Minden oszlop" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/coloring.py:32 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/coloring.py:37 @@ -15855,6 +15886,9 @@ msgid "" "A device (%s) is already detected by calibre. If you wish to debug the " "detection of another device, first disconnect this device." msgstr "" +"Egy (%s) eszközt már felismert a calibre. Ha szeretné egy másik eszköz " +"kapcsolódását ellenőrizni, ezt a felismert eszközt először le kell " +"választania." #: /home/kovid/work/calibre/src/calibre/gui2/preferences/device_debug.py:57 msgid "Debugging failed" @@ -15911,17 +15945,17 @@ msgid "" "automatically sent for downloaded news to all email addresses that have Auto-" "send checked." msgstr "" -"A calibre könyveket tud továbbítani Önnek e-mailen keresztül. Ha bejelöli az " -"Automatikus küldést, akkor az e-mailek a letöltött hírekkel együtt " +"A calibre könyveket tud továbbítani Önnek emaiben. Ha bejelöli az " +"Automatikus küldést, akkor az emailek a letöltött hírekkel együtt " "postázódnak az összes címre." #: /home/kovid/work/calibre/src/calibre/gui2/preferences/email_ui.py:67 msgid "Add an email address to which to send books" -msgstr "E-mail cím hozzáadása könyvküldéshez" +msgstr "Email cím hozzáadása könyvküldéshez" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/email_ui.py:68 msgid "&Add email" -msgstr "E-mail &hozzáadása" +msgstr "Email &hozzáadása" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/email_ui.py:69 msgid "Make &default" @@ -15929,11 +15963,11 @@ msgstr "Legyen &alapértelmezett" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/email_ui.py:70 msgid "&Remove email" -msgstr "E-mail &eltávolítása" +msgstr "Email &eltávolítása" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:28 msgid "Email" -msgstr "E-mail" +msgstr "Email" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:28 msgid "Subject" @@ -15941,7 +15975,7 @@ msgstr "Tárgy" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:29 msgid "Alias" -msgstr "" +msgstr "Álnév" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:29 msgid "Auto send" @@ -15950,7 +15984,7 @@ msgstr "Auto küld" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:34 msgid "Formats to email. The first matching format will be sent." msgstr "" -"E-mailben elküldendő formátum. Az első létező formátum kerül elküldésre." +"Emailben elküldendő formátum. Az első létező formátum kerül elküldésre." #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:35 msgid "" @@ -15958,7 +15992,7 @@ msgid "" "used for the subject. Also, the same templates used for \"Save to disk\" " "such as {title} and {author_sort} can be used here." msgstr "" -"Az e-mail tárgya. Ha üres, akkor a cím lesz a tárgy. A „Mentés lemezre” " +"Az email tárgya. Ha üres, akkor a cím lesz a tárgy. A „Mentés lemezre” " "sablonjait, mint a {title} vagy az {author_sort} is használhatja." #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:39 @@ -15967,8 +16001,8 @@ msgid "" "address (provided it is in one of the listed formats)." msgstr "" "Ha be van jelölve, akkor a letöltött hírek automatikusan
el lesznek " -"küldve e-mailben erre a címre (ha létezik a „Formátumok” oszlopban " -"beírtaknak megfelelő)" +"küldve emailben erre a címre (ha létezik a „Formátumok” oszlopban beírtaknak " +"megfelelő)" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:42 msgid "Friendly name to use for this email address" @@ -15976,7 +16010,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/emailp.py:128 msgid "new email address" -msgstr "új e-mail cím" +msgstr "új email cím" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/ignored_devices.py:24 msgid "" @@ -16138,7 +16172,7 @@ msgstr "Mozgatás lefelé" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:267 msgid "Default author link template:" -msgstr "Alapértelmezett szerzőhivatkozás minta:" +msgstr "Alapértelmezett szerzőhivatkozás sablon:" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:268 msgid "" @@ -16151,15 +16185,15 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:273 msgid "Show &cover in the book details panel" -msgstr "" +msgstr "&Borító megjelenítése a könyv részletei panelen" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:274 msgid "Show the size of the book's cover in pixels" -msgstr "" +msgstr "A borítókép méretét mutatja meg képpontban" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:275 msgid "Show cover &size" -msgstr "" +msgstr "Borítóméret &mutatása" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:277 msgid "" @@ -16205,7 +16239,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:292 msgid "Categories not to partition:" -msgstr "" +msgstr "Csoportosítás nélküli kategóriák:" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:293 msgid "" @@ -16245,7 +16279,7 @@ msgstr "Ha a borítóböngésző külön ablakban van, az legyen &teljes képern #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:305 #, python-format msgid "You can press the %s keys to toggle full screen mode." -msgstr "A következő billentyűkkel válthat teljes képernyős üzemmódra: %s" +msgstr "A következő billentyűkkel válthat teljesképernyős üzemmódra: %s" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:231 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/tweaks_ui.py:123 @@ -16486,7 +16520,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc_ui.py:77 msgid "Debug &device detection" -msgstr "Eszközkapcsolódás ellenőrzése" +msgstr "Eszközkapcsolódás &ellenőrzése" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc_ui.py:78 msgid "Get information to setup the &user defined device" @@ -16716,7 +16750,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:368 #, python-format msgid "Are you sure you want to remove the plugin: %s?" -msgstr "" +msgstr "Biztosan törli a következő bővítményt: %s?" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:373 msgid "Plugin {0} successfully removed" @@ -16791,7 +16825,7 @@ msgid "" "particular book does not have some metadata, the variable will be replaced " "by the empty string." msgstr "" -"A lenti mintát pontosítva be tudja állítani a lemezre történő mentés mappa " +"A lenti sablont pontosítva be tudja állítani a lemezre történő mentés mappa " "és fájlneveit. A „/” karakter jeleni az almappákat. A használható metaadat-" "változókat a listamezőben láthatja. Ha az adott könyv nem tartalmazza a " "megadott metaadatot, akkor az egy üres karakterlánccal lesz helyettesítve." @@ -16944,7 +16978,7 @@ msgstr "A keresés elkezdődik, ahogy beírja a szöveget" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:169 msgid "Unaccented characters match accented characters" -msgstr "" +msgstr "Ékezetes karakterek megfeleltetése ékezet nélkülivel" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/search_ui.py:170 msgid "" @@ -18055,7 +18089,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search.py:178 msgid "You must enter a title, author or keyword to search for." -msgstr "" +msgstr "Meg kell adnia egy címet, szerzőt vagy kulcsszót a kereséshez." #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search.py:300 msgid "Customize get books search" @@ -18079,19 +18113,19 @@ msgstr "Könyv letöltése" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:171 msgid "Search by title" -msgstr "" +msgstr "Keresés cím alapjáán" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:173 msgid "Search by author" -msgstr "" +msgstr "Keresés szerző alapján" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:175 msgid "&Keyword:" -msgstr "" +msgstr "&Kulcsszó:" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:176 msgid "Search by any keyword" -msgstr "" +msgstr "Keresés bármilyen kulcsszó alapján" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:182 msgid "Open a selected book in the system's web browser" @@ -18494,11 +18528,11 @@ msgstr "A(z) %s kezelése" #: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:566 msgid "Change category icon" -msgstr "" +msgstr "Kategória ikon megváltoztatása" #: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:568 msgid "Restore default icon" -msgstr "" +msgstr "Alapértelmezett ikon visszaállítása" #: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/view.py:586 msgid "Show all categories" @@ -18592,7 +18626,7 @@ msgstr "Az aktuális keresés törlése" #: /home/kovid/work/calibre/src/calibre/gui2/ui.py:368 msgid "Debug mode" -msgstr "Hibakövetési mód" +msgstr "Hibakeresési mód" #: /home/kovid/work/calibre/src/calibre/gui2/ui.py:369 #, python-format @@ -18687,7 +18721,7 @@ msgid "" msgstr "" " kommunikál az eszközzel!
\n" " A kilépés adatvesztést okozhat az eszközön.
\n" -" Biztos, hogy ki akarsz lépni??" +" Biztos, hogy ki akar lépni??" #: /home/kovid/work/calibre/src/calibre/gui2/ui.py:745 msgid "Active jobs" @@ -18881,11 +18915,11 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config.py:63 msgid "Start viewer in full screen mode" -msgstr "" +msgstr "Olvasóprogram indítása teljesképernyős módban" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config.py:65 msgid "Show full screen usage help" -msgstr "" +msgstr "Teljesképernyő súgó mutatása" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config.py:74 msgid "Font options" @@ -18960,7 +18994,7 @@ msgstr "E-book olvasó beállítása" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:377 msgid "&Default font size:" -msgstr "&Alap betűméret:" +msgstr "Alapértelmezett &betűméret:" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:382 msgid "Serif" @@ -19035,7 +19069,7 @@ msgstr "Teljesképernyős módban a szöveg maximális szélessége:" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:404 msgid "Show &clock in full screen mode" -msgstr "&Óra megjelenítése teljes képernyős módban" +msgstr "&Óra megjelenítése teljesképernyős módban" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:405 msgid "Show reading &position in full screen mode" @@ -19043,19 +19077,19 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:406 msgid "Show &scrollbar in full screen mode" -msgstr "" +msgstr "Gördítősáv mutatása teljesképernyős módban" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:407 msgid "&Start viewer in full screen mode" -msgstr "" +msgstr "Olvasóprogram &indítása teljesképernyős módban" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:408 msgid "Show &help message when starting full screen mode" -msgstr "" +msgstr "&Súgóüzenet megjelenítése teljesképernyős üzemmódban" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:409 msgid "F&ull screen options" -msgstr "&Teljes képernyős beállítások" +msgstr "&Teljesképernyős beállítások" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:410 msgid "Background color:" @@ -19173,7 +19207,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:442 msgid "Delete a saved theme:" -msgstr "" +msgstr "Mentett téma törlése:" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:444 msgid "&Theming" @@ -19189,11 +19223,11 @@ msgstr "Keresés szótárban" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:507 msgid "View &image..." -msgstr "" +msgstr "Kép megtek&intése..." #: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:509 msgid "View &table..." -msgstr "" +msgstr "&Táblázat megtekintése..." #: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:512 msgid "&Search for next occurrence" @@ -19230,7 +19264,7 @@ msgstr "Szakasz vége" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:547 msgid "Default font size" -msgstr "" +msgstr "Alpértelmezett betűméret" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/documentview.py:614 #, python-format @@ -19253,7 +19287,7 @@ msgstr "&Mentés másként" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/image_popup.py:41 msgid "&Rotate" -msgstr "" +msgstr "Fo&rgatás" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/image_popup.py:67 msgid "Choose a file to save to" @@ -19350,7 +19384,7 @@ msgstr "Szöveg keresése a könyvben" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:246 #, python-format msgid "Toggle full screen (%s)" -msgstr "Teljesképernyős mód be/ki (%s)" +msgstr "Teljesképernyős be/ki (%s)" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:286 msgid "Full screen mode" @@ -19459,7 +19493,7 @@ msgstr "Nem lehet megnyitni a könyvet" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:988 msgid "Unknown error" -msgstr "" +msgstr "Ismeretlen hiba" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1103 msgid "Options to control the ebook viewer" @@ -19476,7 +19510,7 @@ msgstr "" msgid "" "If specified, viewer window will try to open full screen when started." msgstr "" -"Ha be van állítva, akkor az olvasóprogram megpróbál teljes képernyősként " +"Ha be van állítva, akkor az olvasóprogram megpróbál teljesképernyősként " "indulni" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:1118 @@ -19545,7 +19579,7 @@ msgstr "Könyvjelző" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:224 msgid "Toggle full screen" -msgstr "Teljes képernyő be/ki" +msgstr "Teljesképernyő be/ki" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:225 msgid "Print" @@ -19565,11 +19599,11 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:230 msgid "Load theme" -msgstr "" +msgstr "Téma betöltése" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:231 msgid "Load a theme" -msgstr "" +msgstr "Téma betöltése" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/printing.py:68 msgid "Failed to render" @@ -19582,7 +19616,7 @@ msgstr "Nem sikerült a következő dokumentum renderelése: %s" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/table_popup.py:57 msgid "View Table" -msgstr "" +msgstr "Táblázat" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/table_popup.py:72 msgid "No table found" @@ -19756,15 +19790,15 @@ msgid "" "button below. You will also have to register your gmail address in your " "Amazon account." msgstr "" -"

A calibre e-mailben automatikusan tud könyveket küldeni Kindle " -"olvasójára. Ehhez alul be kell állítania az e-mail küldést. A legegyszerűbb, " -"ha létrehoz egy ingyenes gmail fiókot és a " -"„Gmail használata” gombra kattint. Ezt a Gmail címet természetesen az Ön " -"Amazon fiókjában is regisztrálni kell." +"

A calibre emailben automatikusan tud könyveket küldeni Kindle olvasójára. " +"Ehhez alul be kell állítania az email küldést. A legegyszerűbb, ha létrehoz " +"egy ingyenes gmail fiókot és a „Gmail " +"használata” gombra kattint. Ezt a Gmail címet természetesen az Ön Amazon " +"fiókjában is regisztrálni kell." #: /home/kovid/work/calibre/src/calibre/gui2/wizard/kindle_ui.py:50 msgid "&Kindle email:" -msgstr "&Kindle e-mail:" +msgstr "&Kindle email:" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/library_ui.py:57 msgid "Choose your &language:" @@ -19804,25 +19838,25 @@ msgstr "A levél elküldése sikerült." #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:59 msgid "Setup sending email using" -msgstr "Küldésre szolgáló e-mail beállítása a következővel:" +msgstr "Küldésre szolgáló email beállítása a következővel:" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:61 msgid "" "If you don't have an account, you can sign up for a free {name} email " "account at http://{url}. {extra}" msgstr "" -"Ha még nincs fiókja, regisztrálhat egy ingyenes {name} e-mail címet a http://{url} oldalon. {extra}" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:68 #, python-format msgid "Your %s &email address:" -msgstr "Az Ön %s &e-mail címe:" +msgstr "Az Ön %s &email címe:" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:69 #, python-format msgid "Your %s &username:" -msgstr "%s &felhasználóneve:" +msgstr "Az Ön %s &felhasználóneve:" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:70 #, python-format @@ -19836,9 +19870,9 @@ msgid "" "your %s email address to the allowed email addresses in your Amazon.com " "Kindle management page." msgstr "" -"Ha tervezi, hogy Kindle eszközére e-mailen keresztül küld könyveket, akkor " -"a(z) %s e-mail címét hozzá kell adnia az Amazon.com-ra bejelentkezés után a " -"„Your Account > Manage Your Kindle > Personal Document Settings > Approved " +"Ha tervezi, hogy Kindle eszközére emailben küld könyveket, akkor a(z) %s e-" +"mail címét hozzá kell adnia az Amazon.com-ra bejelentkezés után a „Your " +"Account > Manage Your Kindle > Personal Document Settings > Approved " "Personal Document E-mail List” résznél az „Add a new approved e-mail " "address”-re kattintva." @@ -19853,7 +19887,7 @@ msgstr "Helytelen felhasználói név" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:103 #, python-format msgid "%s needs the full email address as your username" -msgstr "%s esetén a teljes e-mail cím használandó felhasználónévként" +msgstr "%s esetén a teljes email cím használandó felhasználónévként" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:154 msgid "OK to proceed?" @@ -19863,7 +19897,7 @@ msgstr "Kívánja folytatni?" msgid "" "This will display your email password on the screen. Is it OK to proceed?" msgstr "" -"Ennek hatására megjelenik az e-mail címéhez tartozó jelszó a képernyőn. " +"Ennek hatására megjelenik az email címéhez tartozó jelszó a képernyőn. " "Kívánja folytatni?" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:199 @@ -19873,7 +19907,7 @@ msgid "" "this case, I strongly suggest you setup a free gmail account instead." msgstr "" "Ha új Hotmail fiókot hoz létre, a Microsoft időnként felszólíthatja a " -"felhasználói fiók érvényesítésére mielőtt engedi, hogy a calibre e-mailt " +"felhasználói fiók érvényesítésére mielőtt engedi, hogy a calibre emailt " "küldjön. Javasoljuk, hogy ebben az esetben használjon GMail fiókot." #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:221 @@ -19909,13 +19943,13 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:124 msgid "Send email &from:" -msgstr "&E-mail küldése a következőről:" +msgstr "&Email küldése a következőről:" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:125 msgid "" "

This is what will be present in the From: field of emails sent by " "calibre.
Set it to your email address" -msgstr "Ez fog megjelenni a Feladó mezőben.
Írja be az e-mail címét." +msgstr "Ez fog megjelenni a Feladó mezőben.
Írja be az email címét." #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:126 msgid "" @@ -19932,7 +19966,7 @@ msgstr "&Levelező kiszolgáló" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:128 msgid "calibre can optionally use a server to send mail" msgstr "" -"A calibre opcionálisan tud kiszolgálót is használni az e-mailek " +"A calibre opcionálisan tud kiszolgálót is használni az emailek " "küldéséhez" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:129 @@ -19954,7 +19988,7 @@ msgstr "Kimenő levek kiszolgálójának portja. Alapbeállítás: 25" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:134 msgid "Your username on the mail server" -msgstr "Az Ön Felhasználóneve a levelező kiszolgálón" +msgstr "Az Ön felhasználóneve a levelező kiszolgálón" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:136 msgid "Your password on the mail server" @@ -20007,7 +20041,7 @@ msgstr "Hotmail használata" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:147 msgid "&Test email" -msgstr "Tes&zt e-mail küldése" +msgstr "Tes&zt email küldése" #: /home/kovid/work/calibre/src/calibre/gui2/wizard/stanza_ui.py:49 msgid "" @@ -20422,21 +20456,21 @@ msgstr "Nincs elérhető könyv a katalógus készítéséhez" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:305 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2477 msgid "Titles" -msgstr "" +msgstr "Címek" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:309 msgid "Genres" -msgstr "" +msgstr "Műfajok" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:311 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1780 msgid "Recently Added" -msgstr "" +msgstr "Utoljára hozzáadva" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:313 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1979 msgid "Recently Read" -msgstr "" +msgstr "Utoljára olvasva" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:315 msgid "Descriptions" @@ -20452,11 +20486,11 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:744 msgid "Sorting database" -msgstr "" +msgstr "Adatbázis rendezése" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:846 msgid "Sorting titles" -msgstr "" +msgstr "Címek rendezése" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:858 msgid "" @@ -20470,11 +20504,11 @@ msgstr "Nincs elérhető könyv a katalógusba illesztéshez" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2060 msgid "Genres HTML" -msgstr "" +msgstr "Műfajok HTML" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2457 msgid "Titles HTML" -msgstr "" +msgstr "Címek HTML" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2654 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2656 @@ -20492,7 +20526,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:2932 msgid "NCX header" -msgstr "" +msgstr "NCX fejléc" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3009 msgid "NCX for Descriptions" @@ -20500,7 +20534,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3136 msgid "NCX for Series" -msgstr "" +msgstr "Sorozatok NCX" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3221 #, python-format @@ -20514,59 +20548,59 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3267 msgid "NCX for Titles" -msgstr "" +msgstr "Címek NCX" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3354 #, python-format msgid "Titles beginning with %s" -msgstr "" +msgstr "Ezzel kezdődő címek: %s" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3356 #, python-format msgid "Titles beginning with '%s'" -msgstr "" +msgstr "Ezzel kezdődő címek: %s" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3398 msgid "NCX for Authors" -msgstr "" +msgstr "Szerzők NCX" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3477 #, python-format msgid "Authors beginning with %s" -msgstr "" +msgstr "Szerzők ezzel kezdődően: %s" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3479 #, python-format msgid "Authors beginning with '%s'" -msgstr "" +msgstr "Szerzők ezzel kezdődően: %s" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3520 msgid "NCX for Recently Added" -msgstr "" +msgstr "Utoljára hozzáadva NCX" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3713 msgid "NCX for Recently Read" -msgstr "" +msgstr "Utoljára olvasva NCX" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3855 msgid "NCX for Genres" -msgstr "" +msgstr "Műfajok NCX" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:3980 msgid "Generating OPF" -msgstr "" +msgstr "OPF generálása" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4357 msgid "Thumbnails" -msgstr "" +msgstr "Miniatűrök" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4363 msgid "Thumbnail" -msgstr "" +msgstr "Miniatűr" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:4897 msgid "Saving NCX" -msgstr "" +msgstr "NCX mentése" #: /home/kovid/work/calibre/src/calibre/library/check_library.py:26 msgid "Invalid titles" @@ -21440,6 +21474,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/restore.py:125 msgid "Cannot restore preferences. Backup file not found." msgstr "" +"A beállításokat nem lehet visszaállítani. Biztonsági mentés nem található." #: /home/kovid/work/calibre/src/calibre/library/restore.py:136 msgid "Finished restoring preferences and column metadata" @@ -21621,7 +21656,7 @@ msgid "" "Failed to calculate path for save to disk. Template: %(templ)s\n" "Error: %(err)s" msgstr "" -"A lemezre mentés útvonalát nem sikerült meghatározni. sablon: %(templ)s\n" +"A lemezre mentés útvonalát nem sikerült meghatározni. Sablon: %(templ)s\n" "Hiba: %(err)s" #: /home/kovid/work/calibre/src/calibre/library/save_to_disk.py:316 @@ -21976,7 +22011,7 @@ msgstr "Az e-book konvertálás alapértelmezett kimeneti formátuma." #: /home/kovid/work/calibre/src/calibre/utils/config_base.py:393 msgid "Ordered list of formats to prefer for input." -msgstr "Rendezett lista az előnyberészesített formátumokról." +msgstr "Rendezett lista az előnyben részesített formátumokról." #: /home/kovid/work/calibre/src/calibre/utils/config_base.py:395 msgid "Read metadata from files" @@ -22045,7 +22080,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/utils/filenames.py:295 msgid "File is open in another process" -msgstr "" +msgstr "A fájlt egy másik művelet is használja" #: /home/kovid/work/calibre/src/calibre/utils/formatter.py:31 #: /home/kovid/work/calibre/src/calibre/utils/formatter.py:182 @@ -23180,7 +23215,7 @@ msgstr "A hitelesítés sikertelen a következő kiszolgálóval: %s" #: /home/kovid/work/calibre/src/calibre/utils/smtp.py:258 msgid "Control email delivery" -msgstr "E-mail küldés beállításai" +msgstr "Email küldés beállításai" #: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:121 msgid "Unknown section" @@ -23208,7 +23243,8 @@ msgstr "A következő letöltése nem sikerült: %s" #, python-format msgid "The \"%s\" recipe needs a username and password." msgstr "" -"A következő recepthez felhasználónévre és jelszóra van szüksége: „%s”." +"A következő hírösszeállításhoz felhasználónévre és jelszóra van szüksége: " +"„%s”." #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:892 msgid "Download finished" @@ -23302,7 +23338,7 @@ msgstr "A következő cikk letöltése nem sikerült: %s" #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1489 msgid "Fetching feed" -msgstr "Hír letöltése" +msgstr "Hírek letöltése" #: /home/kovid/work/calibre/src/calibre/web/feeds/news.py:1633 msgid "" @@ -23322,7 +23358,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/collection.py:45 msgid "You" -msgstr "Te" +msgstr "Ön" #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:75 #: /home/kovid/work/calibre/src/calibre/web/feeds/recipes/model.py:84 @@ -24430,7 +24466,7 @@ msgstr "" #: /home/kovid/work/calibre/resources/default_tweaks.py:428 msgid "The number of seconds to wait before sending emails" -msgstr "Az e-mailek küldése előtti várakozás másodpercben" +msgstr "Az emailek küldése előtti várakozás másodpercben" #: /home/kovid/work/calibre/resources/default_tweaks.py:429 msgid "" @@ -24440,7 +24476,7 @@ msgid "" "making email sending fail. Changes will take effect only after a restart of\n" "calibre." msgstr "" -"Nyilvános e-mail szerverek, mint a gmail vagy a hotmail, használata estén " +"Nyilvános email szerverek, mint a gmail vagy a hotmail, használata estén " "ennyi másodpercet\n" "vár a levél elküldése előtt. Alapérték: 5 perc.\n" "Ennél alacsonyabb érték esetén a szerverek SPAM ellenőrzése kidobhatja a " @@ -24582,7 +24618,7 @@ msgstr "" #: /home/kovid/work/calibre/resources/default_tweaks.py:484 msgid "Compile General Program Mode templates to Python" -msgstr "Általános Program Mód sablonok lefordítása" +msgstr "Általános Program Mód sablonok lefordítása Pythonnal" #: /home/kovid/work/calibre/resources/default_tweaks.py:485 msgid "" @@ -24607,7 +24643,7 @@ msgstr "" #: /home/kovid/work/calibre/resources/default_tweaks.py:494 msgid "What format to default to when using the Tweak feature" -msgstr "" +msgstr "A Finomhangolásnál használt alapértelmezett formátum" #: /home/kovid/work/calibre/resources/default_tweaks.py:495 msgid "" @@ -24638,29 +24674,3 @@ msgid "" "Enter. Which technique you prefer will depend on the state of metadata in\n" "your library and your personal editing style." msgstr "" - -#~ msgid "The orientation of the page. Default is portrait. Choices are %s" -#~ msgstr "A lap tájolása. Alapértelmezett: álló. Lehetőségek: %s" - -#~ msgid "" -#~ "The unit of measure. Default is inch. Choices are %s Note: This does not " -#~ "override the unit for margins!" -#~ msgstr "" -#~ "Mértékegység. Alapértelmezett az inch. A lehetőségek: %s. Figyelem: ez nem " -#~ "írja felül a margók mértékegységét!" - -#~ msgid "" -#~ "Make font size %(which)s\n" -#~ "Current magnification: %(mag).1f" -#~ msgstr "" -#~ "Betűméret változtatása %(which)s\n" -#~ "Jelenlegi nagyítás mértéke: %(mag).1f" - -#~ msgid "smaller" -#~ msgstr "kisebbre" - -#~ msgid "larger" -#~ msgstr "nagyobbra" - -#~ msgid "&Orientation:" -#~ msgstr "Lap táj&olása:" From ed89e9b4657ea3f802eeb4d0963b0741a71a83ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 13:33:38 +0530 Subject: [PATCH 31/67] Add support for non-solid fills, tiled pixmaps, refactor testing --- src/calibre/ebooks/pdf/render/engine.py | 165 +++---------- src/calibre/ebooks/pdf/render/graphics.py | 254 ++++++++++++++++++++- src/calibre/ebooks/pdf/render/serialize.py | 110 ++++++++- src/calibre/ebooks/pdf/render/test.py | 99 ++++++++ 4 files changed, 476 insertions(+), 152 deletions(-) create mode 100644 src/calibre/ebooks/pdf/render/test.py diff --git a/src/calibre/ebooks/pdf/render/engine.py b/src/calibre/ebooks/pdf/render/engine.py index aa1fa17cc3..7987bd9c6e 100644 --- a/src/calibre/ebooks/pdf/render/engine.py +++ b/src/calibre/ebooks/pdf/render/engine.py @@ -13,9 +13,7 @@ from functools import wraps, partial from future_builtins import map import sip -from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter, - QTransform, QImage, QByteArray, QBuffer, - qRgba) +from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QTransform, QBrush) from calibre.constants import plugins from calibre.ebooks.pdf.render.serialize import (PDFStream, Path) @@ -51,11 +49,19 @@ class Font(FontMetrics): class PdfEngine(QPaintEngine): + FEATURES = QPaintEngine.AllFeatures & ~( + QPaintEngine.PorterDuff | QPaintEngine.PerspectiveTransform + | QPaintEngine.ObjectBoundingModeGradients + | QPaintEngine.LinearGradientFill + | QPaintEngine.RadialGradientFill + | QPaintEngine.ConicalGradientFill + ) + def __init__(self, file_object, page_width, page_height, left_margin, top_margin, right_margin, bottom_margin, width, height, errors=print, debug=print, compress=True, mark_links=False): - QPaintEngine.__init__(self, self.features) + QPaintEngine.__init__(self, self.FEATURES) self.file_object = file_object self.compress, self.mark_links = compress, mark_links self.page_height, self.page_width = page_height, page_width @@ -80,9 +86,6 @@ class PdfEngine(QPaintEngine): self.errors_occurred = False self.errors, self.debug = errors, debug self.fonts = {} - i = QImage(1, 1, QImage.Format_ARGB32) - i.fill(qRgba(0, 0, 0, 255)) - self.alpha_bit = i.constBits().asstring(4).find(b'\xff') self.current_page_num = 1 self.current_page_inited = False self.qt_hack, err = plugins['qt_hack'] @@ -107,13 +110,6 @@ class PdfEngine(QPaintEngine): self.pdf.save_stack() self.current_page_inited = True - @property - def features(self): - # gradient_flags = self.MaskedBrush | self.PatternBrush | self.PatternTransform - return (self.Antialiasing | self.AlphaBlend | self.ConstantOpacity | - self.PainterPaths | self.PaintOutsidePaintEvent | - self.PrimitiveTransform | self.PixmapTransform) #| gradient_flags - def begin(self, device): if not hasattr(self, 'pdf'): try: @@ -149,7 +145,23 @@ class PdfEngine(QPaintEngine): def type(self): return QPaintEngine.Pdf - # TODO: Tiled pixmap + def add_image(self, img, cache_key): + if img.isNull(): return + return self.pdf.add_image(img, cache_key) + + @store_error + def drawTiledPixmap(self, rect, pixmap, point): + self.apply_graphics_state() + brush = QBrush(pixmap) + color, opacity, pattern, do_fill = self.graphics.convert_brush( + brush, -point, 1.0, self.pdf, self.pdf_system, + self.painter().transform()) + self.pdf.save_stack() + self.pdf.apply_fill(color, pattern) + bl = rect.topLeft() + self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(), + stroke=False, fill=True) + self.pdf.restore_stack() @store_error def drawPixmap(self, rect, pixmap, source_rect): @@ -160,8 +172,8 @@ class PdfEngine(QPaintEngine): image = pixmap.toImage() ref = self.add_image(image, pixmap.cacheKey()) if ref is not None: - self.pdf.draw_image(rect.x(), rect.height()+rect.y(), rect.width(), - -rect.height(), ref) + self.pdf.draw_image(rect.x(), rect.y(), rect.width(), + rect.height(), ref) @store_error def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor): @@ -171,72 +183,8 @@ class PdfEngine(QPaintEngine): image.copy(source_rect)) ref = self.add_image(image, image.cacheKey()) if ref is not None: - self.pdf.draw_image(rect.x(), rect.height()+rect.y(), rect.width(), - -rect.height(), ref) - - def add_image(self, img, cache_key): - if img.isNull(): return - ref = self.pdf.get_image(cache_key) - if ref is not None: - return ref - - fmt = img.format() - image = QImage(img) - if (image.depth() == 1 and img.colorTable().size() == 2 and - img.colorTable().at(0) == QColor(Qt.black).rgba() and - img.colorTable().at(1) == QColor(Qt.white).rgba()): - if fmt == QImage.Format_MonoLSB: - image = image.convertToFormat(QImage.Format_Mono) - fmt = QImage.Format_Mono - else: - if (fmt != QImage.Format_RGB32 and fmt != QImage.Format_ARGB32): - image = image.convertToFormat(QImage.Format_ARGB32) - fmt = QImage.Format_ARGB32 - - w = image.width() - h = image.height() - d = image.depth() - - if fmt == QImage.Format_Mono: - bytes_per_line = (w + 7) >> 3 - data = image.constBits().asstring(bytes_per_line * h) - return self.pdf.write_image(data, w, h, d, cache_key=cache_key) - - ba = QByteArray() - buf = QBuffer(ba) - image.save(buf, 'jpeg', 94) - data = bytes(ba.data()) - has_alpha = has_mask = False - soft_mask = mask = None - - if fmt == QImage.Format_ARGB32: - tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4] - sdata = bytearray(tmask) - vals = set(sdata) - vals.discard(255) - has_mask = bool(vals) - vals.discard(0) - has_alpha = bool(vals) - - if has_alpha: - soft_mask = self.pdf.write_image(tmask, w, h, 8) - elif has_mask: - # dither the soft mask to 1bit and add it. This also helps PDF - # viewers without transparency support - bytes_per_line = (w + 7) >> 3 - mdata = bytearray(0 for i in xrange(bytes_per_line * h)) - spos = mpos = 0 - for y in xrange(h): - for x in xrange(w): - if sdata[spos]: - mdata[mpos + x>>3] |= (0x80 >> (x&7)) - spos += 1 - mpos += bytes_per_line - mdata = bytes(mdata) - mask = self.pdf.write_image(mdata, w, h, 1) - - return self.pdf.write_image(data, w, h, 32, mask=mask, dct=True, - soft_mask=soft_mask, cache_key=cache_key) + self.pdf.draw_image(rect.x(), rect.y(), rect.width(), + rect.height(), ref) @store_error def updateState(self, state): @@ -411,55 +359,4 @@ class PdfDevice(QPaintDevice): # {{{ # }}} -if __name__ == '__main__': - from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath) - QBrush, QColor, QPoint, QPixmap, QPainterPath - app = QApplication([]) - p = QPainter() - with open('/t/painter.pdf', 'wb') as f: - dev = PdfDevice(f, compress=False) - p.begin(dev) - dev.init_page() - xmax, ymax = p.viewport().width(), p.viewport().height() - b = p.brush() - try: - p.drawRect(0, 0, xmax, ymax) - # p.drawPolyline(QPoint(0, 0), QPoint(xmax, 0), QPoint(xmax, ymax), - # QPoint(0, ymax), QPoint(0, 0)) - # pp = QPainterPath() - # pp.addRect(0, 0, xmax, ymax) - # p.drawPath(pp) - p.save() - for i in xrange(3): - col = [0, 0, 0, 200] - col[i] = 255 - p.setOpacity(0.3) - p.fillRect(0, 0, xmax/10, xmax/10, QBrush(QColor(*col))) - p.setOpacity(1) - p.drawRect(0, 0, xmax/10, xmax/10) - p.translate(xmax/10, xmax/10) - p.scale(1, 1.5) - p.restore() - - # p.scale(2, 2) - # p.rotate(45) - p.drawPixmap(0, 0, 2048, 2048, QPixmap(I('library.png'))) - p.drawRect(0, 0, 2048, 2048) - - f = p.font() - f.setPointSize(20) - # f.setLetterSpacing(f.PercentageSpacing, 200) - # f.setUnderline(True) - # f.setOverline(True) - # f.setStrikeOut(True) - f.setFamily('Calibri') - p.setFont(f) - # p.setPen(QColor(0, 0, 255)) - # p.scale(2, 2) - # p.rotate(45) - p.drawText(QPoint(300, 300), 'Some—text not By’s ū --- Д AV ff ff') - finally: - p.end() - if dev.engine.errors_occurred: - raise SystemExit(1) diff --git a/src/calibre/ebooks/pdf/render/graphics.py b/src/calibre/ebooks/pdf/render/graphics.py index 68efb2514a..384809598a 100644 --- a/src/calibre/ebooks/pdf/render/graphics.py +++ b/src/calibre/ebooks/pdf/render/graphics.py @@ -9,13 +9,14 @@ __docformat__ = 'restructuredtext en' from math import sqrt -from PyQt4.Qt import (QBrush, QPen, Qt, QPointF, QTransform, QPainterPath, - QPaintEngine) +from PyQt4.Qt import ( + QBrush, QPen, Qt, QPointF, QTransform, QPainterPath, QPaintEngine, QImage) -from calibre.ebooks.pdf.render.common import Array +from calibre.ebooks.pdf.render.common import ( + Name, Array, fmtnum, Stream, Dictionary) from calibre.ebooks.pdf.render.serialize import Path, Color -def convert_path(path): +def convert_path(path): # {{{ p = Path() i = 0 while i < path.elementCount(): @@ -38,7 +39,201 @@ def convert_path(path): if not added: raise ValueError('Invalid curve to operation') return p +# }}} +class TilingPattern(Stream): + + def __init__(self, cache_key, matrix, w=8, h=8, paint_type=2, compress=False): + Stream.__init__(self, compress=compress) + self.paint_type = paint_type + self.w, self.h = w, h + self.matrix = (matrix.m11(), matrix.m12(), matrix.m21(), matrix.m22(), + matrix.dx(), matrix.dy()) + self.resources = Dictionary() + self.cache_key = (self.__class__.__name__, cache_key, self.matrix) + + def add_extra_keys(self, d): + d['Type'] = Name('Pattern') + d['PatternType'] = 1 + d['PaintType'] = self.paint_type + d['TilingType'] = 1 + d['BBox'] = Array([0, 0, self.w, self.h]) + d['XStep'] = self.w + d['YStep'] = self.h + d['Matrix'] = Array(self.matrix) + d['Resources'] = self.resources + +class QtPattern(TilingPattern): + + qt_patterns = ( # {{{ + "0 J\n" + "6 w\n" + "[] 0 d\n" + "4 0 m\n" + "4 8 l\n" + "0 4 m\n" + "8 4 l\n" + "S\n", # Dense1Pattern + + "0 J\n" + "2 w\n" + "[6 2] 1 d\n" + "0 0 m\n" + "0 8 l\n" + "8 0 m\n" + "8 8 l\n" + "S\n" + "[] 0 d\n" + "2 0 m\n" + "2 8 l\n" + "6 0 m\n" + "6 8 l\n" + "S\n" + "[6 2] -3 d\n" + "4 0 m\n" + "4 8 l\n" + "S\n", # Dense2Pattern + + "0 J\n" + "2 w\n" + "[6 2] 1 d\n" + "0 0 m\n" + "0 8 l\n" + "8 0 m\n" + "8 8 l\n" + "S\n" + "[2 2] -1 d\n" + "2 0 m\n" + "2 8 l\n" + "6 0 m\n" + "6 8 l\n" + "S\n" + "[6 2] -3 d\n" + "4 0 m\n" + "4 8 l\n" + "S\n", # Dense3Pattern + + "0 J\n" + "2 w\n" + "[2 2] 1 d\n" + "0 0 m\n" + "0 8 l\n" + "8 0 m\n" + "8 8 l\n" + "S\n" + "[2 2] -1 d\n" + "2 0 m\n" + "2 8 l\n" + "6 0 m\n" + "6 8 l\n" + "S\n" + "[2 2] 1 d\n" + "4 0 m\n" + "4 8 l\n" + "S\n", # Dense4Pattern + + "0 J\n" + "2 w\n" + "[2 6] -1 d\n" + "0 0 m\n" + "0 8 l\n" + "8 0 m\n" + "8 8 l\n" + "S\n" + "[2 2] 1 d\n" + "2 0 m\n" + "2 8 l\n" + "6 0 m\n" + "6 8 l\n" + "S\n" + "[2 6] 3 d\n" + "4 0 m\n" + "4 8 l\n" + "S\n", # Dense5Pattern + + "0 J\n" + "2 w\n" + "[2 6] -1 d\n" + "0 0 m\n" + "0 8 l\n" + "8 0 m\n" + "8 8 l\n" + "S\n" + "[2 6] 3 d\n" + "4 0 m\n" + "4 8 l\n" + "S\n", # Dense6Pattern + + "0 J\n" + "2 w\n" + "[2 6] -1 d\n" + "0 0 m\n" + "0 8 l\n" + "8 0 m\n" + "8 8 l\n" + "S\n", # Dense7Pattern + + "1 w\n" + "0 4 m\n" + "8 4 l\n" + "S\n", # HorPattern + + "1 w\n" + "4 0 m\n" + "4 8 l\n" + "S\n", # VerPattern + + "1 w\n" + "4 0 m\n" + "4 8 l\n" + "0 4 m\n" + "8 4 l\n" + "S\n", # CrossPattern + + "1 w\n" + "-1 5 m\n" + "5 -1 l\n" + "3 9 m\n" + "9 3 l\n" + "S\n", # BDiagPattern + + "1 w\n" + "-1 3 m\n" + "5 9 l\n" + "3 -1 m\n" + "9 5 l\n" + "S\n", # FDiagPattern + + "1 w\n" + "-1 3 m\n" + "5 9 l\n" + "3 -1 m\n" + "9 5 l\n" + "-1 5 m\n" + "5 -1 l\n" + "3 9 m\n" + "9 3 l\n" + "S\n", # DiagCrossPattern + ) # }}} + + def __init__(self, pattern_num, matrix): + super(QtPattern, self).__init__(pattern_num, matrix) + self.write(self.qt_patterns[pattern_num-2]) + +class TexturePattern(TilingPattern): + + def __init__(self, pixmap, matrix, pdf): + image = pixmap.toImage() + cache_key = pixmap.cacheKey() + imgref = pdf.add_image(image, cache_key) + paint_type = (2 if image.format() in {QImage.Format_MonoLSB, + QImage.Format_Mono} else 1) + super(TexturePattern, self).__init__( + cache_key, matrix, w=image.width(), h=image.height(), + paint_type=paint_type) + m = (self.w, 0, 0, -self.h, 0, self.h) + self.resources['XObject'] = Dictionary({'Texture':imgref}) + self.write_line('%s cm /Texture Do'%(' '.join(map(fmtnum, m)))) class GraphicsState(object): @@ -54,6 +249,7 @@ class GraphicsState(object): self.clip = QPainterPath() self.do_fill = False self.do_stroke = True + self.qt_pattern_cache = {} def __eq__(self, other): for x in self.FIELDS: @@ -140,6 +336,43 @@ class Graphics(object): self.current_state = self.pending_state self.pending_state = None + def convert_brush(self, brush, brush_origin, global_opacity, pdf, + pdf_system, qt_system): + # Convert a QBrush to PDF operators + style = brush.style() + + pattern = color = None + opacity = 1.0 + do_fill = True + + matrix = (QTransform.fromTranslate(brush_origin.x(), brush_origin.y()) + * pdf_system * qt_system.inverted()[0]) + vals = list(brush.color().getRgbF()) + + if style <= Qt.DiagCrossPattern: + opacity = global_opacity * vals[-1] + color = vals[:3] + + if style > Qt.SolidPattern: + pattern = pdf.add_pattern(QtPattern(style, matrix)) + + if opacity < 1e-4 or style == Qt.NoBrush: + do_fill = False + + elif style == Qt.TexturePattern: + pat = TexturePattern(brush.texture(), matrix, pdf) + opacity = global_opacity + if pat.paint_type == 2: + opacity *= vals[-1] + color = vals[:3] + pattern = pdf.add_pattern(pat) + + if opacity < 1e-4 or style == Qt.NoBrush: + do_fill = False + + # TODO: Add support for gradient fills + return color, opacity, pattern, do_fill + def apply_stroke(self, state, pdf, pdf_system, painter): # TODO: Handle pens with non solid brushes by setting the colorspace # for stroking to a pattern @@ -172,7 +405,7 @@ class Graphics(object): Qt.DashDotDotLine:[3, 2, 1, 2, 1, 2]}.get(pen.style(), []) if ps: pdf.serialize(Array(ps)) - pdf.current_page.write(' d ') + pdf.current_page.write(' 0 d ') # Stroke fill b = pen.brush() @@ -186,11 +419,8 @@ class Graphics(object): def apply_fill(self, state, pdf, pdf_system, painter): self.pending_state.do_fill = True - b = state.fill - if b.style() == Qt.NoBrush: - self.pending_state.do_fill = False - vals = list(b.color().getRgbF()) - vals[-1] *= state.opacity - color = Color(*vals) - pdf.set_fill_color(color) + color, opacity, pattern, self.pending_state.do_fill = self.convert_brush( + state.fill, state.brush_origin, state.opacity, pdf, pdf_system, + painter.transform()) + pdf.apply_fill(color, pattern, opacity) diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index be78ddda66..54a5f674b4 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -12,6 +12,8 @@ from future_builtins import map from itertools import izip from collections import namedtuple +from PyQt4.Qt import QBuffer, QByteArray, QImage, Qt, QColor, qRgba + from calibre.constants import (__appname__, __version__) from calibre.ebooks.pdf.render.common import ( Reference, EOL, serialize, Stream, Dictionary, String, Name, Array, @@ -90,6 +92,7 @@ class Page(Stream): self.opacities = {} self.fonts = {} self.xobjects = {} + self.patterns = {} def set_opacity(self, opref): if opref not in self.opacities: @@ -108,6 +111,11 @@ class Page(Stream): self.xobjects[imgref] = 'Image%d'%len(self.xobjects) return self.xobjects[imgref] + def add_pattern(self, patternref): + if patternref not in self.patterns: + self.patterns[patternref] = 'Pat%d'%len(self.patterns) + return self.patterns[patternref] + def add_resources(self): r = Dictionary() if self.opacities: @@ -125,6 +133,13 @@ class Page(Stream): for ref, name in self.xobjects.iteritems(): xobjects[name] = ref r['XObject'] = xobjects + if self.patterns: + r['ColorSpace'] = Dictionary({'PCSp':Array( + [Name('Pattern'), Name('DeviceRGB')])}) + patterns = Dictionary() + for ref, name in self.patterns.iteritems(): + patterns[name] = ref + r['Pattern'] = patterns if r: self.page_dict['Resources'] = r @@ -299,8 +314,12 @@ class PDFStream(object): self.stroke_opacities, self.fill_opacities = {}, {} self.font_manager = FontManager(self.objects, self.compress) self.image_cache = {} + self.pattern_cache = {} self.debug = debug self.links = Links(self, mark_links, page_size) + i = QImage(1, 1, QImage.Format_ARGB32) + i.fill(qRgba(0, 0, 0, 255)) + self.alpha_bit = i.constBits().asstring(4).find(b'\xff') @property def page_tree(self): @@ -380,13 +399,12 @@ class PDFStream(object): self.current_page.set_opacity(self.stroke_opacities[opacity]) self.current_page.write_line(' '.join(map(fmtnum, color[:3])) + ' SC') - def set_fill_color(self, color): - opacity = color.opacity + def set_fill_opacity(self, opacity): + opacity = float(opacity) if opacity not in self.fill_opacities: op = Dictionary({'Type':Name('ExtGState'), 'ca': opacity}) self.fill_opacities[opacity] = self.objects.add(op) self.current_page.set_opacity(self.fill_opacities[opacity]) - self.current_page.write_line(' '.join(map(fmtnum, color[:3])) + ' sc') def end_page(self): pageref = self.current_page.end(self.objects, self.stream) @@ -425,13 +443,93 @@ class PDFStream(object): self.objects.commit(r, self.stream) return r - def draw_image(self, x, y, xscale, yscale, imgref): + def add_image(self, img, cache_key): + ref = self.get_image(cache_key) + if ref is not None: + return ref + + fmt = img.format() + image = QImage(img) + if (image.depth() == 1 and img.colorTable().size() == 2 and + img.colorTable().at(0) == QColor(Qt.black).rgba() and + img.colorTable().at(1) == QColor(Qt.white).rgba()): + if fmt == QImage.Format_MonoLSB: + image = image.convertToFormat(QImage.Format_Mono) + fmt = QImage.Format_Mono + else: + if (fmt != QImage.Format_RGB32 and fmt != QImage.Format_ARGB32): + image = image.convertToFormat(QImage.Format_ARGB32) + fmt = QImage.Format_ARGB32 + + w = image.width() + h = image.height() + d = image.depth() + + if fmt == QImage.Format_Mono: + bytes_per_line = (w + 7) >> 3 + data = image.constBits().asstring(bytes_per_line * h) + return self.write_image(data, w, h, d, cache_key=cache_key) + + ba = QByteArray() + buf = QBuffer(ba) + image.save(buf, 'jpeg', 94) + data = bytes(ba.data()) + has_alpha = has_mask = False + soft_mask = mask = None + + if fmt == QImage.Format_ARGB32: + tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4] + sdata = bytearray(tmask) + vals = set(sdata) + vals.discard(255) + has_mask = bool(vals) + vals.discard(0) + has_alpha = bool(vals) + + if has_alpha: + soft_mask = self.write_image(tmask, w, h, 8) + elif has_mask: + # dither the soft mask to 1bit and add it. This also helps PDF + # viewers without transparency support + bytes_per_line = (w + 7) >> 3 + mdata = bytearray(0 for i in xrange(bytes_per_line * h)) + spos = mpos = 0 + for y in xrange(h): + for x in xrange(w): + if sdata[spos]: + mdata[mpos + x>>3] |= (0x80 >> (x&7)) + spos += 1 + mpos += bytes_per_line + mdata = bytes(mdata) + mask = self.write_image(mdata, w, h, 1) + + return self.write_image(data, w, h, 32, mask=mask, dct=True, + soft_mask=soft_mask, cache_key=cache_key) + + def add_pattern(self, pattern): + if pattern.cache_key not in self.pattern_cache: + self.pattern_cache[pattern.cache_key] = self.objects.add(pattern) + return self.current_page.add_pattern(self.pattern_cache[pattern.cache_key]) + + def draw_image(self, x, y, width, height, imgref): name = self.current_page.add_image(imgref) - self.current_page.write('q %s 0 0 %s %s %s cm '%(fmtnum(xscale), - fmtnum(yscale), fmtnum(x), fmtnum(y))) + self.current_page.write('q %s 0 0 %s %s %s cm '%(fmtnum(width), + fmtnum(-height), fmtnum(x), fmtnum(y+height))) serialize(Name(name), self.current_page) self.current_page.write_line(' Do Q') + def apply_fill(self, color=None, pattern=None, opacity=None): + if opacity is not None: + self.set_fill_opacity(opacity) + wl = self.current_page.write_line + if color is not None and pattern is None: + wl(' '.join(map(fmtnum, color)) + ' rg') + elif color is None and pattern is not None: + wl('/Pattern cs /%s scn'%pattern) + elif color is not None and pattern is not None: + col = ' '.join(map(fmtnum, color)) + wl('/PCSp cs %s /%s scn'%(col, pattern)) + def end(self): if self.current_page.getvalue(): self.end_page() diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py new file mode 100644 index 0000000000..3ea447dfc7 --- /dev/null +++ b/src/calibre/ebooks/pdf/render/test.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2012, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import os +from tempfile import gettempdir + +from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, + QApplication, QPainter, Qt, QImage) +QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, Qt + +from calibre.ebooks.pdf.render.engine import PdfDevice + +def full(dev): + p = QPainter(dev) + if isinstance(dev, PdfDevice): + dev.init_page() + + xmax, ymax = p.viewport().width(), p.viewport().height() + b = p.brush() + try: + p.drawRect(0, 0, xmax, ymax) + p.drawPolyline(QPoint(0, 0), QPoint(xmax, 0), QPoint(xmax, ymax), + QPoint(0, ymax), QPoint(0, 0)) + pp = QPainterPath() + pp.addRect(0, 0, xmax, ymax) + p.drawPath(pp) + p.save() + for i in xrange(3): + col = [0, 0, 0, 200] + col[i] = 255 + p.setOpacity(0.3) + p.fillRect(0, 0, xmax/10, xmax/10, QBrush(QColor(*col))) + p.setOpacity(1) + p.drawRect(0, 0, xmax/10, xmax/10) + p.translate(xmax/10, xmax/10) + p.scale(1, 1.5) + p.restore() + + # p.scale(2, 2) + # p.rotate(45) + p.drawPixmap(0, 0, xmax/4, xmax/4, QPixmap(I('library.png'))) + p.drawRect(0, 0, xmax/4, xmax/4) + + f = p.font() + f.setPointSize(20) + # f.setLetterSpacing(f.PercentageSpacing, 200) + f.setUnderline(True) + # f.setOverline(True) + # f.setStrikeOut(True) + f.setFamily('Calibri') + p.setFont(f) + # p.setPen(QColor(0, 0, 255)) + # p.scale(2, 2) + # p.rotate(45) + p.drawText(QPoint(xmax/3.9, 30), 'Some—text not By’s ū --- Д AV ff ff') + + b = QBrush(Qt.HorPattern) + b.setColor(QColor(Qt.blue)) + pix = QPixmap(I('console.png')) + w = xmax/4 + p.fillRect(0, ymax/3, w, w, b) + p.fillRect(xmax/3, ymax/3, w, w, QBrush(pix)) + p.drawTiledPixmap(QRectF(2*xmax/3, ymax/3, w, w), pix) + finally: + p.end() + if isinstance(dev, PdfDevice): + if dev.engine.errors_occurred: + raise SystemExit(1) + +def main(): + app = QApplication([]) + app + tdir = gettempdir() + pdf = os.path.join(tdir, 'painter.pdf') + func = full + with open(pdf, 'wb') as f: + dev = PdfDevice(f, xdpi=100, ydpi=100, compress=False) + img = QImage(dev.width(), dev.height(), + QImage.Format_ARGB32_Premultiplied) + img.setDotsPerMeterX(100*39.37) + img.setDotsPerMeterY(100*39.37) + img.fill(Qt.white) + func(dev) + func(img) + path = os.path.join(tdir, 'painter.png') + img.save(path) + print ('PDF written to:', pdf) + print ('Image written to:', path) + +if __name__ == '__main__': + main() + + From 88a13dc8ee2e03b074beffa18b94d844b1ceedd2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 14:44:19 +0530 Subject: [PATCH 32/67] Fix drawTiledPixmap() to use the correct origin --- src/calibre/ebooks/pdf/render/engine.py | 4 ++-- src/calibre/ebooks/pdf/render/test.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/engine.py b/src/calibre/ebooks/pdf/render/engine.py index 7987bd9c6e..6afbef223f 100644 --- a/src/calibre/ebooks/pdf/render/engine.py +++ b/src/calibre/ebooks/pdf/render/engine.py @@ -153,12 +153,12 @@ class PdfEngine(QPaintEngine): def drawTiledPixmap(self, rect, pixmap, point): self.apply_graphics_state() brush = QBrush(pixmap) + bl = rect.topLeft() color, opacity, pattern, do_fill = self.graphics.convert_brush( - brush, -point, 1.0, self.pdf, self.pdf_system, + brush, bl-point, 1.0, self.pdf, self.pdf_system, self.painter().transform()) self.pdf.save_stack() self.pdf.apply_fill(color, pattern) - bl = rect.topLeft() self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(), stroke=False, fill=True) self.pdf.restore_stack() diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 3ea447dfc7..7a53741ca8 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -11,8 +11,9 @@ import os from tempfile import gettempdir from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, - QApplication, QPainter, Qt, QImage) -QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, Qt + QApplication, QPainter, Qt, QImage, QLinearGradient, + QPointF) +QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, Qt, QPointF from calibre.ebooks.pdf.render.engine import PdfDevice @@ -66,7 +67,14 @@ def full(dev): w = xmax/4 p.fillRect(0, ymax/3, w, w, b) p.fillRect(xmax/3, ymax/3, w, w, QBrush(pix)) - p.drawTiledPixmap(QRectF(2*xmax/3, ymax/3, w, w), pix) + x, y = 2*xmax/3, ymax/3 + p.drawTiledPixmap(QRectF(x, y, w, w), pix, QPointF(10, 10)) + + x, y = 1, ymax/1.9 + g = QLinearGradient(QPointF(x, y), QPointF(x+w, y+w)) + g.setColorAt(0, QColor('#00f')) + g.setColorAt(1, QColor('#006')) + p.fillRect(x, y, w, w, QBrush(g)) finally: p.end() if isinstance(dev, PdfDevice): From c84fa4bf80987b8610203a516e3856ee653dc15d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 15:51:59 +0530 Subject: [PATCH 33/67] ... --- src/calibre/ebooks/pdf/render/test.py | 111 +++++++++++++------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 7a53741ca8..cf077607a0 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -17,64 +17,65 @@ QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, Qt, QPointF from calibre.ebooks.pdf.render.engine import PdfDevice -def full(dev): +def full(p, xmax, ymax): + p.drawRect(0, 0, xmax, ymax) + p.drawPolyline(QPoint(0, 0), QPoint(xmax, 0), QPoint(xmax, ymax), + QPoint(0, ymax), QPoint(0, 0)) + pp = QPainterPath() + pp.addRect(0, 0, xmax, ymax) + p.drawPath(pp) + p.save() + for i in xrange(3): + col = [0, 0, 0, 200] + col[i] = 255 + p.setOpacity(0.3) + p.fillRect(0, 0, xmax/10, xmax/10, QBrush(QColor(*col))) + p.setOpacity(1) + p.drawRect(0, 0, xmax/10, xmax/10) + p.translate(xmax/10, xmax/10) + p.scale(1, 1.5) + p.restore() + + # p.scale(2, 2) + # p.rotate(45) + p.drawPixmap(0, 0, xmax/4, xmax/4, QPixmap(I('library.png'))) + p.drawRect(0, 0, xmax/4, xmax/4) + + f = p.font() + f.setPointSize(20) + # f.setLetterSpacing(f.PercentageSpacing, 200) + f.setUnderline(True) + # f.setOverline(True) + # f.setStrikeOut(True) + f.setFamily('Calibri') + p.setFont(f) + # p.setPen(QColor(0, 0, 255)) + # p.scale(2, 2) + # p.rotate(45) + p.drawText(QPoint(xmax/3.9, 30), 'Some—text not By’s ū --- Д AV ff ff') + + b = QBrush(Qt.HorPattern) + b.setColor(QColor(Qt.blue)) + pix = QPixmap(I('console.png')) + w = xmax/4 + p.fillRect(0, ymax/3, w, w, b) + p.fillRect(xmax/3, ymax/3, w, w, QBrush(pix)) + x, y = 2*xmax/3, ymax/3 + p.drawTiledPixmap(QRectF(x, y, w, w), pix, QPointF(10, 10)) + + x, y = 1, ymax/1.9 + g = QLinearGradient(QPointF(x, y), QPointF(x+w, y+w)) + g.setColorAt(0, QColor('#00f')) + g.setColorAt(1, QColor('#fff')) + p.fillRect(x, y, w, w, QBrush(g)) + +def run(dev, func): p = QPainter(dev) if isinstance(dev, PdfDevice): dev.init_page() - xmax, ymax = p.viewport().width(), p.viewport().height() - b = p.brush() try: - p.drawRect(0, 0, xmax, ymax) - p.drawPolyline(QPoint(0, 0), QPoint(xmax, 0), QPoint(xmax, ymax), - QPoint(0, ymax), QPoint(0, 0)) - pp = QPainterPath() - pp.addRect(0, 0, xmax, ymax) - p.drawPath(pp) - p.save() - for i in xrange(3): - col = [0, 0, 0, 200] - col[i] = 255 - p.setOpacity(0.3) - p.fillRect(0, 0, xmax/10, xmax/10, QBrush(QColor(*col))) - p.setOpacity(1) - p.drawRect(0, 0, xmax/10, xmax/10) - p.translate(xmax/10, xmax/10) - p.scale(1, 1.5) - p.restore() - - # p.scale(2, 2) - # p.rotate(45) - p.drawPixmap(0, 0, xmax/4, xmax/4, QPixmap(I('library.png'))) - p.drawRect(0, 0, xmax/4, xmax/4) - - f = p.font() - f.setPointSize(20) - # f.setLetterSpacing(f.PercentageSpacing, 200) - f.setUnderline(True) - # f.setOverline(True) - # f.setStrikeOut(True) - f.setFamily('Calibri') - p.setFont(f) - # p.setPen(QColor(0, 0, 255)) - # p.scale(2, 2) - # p.rotate(45) - p.drawText(QPoint(xmax/3.9, 30), 'Some—text not By’s ū --- Д AV ff ff') - - b = QBrush(Qt.HorPattern) - b.setColor(QColor(Qt.blue)) - pix = QPixmap(I('console.png')) - w = xmax/4 - p.fillRect(0, ymax/3, w, w, b) - p.fillRect(xmax/3, ymax/3, w, w, QBrush(pix)) - x, y = 2*xmax/3, ymax/3 - p.drawTiledPixmap(QRectF(x, y, w, w), pix, QPointF(10, 10)) - - x, y = 1, ymax/1.9 - g = QLinearGradient(QPointF(x, y), QPointF(x+w, y+w)) - g.setColorAt(0, QColor('#00f')) - g.setColorAt(1, QColor('#006')) - p.fillRect(x, y, w, w, QBrush(g)) + func(p, xmax, ymax) finally: p.end() if isinstance(dev, PdfDevice): @@ -94,8 +95,8 @@ def main(): img.setDotsPerMeterX(100*39.37) img.setDotsPerMeterY(100*39.37) img.fill(Qt.white) - func(dev) - func(img) + run(dev, func) + run(img, func) path = os.path.join(tdir, 'painter.png') img.save(path) print ('PDF written to:', pdf) From 9caf30fc1a3405e6cb4b01baebac2cfb0c482de7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 16:20:14 +0530 Subject: [PATCH 34/67] ... --- src/calibre/ebooks/pdf/render/test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index cf077607a0..8935650a92 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -82,12 +82,22 @@ def run(dev, func): if dev.engine.errors_occurred: raise SystemExit(1) +def brush(p, xmax, ymax): + x = y = 0 + w = xmax/3 + 10 + g = QLinearGradient(QPointF(0, 0), QPointF(0, w)) + g.setSpread(g.RepeatSpread) + g.setColorAt(0, QColor('#00f')) + g.setColorAt(1, QColor('#fff')) + p.fillRect(x, y, w, w, QBrush(g)) + p.drawRect(x, y, w, w) + def main(): app = QApplication([]) app tdir = gettempdir() pdf = os.path.join(tdir, 'painter.pdf') - func = full + func = brush with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=100, ydpi=100, compress=False) img = QImage(dev.width(), dev.height(), From 32165539ea51f6e071526d60c883d51e4f700a76 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 16:41:19 +0530 Subject: [PATCH 35/67] ... --- src/calibre/ebooks/pdf/render/test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 8935650a92..55fd5f8aea 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -98,12 +98,13 @@ def main(): tdir = gettempdir() pdf = os.path.join(tdir, 'painter.pdf') func = brush + dpi = 100 with open(pdf, 'wb') as f: - dev = PdfDevice(f, xdpi=100, ydpi=100, compress=False) + dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False) img = QImage(dev.width(), dev.height(), QImage.Format_ARGB32_Premultiplied) - img.setDotsPerMeterX(100*39.37) - img.setDotsPerMeterY(100*39.37) + img.setDotsPerMeterX(dpi*39.37) + img.setDotsPerMeterY(dpi*39.37) img.fill(Qt.white) run(dev, func) run(img, func) From 9f13e30737ef809f731c1c576519acc554b6e63d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 18:42:08 +0530 Subject: [PATCH 36/67] Workaround for Qt's broken emulation of gradients with texture patterns --- src/calibre/ebooks/pdf/render/engine.py | 19 +++-- src/calibre/ebooks/pdf/render/graphics.py | 94 +++++++++++++++++------ src/calibre/ebooks/pdf/render/test.py | 17 ++-- 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/engine.py b/src/calibre/ebooks/pdf/render/engine.py index 6afbef223f..77f1f00c57 100644 --- a/src/calibre/ebooks/pdf/render/engine.py +++ b/src/calibre/ebooks/pdf/render/engine.py @@ -93,7 +93,11 @@ class PdfEngine(QPaintEngine): raise RuntimeError('Failed to load qt_hack with err: %s'%err) def apply_graphics_state(self): - self.graphics(self.pdf, self.pdf_system, self.painter()) + self.graphics(self.pdf_system, self.painter()) + + def resolve_fill(self, rect): + self.graphics.resolve_fill(rect, self.pdf_system, + self.painter().transform()) @property def do_fill(self): @@ -117,6 +121,7 @@ class PdfEngine(QPaintEngine): self.page_height), compress=self.compress, mark_links=self.mark_links, debug=self.debug) + self.graphics.begin(self.pdf) except: self.errors(traceback.format_exc()) self.errors_occurred = True @@ -155,7 +160,7 @@ class PdfEngine(QPaintEngine): brush = QBrush(pixmap) bl = rect.topLeft() color, opacity, pattern, do_fill = self.graphics.convert_brush( - brush, bl-point, 1.0, self.pdf, self.pdf_system, + brush, bl-point, 1.0, self.pdf_system, self.painter().transform()) self.pdf.save_stack() self.pdf.apply_fill(color, pattern) @@ -211,10 +216,12 @@ class PdfEngine(QPaintEngine): @store_error def drawRects(self, rects): self.apply_graphics_state() - for rect in rects: - bl = rect.topLeft() - self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(), - stroke=self.do_stroke, fill=self.do_fill) + with self.graphics: + for rect in rects: + self.resolve_fill(rect) + bl = rect.topLeft() + self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(), + stroke=self.do_stroke, fill=self.do_fill) def create_sfnt(self, text_item): get_table = partial(self.qt_hack.get_sfnt_table, text_item) diff --git a/src/calibre/ebooks/pdf/render/graphics.py b/src/calibre/ebooks/pdf/render/graphics.py index 384809598a..f9353d5358 100644 --- a/src/calibre/ebooks/pdf/render/graphics.py +++ b/src/calibre/ebooks/pdf/render/graphics.py @@ -8,6 +8,7 @@ __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' from math import sqrt +from collections import namedtuple from PyQt4.Qt import ( QBrush, QPen, Qt, QPointF, QTransform, QPainterPath, QPaintEngine, QImage) @@ -41,6 +42,8 @@ def convert_path(path): # {{{ return p # }}} +Brush = namedtuple('Brush', 'origin brush color') + class TilingPattern(Stream): def __init__(self, cache_key, matrix, w=8, h=8, paint_type=2, compress=False): @@ -222,18 +225,25 @@ class QtPattern(TilingPattern): class TexturePattern(TilingPattern): - def __init__(self, pixmap, matrix, pdf): - image = pixmap.toImage() - cache_key = pixmap.cacheKey() - imgref = pdf.add_image(image, cache_key) - paint_type = (2 if image.format() in {QImage.Format_MonoLSB, - QImage.Format_Mono} else 1) - super(TexturePattern, self).__init__( - cache_key, matrix, w=image.width(), h=image.height(), - paint_type=paint_type) - m = (self.w, 0, 0, -self.h, 0, self.h) - self.resources['XObject'] = Dictionary({'Texture':imgref}) - self.write_line('%s cm /Texture Do'%(' '.join(map(fmtnum, m)))) + def __init__(self, pixmap, matrix, pdf, clone=None): + if clone is None: + image = pixmap.toImage() + cache_key = pixmap.cacheKey() + imgref = pdf.add_image(image, cache_key) + paint_type = (2 if image.format() in {QImage.Format_MonoLSB, + QImage.Format_Mono} else 1) + super(TexturePattern, self).__init__( + cache_key, matrix, w=image.width(), h=image.height(), + paint_type=paint_type) + m = (self.w, 0, 0, -self.h, 0, self.h) + self.resources['XObject'] = Dictionary({'Texture':imgref}) + self.write_line('%s cm /Texture Do'%(' '.join(map(fmtnum, m)))) + else: + super(TexturePattern, self).__init__( + clone.cache_key[1], matrix, w=clone.w, h=clone.h, + paint_type=clone.paint_type) + self.resources['XObject'] = Dictionary(clone.resources['XObject']) + self.write(clone.getvalue()) class GraphicsState(object): @@ -275,6 +285,9 @@ class Graphics(object): self.current_state = GraphicsState() self.pending_state = None + def begin(self, pdf): + self.pdf = pdf + def update_state(self, state, painter): flags = state.state() if self.pending_state is None: @@ -304,13 +317,14 @@ class Graphics(object): self.current_state = GraphicsState() self.pending_state = None - def __call__(self, pdf, pdf_system, painter): + def __call__(self, pdf_system, painter): # Apply the currently pending state to the PDF if self.pending_state is None: return pdf_state = self.current_state ps = self.pending_state + pdf = self.pdf if (ps.transform != pdf_state.transform or ps.clip != pdf_state.clip): pdf.restore_stack() @@ -321,11 +335,11 @@ class Graphics(object): pdf.transform(ps.transform) if (pdf_state.opacity != ps.opacity or pdf_state.stroke != ps.stroke): - self.apply_stroke(ps, pdf, pdf_system, painter) + self.apply_stroke(ps, pdf_system, painter) if (pdf_state.opacity != ps.opacity or pdf_state.fill != ps.fill or pdf_state.brush_origin != ps.brush_origin): - self.apply_fill(ps, pdf, pdf_system, painter) + self.apply_fill(ps, pdf_system, painter) if (pdf_state.clip != ps.clip): p = convert_path(ps.clip) @@ -336,25 +350,28 @@ class Graphics(object): self.current_state = self.pending_state self.pending_state = None - def convert_brush(self, brush, brush_origin, global_opacity, pdf, + def convert_brush(self, brush, brush_origin, global_opacity, pdf_system, qt_system): # Convert a QBrush to PDF operators style = brush.style() + pdf = self.pdf - pattern = color = None + pattern = color = pat = None opacity = 1.0 do_fill = True matrix = (QTransform.fromTranslate(brush_origin.x(), brush_origin.y()) * pdf_system * qt_system.inverted()[0]) vals = list(brush.color().getRgbF()) + self.brushobj = None if style <= Qt.DiagCrossPattern: opacity = global_opacity * vals[-1] color = vals[:3] if style > Qt.SolidPattern: - pattern = pdf.add_pattern(QtPattern(style, matrix)) + pat = QtPattern(style, matrix) + pattern = pdf.add_pattern(pat) if opacity < 1e-4 or style == Qt.NoBrush: do_fill = False @@ -370,15 +387,17 @@ class Graphics(object): if opacity < 1e-4 or style == Qt.NoBrush: do_fill = False + self.brushobj = Brush(brush_origin, pat, color) # TODO: Add support for gradient fills return color, opacity, pattern, do_fill - def apply_stroke(self, state, pdf, pdf_system, painter): + def apply_stroke(self, state, pdf_system, painter): # TODO: Handle pens with non solid brushes by setting the colorspace # for stroking to a pattern # TODO: Support miter limit by using QPainterPathStroker pen = state.stroke self.pending_state.do_stroke = True + pdf = self.pdf if pen.style() == Qt.NoPen: self.pending_state.do_stroke = False @@ -417,10 +436,41 @@ class Graphics(object): if vals[-1] < 1e-5 or b.style() == Qt.NoBrush: self.pending_state.do_stroke = False - def apply_fill(self, state, pdf, pdf_system, painter): + def apply_fill(self, state, pdf_system, painter): self.pending_state.do_fill = True color, opacity, pattern, self.pending_state.do_fill = self.convert_brush( - state.fill, state.brush_origin, state.opacity, pdf, pdf_system, + state.fill, state.brush_origin, state.opacity, pdf_system, painter.transform()) - pdf.apply_fill(color, pattern, opacity) + self.pdf.apply_fill(color, pattern, opacity) + self.last_fill = self.brushobj + + def __enter__(self): + self.pdf.save_stack() + + def __exit__(self, *args): + self.pdf.restore_stack() + + def resolve_fill(self, rect, pdf_system, qt_system): + ''' + Qt's paint system does not update brushOrigin when using + TexturePatterns and it also uses TexturePatterns to emulate gradients, + leading to brokenness. So this method allows the paint engine to update + the brush origin before painting an object. While not perfect, this is + better than nothing. + ''' + if not self.current_state.do_fill: + return + + if isinstance(self.last_fill.brush, TexturePattern): + tl = rect.topLeft() + if tl == self.last_fill.origin: + return + + matrix = (QTransform.fromTranslate(tl.x(), tl.y()) + * pdf_system * qt_system.inverted()[0]) + + pat = TexturePattern(None, matrix, self.pdf, clone=self.last_fill.brush) + pattern = self.pdf.add_pattern(pat) + self.pdf.apply_fill(self.last_fill.color, pattern) + diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 55fd5f8aea..555af9206f 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -83,21 +83,20 @@ def run(dev, func): raise SystemExit(1) def brush(p, xmax, ymax): - x = y = 0 - w = xmax/3 + 10 - g = QLinearGradient(QPointF(0, 0), QPointF(0, w)) - g.setSpread(g.RepeatSpread) - g.setColorAt(0, QColor('#00f')) - g.setColorAt(1, QColor('#fff')) - p.fillRect(x, y, w, w, QBrush(g)) - p.drawRect(x, y, w, w) + x = xmax/3 + y = 0 + w = xmax/2 + pix = QPixmap(I('console.png')) + p.fillRect(x, y, w, w, QBrush(pix)) + + p.fillRect(0, y+xmax/1.9, w, w, QBrush(pix)) def main(): app = QApplication([]) app tdir = gettempdir() pdf = os.path.join(tdir, 'painter.pdf') - func = brush + func = full dpi = 100 with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False) From 0dd9c26dbe085f6d2915f30022ec9484088fff49 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 31 Dec 2012 20:08:17 +0530 Subject: [PATCH 37/67] Handle non-solid pens --- src/calibre/ebooks/pdf/render/engine.py | 1 - src/calibre/ebooks/pdf/render/graphics.py | 18 ++++------- src/calibre/ebooks/pdf/render/serialize.py | 36 ++++++++++++---------- src/calibre/ebooks/pdf/render/test.py | 18 +++++++++-- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/engine.py b/src/calibre/ebooks/pdf/render/engine.py index 77f1f00c57..b65aed0660 100644 --- a/src/calibre/ebooks/pdf/render/engine.py +++ b/src/calibre/ebooks/pdf/render/engine.py @@ -109,7 +109,6 @@ class PdfEngine(QPaintEngine): def init_page(self): self.pdf.transform(self.pdf_system) - self.pdf.set_rgb_colorspace() self.graphics.reset() self.pdf.save_stack() self.current_page_inited = True diff --git a/src/calibre/ebooks/pdf/render/graphics.py b/src/calibre/ebooks/pdf/render/graphics.py index f9353d5358..43b1c72fdb 100644 --- a/src/calibre/ebooks/pdf/render/graphics.py +++ b/src/calibre/ebooks/pdf/render/graphics.py @@ -15,7 +15,7 @@ from PyQt4.Qt import ( from calibre.ebooks.pdf.render.common import ( Name, Array, fmtnum, Stream, Dictionary) -from calibre.ebooks.pdf.render.serialize import Path, Color +from calibre.ebooks.pdf.render.serialize import Path def convert_path(path): # {{{ p = Path() @@ -392,8 +392,6 @@ class Graphics(object): return color, opacity, pattern, do_fill def apply_stroke(self, state, pdf_system, painter): - # TODO: Handle pens with non solid brushes by setting the colorspace - # for stroking to a pattern # TODO: Support miter limit by using QPainterPathStroker pen = state.stroke self.pending_state.do_stroke = True @@ -427,14 +425,10 @@ class Graphics(object): pdf.current_page.write(' 0 d ') # Stroke fill - b = pen.brush() - vals = list(b.color().getRgbF()) - vals[-1] *= state.opacity - color = Color(*vals) - pdf.set_stroke_color(color) - - if vals[-1] < 1e-5 or b.style() == Qt.NoBrush: - self.pending_state.do_stroke = False + color, opacity, pattern, self.pending_state.do_stroke = self.convert_brush( + pen.brush(), state.brush_origin, state.opacity, pdf_system, + painter.transform()) + self.pdf.apply_stroke(color, pattern, opacity) def apply_fill(self, state, pdf_system, painter): self.pending_state.do_fill = True @@ -458,7 +452,7 @@ class Graphics(object): the brush origin before painting an object. While not perfect, this is better than nothing. ''' - if not self.current_state.do_fill: + if not hasattr(self, 'last_fill') or not self.current_state.do_fill: return if isinstance(self.last_fill.brush, TexturePattern): diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index 54a5f674b4..ce4ae7fc6c 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -10,7 +10,6 @@ __docformat__ = 'restructuredtext en' import hashlib from future_builtins import map from itertools import izip -from collections import namedtuple from PyQt4.Qt import QBuffer, QByteArray, QImage, Qt, QColor, qRgba @@ -23,8 +22,6 @@ from calibre.ebooks.pdf.render.links import Links PDFVER = b'%PDF-1.3' -Color = namedtuple('Color', 'red green blue opacity') - class IndirectObjects(object): def __init__(self): @@ -353,9 +350,6 @@ class PDFStream(object): cm = ' '.join(map(fmtnum, vals)) self.current_page.write_line(cm + ' cm') - def set_rgb_colorspace(self): - self.current_page.write_line('/DeviceRGB CS /DeviceRGB cs') - def save_stack(self): self.current_page.write_line('q') @@ -391,13 +385,11 @@ class PDFStream(object): def serialize(self, o): serialize(o, self.current_page) - def set_stroke_color(self, color): - opacity = color.opacity + def set_stroke_opacity(self, opacity): if opacity not in self.stroke_opacities: op = Dictionary({'Type':Name('ExtGState'), 'CA': opacity}) self.stroke_opacities[opacity] = self.objects.add(op) self.current_page.set_opacity(self.stroke_opacities[opacity]) - self.current_page.write_line(' '.join(map(fmtnum, color[:3])) + ' SC') def set_fill_opacity(self, opacity): opacity = float(opacity) @@ -518,17 +510,27 @@ class PDFStream(object): serialize(Name(name), self.current_page) self.current_page.write_line(' Do Q') + def apply_color_space(self, color, pattern, stroke=False): + wl = self.current_page.write_line + if color is not None and pattern is None: + wl(' '.join(map(fmtnum, color)) + (' RG' if stroke else ' rg')) + elif color is None and pattern is not None: + wl('/Pattern %s /%s %s'%('CS' if stroke else 'cs', pattern, + 'SCN' if stroke else 'scn')) + elif color is not None and pattern is not None: + col = ' '.join(map(fmtnum, color)) + wl('/PCSp %s %s /%s %s'%('CS' if stroke else 'cs', col, pattern, + 'SCN' if stroke else 'scn')) + def apply_fill(self, color=None, pattern=None, opacity=None): if opacity is not None: self.set_fill_opacity(opacity) - wl = self.current_page.write_line - if color is not None and pattern is None: - wl(' '.join(map(fmtnum, color)) + ' rg') - elif color is None and pattern is not None: - wl('/Pattern cs /%s scn'%pattern) - elif color is not None and pattern is not None: - col = ' '.join(map(fmtnum, color)) - wl('/PCSp cs %s /%s scn'%(col, pattern)) + self.apply_color_space(color, pattern) + + def apply_stroke(self, color=None, pattern=None, opacity=None): + if opacity is not None: + self.set_stroke_opacity(opacity) + self.apply_color_space(color, pattern, stroke=True) def end(self): if self.current_page.getvalue(): diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 555af9206f..b631dc8276 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -12,7 +12,7 @@ from tempfile import gettempdir from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, QApplication, QPainter, Qt, QImage, QLinearGradient, - QPointF) + QPointF, QPen) QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, Qt, QPointF from calibre.ebooks.pdf.render.engine import PdfDevice @@ -69,6 +69,14 @@ def full(p, xmax, ymax): g.setColorAt(1, QColor('#fff')) p.fillRect(x, y, w, w, QBrush(g)) + pen = QPen(QBrush(Qt.blue)) + pen.setWidth(xmax/3) + p.setPen(pen) + x += w + w/10 + y += w + p.drawLine(x, y, x+w, y) + + def run(dev, func): p = QPainter(dev) if isinstance(dev, PdfDevice): @@ -91,12 +99,18 @@ def brush(p, xmax, ymax): p.fillRect(0, y+xmax/1.9, w, w, QBrush(pix)) +def pen(p, xmax, ymax): + pix = QPixmap(I('console.png')) + pen = QPen(QBrush(pix), 60) + p.setPen(pen) + p.drawRect(0, xmax/3, xmax/3, xmax/2) + def main(): app = QApplication([]) app tdir = gettempdir() pdf = os.path.join(tdir, 'painter.pdf') - func = full + func = pen dpi = 100 with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False) From 0c0cb03bb0b48746adfc7e901ead88965cdc667b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 09:31:44 +0530 Subject: [PATCH 38/67] Updated Alternet --- recipes/alternet.recipe | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/recipes/alternet.recipe b/recipes/alternet.recipe index e58376cc42..0bd608e0e7 100644 --- a/recipes/alternet.recipe +++ b/recipes/alternet.recipe @@ -10,14 +10,12 @@ class Alternet(BasicNewsRecipe): category = 'News, Magazine' description = 'News magazine and online community' feeds = [ - (u'Front Page', u'http://feeds.feedblitz.com/alternet'), - (u'Breaking News', u'http://feeds.feedblitz.com/alternet_breaking_news'), - (u'Top Ten Campaigns', u'http://feeds.feedblitz.com/alternet_top_10_campaigns'), - (u'Special Coverage Areas', u'http://feeds.feedblitz.com/alternet_coverage') + (u'Front Page', u'http://feeds.feedblitz.com/alternet') ] + remove_attributes = ['width', 'align','cellspacing'] remove_javascript = True - use_embedded_content = False + use_embedded_content = True no_stylesheets = True language = 'en' encoding = 'UTF-8' From f77765ff3c458819ac8c0ae696a46012b5b70b3c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 09:52:44 +0530 Subject: [PATCH 39/67] Update NY Times --- recipes/nytimes.recipe | 83 +++++++++++++++++++++++++++++++++++--- recipes/nytimes_sub.recipe | 83 +++++++++++++++++++++++++++++++++++--- 2 files changed, 156 insertions(+), 10 deletions(-) diff --git a/recipes/nytimes.recipe b/recipes/nytimes.recipe index ba97a2c0be..f5b994275e 100644 --- a/recipes/nytimes.recipe +++ b/recipes/nytimes.recipe @@ -15,6 +15,7 @@ from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, BeautifulStoneSoup class NYTimes(BasicNewsRecipe): recursions=1 # set this to zero to omit Related articles lists + match_regexps=[r'/[12][0-9][0-9][0-9]/[0-9]+/'] # speeds up processing by preventing index page links from being followed # set getTechBlogs to True to include the technology blogs # set tech_oldest_article to control article age @@ -24,6 +25,14 @@ class NYTimes(BasicNewsRecipe): tech_oldest_article = 14 tech_max_articles_per_feed = 25 + # set getPopularArticles to False if you don't want the Most E-mailed and Most Viewed articles + # otherwise you will get up to 20 of the most popular e-mailed and viewed articles (in each category) + getPopularArticles = True + popularPeriod = '1' # set this to the number of days to include in the measurement + # e.g. 7 will get the most popular measured over the last 7 days + # and 30 will get the most popular measured over 30 days. + # you still only get up to 20 articles in each category + # set headlinesOnly to True for the headlines-only version. If True, webEdition is ignored. headlinesOnly = True @@ -376,6 +385,7 @@ class NYTimes(BasicNewsRecipe): masthead_url = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif' + def short_title(self): return self.title @@ -384,6 +394,7 @@ class NYTimes(BasicNewsRecipe): from contextlib import closing import copy from calibre.ebooks.chardet import xml_to_unicode + print("ARTICLE_TO_SOUP "+url_or_raw) if re.match(r'\w+://', url_or_raw): br = self.clone_browser(self.browser) open_func = getattr(br, 'open_novisit', br.open) @@ -475,6 +486,67 @@ class NYTimes(BasicNewsRecipe): description=description, author=author, content='')) + def get_popular_articles(self,ans): + if self.getPopularArticles: + popular_articles = {} + key_list = [] + + def handleh3(h3tag): + try: + url = h3tag.a['href'] + except: + return ('','','','') + url = re.sub(r'\?.*', '', url) + if self.exclude_url(url): + return ('','','','') + url += '?pagewanted=all' + title = self.tag_to_string(h3tag.a,False) + h6tag = h3tag.findNextSibling('h6') + if h6tag is not None: + author = self.tag_to_string(h6tag,False) + else: + author = '' + ptag = h3tag.findNextSibling('p') + if ptag is not None: + desc = self.tag_to_string(ptag,False) + else: + desc = '' + return(title,url,author,desc) + + + have_emailed = False + emailed_soup = self.index_to_soup('http://www.nytimes.com/most-popular-emailed?period='+self.popularPeriod) + for h3tag in emailed_soup.findAll('h3'): + (title,url,author,desc) = handleh3(h3tag) + if url=='': + continue + if not have_emailed: + key_list.append('Most E-Mailed') + popular_articles['Most E-Mailed'] = [] + have_emailed = True + popular_articles['Most E-Mailed'].append( + dict(title=title, url=url, date=strftime('%a, %d %b'), + description=desc, author=author, + content='')) + have_viewed = False + viewed_soup = self.index_to_soup('http://www.nytimes.com/most-popular-viewed?period='+self.popularPeriod) + for h3tag in viewed_soup.findAll('h3'): + (title,url,author,desc) = handleh3(h3tag) + if url=='': + continue + if not have_viewed: + key_list.append('Most Viewed') + popular_articles['Most Viewed'] = [] + have_viewed = True + popular_articles['Most Viewed'].append( + dict(title=title, url=url, date=strftime('%a, %d %b'), + description=desc, author=author, + content='')) + viewed_ans = [(k, popular_articles[k]) for k in key_list if popular_articles.has_key(k)] + for x in viewed_ans: + ans.append(x) + return ans + def get_tech_feeds(self,ans): if self.getTechBlogs: tech_articles = {} @@ -536,7 +608,7 @@ class NYTimes(BasicNewsRecipe): self.handle_article(lidiv) self.ans = [(k, self.articles[k]) for k in self.ans if self.articles.has_key(k)] - return self.filter_ans(self.get_tech_feeds(self.ans)) + return self.filter_ans(self.get_tech_feeds(self.get_popular_articles(self.ans))) def parse_todays_index(self): @@ -569,7 +641,7 @@ class NYTimes(BasicNewsRecipe): self.handle_article(lidiv) self.ans = [(k, self.articles[k]) for k in self.ans if self.articles.has_key(k)] - return self.filter_ans(self.get_tech_feeds(self.ans)) + return self.filter_ans(self.get_tech_feeds(self.get_popular_articles(self.ans))) def parse_headline_index(self): @@ -643,7 +715,7 @@ class NYTimes(BasicNewsRecipe): self.articles[section_name].append(dict(title=title, url=url, date=pubdate, description=description, author=author, content='')) self.ans = [(k, self.articles[k]) for k in self.ans if self.articles.has_key(k)] - return self.filter_ans(self.get_tech_feeds(self.ans)) + return self.filter_ans(self.get_tech_feeds(self.get_popular_articles(self.ans))) def parse_index(self): if self.headlinesOnly: @@ -731,7 +803,7 @@ class NYTimes(BasicNewsRecipe): def preprocess_html(self, soup): - #print("PREPROCESS TITLE="+self.tag_to_string(soup.title)) + #print(strftime("%H:%M:%S")+" -- PREPROCESS TITLE="+self.tag_to_string(soup.title)) skip_tag = soup.find(True, {'name':'skip'}) if skip_tag is not None: #url = 'http://www.nytimes.com' + re.sub(r'\?.*', '', skip_tag.parent['href']) @@ -907,6 +979,7 @@ class NYTimes(BasicNewsRecipe): for aside in soup.findAll('div','aside'): aside.extract() soup = self.strip_anchors(soup,True) + #print("RECURSIVE: "+self.tag_to_string(soup.title)) if soup.find('div',attrs={'id':'blogcontent'}) is None: if first_fetch: @@ -1071,7 +1144,7 @@ class NYTimes(BasicNewsRecipe): divTag.replaceWith(tag) except: self.log("ERROR: Problem in Add class=authorId to

so we can format with CSS") - + #print(strftime("%H:%M:%S")+" -- POSTPROCESS TITLE="+self.tag_to_string(soup.title)) return soup def populate_article_metadata(self, article, soup, first): diff --git a/recipes/nytimes_sub.recipe b/recipes/nytimes_sub.recipe index d550a5158f..df44856293 100644 --- a/recipes/nytimes_sub.recipe +++ b/recipes/nytimes_sub.recipe @@ -15,6 +15,7 @@ from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, BeautifulStoneSoup class NYTimes(BasicNewsRecipe): recursions=1 # set this to zero to omit Related articles lists + match_regexps=[r'/[12][0-9][0-9][0-9]/[0-9]+/'] # speeds up processing by preventing index page links from being followed # set getTechBlogs to True to include the technology blogs # set tech_oldest_article to control article age @@ -24,6 +25,14 @@ class NYTimes(BasicNewsRecipe): tech_oldest_article = 14 tech_max_articles_per_feed = 25 + # set getPopularArticles to False if you don't want the Most E-mailed and Most Viewed articles + # otherwise you will get up to 20 of the most popular e-mailed and viewed articles (in each category) + getPopularArticles = True + popularPeriod = '1' # set this to the number of days to include in the measurement + # e.g. 7 will get the most popular measured over the last 7 days + # and 30 will get the most popular measured over 30 days. + # you still only get up to 20 articles in each category + # set headlinesOnly to True for the headlines-only version. If True, webEdition is ignored. headlinesOnly = False @@ -376,6 +385,7 @@ class NYTimes(BasicNewsRecipe): masthead_url = 'http://graphics8.nytimes.com/images/misc/nytlogo379x64.gif' + def short_title(self): return self.title @@ -384,6 +394,7 @@ class NYTimes(BasicNewsRecipe): from contextlib import closing import copy from calibre.ebooks.chardet import xml_to_unicode + print("ARTICLE_TO_SOUP "+url_or_raw) if re.match(r'\w+://', url_or_raw): br = self.clone_browser(self.browser) open_func = getattr(br, 'open_novisit', br.open) @@ -475,6 +486,67 @@ class NYTimes(BasicNewsRecipe): description=description, author=author, content='')) + def get_popular_articles(self,ans): + if self.getPopularArticles: + popular_articles = {} + key_list = [] + + def handleh3(h3tag): + try: + url = h3tag.a['href'] + except: + return ('','','','') + url = re.sub(r'\?.*', '', url) + if self.exclude_url(url): + return ('','','','') + url += '?pagewanted=all' + title = self.tag_to_string(h3tag.a,False) + h6tag = h3tag.findNextSibling('h6') + if h6tag is not None: + author = self.tag_to_string(h6tag,False) + else: + author = '' + ptag = h3tag.findNextSibling('p') + if ptag is not None: + desc = self.tag_to_string(ptag,False) + else: + desc = '' + return(title,url,author,desc) + + + have_emailed = False + emailed_soup = self.index_to_soup('http://www.nytimes.com/most-popular-emailed?period='+self.popularPeriod) + for h3tag in emailed_soup.findAll('h3'): + (title,url,author,desc) = handleh3(h3tag) + if url=='': + continue + if not have_emailed: + key_list.append('Most E-Mailed') + popular_articles['Most E-Mailed'] = [] + have_emailed = True + popular_articles['Most E-Mailed'].append( + dict(title=title, url=url, date=strftime('%a, %d %b'), + description=desc, author=author, + content='')) + have_viewed = False + viewed_soup = self.index_to_soup('http://www.nytimes.com/most-popular-viewed?period='+self.popularPeriod) + for h3tag in viewed_soup.findAll('h3'): + (title,url,author,desc) = handleh3(h3tag) + if url=='': + continue + if not have_viewed: + key_list.append('Most Viewed') + popular_articles['Most Viewed'] = [] + have_viewed = True + popular_articles['Most Viewed'].append( + dict(title=title, url=url, date=strftime('%a, %d %b'), + description=desc, author=author, + content='')) + viewed_ans = [(k, popular_articles[k]) for k in key_list if popular_articles.has_key(k)] + for x in viewed_ans: + ans.append(x) + return ans + def get_tech_feeds(self,ans): if self.getTechBlogs: tech_articles = {} @@ -536,7 +608,7 @@ class NYTimes(BasicNewsRecipe): self.handle_article(lidiv) self.ans = [(k, self.articles[k]) for k in self.ans if self.articles.has_key(k)] - return self.filter_ans(self.get_tech_feeds(self.ans)) + return self.filter_ans(self.get_tech_feeds(self.get_popular_articles(self.ans))) def parse_todays_index(self): @@ -569,7 +641,7 @@ class NYTimes(BasicNewsRecipe): self.handle_article(lidiv) self.ans = [(k, self.articles[k]) for k in self.ans if self.articles.has_key(k)] - return self.filter_ans(self.get_tech_feeds(self.ans)) + return self.filter_ans(self.get_tech_feeds(self.get_popular_articles(self.ans))) def parse_headline_index(self): @@ -643,7 +715,7 @@ class NYTimes(BasicNewsRecipe): self.articles[section_name].append(dict(title=title, url=url, date=pubdate, description=description, author=author, content='')) self.ans = [(k, self.articles[k]) for k in self.ans if self.articles.has_key(k)] - return self.filter_ans(self.get_tech_feeds(self.ans)) + return self.filter_ans(self.get_tech_feeds(self.get_popular_articles(self.ans))) def parse_index(self): if self.headlinesOnly: @@ -731,7 +803,7 @@ class NYTimes(BasicNewsRecipe): def preprocess_html(self, soup): - #print("PREPROCESS TITLE="+self.tag_to_string(soup.title)) + #print(strftime("%H:%M:%S")+" -- PREPROCESS TITLE="+self.tag_to_string(soup.title)) skip_tag = soup.find(True, {'name':'skip'}) if skip_tag is not None: #url = 'http://www.nytimes.com' + re.sub(r'\?.*', '', skip_tag.parent['href']) @@ -907,6 +979,7 @@ class NYTimes(BasicNewsRecipe): for aside in soup.findAll('div','aside'): aside.extract() soup = self.strip_anchors(soup,True) + #print("RECURSIVE: "+self.tag_to_string(soup.title)) if soup.find('div',attrs={'id':'blogcontent'}) is None: if first_fetch: @@ -1071,7 +1144,7 @@ class NYTimes(BasicNewsRecipe): divTag.replaceWith(tag) except: self.log("ERROR: Problem in Add class=authorId to
so we can format with CSS") - + #print(strftime("%H:%M:%S")+" -- POSTPROCESS TITLE="+self.tag_to_string(soup.title)) return soup def populate_article_metadata(self, article, soup, first): From afe53a88449c7259f5232260fa2358417c1d9aea Mon Sep 17 00:00:00 2001 From: Translators <> Date: Tue, 1 Jan 2013 04:45:35 +0000 Subject: [PATCH 40/67] Launchpad automatic translations update. --- setup/iso_639/ca.po | 280 ++++++++++++++++++++++---------------------- 1 file changed, 140 insertions(+), 140 deletions(-) diff --git a/setup/iso_639/ca.po b/setup/iso_639/ca.po index fa4aebed41..8ca2f37de2 100644 --- a/setup/iso_639/ca.po +++ b/setup/iso_639/ca.po @@ -12,13 +12,13 @@ msgstr "" "Report-Msgid-Bugs-To: Debian iso-codes team \n" "POT-Creation-Date: 2011-11-25 14:01+0000\n" -"PO-Revision-Date: 2012-12-22 17:18+0000\n" +"PO-Revision-Date: 2012-12-31 12:50+0000\n" "Last-Translator: Ferran Rius \n" "Language-Team: Catalan \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-23 04:38+0000\n" +"X-Launchpad-Export-Date: 2013-01-01 04:45+0000\n" "X-Generator: Launchpad (build 16378)\n" "Language: ca\n" @@ -1744,7 +1744,7 @@ msgstr "Asu (Nigèria)" #. name for aun msgid "One; Molmo" -msgstr "One; Molmo" +msgstr "Oneià; Molmo" #. name for auo msgid "Auyokawa" @@ -1964,7 +1964,7 @@ msgstr "Leyigha" #. name for ayk msgid "Akuku" -msgstr "Akuku" +msgstr "Okpe-Idesa-Akuku; Akuku" #. name for ayl msgid "Arabic; Libyan" @@ -9984,7 +9984,7 @@ msgstr "Indri" #. name for ids msgid "Idesa" -msgstr "Idesa" +msgstr "Okpe-Idesa-Akuku; Idesa" #. name for idt msgid "Idaté" @@ -19524,7 +19524,7 @@ msgstr "" #. name for obi msgid "Obispeño" -msgstr "" +msgstr "Obispeño" #. name for obk msgid "Bontok; Southern" @@ -19532,7 +19532,7 @@ msgstr "Bontoc; meridional" #. name for obl msgid "Oblo" -msgstr "" +msgstr "Oblo" #. name for obm msgid "Moabite" @@ -19552,11 +19552,11 @@ msgstr "Bretó; antic" #. name for obu msgid "Obulom" -msgstr "" +msgstr "Obulom" #. name for oca msgid "Ocaina" -msgstr "" +msgstr "Ocaina" #. name for och msgid "Chinese; Old" @@ -19576,11 +19576,11 @@ msgstr "Matlazinca; Atzingo" #. name for oda msgid "Odut" -msgstr "" +msgstr "Odut" #. name for odk msgid "Od" -msgstr "" +msgstr "Od" #. name for odt msgid "Dutch; Old" @@ -19588,11 +19588,11 @@ msgstr "Holandès; antic" #. name for odu msgid "Odual" -msgstr "" +msgstr "Odual" #. name for ofo msgid "Ofo" -msgstr "" +msgstr "Ofo" #. name for ofs msgid "Frisian; Old" @@ -19604,11 +19604,11 @@ msgstr "" #. name for ogb msgid "Ogbia" -msgstr "" +msgstr "Ogbia" #. name for ogc msgid "Ogbah" -msgstr "" +msgstr "Ogbah" #. name for oge msgid "Georgian; Old" @@ -19616,7 +19616,7 @@ msgstr "" #. name for ogg msgid "Ogbogolo" -msgstr "" +msgstr "Ogbogolo" #. name for ogo msgid "Khana" @@ -19624,7 +19624,7 @@ msgstr "" #. name for ogu msgid "Ogbronuagum" -msgstr "" +msgstr "Ogbronuagum" #. name for oht msgid "Hittite; Old" @@ -19636,27 +19636,27 @@ msgstr "Hongarès; antic" #. name for oia msgid "Oirata" -msgstr "" +msgstr "Oirata" #. name for oin msgid "One; Inebu" -msgstr "" +msgstr "Oneià; Inebu" #. name for ojb msgid "Ojibwa; Northwestern" -msgstr "" +msgstr "Ojibwa; Nordoccidental" #. name for ojc msgid "Ojibwa; Central" -msgstr "" +msgstr "Ojibwa; Central" #. name for ojg msgid "Ojibwa; Eastern" -msgstr "" +msgstr "Ojibwa; Oriental" #. name for oji msgid "Ojibwa" -msgstr "" +msgstr "Ojibwa; Occidental" #. name for ojp msgid "Japanese; Old" @@ -19664,11 +19664,11 @@ msgstr "Japonès; antic" #. name for ojs msgid "Ojibwa; Severn" -msgstr "" +msgstr "Ojibwa; Severn" #. name for ojv msgid "Ontong Java" -msgstr "" +msgstr "Ontong Java" #. name for ojw msgid "Ojibwa; Western" @@ -19676,19 +19676,19 @@ msgstr "" #. name for oka msgid "Okanagan" -msgstr "" +msgstr "Colville-Okanagà" #. name for okb msgid "Okobo" -msgstr "" +msgstr "Okobo" #. name for okd msgid "Okodia" -msgstr "" +msgstr "Okodia" #. name for oke msgid "Okpe (Southwestern Edo)" -msgstr "" +msgstr "Okpe" #. name for okh msgid "Koresh-e Rostam" @@ -19696,15 +19696,15 @@ msgstr "" #. name for oki msgid "Okiek" -msgstr "" +msgstr "Okiek" #. name for okj msgid "Oko-Juwoi" -msgstr "" +msgstr "Oko-Juwoi" #. name for okk msgid "One; Kwamtim" -msgstr "" +msgstr "Oneià; Kwamtim" #. name for okl msgid "Kentish Sign Language; Old" @@ -19716,7 +19716,7 @@ msgstr "" #. name for okn msgid "Oki-No-Erabu" -msgstr "" +msgstr "Oki-No-Erabu" #. name for oko msgid "Korean; Old (3rd-9th cent.)" @@ -19728,19 +19728,19 @@ msgstr "" #. name for oks msgid "Oko-Eni-Osayen" -msgstr "" +msgstr "Oko-Eni-Osayen" #. name for oku msgid "Oku" -msgstr "" +msgstr "Oku" #. name for okv msgid "Orokaiva" -msgstr "" +msgstr "Orokaiwa" #. name for okx msgid "Okpe (Northwestern Edo)" -msgstr "" +msgstr "Okpe-Idesa-Akuku; Okpe" #. name for ola msgid "Walungge" @@ -19752,11 +19752,11 @@ msgstr "" #. name for ole msgid "Olekha" -msgstr "" +msgstr "Olekha" #. name for olm msgid "Oloma" -msgstr "" +msgstr "Oloma" #. name for olo msgid "Livvi" @@ -19768,7 +19768,7 @@ msgstr "" #. name for oma msgid "Omaha-Ponca" -msgstr "" +msgstr "Omaha-Ponca" #. name for omb msgid "Ambae; East" @@ -19780,23 +19780,23 @@ msgstr "" #. name for ome msgid "Omejes" -msgstr "" +msgstr "Omejes" #. name for omg msgid "Omagua" -msgstr "" +msgstr "Omagua" #. name for omi msgid "Omi" -msgstr "" +msgstr "Omi" #. name for omk msgid "Omok" -msgstr "" +msgstr "Omok" #. name for oml msgid "Ombo" -msgstr "" +msgstr "Ombo" #. name for omn msgid "Minoan" @@ -19816,11 +19816,11 @@ msgstr "" #. name for omt msgid "Omotik" -msgstr "" +msgstr "Omotik" #. name for omu msgid "Omurano" -msgstr "" +msgstr "Omurano" #. name for omw msgid "Tairora; South" @@ -19832,7 +19832,7 @@ msgstr "" #. name for ona msgid "Ona" -msgstr "" +msgstr "Ona" #. name for onb msgid "Lingao" @@ -19840,31 +19840,31 @@ msgstr "" #. name for one msgid "Oneida" -msgstr "" +msgstr "Oneida" #. name for ong msgid "Olo" -msgstr "" +msgstr "Olo" #. name for oni msgid "Onin" -msgstr "" +msgstr "Onin" #. name for onj msgid "Onjob" -msgstr "" +msgstr "Onjob" #. name for onk msgid "One; Kabore" -msgstr "" +msgstr "Oneià; Kabore" #. name for onn msgid "Onobasulu" -msgstr "" +msgstr "Onobasulu" #. name for ono msgid "Onondaga" -msgstr "" +msgstr "Onondaga" #. name for onp msgid "Sartang" @@ -19872,15 +19872,15 @@ msgstr "" #. name for onr msgid "One; Northern" -msgstr "" +msgstr "Oneià; Septentrional" #. name for ons msgid "Ono" -msgstr "" +msgstr "Ono" #. name for ont msgid "Ontenu" -msgstr "" +msgstr "Ontenu" #. name for onu msgid "Unua" @@ -19900,23 +19900,23 @@ msgstr "" #. name for oog msgid "Ong" -msgstr "" +msgstr "Ong" #. name for oon msgid "Önge" -msgstr "" +msgstr "Onge" #. name for oor msgid "Oorlams" -msgstr "" +msgstr "Oorlams" #. name for oos msgid "Ossetic; Old" -msgstr "" +msgstr "Osset" #. name for opa msgid "Okpamheri" -msgstr "" +msgstr "Okpamheri" #. name for opk msgid "Kopkaka" @@ -19924,39 +19924,39 @@ msgstr "" #. name for opm msgid "Oksapmin" -msgstr "" +msgstr "Oksapmin" #. name for opo msgid "Opao" -msgstr "" +msgstr "Opao" #. name for opt msgid "Opata" -msgstr "" +msgstr "Opata" #. name for opy msgid "Ofayé" -msgstr "" +msgstr "Opaie" #. name for ora msgid "Oroha" -msgstr "" +msgstr "Oroha" #. name for orc msgid "Orma" -msgstr "" +msgstr "Orma" #. name for ore msgid "Orejón" -msgstr "" +msgstr "Orejon" #. name for org msgid "Oring" -msgstr "" +msgstr "Oring" #. name for orh msgid "Oroqen" -msgstr "" +msgstr "Orotxen" #. name for ori msgid "Oriya" @@ -19968,19 +19968,19 @@ msgstr "Oromo" #. name for orn msgid "Orang Kanaq" -msgstr "" +msgstr "Orang; Kanaq" #. name for oro msgid "Orokolo" -msgstr "" +msgstr "Orocolo" #. name for orr msgid "Oruma" -msgstr "" +msgstr "Oruma" #. name for ors msgid "Orang Seletar" -msgstr "" +msgstr "Orang; Seletar" #. name for ort msgid "Oriya; Adivasi" @@ -19988,7 +19988,7 @@ msgstr "Oriya; Adivasi" #. name for oru msgid "Ormuri" -msgstr "" +msgstr "Ormuri" #. name for orv msgid "Russian; Old" @@ -19996,31 +19996,31 @@ msgstr "Rus; antic" #. name for orw msgid "Oro Win" -msgstr "" +msgstr "Oro Win" #. name for orx msgid "Oro" -msgstr "" +msgstr "Oro" #. name for orz msgid "Ormu" -msgstr "" +msgstr "Ormu" #. name for osa msgid "Osage" -msgstr "" +msgstr "Osage" #. name for osc msgid "Oscan" -msgstr "" +msgstr "Osc" #. name for osi msgid "Osing" -msgstr "" +msgstr "Osing" #. name for oso msgid "Ososo" -msgstr "" +msgstr "Ososo" #. name for osp msgid "Spanish; Old" @@ -20028,15 +20028,15 @@ msgstr "Espanyol; antic" #. name for oss msgid "Ossetian" -msgstr "" +msgstr "Osset" #. name for ost msgid "Osatu" -msgstr "" +msgstr "Osatu" #. name for osu msgid "One; Southern" -msgstr "" +msgstr "One; Meridional" #. name for osx msgid "Saxon; Old" @@ -20052,15 +20052,15 @@ msgstr "" #. name for otd msgid "Ot Danum" -msgstr "" +msgstr "Dohoi" #. name for ote msgid "Otomi; Mezquital" -msgstr "" +msgstr "Otomí; Mezquital" #. name for oti msgid "Oti" -msgstr "" +msgstr "Oti" #. name for otk msgid "Turkish; Old" @@ -20068,43 +20068,43 @@ msgstr "Turc; antic" #. name for otl msgid "Otomi; Tilapa" -msgstr "" +msgstr "Otomí; Tilapa" #. name for otm msgid "Otomi; Eastern Highland" -msgstr "" +msgstr "Otomí; Oriental" #. name for otn msgid "Otomi; Tenango" -msgstr "" +msgstr "Otomí; Tenango" #. name for otq msgid "Otomi; Querétaro" -msgstr "" +msgstr "Otomí; Queretaro" #. name for otr msgid "Otoro" -msgstr "" +msgstr "Otoro" #. name for ots msgid "Otomi; Estado de México" -msgstr "" +msgstr "Otomí; Estat de Mèxic" #. name for ott msgid "Otomi; Temoaya" -msgstr "" +msgstr "Otomí; Temoaya" #. name for otu msgid "Otuke" -msgstr "" +msgstr "Otuke" #. name for otw msgid "Ottawa" -msgstr "" +msgstr "Ottawa" #. name for otx msgid "Otomi; Texcatepec" -msgstr "" +msgstr "Otomí; Texcatepec" #. name for oty msgid "Tamil; Old" @@ -20112,7 +20112,7 @@ msgstr "" #. name for otz msgid "Otomi; Ixtenco" -msgstr "" +msgstr "Otomí; Ixtenc" #. name for oua msgid "Tagargrent" @@ -20124,7 +20124,7 @@ msgstr "" #. name for oue msgid "Oune" -msgstr "" +msgstr "Oune" #. name for oui msgid "Uighur; Old" @@ -20132,15 +20132,15 @@ msgstr "" #. name for oum msgid "Ouma" -msgstr "" +msgstr "Ouma" #. name for oun msgid "!O!ung" -msgstr "" +msgstr "Oung" #. name for owi msgid "Owiniga" -msgstr "" +msgstr "Owiniga" #. name for owl msgid "Welsh; Old" @@ -20148,11 +20148,11 @@ msgstr "Gal·lès; antic" #. name for oyb msgid "Oy" -msgstr "" +msgstr "Oy" #. name for oyd msgid "Oyda" -msgstr "" +msgstr "Oyda" #. name for oym msgid "Wayampi" @@ -20160,7 +20160,7 @@ msgstr "" #. name for oyy msgid "Oya'oya" -msgstr "" +msgstr "Oya'oya" #. name for ozm msgid "Koonzime" @@ -20168,27 +20168,27 @@ msgstr "" #. name for pab msgid "Parecís" -msgstr "" +msgstr "Pareci" #. name for pac msgid "Pacoh" -msgstr "" +msgstr "Pacoh" #. name for pad msgid "Paumarí" -msgstr "" +msgstr "Paumarí" #. name for pae msgid "Pagibete" -msgstr "" +msgstr "Pagibete" #. name for paf msgid "Paranawát" -msgstr "" +msgstr "Paranawat" #. name for pag msgid "Pangasinan" -msgstr "" +msgstr "Pangasi" #. name for pah msgid "Tenharim" @@ -20196,19 +20196,19 @@ msgstr "" #. name for pai msgid "Pe" -msgstr "" +msgstr "Pe" #. name for pak msgid "Parakanã" -msgstr "" +msgstr "Akwawa; Parakanà" #. name for pal msgid "Pahlavi" -msgstr "" +msgstr "Pahlavi" #. name for pam msgid "Pampanga" -msgstr "" +msgstr "Pampangà" #. name for pan msgid "Panjabi" @@ -20220,63 +20220,63 @@ msgstr "" #. name for pap msgid "Papiamento" -msgstr "" +msgstr "Papiament" #. name for paq msgid "Parya" -msgstr "" +msgstr "Parya" #. name for par msgid "Panamint" -msgstr "" +msgstr "Panamint" #. name for pas msgid "Papasena" -msgstr "" +msgstr "Papasena" #. name for pat msgid "Papitalai" -msgstr "" +msgstr "Papitalai" #. name for pau msgid "Palauan" -msgstr "" +msgstr "Palavà" #. name for pav msgid "Pakaásnovos" -msgstr "" +msgstr "Pakaa Nova" #. name for paw msgid "Pawnee" -msgstr "" +msgstr "Pawnee" #. name for pax msgid "Pankararé" -msgstr "" +msgstr "Pankararé" #. name for pay msgid "Pech" -msgstr "" +msgstr "Pech" #. name for paz msgid "Pankararú" -msgstr "" +msgstr "Pankarurú" #. name for pbb msgid "Páez" -msgstr "" +msgstr "Páez" #. name for pbc msgid "Patamona" -msgstr "" +msgstr "Patamona" #. name for pbe msgid "Popoloca; Mezontla" -msgstr "" +msgstr "Popoloca; Mezontla" #. name for pbf msgid "Popoloca; Coyotepec" -msgstr "" +msgstr "Popoloca; Coyotepec" #. name for pbg msgid "Paraujano" @@ -20288,7 +20288,7 @@ msgstr "" #. name for pbi msgid "Parkwa" -msgstr "" +msgstr "Parkwa" #. name for pbl msgid "Mak (Nigeria)" @@ -20300,7 +20300,7 @@ msgstr "" #. name for pbo msgid "Papel" -msgstr "" +msgstr "Papel" #. name for pbp msgid "Badyara" @@ -20336,7 +20336,7 @@ msgstr "" #. name for pca msgid "Popoloca; Santa Inés Ahuatempan" -msgstr "" +msgstr "Popoloca; Ahuatempan" #. name for pcb msgid "Pear" @@ -20832,7 +20832,7 @@ msgstr "Senufo; Palaka" #. name for pls msgid "Popoloca; San Marcos Tlalcoyalco" -msgstr "" +msgstr "Popoloca; Tlalcoyalc" #. name for plt msgid "Malagasy; Plateau" @@ -21040,7 +21040,7 @@ msgstr "" #. name for poe msgid "Popoloca; San Juan Atzingo" -msgstr "" +msgstr "Popoloca; Atzingo" #. name for pof msgid "Poke" @@ -21104,7 +21104,7 @@ msgstr "" #. name for pow msgid "Popoloca; San Felipe Otlaltepec" -msgstr "" +msgstr "Popoloca; Otlaltepec" #. name for pox msgid "Polabian" @@ -21160,7 +21160,7 @@ msgstr "" #. name for pps msgid "Popoloca; San Luís Temalacayuca" -msgstr "" +msgstr "Popoloca; Temalacayuca" #. name for ppt msgid "Pare" From eca40f6b51bdb77555fbacad960df25274986ab7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 13:06:14 +0530 Subject: [PATCH 41/67] Speedup serialization of numbers to PDF by a factor of 10 --- .../ebooks/conversion/plugins/pdf_output.py | 10 +++- src/calibre/ebooks/pdf/render/common.py | 49 +++++++-------- src/calibre/ebooks/pdf/render/serialize.py | 60 ++----------------- src/calibre/ebooks/pdf/render/test.py | 5 +- src/calibre/utils/speedup.c | 40 +++++++++++++ 5 files changed, 79 insertions(+), 85 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index c042de7050..c2b0050dd7 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -232,7 +232,15 @@ class PDFOutput(OutputFormatPlugin): out_stream.seek(0) out_stream.truncate() self.log.debug('Rendering pages to PDF...') - writer.dump(items, out_stream, PDFMetadata(self.metadata)) + import time + st = time.time() + if False: + import cProfile + cProfile.runctx('writer.dump(items, out_stream, PDFMetadata(self.metadata))', + globals(), locals(), '/tmp/profile') + else: + writer.dump(items, out_stream, PDFMetadata(self.metadata)) + self.log('Rendered PDF in %g seconds:'%(time.time()-st)) if close: out_stream.close() diff --git a/src/calibre/ebooks/pdf/render/common.py b/src/calibre/ebooks/pdf/render/common.py index 5be06b1b98..03774e2d69 100644 --- a/src/calibre/ebooks/pdf/render/common.py +++ b/src/calibre/ebooks/pdf/render/common.py @@ -9,8 +9,10 @@ __docformat__ = 'restructuredtext en' import codecs, zlib from io import BytesIO -from struct import pack -from decimal import Decimal + +from calibre.constants import plugins, ispy3 + +pdf_float = plugins['speedup'][0].pdf_float EOL = b'\n' @@ -52,32 +54,25 @@ PAPER_SIZES = {k:globals()[k.upper()] for k in ('a0 a1 a2 a3 a4 a5 a6 b0 b1 b2' # Basic PDF datatypes {{{ -def format_float(f): - if abs(f) < 1e-7: - return '0' - places = 6 - a, b = type(u'')(Decimal(f).quantize(Decimal(10)**-places)).partition('.')[0::2] - b = b.rstrip('0') - if not b: - return '0' if a == '-0' else a - return '%s.%s'%(a, b) +ic = str if ispy3 else unicode +icb = (lambda x: str(x).encode('ascii')) if ispy3 else bytes def fmtnum(o): - if isinstance(o, (int, long)): - return type(u'')(o) - return format_float(o) + if isinstance(o, float): + return pdf_float(o) + return ic(o) def serialize(o, stream): - if hasattr(o, 'pdf_serialize'): - o.pdf_serialize(stream) - elif isinstance(o, bool): - stream.write(b'true' if o else b'false') + if isinstance(o, float): + stream.write_raw(pdf_float(o).encode('ascii')) elif isinstance(o, (int, long)): - stream.write(type(u'')(o).encode('ascii')) - elif isinstance(o, float): - stream.write(format_float(o).encode('ascii')) + stream.write_raw(icb(o)) + elif hasattr(o, 'pdf_serialize'): + o.pdf_serialize(stream) elif o is None: - stream.write(b'null') + stream.write_raw(b'null') + elif isinstance(o, bool): + stream.write_raw(b'true' if o else b'false') else: raise ValueError('Unknown object: %r'%o) @@ -103,13 +98,6 @@ class String(unicode): raw = codecs.BOM_UTF16_BE + s.encode('utf-16-be') stream.write(b'('+raw+b')') -class GlyphIndex(int): - - def pdf_serialize(self, stream): - byts = bytearray(pack(b'>H', self)) - stream.write('<%s>'%''.join(map( - lambda x: bytes(hex(x)[2:]).rjust(2, b'0'), byts))) - class Dictionary(dict): def pdf_serialize(self, stream): @@ -180,6 +168,9 @@ class Stream(BytesIO): super(Stream, self).write(raw if isinstance(raw, bytes) else raw.encode('ascii')) + def write_raw(self, raw): + BytesIO.write(self, raw) + class Reference(object): def __init__(self, num, obj): diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index ce4ae7fc6c..1e28e59a96 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -9,14 +9,13 @@ __docformat__ = 'restructuredtext en' import hashlib from future_builtins import map -from itertools import izip from PyQt4.Qt import QBuffer, QByteArray, QImage, Qt, QColor, qRgba from calibre.constants import (__appname__, __version__) from calibre.ebooks.pdf.render.common import ( Reference, EOL, serialize, Stream, Dictionary, String, Name, Array, - GlyphIndex, fmtnum) + fmtnum) from calibre.ebooks.pdf.render.fonts import FontManager from calibre.ebooks.pdf.render.links import Links @@ -166,54 +165,6 @@ class Path(object): def close(self): self.ops.append(('h',)) -class Text(object): - - def __init__(self): - self.transform = self.default_transform = [1, 0, 0, 1, 0, 0] - self.font_name = 'Times-Roman' - self.font_path = None - self.horizontal_scale = self.default_horizontal_scale = 100 - self.word_spacing = self.default_word_spacing = 0 - self.char_space = self.default_char_space = 0 - self.glyph_adjust = self.default_glyph_adjust = None - self.size = 12 - self.text = '' - - def set_transform(self, *args): - if len(args) == 1: - m = args[0] - vals = [m.m11(), m.m12(), m.m21(), m.m22(), m.dx(), m.dy()] - else: - vals = args - self.transform = vals - - def pdf_serialize(self, stream, font_name): - if not self.text: return - stream.write_line('BT ') - serialize(Name(font_name), stream) - stream.write(' %s Tf '%fmtnum(self.size)) - stream.write(' '.join(map(fmtnum, self.transform)) + ' Tm ') - if self.horizontal_scale != self.default_horizontal_scale: - stream.write('%s Tz '%fmtnum(self.horizontal_scale)) - if self.word_spacing != self.default_word_spacing: - stream.write('%s Tw '%fmtnum(self.word_spacing)) - if self.char_space != self.default_char_space: - stream.write('%s Tc '%fmtnum(self.char_space)) - stream.write_line() - if self.glyph_adjust is self.default_glyph_adjust: - serialize(String(self.text), stream) - stream.write(' Tj ') - else: - chars = Array() - frac, widths = self.glyph_adjust - for c, width in izip(self.text, widths): - chars.append(String(c)) - chars.append(int(width * frac)) - serialize(chars, stream) - stream.write(' TJ ') - stream.write_line('ET') - - class Catalog(Dictionary): def __init__(self, pagetree): @@ -244,7 +195,9 @@ class HashingStream(object): self.last_char = b'' def write(self, raw): - raw = raw if isinstance(raw, bytes) else raw.encode('ascii') + self.write_raw(raw if isinstance(raw, bytes) else raw.encode('ascii')) + + def write_raw(self, raw): self.f.write(raw) self.hashobj.update(raw) if raw: @@ -420,9 +373,8 @@ class PDFStream(object): self.current_page.write(' %s Tf '%fmtnum(size)) self.current_page.write('%s Tm '%' '.join(map(fmtnum, transform))) for x, y, glyph_id in glyphs: - self.current_page.write('%s %s Td '%(fmtnum(x), fmtnum(y))) - serialize(GlyphIndex(glyph_id), self.current_page) - self.current_page.write(' Tj ') + self.current_page.write_raw(('%s %s Td <%04X> Tj '%( + fmtnum(x), fmtnum(y), glyph_id)).encode('ascii')) self.current_page.write_line(b' ET') def get_image(self, cache_key): diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index b631dc8276..8fe1709491 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -105,12 +105,15 @@ def pen(p, xmax, ymax): p.setPen(pen) p.drawRect(0, xmax/3, xmax/3, xmax/2) +def text(p, xmax, ymax): + p.drawText(QPoint(0, ymax/3), 'Text') + def main(): app = QApplication([]) app tdir = gettempdir() pdf = os.path.join(tdir, 'painter.pdf') - func = pen + func = full dpi = 100 with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False) diff --git a/src/calibre/utils/speedup.c b/src/calibre/utils/speedup.c index 0626d351a4..171179a88a 100644 --- a/src/calibre/utils/speedup.c +++ b/src/calibre/utils/speedup.c @@ -3,6 +3,9 @@ #include +#define min(x, y) ((x < y) ? x : y) +#define max(x, y) ((x > y) ? x : y) + static PyObject * speedup_parse_date(PyObject *self, PyObject *args) { const char *raw, *orig, *tz; @@ -61,11 +64,48 @@ speedup_parse_date(PyObject *self, PyObject *args) { (tzh*60 + tzm)*sign*60); } + +static PyObject* +speedup_pdf_float(PyObject *self, PyObject *args) { + double f = 0.0, a = 0.0; + char *buf = "0", *dot; + void *free_buf = NULL; + int precision = 6, l = 0; + PyObject *ret; + + if(!PyArg_ParseTuple(args, "d", &f)) return NULL; + + a = fabs(f); + + if (a > 1.0e-7) { + if(a > 1) precision = min(max(0, 6-(int)log10(a)), 6); + buf = PyOS_double_to_string(f, 'f', precision, 0, NULL); + if (buf != NULL) { + free_buf = (void*)buf; + if (precision > 0) { + l = strlen(buf) - 1; + while (l > 0 && buf[l] == '0') l--; + if (buf[l] == ',' || buf[l] == '.') buf[l] = 0; + else buf[l+1] = 0; + if ( (dot = strchr(buf, ',')) ) *dot = '.'; + } + } else if (!PyErr_Occurred()) PyErr_SetString(PyExc_TypeError, "Float->str failed."); + } + + ret = PyUnicode_FromString(buf); + if (free_buf != NULL) PyMem_Free(free_buf); + return ret; +} + static PyMethodDef speedup_methods[] = { {"parse_date", speedup_parse_date, METH_VARARGS, "parse_date()\n\nParse ISO dates faster." }, + {"pdf_float", speedup_pdf_float, METH_VARARGS, + "pdf_float()\n\nConvert float to a string representation suitable for PDF" + }, + {NULL, NULL, 0, NULL} }; From 7e0acebabc0839b31f63373441203910119031cf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 15:26:32 +0530 Subject: [PATCH 42/67] ... --- src/calibre/ebooks/pdf/render/graphics.py | 4 ++-- src/calibre/ebooks/pdf/render/test.py | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/graphics.py b/src/calibre/ebooks/pdf/render/graphics.py index 43b1c72fdb..71a8a22e85 100644 --- a/src/calibre/ebooks/pdf/render/graphics.py +++ b/src/calibre/ebooks/pdf/render/graphics.py @@ -396,8 +396,6 @@ class Graphics(object): pen = state.stroke self.pending_state.do_stroke = True pdf = self.pdf - if pen.style() == Qt.NoPen: - self.pending_state.do_stroke = False # Width w = pen.widthF() @@ -429,6 +427,8 @@ class Graphics(object): pen.brush(), state.brush_origin, state.opacity, pdf_system, painter.transform()) self.pdf.apply_stroke(color, pattern, opacity) + if pen.style() == Qt.NoPen: + self.pending_state.do_stroke = False def apply_fill(self, state, pdf_system, painter): self.pending_state.do_fill = True diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 8fe1709491..d57678a057 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -69,13 +69,6 @@ def full(p, xmax, ymax): g.setColorAt(1, QColor('#fff')) p.fillRect(x, y, w, w, QBrush(g)) - pen = QPen(QBrush(Qt.blue)) - pen.setWidth(xmax/3) - p.setPen(pen) - x += w + w/10 - y += w - p.drawLine(x, y, x+w, y) - def run(dev, func): p = QPainter(dev) From f368250f1dac9903c31cc24fe9ae79ae0a92bf3c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 18:40:27 +0530 Subject: [PATCH 43/67] Nicer error message when no supported formats for conversion found --- src/calibre/gui2/convert/single.py | 7 ++++-- src/calibre/gui2/tools.py | 37 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/calibre/gui2/convert/single.py b/src/calibre/gui2/convert/single.py index 332dc4ae92..cff7ee8c3d 100644 --- a/src/calibre/gui2/convert/single.py +++ b/src/calibre/gui2/convert/single.py @@ -33,7 +33,10 @@ from calibre.utils.config import prefs from calibre.utils.logging import Log class NoSupportedInputFormats(Exception): - pass + + def __init__(self, available_formats): + Exception.__init__(self) + self.available_formats = available_formats def sort_formats_by_preference(formats, prefs): uprefs = [x.upper() for x in prefs] @@ -86,7 +89,7 @@ def get_supported_input_formats_for_book(db, book_id): input_formats = set([x.lower() for x in supported_input_formats()]) input_formats = sorted(available_formats.intersection(input_formats)) if not input_formats: - raise NoSupportedInputFormats + raise NoSupportedInputFormats(tuple(x for x in available_formats if x)) return input_formats diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index f4ee92b565..98a59ccdd5 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -88,20 +88,35 @@ def convert_single_ebook(parent, db, book_ids, auto_conversion=False, # {{{ changed = True d.break_cycles() - except NoSupportedInputFormats: - bad.append(book_id) + except NoSupportedInputFormats as nsif: + bad.append((book_id, nsif.available_formats)) if bad and show_no_format_warning: - res = [] - for id in bad: - title = db.title(id, True) - res.append('%s'%title) + if len(bad) == 1 and not bad[0][1]: + title = db.title(bad[0][0], True) + warning_dialog(parent, _('Could not convert'), '

'+ + _('Could not convert %s as it has no ebook files. If you ' + 'think it should have files, but calibre is not finding ' + 'them, that is most likely because you moved the book\'s ' + 'files around outside of calibre. You will need to find those files ' + 'and re-add them to calibre.')%title, show=True) + else: + res = [] + for id, available_formats in bad: + title = db.title(id, True) + if available_formats: + msg = _('No supported formats (Available formats: %s)')%( + ', '.join(available_formats)) + else: + msg = _('This book has no actual ebook files') + res.append('%s - %s'%(title, msg)) - msg = '%s' % '\n'.join(res) - warning_dialog(parent, _('Could not convert some books'), - _('Could not convert %(num)d of %(tot)d books, because no suitable source' - ' format was found.') % dict(num=len(res), tot=total), - msg).exec_() + + msg = '%s' % '\n'.join(res) + warning_dialog(parent, _('Could not convert some books'), + _('Could not convert %(num)d of %(tot)d books, because no supported source' + ' formats were found.') % dict(num=len(res), tot=total), + msg).exec_() return jobs, changed, bad # }}} From e1d40f36036ad1aac1c0785bba979e8b6a92d47f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 20:50:22 +0530 Subject: [PATCH 44/67] Show disabled device plugins in Preferences->Ignored Devices --- .../gui2/preferences/ignored_devices.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/calibre/gui2/preferences/ignored_devices.py b/src/calibre/gui2/preferences/ignored_devices.py index 99fa350f73..f4215c49ad 100644 --- a/src/calibre/gui2/preferences/ignored_devices.py +++ b/src/calibre/gui2/preferences/ignored_devices.py @@ -9,6 +9,7 @@ __docformat__ = 'restructuredtext en' from PyQt4.Qt import (QLabel, QVBoxLayout, QListWidget, QListWidgetItem, Qt) +from calibre.customize.ui import enable_plugin from calibre.gui2.preferences import ConfigWidgetBase, test_widget class ConfigWidget(ConfigWidgetBase): @@ -31,6 +32,18 @@ class ConfigWidget(ConfigWidgetBase): f.itemChanged.connect(self.changed_signal) f.itemDoubleClicked.connect(self.toggle_item) + self.la2 = la = QLabel(_( + 'The list of device plugins you have disabled. Uncheck an entry ' + 'to enable the plugin. calibre cannot detect devices that are ' + 'managed by disabled plugins.')) + la.setWordWrap(True) + l.addWidget(la) + + self.device_plugins = f = QListWidget(f) + l.addWidget(f) + f.itemChanged.connect(self.changed_signal) + f.itemDoubleClicked.connect(self.toggle_item) + def toggle_item(self, item): item.setCheckState(Qt.Checked if item.checkState() == Qt.Unchecked else Qt.Unchecked) @@ -46,6 +59,16 @@ class ConfigWidget(ConfigWidgetBase): item.setCheckState(Qt.Checked) self.devices.blockSignals(False) + self.device_plugins.blockSignals(True) + for dev in self.gui.device_manager.disabled_device_plugins: + n = dev.get_gui_name() + item = QListWidgetItem(n, self.device_plugins) + item.setData(Qt.UserRole, dev) + item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable) + item.setCheckState(Qt.Checked) + self.device_plugins.sortItems() + self.device_plugins.blockSignals(False) + def restore_defaults(self): if self.devices.count() > 0: self.devices.clear() @@ -63,6 +86,12 @@ class ConfigWidget(ConfigWidgetBase): for dev, bl in devs.iteritems(): dev.set_user_blacklisted_devices(bl) + for i in xrange(self.device_plugins.count()): + e = self.device_plugins.item(i) + dev = e.data(Qt.UserRole).toPyObject() + if e.checkState() == Qt.Unchecked: + enable_plugin(dev) + return True # Restart required if __name__ == '__main__': From 90f19d6e8561c3c90ebff40be10fef1c8ee77af4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 1 Jan 2013 21:59:41 +0530 Subject: [PATCH 45/67] ... --- src/calibre/gui2/preferences/ignored_devices.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/preferences/ignored_devices.py b/src/calibre/gui2/preferences/ignored_devices.py index f4215c49ad..e0c0ae1dc1 100644 --- a/src/calibre/gui2/preferences/ignored_devices.py +++ b/src/calibre/gui2/preferences/ignored_devices.py @@ -7,7 +7,8 @@ __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from PyQt4.Qt import (QLabel, QVBoxLayout, QListWidget, QListWidgetItem, Qt) +from PyQt4.Qt import (QLabel, QVBoxLayout, QListWidget, QListWidgetItem, Qt, + QIcon) from calibre.customize.ui import enable_plugin from calibre.gui2.preferences import ConfigWidgetBase, test_widget @@ -66,6 +67,7 @@ class ConfigWidget(ConfigWidgetBase): item.setData(Qt.UserRole, dev) item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable) item.setCheckState(Qt.Checked) + item.setIcon(QIcon(I('plugins.png'))) self.device_plugins.sortItems() self.device_plugins.blockSignals(False) From b3b37a2029bca2ad62ef90e1df0fa7844e8f4fa6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Jan 2013 08:25:27 +0530 Subject: [PATCH 46/67] Update Foreign Affairs --- recipes/foreignaffairs.recipe | 94 ++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/recipes/foreignaffairs.recipe b/recipes/foreignaffairs.recipe index 6b36170288..b383609860 100644 --- a/recipes/foreignaffairs.recipe +++ b/recipes/foreignaffairs.recipe @@ -11,21 +11,21 @@ class ForeignAffairsRecipe(BasicNewsRecipe): by Chen Wei weichen302@gmx.com, 2012-02-05''' __license__ = 'GPL v3' - __author__ = 'kwetal' + __author__ = 'Rick Shang, kwetal' language = 'en' version = 1.01 - title = u'Foreign Affairs (Subcription or (free) Registration)' + title = u'Foreign Affairs (Subcription)' publisher = u'Council on Foreign Relations' category = u'USA, Foreign Affairs' description = u'The leading forum for serious discussion of American foreign policy and international affairs.' no_stylesheets = True remove_javascript = True + needs_subscription = True INDEX = 'http://www.foreignaffairs.com' FRONTPAGE = 'http://www.foreignaffairs.com/magazine' - INCLUDE_PREMIUM = False remove_tags = [] @@ -68,43 +68,57 @@ class ForeignAffairsRecipe(BasicNewsRecipe): def parse_index(self): + answer = [] soup = self.index_to_soup(self.FRONTPAGE) - sec_start = soup.findAll('div', attrs={'class':'panel-separator'}) + #get dates + date = re.split('\s\|\s',self.tag_to_string(soup.head.title.string))[0] + self.timefmt = u' [%s]'%date + + sec_start = soup.findAll('div', attrs= {'class':'panel-pane'}) for sec in sec_start: - content = sec.nextSibling - if content: - section = self.tag_to_string(content.find('h2')) - articles = [] - - tags = [] - for div in content.findAll('div', attrs = {'class': re.compile(r'view-row\s+views-row-[0-9]+\s+views-row-[odd|even].*')}): - tags.append(div) - for li in content.findAll('li'): - tags.append(li) - - for div in tags: - title = url = description = author = None - - if self.INCLUDE_PREMIUM: - found_premium = False - else: - found_premium = div.findAll('span', attrs={'class': - 'premium-icon'}) - if not found_premium: - tag = div.find('div', attrs={'class': 'views-field-title'}) - - if tag: - a = tag.find('a') - if a: - title = self.tag_to_string(a) - url = self.INDEX + a['href'] - author = self.tag_to_string(div.find('div', attrs = {'class': 'views-field-field-article-display-authors-value'})) - tag_summary = div.find('span', attrs = {'class': 'views-field-field-article-summary-value'}) - description = self.tag_to_string(tag_summary) - articles.append({'title':title, 'date':None, 'url':url, - 'description':description, 'author':author}) - if articles: + articles = [] + section = self.tag_to_string(sec.find('h2')) + if 'Books' in section: + reviewsection=sec.find('div', attrs = {'class': 'item-list'}) + for subsection in reviewsection.findAll('div'): + subsectiontitle=self.tag_to_string(subsection.span.a) + subsectionurl=self.INDEX + subsection.span.a['href'] + soup1 = self.index_to_soup(subsectionurl) + for div in soup1.findAll('div', attrs = {'class': 'views-field-title'}): + if div.find('a') is not None: + originalauthor=self.tag_to_string(div.findNext('div', attrs = {'class':'views-field-field-article-book-nid'}).div.a) + title=subsectiontitle+': '+self.tag_to_string(div.span.a)+' by '+originalauthor + url=self.INDEX+div.span.a['href'] + atr=div.findNext('div', attrs = {'class': 'views-field-field-article-display-authors-value'}) + if atr is not None: + author=self.tag_to_string(atr.span.a) + else: + author='' + desc=div.findNext('span', attrs = {'class': 'views-field-field-article-summary-value'}) + if desc is not None: + description=self.tag_to_string(desc.div.p) + else: + description='' + articles.append({'title':title, 'date':None, 'url':url, 'description':description, 'author':author}) + subsectiontitle='' + else: + for div in sec.findAll('div', attrs = {'class': 'views-field-title'}): + if div.find('a') is not None: + title=self.tag_to_string(div.span.a) + url=self.INDEX+div.span.a['href'] + atr=div.findNext('div', attrs = {'class': 'views-field-field-article-display-authors-value'}) + if atr is not None: + author=self.tag_to_string(atr.span.a) + else: + author='' + desc=div.findNext('span', attrs = {'class': 'views-field-field-article-summary-value'}) + if desc is not None: + description=self.tag_to_string(desc.div.p) + else: + description='' + articles.append({'title':title, 'date':None, 'url':url, 'description':description, 'author':author}) + if articles: answer.append((section, articles)) return answer @@ -115,15 +129,17 @@ class ForeignAffairsRecipe(BasicNewsRecipe): return soup - needs_subscription = True + def get_browser(self): br = BasicNewsRecipe.get_browser() if self.username is not None and self.password is not None: - br.open('https://www.foreignaffairs.com/user?destination=home') + br.open('https://www.foreignaffairs.com/user?destination=user%3Fop%3Dlo') br.select_form(nr = 1) br['name'] = self.username br['pass'] = self.password br.submit() return br + def cleanup(self): + self.browser.open('http://www.foreignaffairs.com/logout?destination=user%3Fop=lo') From 3bdc5da43e7bf874ca6d2274adbf70036bd40d61 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Jan 2013 08:25:43 +0530 Subject: [PATCH 47/67] ... --- manual/faq.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/manual/faq.rst b/manual/faq.rst index e9bb6fc70f..876a04d601 100644 --- a/manual/faq.rst +++ b/manual/faq.rst @@ -164,7 +164,6 @@ Follow these steps to find the problem: * Ensure your operating system is seeing the device. That is, the device should show up in Windows Explorer (in Windows) or Finder (in OS X). * In |app|, go to Preferences->Ignored Devices and check that your device is not being ignored - * In |app|, go to Preferences->Plugins->Device Interface plugin and make sure the plugin for your device is enabled, the plugin icon next to it should be green when it is enabled. * If all the above steps fail, go to Preferences->Miscellaneous and click debug device detection with your device attached and post the output as a ticket on `the calibre bug tracker `_. My device is non-standard or unusual. What can I do to connect to it? From f213f90fbe52e2a7b681f5e71ae2eb8b14bfde73 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Jan 2013 08:31:29 +0530 Subject: [PATCH 48/67] ... --- src/calibre/ebooks/pdf/render/serialize.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/serialize.py b/src/calibre/ebooks/pdf/render/serialize.py index 1e28e59a96..b2a17734db 100644 --- a/src/calibre/ebooks/pdf/render/serialize.py +++ b/src/calibre/ebooks/pdf/render/serialize.py @@ -356,14 +356,6 @@ class PDFStream(object): self.page_tree.obj.add_page(pageref) self.current_page = Page(self.page_tree, compress=self.compress) - def draw_text(self, text_object): - if text_object.font_path is None: - fontref = self.font_manager.add_standard_font(text_object.font_name) - else: - raise NotImplementedError() - name = self.current_page.add_font(fontref) - text_object.pdf_serialize(self.current_page, name) - def draw_glyph_run(self, transform, size, font_metrics, glyphs): glyph_ids = {x[-1] for x in glyphs} fontref = self.font_manager.add_font(font_metrics, glyph_ids) From 99551f1a6da571d376ce262128331edb9f3d23d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Jan 2013 09:02:24 +0530 Subject: [PATCH 49/67] Fix trutype embedded in PDF crashing ADE --- src/calibre/utils/fonts/sfnt/subset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/utils/fonts/sfnt/subset.py b/src/calibre/utils/fonts/sfnt/subset.py index 9e20127614..bb8b73d013 100644 --- a/src/calibre/utils/fonts/sfnt/subset.py +++ b/src/calibre/utils/fonts/sfnt/subset.py @@ -84,7 +84,7 @@ def do_warn(warnings, *args): def pdf_subset(sfnt, glyphs): for tag in tuple(sfnt.tables): if tag not in {b'hhea', b'head', b'hmtx', b'maxp', - b'OS/2', b'post', b'cvt', b'fpgm', b'glyf', b'loca', + b'OS/2', b'post', b'cvt ', b'fpgm', b'glyf', b'loca', b'prep', b'CFF ', b'VORG'}: # Remove non core tables since they are unused in PDF rendering del sfnt[tag] From 2fe223bf9713ab64fad4bf5059272cad9f263ce2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Jan 2013 10:42:49 +0530 Subject: [PATCH 50/67] Update bundled pyparsing --- src/calibre/utils/pyparsing.py | 318 +++++++++++++++------------------ 1 file changed, 147 insertions(+), 171 deletions(-) diff --git a/src/calibre/utils/pyparsing.py b/src/calibre/utils/pyparsing.py index 9be97dc287..149ccaf1b0 100644 --- a/src/calibre/utils/pyparsing.py +++ b/src/calibre/utils/pyparsing.py @@ -58,8 +58,8 @@ The pyparsing module handles some of the problems that are typically vexing when - embedded comments """ -__version__ = "1.5.6" -__versionTime__ = "26 June 2011 10:53" +__version__ = "1.5.7" +__versionTime__ = "17 November 2012 16:18" __author__ = "Paul McGuire " import string @@ -81,66 +81,51 @@ __all__ = [ 'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', 'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', 'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', -'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'getTokensEndLoc', 'hexnums', +'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', 'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', 'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', 'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', 'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', 'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', -'indentedBlock', 'originalTextFor', +'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation', ] -""" -Detect if we are running version 3.X and make appropriate changes -Robert A. Clark -""" -_PY3K = sys.version_info[0] > 2 -if _PY3K: - _MAX_INT = sys.maxsize - basestring = str - unichr = chr - _ustr = str - alphas = string.ascii_lowercase + string.ascii_uppercase -else: - _MAX_INT = sys.maxint - range = xrange - set = lambda s : dict( [(c,0) for c in s] ) - alphas = string.lowercase + string.uppercase +_MAX_INT = sys.maxint +range = xrange +set = lambda s : dict( [(c,0) for c in s] ) - def _ustr(obj): - """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries - str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It - then < returns the unicode object | encodes it with the default encoding | ... >. - """ - if isinstance(obj,unicode): - return obj +def _ustr(obj): + """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries + str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It + then < returns the unicode object | encodes it with the default encoding | ... >. + """ + if isinstance(obj,unicode): + return obj - try: - # If this works, then _ustr(obj) has the same behaviour as str(obj), so - # it won't break any existing code. - return str(obj) + try: + # If this works, then _ustr(obj) has the same behaviour as str(obj), so + # it won't break any existing code. + return str(obj) - except UnicodeEncodeError: - # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) - # state that "The return value must be a string object". However, does a - # unicode object (being a subclass of basestring) count as a "string - # object"? - # If so, then return a unicode object: - return unicode(obj) - # Else encode it... but how? There are many choices... :) - # Replace unprintables with escape codes? - #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') - # Replace unprintables with question marks? - #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') - # ... - - alphas = string.lowercase + string.uppercase + except UnicodeEncodeError: + # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) + # state that "The return value must be a string object". However, does a + # unicode object (being a subclass of basestring) count as a "string + # object"? + # If so, then return a unicode object: + return unicode(obj) + # Else encode it... but how? There are many choices... :) + # Replace unprintables with escape codes? + #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') + # Replace unprintables with question marks? + #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') + # ... # build list of single arg builtins, tolerant of Python version, that can be used as parse actions singleArgBuiltins = [] import __builtin__ -for fname in "sum len enumerate sorted reversed list tuple set any all".split(): +for fname in "sum len sorted reversed list tuple set any all min max".split(): try: singleArgBuiltins.append(getattr(__builtin__,fname)) except AttributeError: @@ -159,7 +144,8 @@ def _xml_escape(data): class _Constants(object): pass -nums = string.digits +alphas = string.ascii_lowercase + string.ascii_uppercase +nums = "0123456789" hexnums = nums + "ABCDEFabcdef" alphanums = alphas + nums _bslash = chr(92) @@ -211,7 +197,7 @@ class ParseBaseException(Exception): return line_str.strip() def __dir__(self): return "loc msg pstr parserElement lineno col line " \ - "markInputLine __str__ __repr__".split() + "markInputline __str__ __repr__".split() class ParseException(ParseBaseException): """exception thrown when parse expressions don't match class; @@ -228,8 +214,8 @@ class ParseFatalException(ParseBaseException): pass class ParseSyntaxException(ParseFatalException): - """just like C{ParseFatalException}, but thrown internally when an - C{ErrorStop} ('-' operator) indicates that parsing is to stop immediately because + """just like C{L{ParseFatalException}}, but thrown internally when an + C{L{ErrorStop}} ('-' operator) indicates that parsing is to stop immediately because an unbacktrackable syntax error has been found""" def __init__(self, pe): super(ParseSyntaxException, self).__init__( @@ -444,16 +430,13 @@ class ParseResults(object): return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) def __str__( self ): - out = "[" - sep = "" + out = [] for i in self.__toklist: if isinstance(i, ParseResults): - out += sep + _ustr(i) + out.append(_ustr(i)) else: - out += sep + repr(i) - sep = ", " - out += "]" - return out + out.append(repr(i)) + return '[' + ', '.join(out) + ']' def _asStringList( self, sep='' ): out = [] @@ -616,7 +599,7 @@ class ParseResults(object): self.__parent = None def __dir__(self): - return dir(super(ParseResults,self)) + self.keys() + return dir(super(ParseResults,self)) + list(self.keys()) def col (loc,strg): """Returns current column within a string, counting newlines as line separators. @@ -624,7 +607,7 @@ def col (loc,strg): Note: the default parsing behavior is to expand tabs in the input string before starting the parsing process. See L{I{ParserElement.parseString}} for more information - on parsing strings containing s, and suggested methods to maintain a + on parsing strings containing C{}s, and suggested methods to maintain a consistent view of the parsed string, the parse location, and line and column positions within the parsed string. """ @@ -636,7 +619,7 @@ def lineno(loc,strg): Note: the default parsing behavior is to expand tabs in the input string before starting the parsing process. See L{I{ParserElement.parseString}} for more information - on parsing strings containing s, and suggested methods to maintain a + on parsing strings containing C{}s, and suggested methods to maintain a consistent view of the parsed string, the parse location, and line and column positions within the parsed string. """ @@ -666,33 +649,23 @@ def nullDebugAction(*args): pass 'decorator to trim function calls to match the arity of the target' -if not _PY3K: - def _trim_arity(func, maxargs=2): - limit = [0] - def wrapper(*args): - while 1: - try: - return func(*args[limit[0]:]) - except TypeError: - if limit[0] <= maxargs: - limit[0] += 1 - continue - raise - return wrapper -else: - def _trim_arity(func, maxargs=2): - limit = maxargs - def wrapper(*args): - #~ nonlocal limit - while 1: - try: - return func(*args[limit:]) - except TypeError: - if limit: - limit -= 1 - continue - raise - return wrapper +def _trim_arity(func, maxargs=2): + if func in singleArgBuiltins: + return lambda s,l,t: func(t) + limit = [0] + foundArity = [False] + def wrapper(*args): + while 1: + try: + ret = func(*args[limit[0]:]) + foundArity[0] = True + return ret + except TypeError: + if limit[0] <= maxargs and not foundArity[0]: + limit[0] += 1 + continue + raise + return wrapper class ParserElement(object): """Abstract base level parser element class.""" @@ -705,6 +678,13 @@ class ParserElement(object): ParserElement.DEFAULT_WHITE_CHARS = chars setDefaultWhitespaceChars = staticmethod(setDefaultWhitespaceChars) + def inlineLiteralsUsing(cls): + """ + Set class to be used for inclusion of string literals into a parser. + """ + ParserElement.literalStringClass = cls + inlineLiteralsUsing = staticmethod(inlineLiteralsUsing) + def __init__( self, savelist=False ): self.parseAction = list() self.failAction = None @@ -789,14 +769,14 @@ class ParserElement(object): C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: - s = the original string being parsed (see note below) - loc = the location of the matching substring - - toks = a list of the matched tokens, packaged as a ParseResults object + - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object If the functions in fns modify the tokens, they can return them as the return value from fn, and the modified list of tokens will replace the original. Otherwise, fn does not need to return any value. Note: the default parsing behavior is to expand tabs in the input string before starting the parsing process. See L{I{parseString}} for more information - on parsing strings containing s, and suggested methods to maintain a + on parsing strings containing C{}s, and suggested methods to maintain a consistent view of the parsed string, the parse location, and line and column positions within the parsed string. """ @@ -818,7 +798,7 @@ class ParserElement(object): - loc = location where expression match was attempted and failed - expr = the parse expression that failed - err = the exception thrown - The function returns no value. It may throw C{ParseFatalException} + The function returns no value. It may throw C{L{ParseFatalException}} if it is desired to stop parsing immediately.""" self.failAction = fn return self @@ -872,15 +852,12 @@ class ParserElement(object): loc,tokens = self.parseImpl( instring, preloc, doActions ) except IndexError: raise ParseException( instring, len(instring), self.errmsg, self ) - except ParseBaseException: + except ParseBaseException, err: #~ print ("Exception raised:", err) err = None if self.debugActions[2]: - err = sys.exc_info()[1] self.debugActions[2]( instring, tokensStart, self, err ) if self.failAction: - if err is None: - err = sys.exc_info()[1] self.failAction( instring, tokensStart, self, err ) raise else: @@ -910,10 +887,9 @@ class ParserElement(object): self.resultsName, asList=self.saveAsList and isinstance(tokens,(ParseResults,list)), modal=self.modalResults ) - except ParseBaseException: + except ParseBaseException, err: #~ print "Exception raised in user parse action:", err if (self.debugActions[2] ): - err = sys.exc_info()[1] self.debugActions[2]( instring, tokensStart, self, err ) raise else: @@ -952,8 +928,7 @@ class ParserElement(object): value = self._parseNoCache( instring, loc, doActions, callPreParse ) ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) return value - except ParseBaseException: - pe = sys.exc_info()[1] + except ParseBaseException, pe: ParserElement._exprArgCache[ lookup ] = pe raise @@ -994,7 +969,7 @@ class ParserElement(object): If you want the grammar to require that the entire input string be successfully parsed, then set C{parseAll} to True (equivalent to ending - the grammar with C{StringEnd()}). + the grammar with C{L{StringEnd()}}). Note: C{parseString} implicitly calls C{expandtabs()} on the input string, in order to report proper column numbers in parse actions. @@ -1023,12 +998,11 @@ class ParserElement(object): loc = self.preParse( instring, loc ) se = Empty() + StringEnd() se._parse( instring, loc ) - except ParseBaseException: + except ParseBaseException, exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] raise exc else: return tokens @@ -1076,16 +1050,15 @@ class ParserElement(object): loc = nextLoc else: loc = preloc+1 - except ParseBaseException: + except ParseBaseException, exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] raise exc def transformString( self, instring ): - """Extension to C{scanString}, to modify matching text with modified tokens that may + """Extension to C{L{scanString}}, to modify matching text with modified tokens that may be returned from a parse action. To use C{transformString}, define a grammar and attach a parse action to it that modifies the returned token list. Invoking C{transformString()} on a target string will then scan for matches, @@ -1110,33 +1083,31 @@ class ParserElement(object): out.append(instring[lastE:]) out = [o for o in out if o] return "".join(map(_ustr,_flatten(out))) - except ParseBaseException: + except ParseBaseException, exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] raise exc def searchString( self, instring, maxMatches=_MAX_INT ): - """Another extension to C{scanString}, simplifying the access to the tokens found + """Another extension to C{L{scanString}}, simplifying the access to the tokens found to match the given parse expression. May be called with optional C{maxMatches} argument, to clip searching after 'n' matches are found. """ try: return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) - except ParseBaseException: + except ParseBaseException, exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] raise exc def __add__(self, other ): - """Implementation of + operator - returns And""" + """Implementation of + operator - returns C{L{And}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1144,9 +1115,9 @@ class ParserElement(object): return And( [ self, other ] ) def __radd__(self, other ): - """Implementation of + operator when left operand is not a C{ParserElement}""" + """Implementation of + operator when left operand is not a C{L{ParserElement}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1154,9 +1125,9 @@ class ParserElement(object): return other + self def __sub__(self, other): - """Implementation of - operator, returns C{And} with error stop""" + """Implementation of - operator, returns C{L{And}} with error stop""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1164,9 +1135,9 @@ class ParserElement(object): return And( [ self, And._ErrorStop(), other ] ) def __rsub__(self, other ): - """Implementation of - operator when left operand is not a C{ParserElement}""" + """Implementation of - operator when left operand is not a C{L{ParserElement}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1179,12 +1150,12 @@ class ParserElement(object): tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples may also include C{None} as in: - C{expr*(n,None)} or C{expr*(n,)} is equivalent - to C{expr*n + ZeroOrMore(expr)} + to C{expr*n + L{ZeroOrMore}(expr)} (read as "at least n instances of C{expr}") - C{expr*(None,n)} is equivalent to C{expr*(0,n)} (read as "0 to n instances of C{expr}") - - C{expr*(None,None)} is equivalent to C{ZeroOrMore(expr)} - - C{expr*(1,None)} is equivalent to C{OneOrMore(expr)} + - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} + - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} Note that C{expr*(None,n)} does not raise an exception if more than n exprs exist in the input stream; that is, @@ -1245,9 +1216,9 @@ class ParserElement(object): return self.__mul__(other) def __or__(self, other ): - """Implementation of | operator - returns C{MatchFirst}""" + """Implementation of | operator - returns C{L{MatchFirst}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1255,9 +1226,9 @@ class ParserElement(object): return MatchFirst( [ self, other ] ) def __ror__(self, other ): - """Implementation of | operator when left operand is not a C{ParserElement}""" + """Implementation of | operator when left operand is not a C{L{ParserElement}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1265,9 +1236,9 @@ class ParserElement(object): return other | self def __xor__(self, other ): - """Implementation of ^ operator - returns C{Or}""" + """Implementation of ^ operator - returns C{L{Or}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1275,9 +1246,9 @@ class ParserElement(object): return Or( [ self, other ] ) def __rxor__(self, other ): - """Implementation of ^ operator when left operand is not a C{ParserElement}""" + """Implementation of ^ operator when left operand is not a C{L{ParserElement}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1285,9 +1256,9 @@ class ParserElement(object): return other ^ self def __and__(self, other ): - """Implementation of & operator - returns C{Each}""" + """Implementation of & operator - returns C{L{Each}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1295,9 +1266,9 @@ class ParserElement(object): return Each( [ self, other ] ) def __rand__(self, other ): - """Implementation of & operator when left operand is not a C{ParserElement}""" + """Implementation of & operator when left operand is not a C{L{ParserElement}}""" if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1305,11 +1276,11 @@ class ParserElement(object): return other & self def __invert__( self ): - """Implementation of ~ operator - returns C{NotAny}""" + """Implementation of ~ operator - returns C{L{NotAny}}""" return NotAny( self ) def __call__(self, name): - """Shortcut for C{setResultsName}, with C{listAllMatches=default}:: + """Shortcut for C{L{setResultsName}}, with C{listAllMatches=default}:: userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") could be written as:: userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") @@ -1403,15 +1374,17 @@ class ParserElement(object): try: file_contents = file_or_filename.read() except AttributeError: - f = open(file_or_filename, "rb") + f = open(file_or_filename, "r") file_contents = f.read() f.close() try: return self.parseString(file_contents, parseAll) - except ParseBaseException: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - exc = sys.exc_info()[1] - raise exc + except ParseBaseException, exc: + if ParserElement.verbose_stacktrace: + raise + else: + # catch and re-raise exception from here, clears out pyparsing internal stack trace + raise exc def getException(self): return ParseException("",0,self.errmsg,self) @@ -1515,10 +1488,11 @@ class Literal(Token): exc.pstr = instring raise exc _L = Literal +ParserElement.literalStringClass = Literal class Keyword(Token): """Token to exactly match a specified string as a keyword, that is, it must be - immediately followed by a non-keyword character. Compare with C{Literal}:: + immediately followed by a non-keyword character. Compare with C{L{Literal}}:: Literal("if") will match the leading C{'if'} in C{'ifAndOnlyIf'}. Keyword("if") will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} Accepts two optional constructor arguments in addition to the keyword string: @@ -1821,9 +1795,9 @@ class QuotedString(Token): - quoteChar - string of one or more characters defining the quote delimiting string - escChar - character to escape quotes, typically backslash (default=None) - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) - - multiline - boolean indicating whether quotes can span multiple lines (default=False) - - unquoteResults - boolean indicating whether the matched text should be unquoted (default=True) - - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=None => same as quoteChar) + - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) """ super(QuotedString,self).__init__() @@ -2003,7 +1977,7 @@ class White(Token): by pyparsing grammars. This class is included when some whitespace structures are significant. Define with a string containing the whitespace characters to be matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, - as defined for the C{Word} class.""" + as defined for the C{L{Word}} class.""" whiteStrs = { " " : "", "\t": "", @@ -2331,7 +2305,8 @@ class And(ParseExpression): class _ErrorStop(Empty): def __init__(self, *args, **kwargs): - super(Empty,self).__init__(*args, **kwargs) + super(And._ErrorStop,self).__init__(*args, **kwargs) + self.name = '-' self.leaveWhitespace() def __init__( self, exprs, savelist = True ): @@ -2359,8 +2334,7 @@ class And(ParseExpression): loc, exprtokens = e._parse( instring, loc, doActions ) except ParseSyntaxException: raise - except ParseBaseException: - pe = sys.exc_info()[1] + except ParseBaseException, pe: raise ParseSyntaxException(pe) except IndexError: raise ParseSyntaxException( ParseException(instring, len(instring), self.errmsg, self) ) @@ -2412,8 +2386,7 @@ class Or(ParseExpression): for e in self.exprs: try: loc2 = e.tryParse( instring, loc ) - except ParseException: - err = sys.exc_info()[1] + except ParseException, err: if err.loc > maxExcLoc: maxException = err maxExcLoc = err.loc @@ -2436,7 +2409,7 @@ class Or(ParseExpression): def __ixor__(self, other ): if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) return self.append( other ) #Or( [ self, other ] ) def __str__( self ): @@ -2495,7 +2468,7 @@ class MatchFirst(ParseExpression): def __ior__(self, other ): if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement.literalStringClass( other ) return self.append( other ) #MatchFirst( [ self, other ] ) def __str__( self ): @@ -2916,13 +2889,14 @@ class Forward(ParseElementEnhance): thereby leaving b and c out as parseable alternatives. It is recommended that you explicitly group the values inserted into the C{Forward}:: fwdExpr << (a | b | c) + Converting to use the '<<=' operator instead will avoid this problem. """ def __init__( self, other=None ): super(Forward,self).__init__( other, savelist=False ) def __lshift__( self, other ): if isinstance( other, basestring ): - other = Literal(other) + other = ParserElement.literalStringClass(other) self.expr = other self.mayReturnEmpty = other.mayReturnEmpty self.strRepr = None @@ -2933,7 +2907,8 @@ class Forward(ParseElementEnhance): self.saveAsList = self.expr.saveAsList self.ignoreExprs.extend(self.expr.ignoreExprs) return None - + __ilshift__ = __lshift__ + def leaveWhitespace( self ): self.skipWhitespace = False return self @@ -2993,7 +2968,7 @@ class Upcase(TokenConverter): DeprecationWarning,stacklevel=2) def postParse( self, instring, loc, tokenlist ): - return list(map( string.upper, tokenlist )) + return list(map( str.upper, tokenlist )) class Combine(TokenConverter): @@ -3029,7 +3004,7 @@ class Combine(TokenConverter): return retToks class Group(TokenConverter): - """Converter to return the matched tokens as a list - useful for returning tokens of C{ZeroOrMore} and C{OneOrMore} expressions.""" + """Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.""" def __init__( self, expr ): super(Group,self).__init__( expr ) self.saveAsList = True @@ -3105,8 +3080,7 @@ def traceParseAction(f): sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) try: ret = f(*paArgs) - except Exception: - exc = sys.exc_info()[1] + except Exception, exc: sys.stderr.write( "<}()}. """ def _replFunc(*args): return [replStr] @@ -3398,7 +3372,7 @@ def downcaseTokens(s,l,t): return [ tt.lower() for tt in map(_ustr,t) ] def keepOriginalText(s,startLoc,t): - """DEPRECATED - use new helper method C{originalTextFor}. + """DEPRECATED - use new helper method C{L{originalTextFor}}. Helper parse action to preserve original parsed text, overriding any nested parse actions.""" try: @@ -3464,7 +3438,7 @@ def makeXMLTags(tagStr): def withAttribute(*args,**attrDict): """Helper to create a validating parse action to be used with start tags created - with C{makeXMLTags} or C{makeHTMLTags}. Use C{withAttribute} to qualify a starting tag + with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag with a required attribute value, to avoid false matches on common tags such as C{} or C{

}. @@ -3499,7 +3473,7 @@ opAssoc = _Constants() opAssoc.LEFT = object() opAssoc.RIGHT = object() -def operatorPrecedence( baseExpr, opList ): +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): """Helper method for constructing grammars of expressions made up of operators working in a precedence hierarchy. Operators may be unary or binary, left- or right-associative. Parse actions can also be attached @@ -3518,13 +3492,15 @@ def operatorPrecedence( baseExpr, opList ): be 1, 2, or 3) - rightLeftAssoc is the indicator whether the operator is right or left associative, using the pyparsing-defined - constants opAssoc.RIGHT and opAssoc.LEFT. + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. - parseAction is the parse action to be associated with expressions matching this operator expression (the parse action tuple member may be omitted) + - lpar - expression for matching left-parentheses (default=Suppress('(')) + - rpar - expression for matching right-parentheses (default=Suppress(')')) """ ret = Forward() - lastExpr = baseExpr | ( Suppress('(') + ret + Suppress(')') ) + lastExpr = baseExpr | ( lpar + ret + rpar ) for i,operDef in enumerate(opList): opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] if arity == 3: @@ -3569,6 +3545,7 @@ def operatorPrecedence( baseExpr, opList ): lastExpr = thisExpr ret << lastExpr return ret +operatorPrecedence = infixNotation dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") @@ -3715,8 +3692,7 @@ if __name__ == "__main__": print ("tokens.columns = " + str(tokens.columns)) print ("tokens.tables = " + str(tokens.tables)) print (tokens.asXML("SQL",True)) - except ParseBaseException: - err = sys.exc_info()[1] + except ParseBaseException, err: print (teststring + "->") print (err.line) print (" "*(err.column-1) + "^") From 3c04f699c38bbf3a32cbcab3a9f69ffb930c8e57 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 2 Jan 2013 11:21:26 +0100 Subject: [PATCH 51/67] Force use of non-unicode constants in compiled templates. Fixes a problem with regular expression character classes and probably other things. --- src/calibre/utils/formatter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 0711013437..eff38203a0 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -294,11 +294,11 @@ class _CompileParser(_Parser): self.parent_book, self.parent_locals, *args) elif self.token_is_constant(): # String or number - v = self.token() + v = unicode(self.token()) if self.compile_text: tv = v.replace("\\", "\\\\") tv = tv.replace("'", "\\'") - self.compile_text += "\targs[%d].append('%s')\n"%(level, tv) + self.compile_text += "\targs[%d].append(unicode('%s'))\n"%(level, tv) return v else: self.error(_('expression is not function or constant')) From 2179f79f8656d871810cf3c498659271926d8a41 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 2 Jan 2013 23:41:59 +0530 Subject: [PATCH 52/67] Fix Today's Zaman --- recipes/todays_zaman.recipe | 43 +++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/recipes/todays_zaman.recipe b/recipes/todays_zaman.recipe index 5f3b85131a..13d82e31fb 100644 --- a/recipes/todays_zaman.recipe +++ b/recipes/todays_zaman.recipe @@ -26,28 +26,33 @@ class TodaysZaman_en(BasicNewsRecipe): # remove_attributes = ['width','height'] feeds = [ - ( u'Home', u'http://www.todayszaman.com/rss?sectionId=0'), - ( u'News', u'http://www.todayszaman.com/rss?sectionId=100'), - ( u'Business', u'http://www.todayszaman.com/rss?sectionId=105'), - ( u'Interviews', u'http://www.todayszaman.com/rss?sectionId=8'), - ( u'Columnists', u'http://www.todayszaman.com/rss?sectionId=6'), - ( u'Op-Ed', u'http://www.todayszaman.com/rss?sectionId=109'), - ( u'Arts & Culture', u'http://www.todayszaman.com/rss?sectionId=110'), - ( u'Expat Zone', u'http://www.todayszaman.com/rss?sectionId=132'), - ( u'Sports', u'http://www.todayszaman.com/rss?sectionId=5'), - ( u'Features', u'http://www.todayszaman.com/rss?sectionId=116'), - ( u'Travel', u'http://www.todayszaman.com/rss?sectionId=117'), - ( u'Leisure', u'http://www.todayszaman.com/rss?sectionId=118'), - ( u'Weird But True', u'http://www.todayszaman.com/rss?sectionId=134'), - ( u'Life', u'http://www.todayszaman.com/rss?sectionId=133'), - ( u'Health', u'http://www.todayszaman.com/rss?sectionId=126'), - ( u'Press Review', u'http://www.todayszaman.com/rss?sectionId=130'), - ( u'Todays think tanks', u'http://www.todayszaman.com/rss?sectionId=159'), - - ] + ( u'Home', u'http://www.todayszaman.com/0.rss'), + ( u'Sports', u'http://www.todayszaman.com/5.rss'), + ( u'Columnists', u'http://www.todayszaman.com/6.rss'), + ( u'Interviews', u'http://www.todayszaman.com/9.rss'), + ( u'News', u'http://www.todayszaman.com/100.rss'), + ( u'National', u'http://www.todayszaman.com/101.rss'), + ( u'Diplomacy', u'http://www.todayszaman.com/102.rss'), + ( u'World', u'http://www.todayszaman.com/104.rss'), + ( u'Business', u'http://www.todayszaman.com/105.rss'), + ( u'Op-Ed', u'http://www.todayszaman.com/109.rss'), + ( u'Arts & Culture', u'http://www.todayszaman.com/110.rss'), + ( u'Features', u'http://www.todayszaman.com/116.rss'), + ( u'Travel', u'http://www.todayszaman.com/117.rss'), + ( u'Food', u'http://www.todayszaman.com/124.rss'), + ( u'Press Review', u'http://www.todayszaman.com/130.rss'), + ( u'Expat Zone', u'http://www.todayszaman.com/132.rss'), + ( u'Life', u'http://www.todayszaman.com/133.rss'), + ( u'Think Tanks', u'http://www.todayszaman.com/159.rss'), + ( u'Almanac', u'http://www.todayszaman.com/161.rss'), + ( u'Health', u'http://www.todayszaman.com/162.rss'), + ( u'Fashion & Beauty', u'http://www.todayszaman.com/163.rss'), + ( u'Science & Technology', u'http://www.todayszaman.com/349.rss'), + ] #def preprocess_html(self, soup): # return self.adeify_images(soup) #def print_version(self, url): #there is a probem caused by table format #return url.replace('http://www.todayszaman.com/newsDetail_getNewsById.action?load=detay&', 'http://www.todayszaman.com/newsDetail_openPrintPage.action?') + From f1863d3971d84f61b78d3d263b5e7fb215353c3f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 09:01:53 +0530 Subject: [PATCH 53/67] Update The Economist --- recipes/economist.recipe | 14 +-- recipes/economist_free.recipe | 195 ++++------------------------------ 2 files changed, 21 insertions(+), 188 deletions(-) diff --git a/recipes/economist.recipe b/recipes/economist.recipe index 25e46892f8..b5e2a1fd9b 100644 --- a/recipes/economist.recipe +++ b/recipes/economist.recipe @@ -70,18 +70,6 @@ class Economist(BasicNewsRecipe): return br ''' - def get_cover_url(self): - soup = self.index_to_soup('http://www.economist.com/printedition/covers') - div = soup.find('div', attrs={'class':lambda x: x and - 'print-cover-links' in x}) - a = div.find('a', href=True) - url = a.get('href') - if url.startswith('/'): - url = 'http://www.economist.com' + url - soup = self.index_to_soup(url) - div = soup.find('div', attrs={'class':'cover-content'}) - img = div.find('img', src=True) - return img.get('src') def parse_index(self): return self.economist_parse_index() @@ -92,7 +80,7 @@ class Economist(BasicNewsRecipe): if div is not None: img = div.find('img', src=True) if img is not None: - self.cover_url = img['src'] + self.cover_url = re.sub('thumbnail','full',img['src']) feeds = OrderedDict() for section in soup.findAll(attrs={'class':lambda x: x and 'section' in x}): diff --git a/recipes/economist_free.recipe b/recipes/economist_free.recipe index a64310c252..b5e2a1fd9b 100644 --- a/recipes/economist_free.recipe +++ b/recipes/economist_free.recipe @@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe from calibre.ebooks.BeautifulSoup import Tag, NavigableString from collections import OrderedDict -import time, re +import re class Economist(BasicNewsRecipe): @@ -37,7 +37,6 @@ class Economist(BasicNewsRecipe): padding: 7px 0px 9px; } ''' - oldest_article = 7.0 remove_tags = [ dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']), @@ -46,7 +45,6 @@ class Economist(BasicNewsRecipe): {'class': lambda x: x and 'share-links-header' in x}, ] keep_only_tags = [dict(id='ec-article-body')] - needs_subscription = False no_stylesheets = True preprocess_regexps = [(re.compile('.*', re.DOTALL), lambda x:'')] @@ -55,28 +53,26 @@ class Economist(BasicNewsRecipe): # downloaded with connection reset by peer (104) errors. delay = 1 - def get_cover_url(self): - soup = self.index_to_soup('http://www.economist.com/printedition/covers') - div = soup.find('div', attrs={'class':lambda x: x and - 'print-cover-links' in x}) - a = div.find('a', href=True) - url = a.get('href') - if url.startswith('/'): - url = 'http://www.economist.com' + url - soup = self.index_to_soup(url) - div = soup.find('div', attrs={'class':'cover-content'}) - img = div.find('img', src=True) - return img.get('src') + needs_subscription = False + ''' + def get_browser(self): + br = BasicNewsRecipe.get_browser() + if self.username and self.password: + br.open('http://www.economist.com/user/login') + br.select_form(nr=1) + br['name'] = self.username + br['pass'] = self.password + res = br.submit() + raw = res.read() + if '>Log out<' not in raw: + raise ValueError('Failed to login to economist.com. ' + 'Check your username and password.') + return br + ''' + def parse_index(self): - try: - return self.economist_parse_index() - except: - raise - self.log.warn( - 'Initial attempt to parse index failed, retrying in 30 seconds') - time.sleep(30) - return self.economist_parse_index() + return self.economist_parse_index() def economist_parse_index(self): soup = self.index_to_soup(self.INDEX) @@ -84,7 +80,7 @@ class Economist(BasicNewsRecipe): if div is not None: img = div.find('img', src=True) if img is not None: - self.cover_url = img['src'] + self.cover_url = re.sub('thumbnail','full',img['src']) feeds = OrderedDict() for section in soup.findAll(attrs={'class':lambda x: x and 'section' in x}): @@ -151,154 +147,3 @@ class Economist(BasicNewsRecipe): div.insert(2, img) table.replaceWith(div) return soup - -''' -from calibre.web.feeds.news import BasicNewsRecipe -from calibre.utils.threadpool import ThreadPool, makeRequests -from calibre.ebooks.BeautifulSoup import Tag, NavigableString -import time, string, re -from datetime import datetime -from lxml import html - -class Economist(BasicNewsRecipe): - - title = 'The Economist (RSS)' - language = 'en' - - __author__ = "Kovid Goyal" - description = ('Global news and current affairs from a European' - ' perspective. Best downloaded on Friday mornings (GMT).' - ' Much slower than the print edition based version.') - extra_css = '.headline {font-size: x-large;} \n h2 { font-size: small; } \n h1 { font-size: medium; }' - oldest_article = 7.0 - cover_url = 'http://media.economist.com/sites/default/files/imagecache/print-cover-thumbnail/print-covers/currentcoverus_large.jpg' - #cover_url = 'http://www.economist.com/images/covers/currentcoverus_large.jpg' - remove_tags = [ - dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']), - dict(attrs={'class':['dblClkTrk', 'ec-article-info', - 'share_inline_header', 'related-items']}), - {'class': lambda x: x and 'share-links-header' in x}, - ] - keep_only_tags = [dict(id='ec-article-body')] - no_stylesheets = True - preprocess_regexps = [(re.compile('.*', re.DOTALL), - lambda x:'')] - - def parse_index(self): - from calibre.web.feeds.feedparser import parse - if self.test: - self.oldest_article = 14.0 - raw = self.index_to_soup( - 'http://feeds.feedburner.com/economist/full_print_edition', - raw=True) - entries = parse(raw).entries - pool = ThreadPool(10) - self.feed_dict = {} - requests = [] - for i, item in enumerate(entries): - title = item.get('title', _('Untitled article')) - published = item.date_parsed - if not published: - published = time.gmtime() - utctime = datetime(*published[:6]) - delta = datetime.utcnow() - utctime - if delta.days*24*3600 + delta.seconds > 24*3600*self.oldest_article: - self.log.debug('Skipping article %s as it is too old.'%title) - continue - link = item.get('link', None) - description = item.get('description', '') - author = item.get('author', '') - - requests.append([i, link, title, description, author, published]) - if self.test: - requests = requests[:4] - requests = makeRequests(self.process_eco_feed_article, requests, self.eco_article_found, - self.eco_article_failed) - for r in requests: pool.putRequest(r) - pool.wait() - - return self.eco_sort_sections([(t, a) for t, a in - self.feed_dict.items()]) - - def eco_sort_sections(self, feeds): - if not feeds: - raise ValueError('No new articles found') - order = { - 'The World This Week': 1, - 'Leaders': 2, - 'Letters': 3, - 'Briefing': 4, - 'Business': 5, - 'Finance And Economics': 6, - 'Science & Technology': 7, - 'Books & Arts': 8, - 'International': 9, - 'United States': 10, - 'Asia': 11, - 'Europe': 12, - 'The Americas': 13, - 'Middle East & Africa': 14, - 'Britain': 15, - 'Obituary': 16, - } - return sorted(feeds, cmp=lambda x,y:cmp(order.get(x[0], 100), - order.get(y[0], 100))) - - def process_eco_feed_article(self, args): - from calibre import browser - i, url, title, description, author, published = args - br = browser() - ret = br.open(url) - raw = ret.read() - url = br.geturl().split('?')[0]+'/print' - root = html.fromstring(raw) - matches = root.xpath('//*[@class = "ec-article-info"]') - feedtitle = 'Miscellaneous' - if matches: - feedtitle = string.capwords(html.tostring(matches[-1], method='text', - encoding=unicode).split('|')[-1].strip()) - return (i, feedtitle, url, title, description, author, published) - - def eco_article_found(self, req, result): - from calibre.web.feeds import Article - i, feedtitle, link, title, description, author, published = result - self.log('Found print version for article:', title, 'in', feedtitle, - 'at', link) - - a = Article(i, title, link, author, description, published, '') - - article = dict(title=a.title, description=a.text_summary, - date=time.strftime(self.timefmt, a.date), author=a.author, url=a.url) - if feedtitle not in self.feed_dict: - self.feed_dict[feedtitle] = [] - self.feed_dict[feedtitle].append(article) - - def eco_article_failed(self, req, tb): - self.log.error('Failed to download %s with error:'%req.args[0][2]) - self.log.debug(tb) - - def eco_find_image_tables(self, soup): - for x in soup.findAll('table', align=['right', 'center']): - if len(x.findAll('font')) in (1,2) and len(x.findAll('img')) == 1: - yield x - - def postprocess_html(self, soup, first): - body = soup.find('body') - for name, val in body.attrs: - del body[name] - for table in list(self.eco_find_image_tables(soup)): - caption = table.find('font') - img = table.find('img') - div = Tag(soup, 'div') - div['style'] = 'text-align:left;font-size:70%' - ns = NavigableString(self.tag_to_string(caption)) - div.insert(0, ns) - div.insert(1, Tag(soup, 'br')) - img.extract() - del img['width'] - del img['height'] - div.insert(2, img) - table.replaceWith(div) - return soup -''' - From 16a95871ba259c25ce9ba763b9ae4a4f9e99718d Mon Sep 17 00:00:00 2001 From: Translators <> Date: Thu, 3 Jan 2013 04:47:22 +0000 Subject: [PATCH 54/67] Launchpad automatic translations update. --- src/calibre/translations/fr.po | 51 ++++------------------------------ 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/src/calibre/translations/fr.po b/src/calibre/translations/fr.po index c4e51417ba..fa7fae3c4f 100644 --- a/src/calibre/translations/fr.po +++ b/src/calibre/translations/fr.po @@ -8,15 +8,15 @@ msgstr "" "Project-Id-Version: calibre 0.4.22\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-12-28 04:12+0000\n" -"PO-Revision-Date: 2012-12-20 07:11+0000\n" +"PO-Revision-Date: 2013-01-02 22:48+0000\n" "Last-Translator: Franck \n" "Language-Team: PCGen\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Launchpad-Export-Date: 2012-12-29 04:44+0000\n" -"X-Generator: Launchpad (build 16378)\n" +"X-Launchpad-Export-Date: 2013-01-03 04:47+0000\n" +"X-Generator: Launchpad (build 16393)\n" "Language: fr\n" "X-Poedit-Bookmarks: 1177,1104,-1,-1,-1,-1,-1,-1,-1,-1\n" "Generated-By: pygettext.py 1.5\n" @@ -5158,7 +5158,7 @@ msgstr "Espagne" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:525 msgid "Brazil" -msgstr "" +msgstr "Brésil" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/sources/amazon.py:529 msgid "Amazon website to use:" @@ -19867,7 +19867,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:438 msgid "Save current settings as a theme:" -msgstr "Sauver les réglages acutels en tant que thèmes :" +msgstr "Sauver les réglages actuels en tant que thèmes :" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:440 msgid "Load a previously saved theme:" @@ -25716,44 +25716,3 @@ msgstr "" "dépendra\n" "de l'état des métadonnées dans votre bibliothèque et de votre style " "d'édition personnel." - -#~ msgid "smaller" -#~ msgstr "plus petit" - -#~ msgid "larger" -#~ msgstr "plus grand" - -#, python-format -#~ msgid "" -#~ "Make font size %(which)s\n" -#~ "Current magnification: %(mag).1f" -#~ msgstr "" -#~ "Modifie la taille de fonte %(which)s\n" -#~ "Agrandissement courant: %(mag).1f" - -#, python-format -#~ msgid "" -#~ "The unit of measure. Default is inch. Choices are %s Note: This does not " -#~ "override the unit for margins!" -#~ msgstr "" -#~ "L’unité de mesure. Par défaut : pouce (inch). Les choix sont %s. Note : cela " -#~ "n’écrase pas l’unité des marges !" - -#, python-format -#~ msgid "The orientation of the page. Default is portrait. Choices are %s" -#~ msgstr "L’orientation de la page. Par défaut : portrait. Les choix sont %s" - -#~ msgid "&Orientation:" -#~ msgstr "&Orientation :" - -#~ msgid "" -#~ "Note: The paper size settings below only take effect if you have set " -#~ "the output profile to the default output profile. Otherwise the output " -#~ "profile will override these settings." -#~ msgstr "" -#~ "Note : Les réglages de taille de papier ci-dessous prennent seulement " -#~ "effet si vous avez défini le profil de sortie au profile de sortie par " -#~ "défaut. Sinon, le profil de sortie passera outre ces réglages." - -#~ msgid "Normal font size" -#~ msgstr "Taille de police normale" From b33929bbae37496a2a35e4cc41d576001677350f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 13:34:31 +0530 Subject: [PATCH 55/67] Fix text rendering on windows (font size was incorrect throwing off intra-glyph spacing and line spacing) --- src/calibre/ebooks/pdf/render/engine.py | 24 +++++++++++------------ src/calibre/ebooks/pdf/render/qt_hack.cpp | 13 ++++++++---- src/calibre/ebooks/pdf/render/qt_hack.h | 3 ++- src/calibre/ebooks/pdf/render/qt_hack.sip | 3 ++- src/calibre/ebooks/pdf/render/test.py | 12 ++++++++---- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/calibre/ebooks/pdf/render/engine.py b/src/calibre/ebooks/pdf/render/engine.py index b65aed0660..149d75d1f0 100644 --- a/src/calibre/ebooks/pdf/render/engine.py +++ b/src/calibre/ebooks/pdf/render/engine.py @@ -19,7 +19,7 @@ from calibre.constants import plugins from calibre.ebooks.pdf.render.serialize import (PDFStream, Path) from calibre.ebooks.pdf.render.common import inch, A4, fmtnum from calibre.ebooks.pdf.render.graphics import convert_path, Graphics -from calibre.utils.fonts.sfnt.container import Sfnt +from calibre.utils.fonts.sfnt.container import Sfnt, UnsupportedFont from calibre.utils.fonts.sfnt.metrics import FontMetrics Point = namedtuple('Point', 'x y') @@ -224,7 +224,11 @@ class PdfEngine(QPaintEngine): def create_sfnt(self, text_item): get_table = partial(self.qt_hack.get_sfnt_table, text_item) - ans = Font(Sfnt(get_table)) + try: + ans = Font(Sfnt(get_table)) + except UnsupportedFont as e: + raise UnsupportedFont('The font %s is not a valid sfnt. Error: %s'%( + text_item.font().family(), e)) glyph_map = self.qt_hack.get_glyph_map(text_item) gm = {} for uc, glyph_id in enumerate(glyph_map): @@ -251,18 +255,14 @@ class PdfEngine(QPaintEngine): except (KeyError, ValueError): pass glyphs = [] - pdf_pos = point - first_baseline = None + last_x = last_y = 0 for i, pos in enumerate(gi.positions): - if first_baseline is None: - first_baseline = pos.y() - glyph_pos = pos - delta = glyph_pos - pdf_pos - glyphs.append((delta.x(), pos.y()-first_baseline, gi.indices[i])) - pdf_pos = glyph_pos + x, y = pos.x(), pos.y() + glyphs.append((x-last_x, last_y - y, gi.indices[i])) + last_x, last_y = x, y - self.pdf.draw_glyph_run([1, 0, 0, -1, point.x(), - point.y()], gi.size, metrics, glyphs) + self.pdf.draw_glyph_run([gi.stretch, 0, 0, -1, 0, 0], gi.size, metrics, + glyphs) sip.delete(gi) @store_error diff --git a/src/calibre/ebooks/pdf/render/qt_hack.cpp b/src/calibre/ebooks/pdf/render/qt_hack.cpp index f68f40c921..e5f74c0a0f 100644 --- a/src/calibre/ebooks/pdf/render/qt_hack.cpp +++ b/src/calibre/ebooks/pdf/render/qt_hack.cpp @@ -17,18 +17,23 @@ GlyphInfo* get_glyphs(QPointF &p, const QTextItem &text_item) { QFontEngine *fe = ti.fontEngine; qreal size = ti.fontEngine->fontDef.pixelSize; #ifdef Q_WS_WIN - if (ti.fontEngine->type() == QFontEngine::Win) { + if (false && ti.fontEngine->type() == QFontEngine::Win) { + // This is used in the Qt sourcecode, but it gives incorrect results, + // so I have disabled it. I dont understand how it works in qpdf.cpp QFontEngineWin *fe = static_cast(ti.fontEngine); size = fe->tm.tmHeight; } #endif + int synthesized = ti.fontEngine->synthesized(); + qreal stretch = synthesized & QFontEngine::SynthesizedStretch ? ti.fontEngine->fontDef.stretch/100. : 1.; + QVarLengthArray glyphs; QVarLengthArray positions; QTransform m = QTransform::fromTranslate(p.x(), p.y()); fe->getGlyphPositions(ti.glyphs, m, ti.flags, glyphs, positions); QVector points = QVector(positions.count()); for (int i = 0; i < positions.count(); i++) { - points[i].setX(positions[i].x.toReal()); + points[i].setX(positions[i].x.toReal()/stretch); points[i].setY(positions[i].y.toReal()); } @@ -38,10 +43,10 @@ GlyphInfo* get_glyphs(QPointF &p, const QTextItem &text_item) { const quint32 *tag = reinterpret_cast("name"); - return new GlyphInfo(fe->getSfntTable(qToBigEndian(*tag)), size, points, indices); + return new GlyphInfo(fe->getSfntTable(qToBigEndian(*tag)), size, stretch, points, indices); } -GlyphInfo::GlyphInfo(const QByteArray& name, qreal size, const QVector &positions, const QVector &indices) :name(name), positions(positions), size(size), indices(indices) { +GlyphInfo::GlyphInfo(const QByteArray& name, qreal size, qreal stretch, const QVector &positions, const QVector &indices) :name(name), positions(positions), size(size), stretch(stretch), indices(indices) { } QByteArray get_sfnt_table(const QTextItem &text_item, const char* tag_name) { diff --git a/src/calibre/ebooks/pdf/render/qt_hack.h b/src/calibre/ebooks/pdf/render/qt_hack.h index d1cb5e208d..1bae8f2624 100644 --- a/src/calibre/ebooks/pdf/render/qt_hack.h +++ b/src/calibre/ebooks/pdf/render/qt_hack.h @@ -17,9 +17,10 @@ class GlyphInfo { QByteArray name; QVector positions; qreal size; + qreal stretch; QVector indices; - GlyphInfo(const QByteArray &name, qreal size, const QVector &positions, const QVector &indices); + GlyphInfo(const QByteArray &name, qreal size, qreal stretch, const QVector &positions, const QVector &indices); private: GlyphInfo(const GlyphInfo&); diff --git a/src/calibre/ebooks/pdf/render/qt_hack.sip b/src/calibre/ebooks/pdf/render/qt_hack.sip index b5a6fcf55e..54d8726f4d 100644 --- a/src/calibre/ebooks/pdf/render/qt_hack.sip +++ b/src/calibre/ebooks/pdf/render/qt_hack.sip @@ -13,9 +13,10 @@ class GlyphInfo { public: QByteArray name; qreal size; + qreal stretch; QVector &positions; QVector indices; - GlyphInfo(const QByteArray &name, qreal size, const QVector &positions, const QVector &indices); + GlyphInfo(const QByteArray &name, qreal size, qreal stretch, const QVector &positions, const QVector &indices); private: GlyphInfo(const GlyphInfo& g); diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index d57678a057..8c9c2f45bb 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -8,7 +8,6 @@ __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' import os -from tempfile import gettempdir from PyQt4.Qt import (QBrush, QColor, QPoint, QPixmap, QPainterPath, QRectF, QApplication, QPainter, Qt, QImage, QLinearGradient, @@ -99,14 +98,19 @@ def pen(p, xmax, ymax): p.drawRect(0, xmax/3, xmax/3, xmax/2) def text(p, xmax, ymax): - p.drawText(QPoint(0, ymax/3), 'Text') + f = p.font() + f.setPixelSize(24) + f.setFamily('Candara') + p.setFont(f) + p.drawText(QPoint(0, 100), + 'Test intra glyph spacing ffagain imceo') def main(): app = QApplication([]) app - tdir = gettempdir() + tdir = os.path.abspath('.') pdf = os.path.join(tdir, 'painter.pdf') - func = full + func = text dpi = 100 with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False) From 2d37f941435e761eea014232395f78737d4ab097 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 15:35:05 +0530 Subject: [PATCH 56/67] PDF Output: Fix OpenType fonts causing conversion to fail on windows --- .../ebooks/conversion/plugins/pdf_output.py | 37 ++++++++++++++----- src/calibre/ebooks/pdf/render/qt_hack.cpp | 2 + src/calibre/utils/fonts/sfnt/container.py | 6 ++- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index c2b0050dd7..44d0a6bce7 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -8,11 +8,13 @@ __docformat__ = 'restructuredtext en' Convert OEB ebook format to PDF. ''' -import glob -import os +import glob, os -from calibre.customize.conversion import OutputFormatPlugin, \ - OptionRecommendation +from PyQt4.Qt import QRawFont, QFont + +from calibre.constants import iswindows +from calibre.customize.conversion import (OutputFormatPlugin, + OptionRecommendation) from calibre.ptempfile import TemporaryDirectory UNITS = ['millimeter', 'centimeter', 'point', 'inch' , 'pica' , 'didot', @@ -136,7 +138,7 @@ class PDFOutput(OutputFormatPlugin): ''' from calibre.ebooks.oeb.base import urlnormalize from calibre.gui2 import must_use_qt - from calibre.utils.fonts.utils import get_font_names, remove_embed_restriction + from calibre.utils.fonts.utils import remove_embed_restriction from PyQt4.Qt import QFontDatabase, QByteArray # First find all @font-face rules and remove them, adding the embedded @@ -166,11 +168,13 @@ class PDFOutput(OutputFormatPlugin): except: continue must_use_qt() - QFontDatabase.addApplicationFontFromData(QByteArray(raw)) - try: - family_name = get_font_names(raw)[0] - except: - family_name = None + fid = QFontDatabase.addApplicationFontFromData(QByteArray(raw)) + family_name = None + if fid > -1: + try: + family_name = unicode(QFontDatabase.applicationFontFamilies(fid)[0]) + except (IndexError, KeyError): + pass if family_name: family_map[icu_lower(font_family)] = family_name @@ -190,6 +194,19 @@ class PDFOutput(OutputFormatPlugin): k = icu_lower(val[i].value) if k in family_map: val[i].value = family_map[k] + if iswindows: + # On windows, Qt uses GDI which does not support OpenType + # (CFF) fonts, so we need to nuke references to OpenType + # fonts. Note that you could compile QT with configure + # -directwrite, but that requires atleast Vista SP2 + for i in xrange(val.length): + if val[i].value: + f = QRawFont.fromFont(QFont(val[i].value)) + if len(f.fontTable('head')) == 0: + self.log.warn('Ignoring unsupported font: %s' + %val[i].value) + # Either a bitmap or (more likely) a CFF font + val[i].value = 'serif' def convert_text(self, oeb_book): from calibre.ebooks.metadata.opf2 import OPF diff --git a/src/calibre/ebooks/pdf/render/qt_hack.cpp b/src/calibre/ebooks/pdf/render/qt_hack.cpp index e5f74c0a0f..1a9a33c35f 100644 --- a/src/calibre/ebooks/pdf/render/qt_hack.cpp +++ b/src/calibre/ebooks/pdf/render/qt_hack.cpp @@ -21,6 +21,8 @@ GlyphInfo* get_glyphs(QPointF &p, const QTextItem &text_item) { // This is used in the Qt sourcecode, but it gives incorrect results, // so I have disabled it. I dont understand how it works in qpdf.cpp QFontEngineWin *fe = static_cast(ti.fontEngine); + // I think this should be tmHeight - tmInternalLeading, but pixelSize + // seems to work on windows as well, so leave it as pixelSize size = fe->tm.tmHeight; } #endif diff --git a/src/calibre/utils/fonts/sfnt/container.py b/src/calibre/utils/fonts/sfnt/container.py index 932cd6a3d2..0987869610 100644 --- a/src/calibre/utils/fonts/sfnt/container.py +++ b/src/calibre/utils/fonts/sfnt/container.py @@ -66,8 +66,10 @@ class Sfnt(object): if table: self.tables[table_tag] = self.TABLE_MAP.get( table_tag, UnknownTable)(table) - self.sfnt_version = (b'\0\x01\0\0' if b'glyf' in self.tables - else b'OTTO') + if not self.tables: + raise UnsupportedFont('This font has no tables') + self.sfnt_version = (b'\0\x01\0\0' if b'glyf' in self.tables + else b'OTTO') def __getitem__(self, key): return self.tables[key] From 45fbc31c8d8b439d52874e3f8f97458abb2398b9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 15:46:15 +0530 Subject: [PATCH 57/67] ... --- .../ebooks/conversion/plugins/pdf_output.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index 44d0a6bce7..dae3065d1f 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -183,6 +183,7 @@ class PDFOutput(OutputFormatPlugin): # Now map the font family name specified in the css to the actual # family name of the embedded font (they may be different in general). + font_warnings = set() for item in self.oeb.manifest: if not hasattr(item.data, 'cssRules'): continue for i, rule in enumerate(item.data.cssRules): @@ -200,13 +201,16 @@ class PDFOutput(OutputFormatPlugin): # fonts. Note that you could compile QT with configure # -directwrite, but that requires atleast Vista SP2 for i in xrange(val.length): - if val[i].value: - f = QRawFont.fromFont(QFont(val[i].value)) + family = val[i].value + if family: + f = QRawFont.fromFont(QFont(family)) if len(f.fontTable('head')) == 0: - self.log.warn('Ignoring unsupported font: %s' - %val[i].value) + if family not in font_warnings: + self.log.warn('Ignoring unsupported font: %s' + %family) + font_warnings.add(family) # Either a bitmap or (more likely) a CFF font - val[i].value = 'serif' + val[i].value = 'times' def convert_text(self, oeb_book): from calibre.ebooks.metadata.opf2 import OPF From 19a80e1d2863d79b8423639a8225fa549f5b716f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 16:00:10 +0530 Subject: [PATCH 58/67] Fix #1095016 (User category icon mixup on webserver) --- src/calibre/library/server/browse.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index a7789e5035..576f8801da 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -441,7 +441,11 @@ class BrowseServer(object): cat_len = len(category) if not (len(ucat) > cat_len and ucat.startswith(category+'.')): continue - icon = category_icon_map['user:'] + + if ucat in self.icon_map: + icon = '_'+quote(self.icon_map[ucat]) + else: + icon = category_icon_map['user:'] # we have a subcategory. Find any further dots (further subcats) cat_len += 1 cat = ucat[cat_len:] From a2578335c837a7081a8a07315cf4232e224f0abc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 16:21:07 +0530 Subject: [PATCH 59/67] ... --- src/calibre/ebooks/pdf/render/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/pdf/render/test.py b/src/calibre/ebooks/pdf/render/test.py index 8c9c2f45bb..866c15f83f 100644 --- a/src/calibre/ebooks/pdf/render/test.py +++ b/src/calibre/ebooks/pdf/render/test.py @@ -110,7 +110,7 @@ def main(): app tdir = os.path.abspath('.') pdf = os.path.join(tdir, 'painter.pdf') - func = text + func = full dpi = 100 with open(pdf, 'wb') as f: dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False) From 214643208ddacc7dfe079a78f330c724efd73f07 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 16:43:07 +0530 Subject: [PATCH 60/67] ... --- setup/translations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup/translations.py b/setup/translations.py index 3942953179..e3622d394e 100644 --- a/setup/translations.py +++ b/setup/translations.py @@ -230,6 +230,7 @@ class GetTranslations(Translations): # {{{ def run(self, opts): if not self.modified_translations: subprocess.check_call(['bzr', 'merge', self.BRANCH]) + subprocess.check_call(['bzr', 'resolve', '--take-other']) self.check_for_errors() if self.modified_translations: From 34ea3a00a89492cf0a48f60a7e19157e98a61bd7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Jan 2013 16:47:14 +0530 Subject: [PATCH 61/67] ... --- setup/translations.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/setup/translations.py b/setup/translations.py index e3622d394e..e0a512d21c 100644 --- a/setup/translations.py +++ b/setup/translations.py @@ -227,10 +227,22 @@ class GetTranslations(Translations): # {{{ ans.append(line.split()[-1]) return ans + def resolve_conflicts(self): + conflict = False + for line in subprocess.check_output(['bzr', 'status']).splitlines(): + if line == 'conflicts:': + conflict = True + break + if not conflict: + raise Exception('bzr merge failed and no conflicts found') + subprocess.check_call(['bzr', 'resolve', '--take-other']) + def run(self, opts): if not self.modified_translations: - subprocess.check_call(['bzr', 'merge', self.BRANCH]) - subprocess.check_call(['bzr', 'resolve', '--take-other']) + try: + subprocess.check_call(['bzr', 'merge', self.BRANCH]) + except subprocess.CalledProcessError: + self.resolve_conflicts() self.check_for_errors() if self.modified_translations: From d71f1e32927c96020bea63c6fbc17ea34be84f0e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Jan 2013 08:57:28 +0530 Subject: [PATCH 62/67] Add usb ids for D70 Pro II Android tablet --- src/calibre/devices/android/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index cc42cf33b2..f6220976a9 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -191,7 +191,7 @@ class ANDROID(USBMS): 0x10a9 : { 0x6050 : [0x227] }, # Prestigio - 0x2207 : { 0 : [0x222] }, + 0x2207 : { 0 : [0x222], 0x10 : [0x222] }, } EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books', From d4d7d69997705af403863e2a7ef27180de9f9e47 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Jan 2013 09:53:00 +0530 Subject: [PATCH 63/67] version 0.9.13 --- Changelog.yaml | 51 ++++++++++++++++++++++++++++++++++++++++ src/calibre/constants.py | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/Changelog.yaml b/Changelog.yaml index afe40af4b3..0f4ecdd360 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -19,6 +19,57 @@ # new recipes: # - title: +- version: 0.9.13 + date: 2013-01-04 + + new features: + - title: "Complete rewrite of the PDF Output engine, to support links and fix various bugs" + type: major + description: "calibre now has a new PDF output engine that supports links in the text. It also fixes various bugs, detailed below. In order to implement support for links and fix these bugs, the engine had to be completely rewritten, so there may be some regressions." + + - title: "Show disabled device plugins in Preferences->Ignored Devices" + + - title: "Get Books: Fix Smashwords, Google boks and B&N stores. Add Nook UK store" + + - title: "Allow series numbers lower than -100 for custom series columns." + tickets: [1094475] + + - title: "Add mass storage driver for rockhip based android smart phones" + tickets: [1087809] + + - title: "Add a clear ratings button to the edit metadata dialog" + + bug fixes: + - title: "PDF Output: Fix custom page sizes not working on OS X" + + - title: "PDF Output: Fix embedding of many fonts not supported (note that embedding of OpenType fonts with Postscript outlines is still not supported on windows, though it is supported on other operating systems)" + + - title: "PDF Output: Fix crashes converting some books to PDF on OS X" + tickets: [1087688] + + - title: "HTML Input: Handle entities inside href attributes when following the links in an HTML file." + tickets: [1094203] + + - title: "Content server: Fix custom icons not used for sub categories" + tickets: [1095016] + + - title: "Force use of non-unicode constants in compiled templates. Fixes a problem with regular expression character classes and probably other things." + + - title: "Kobo driver: Do not error out if there are invalid dates in the device database" + tickets: [1094597] + + - title: "Content server: Fix for non-unicode hostnames when using mDNS" + tickets: [1094063] + + improved recipes: + - Today's Zaman + - The Economist + - Foreign Affairs + - New York Times + - Alternet + - Harper's Magazine + - La Stampa + - version: 0.9.12 date: 2012-12-28 diff --git a/src/calibre/constants.py b/src/calibre/constants.py index ba56e1fc9e..60e4006ac6 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -4,7 +4,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = u'calibre' -numeric_version = (0, 9, 12) +numeric_version = (0, 9, 13) __version__ = u'.'.join(map(unicode, numeric_version)) __author__ = u"Kovid Goyal " From 418b5f8e3f6c4ed51d506a2ea3da4b6c470fe173 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Jan 2013 10:32:59 +0530 Subject: [PATCH 64/67] IGN:Tag release --- src/calibre/translations/calibre.pot | 425 +++++++++++++++------------ 1 file changed, 234 insertions(+), 191 deletions(-) diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index b991330c99..561a30d9ff 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -1,12 +1,12 @@ # Translation template file.. -# Copyright (C) 2012 Kovid Goyal -# Kovid Goyal , 2012. +# Copyright (C) 2013 Kovid Goyal +# Kovid Goyal , 2013. # msgid "" msgstr "" -"Project-Id-Version: calibre 0.9.12\n" -"POT-Creation-Date: 2012-12-28 08:51+IST\n" -"PO-Revision-Date: 2012-12-28 08:51+IST\n" +"Project-Id-Version: calibre 0.9.13\n" +"POT-Creation-Date: 2013-01-04 09:54+IST\n" +"PO-Revision-Date: 2013-01-04 09:54+IST\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -24,8 +24,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/db/cache.py:106 #: /home/kovid/work/calibre/src/calibre/db/cache.py:109 #: /home/kovid/work/calibre/src/calibre/db/cache.py:120 -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:376 -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:377 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:378 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:379 #: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:114 #: /home/kovid/work/calibre/src/calibre/devices/hanvon/driver.py:115 #: /home/kovid/work/calibre/src/calibre/devices/jetbook/driver.py:74 @@ -51,8 +51,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/html_input.py:121 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/lrf_output.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdb_input.py:27 -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:28 -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:29 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:30 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:31 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/rtf_input.py:289 #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/rtf_input.py:291 #: /home/kovid/work/calibre/src/calibre/ebooks/epub/periodical.py:140 @@ -131,6 +131,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ereader/writer.py:174 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/palmdoc/writer.py:29 #: /home/kovid/work/calibre/src/calibre/ebooks/pdb/ztxt/writer.py:27 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/render/links.py:119 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:447 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:455 #: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:171 @@ -162,7 +163,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:85 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:250 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:261 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:402 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:407 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:178 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:182 #: /home/kovid/work/calibre/src/calibre/gui2/store/search/models.py:202 @@ -235,7 +236,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:197 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:288 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:311 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:221 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:228 msgid "Preferences" msgstr "" @@ -919,19 +920,19 @@ msgstr "" msgid "Communicate with Android phones." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:198 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:199 msgid "Comma separated list of directories to send e-books to on the device's main memory. The first one that exists will be used" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:201 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:202 msgid "Comma separated list of directories to send e-books to on the device's storage cards. The first one that exists will be used" msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:316 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:318 msgid "Communicate with S60 phones." msgstr "" -#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:335 +#: /home/kovid/work/calibre/src/calibre/devices/android/driver.py:337 msgid "Communicate with WebOS tablets." msgstr "" @@ -2179,7 +2180,7 @@ msgid "There is insufficient free space on the storage card" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:210 -#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/render/from_html.py:229 +#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/render/from_html.py:230 #, python-format msgid "Rendered %s" msgstr "" @@ -2591,49 +2592,61 @@ msgstr "" msgid "Use the new PDF conversion engine." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:52 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:54 msgid "Normally, the PDF page size is set by the output profile chosen under page options. This option will cause the page size settings under PDF Output to override the size specified by the output profile." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:58 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:60 #, python-format msgid "The unit of measure for page sizes. Default is inch. Choices are %s Note: This does not override the unit for margins!" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:63 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:65 #, python-format msgid "The size of the paper. This size will be overridden when a non default output profile is used. Default is letter. Choices are %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:67 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:69 msgid "Custom size of the document. Use the form widthxheight EG. `123x321` to specify the width and height. This overrides any specified paper-size." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:72 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:74 msgid "Preserve the aspect ratio of the cover, instead of stretching it to fill the full first page of the generated pdf." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:77 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:79 msgid "The font family used to render serif fonts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:80 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:82 msgid "The font family used to render sans-serif fonts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:83 -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:87 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:85 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:89 msgid "The font family used to render monospaced fonts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:90 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:92 msgid "The default font size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:93 +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:95 msgid "The default font size for monospaced text" msgstr "" +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:97 +msgid "Surround all links with a red box, useful for debugging." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:99 +msgid "Use the old, less capable engine to generate the PDF" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pdf_output.py:102 +msgid "Generate an uncompressed PDF, useful for debugging, only works with the new PDF engine." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/ebooks/conversion/plugins/pml_output.py:22 msgid "Specify the character encoding of the output document. The default is cp1252." msgstr "" @@ -3447,7 +3460,7 @@ msgid "Producer" msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:773 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:957 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:963 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:157 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:245 msgid "Comments" @@ -3476,7 +3489,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:982 #: /home/kovid/work/calibre/src/calibre/library/catalogs/epub_mobi_builder.py:1228 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:201 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:792 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:796 msgid "Tags" msgstr "" @@ -3612,7 +3625,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1487 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1279 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:969 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:975 #: /home/kovid/work/calibre/src/calibre/gui2/store/search/models.py:39 msgid "Cover" msgstr "" @@ -3752,7 +3765,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/writer8/toc.py:15 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1281 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/htmltoc.py:15 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:214 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:221 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/toc.py:217 msgid "Table of Contents" msgstr "" @@ -3838,7 +3851,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/create_custom_column.py:71 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:160 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:176 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:790 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:794 msgid "Rating" msgstr "" @@ -5082,7 +5095,7 @@ msgid "Move to next match" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/next_match.py:13 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:219 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:226 msgid "F3" msgstr "" @@ -5108,7 +5121,7 @@ msgid "Shift+N" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/next_match.py:27 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:228 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:235 msgid "Shift+F3" msgstr "" @@ -5225,7 +5238,7 @@ msgid "Click the show details button to see which ones." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/show_book_details.py:16 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:797 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:801 msgid "Show book details" msgstr "" @@ -5751,7 +5764,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:174 #: /home/kovid/work/calibre/src/calibre/gui2/store/search/search_ui.py:181 #: /home/kovid/work/calibre/src/calibre/gui2/store/stores/mobileread/store_dialog_ui.py:75 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:206 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:213 msgid "..." msgstr "" @@ -5787,7 +5800,7 @@ msgid "Click to open" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:180 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:846 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:850 msgid "Ids" msgstr "" @@ -5928,7 +5941,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/conversion_ui.py:54 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/email_ui.py:65 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel_ui.py:246 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:118 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:125 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc_ui.py:74 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugboard_ui.py:113 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins_ui.py:108 @@ -6991,7 +7004,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:47 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:98 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:215 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:222 msgid "Metadata" msgstr "" @@ -7090,7 +7103,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:561 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1101 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1104 msgid "Tags categorize the book. This is particularly useful while searching.

They can be any words or phrases, separated by commas." msgstr "" @@ -7509,7 +7522,7 @@ msgstr "" msgid "

Search and replace uses regular expressions. See the regular expressions tutorial to get started with regular expressions. Also clicking the wizard button below will allow you to test your regular expression against the current input document. When you are happy with an expression, click the Add button to add it to the list of expressions." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:184 +#: /home/kovid/work/calibre/src/calibre/gui2/convert/single.py:187 msgid "Convert" msgstr "" @@ -7848,7 +7861,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:233 #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:290 #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:294 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1413 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1416 msgid "Undefined" msgstr "" @@ -8305,7 +8318,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box.py:141 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:885 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/tweaks.py:344 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:220 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:227 msgid "Copy to clipboard" msgstr "" @@ -8431,7 +8444,7 @@ msgid "No help available for this output format." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/catalog_ui.py:92 -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:333 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:348 msgid "Generate catalog" msgstr "" @@ -9102,7 +9115,7 @@ msgid "Standard metadata" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:63 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:939 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:945 msgid "Custom metadata" msgstr "" @@ -9261,7 +9274,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:562 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:563 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:188 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:193 msgid "Open Tag Editor" msgstr "" @@ -9314,7 +9327,7 @@ msgid "&Force numbers to start with:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:582 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1395 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1398 msgid "&Date:" msgstr "" @@ -9336,7 +9349,7 @@ msgid "Clear published date" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:591 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1164 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1167 msgid "&Languages:" msgstr "" @@ -9403,13 +9416,13 @@ msgid "Set from &ebook file(s)" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:613 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:575 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:741 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:580 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:747 msgid "&Basic metadata" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:614 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:582 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:587 msgid "&Custom metadata" msgstr "" @@ -10376,7 +10389,7 @@ msgid "&Author:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:199 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1100 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1103 msgid "Ta&gs:" msgstr "" @@ -11114,7 +11127,7 @@ msgid "Regular expression (?P)" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/filename_pattern_ui.py:149 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1297 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1300 msgid "ISBN:" msgstr "" @@ -11511,28 +11524,28 @@ msgid "Modified" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:819 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1451 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1455 #: /home/kovid/work/calibre/src/calibre/gui2/tag_browser/model.py:335 msgid "The lookup/search name is \"{0}\"" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:825 -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1453 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1457 msgid "This book's UUID is \"{0}\"" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:912 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:108 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:280 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:324 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:454 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:329 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:459 msgid "Permission denied" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:913 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:109 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:281 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:455 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:460 msgid "Could not change the on disk location of this book. Is it open in another program?" msgstr "" @@ -11555,11 +11568,11 @@ msgstr "" msgid "Size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1433 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1437 msgid "Marked for deletion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1436 +#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1440 msgid "Double click to edit me

" msgstr "" @@ -11664,12 +11677,12 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:133 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:947 #: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:62 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:208 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:215 msgid "Back" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:134 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:209 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:216 msgid "Forward" msgstr "" @@ -11678,7 +11691,7 @@ msgid "Next match" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:136 -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:216 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:223 msgid "Open ebook" msgstr "" @@ -11968,34 +11981,34 @@ msgstr "" msgid "Could not change cover as the image is invalid." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1136 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1139 msgid "Tags changed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1137 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1140 msgid "You have changed the tags. In order to use the tags editor, you must either discard or apply these changes. Apply changes?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1165 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1168 msgid "A comma separated list of languages for this book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1188 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1191 msgid "Unknown language" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1189 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1192 #, python-format msgid "The language %s is not recognized" msgid_plural "The languages %s are not recognized" msgstr[0] "" msgstr[1] "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1201 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1204 msgid "I&ds:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1202 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1205 #, python-format msgid "" "Edit the identifiers for this book. For example: \n" @@ -12003,38 +12016,38 @@ msgid "" "%s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1266 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1328 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1269 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1331 msgid "This ISBN number is valid" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1269 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1331 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1272 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1334 msgid "This ISBN number is invalid" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1294 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1316 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1297 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1319 msgid "Invalid ISBN" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1295 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1298 msgid "Enter an ISBN" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1317 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1320 msgid "The ISBN you entered is not valid. Try again." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1341 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1344 msgid "&Publisher:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1416 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1419 msgid "Clear date" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1450 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1453 msgid "Publishe&d:" msgstr "" @@ -12094,7 +12107,7 @@ msgid "Processed %s" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/config.py:61 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:124 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:131 msgid "Downloaded metadata fields" msgstr "" @@ -12155,88 +12168,92 @@ msgstr "" msgid "Clear series" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:192 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:185 +msgid "Clear rating" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:197 msgid "Clear all tags" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:201 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:206 msgid "Clear Ids" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:210 msgid "Paste the contents of the clipboard into the identifiers box prefixed with isbn:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:218 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:223 msgid "&Download metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:230 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:235 msgid "Configure download metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:239 msgid "Change how calibre downloads metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:301 #, python-format msgid " [%(num)d of %(tot)d]" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:325 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:330 #, python-format msgid "Could not open %s. Is it being used by another program?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:338 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:345 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:343 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:350 msgid "Could not read cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:339 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:344 #, python-format msgid "Could not read cover from %s format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:346 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:351 #, python-format msgid "The cover in the %s format is invalid" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:522 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:527 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:532 #, python-format msgid "Save changes and edit the metadata of %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:625 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:831 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:630 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:837 msgid "Change cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:684 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:690 msgid "Co&mments" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:724 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:872 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:730 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:878 msgid "&Metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:729 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:735 msgid "&Cover and formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:801 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:807 msgid "C&ustom metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:812 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:818 msgid "&Comments" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:878 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:884 msgid "Basic metadata" msgstr "" @@ -13250,10 +13267,14 @@ msgstr "" msgid "new email address" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/ignored_devices.py:24 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/ignored_devices.py:26 msgid "The list of devices that you have asked calibre to ignore. Uncheck a device to have calibre stop ignoring it." msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/ignored_devices.py:37 +msgid "The list of device plugins you have disabled. Uncheck an entry to enable the plugin. calibre cannot detect devices that are managed by disabled plugins." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/preferences/look_feel.py:103 msgid "Narrow" msgstr "" @@ -13568,87 +13589,87 @@ msgstr "" msgid "No source selected, cannot configure." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:119 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:126 msgid "Metadata sources" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:120 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:127 msgid "Disable any metadata sources you do not want by unchecking them. You can also set the cover priority. Covers from sources that have a higher (smaller) priority will be preferred when bulk downloading metadata.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:122 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:129 msgid "Sources with a red X next to their names must be configured before they will be used. " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:123 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:130 msgid "Configure selected source" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:125 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:132 msgid "If you uncheck any fields, metadata for those fields will not be downloaded" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:126 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:133 msgid "&Select all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:127 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:134 msgid "&Clear all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:128 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:135 msgid "Restore your own subset of checked fields that you define using the 'Set as default' button" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:129 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:136 msgid "&Select default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:130 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:137 msgid "Store the currently checked fields as a default you can restore using the 'Select default' button" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:138 msgid "&Set as default" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:139 msgid "Convert all downloaded comments to plain &text" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:140 msgid "Swap author names from FN LN to LN, FN" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:134 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:141 msgid "Max. number of &tags to download:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:135 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:142 msgid "Max. &time to wait after first match is found:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:136 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:138 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:143 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:145 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/config_ui.py:421 msgid " secs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:137 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:144 msgid "Max. time to wait after first &cover is found:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:139 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:146 msgid "" "

Different metadata sources have different sets of tags for the same book. If this option is checked, then calibre will use the smaller tag sets. These tend to be more like genres, while the larger tag sets tend to describe the books content.\n" "

Note that this option will only make a practical difference if one of the metadata sources has a genre like tag set for the book you are searching for. Most often, they all have large tag sets." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:141 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:148 msgid "Prefer &fewer tags" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:142 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources_ui.py:149 msgid "Use published date of \"first edition\" (from worldcat.org)" msgstr "" @@ -15318,39 +15339,61 @@ msgstr "" msgid "Convert book %(num)d of %(total)d (%(title)s)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:101 -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:227 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:97 +msgid "Could not convert" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:98 +#, python-format +msgid "Could not convert %s as it has no ebook files. If you think it should have files, but calibre is not finding them, that is most likely because you moved the book's files around outside of calibre. You will need to find those files and re-add them to calibre." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:108 +#, python-format +msgid "No supported formats (Available formats: %s)" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:111 +msgid "This book has no actual ebook files" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:116 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:242 msgid "Could not convert some books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:102 -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:228 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:117 #, python-format -msgid "Could not convert %(num)d of %(tot)d books, because no suitable source format was found." +msgid "Could not convert %(num)d of %(tot)d books, because no supported source formats were found." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:136 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:151 msgid "Queueing books for bulk conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:201 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:216 msgid "Queueing " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:202 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:217 #, python-format msgid "Convert book %(num)d of %(tot)d (%(title)s)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:273 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:243 +#, python-format +msgid "Could not convert %(num)d of %(tot)d books, because no suitable source format was found." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:288 msgid "Fetch news from " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:346 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:361 msgid "Convert existing" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:347 +#: /home/kovid/work/calibre/src/calibre/gui2/tools.py:362 #, python-format msgid "The following books have already been converted to %s format. Do you wish to reconvert them?" msgstr "" @@ -16199,75 +16242,75 @@ msgid "" "View an ebook.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:204 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:211 msgid "E-book Viewer" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:212 msgid "Close dictionary" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:207 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:214 msgid "toolBar" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:210 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:217 msgid "Next page" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:211 +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:218 msgid "Previous page" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:212 -msgid "Font size larger" +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:219 +msgid "Increase font size" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:213 -msgid "Font size smaller" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:217 -msgid "Find next" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:218 -msgid "Find next occurrence" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:222 -msgid "Reference Mode" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:223 -msgid "Bookmark" +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:220 +msgid "Decrease font size" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:224 -msgid "Toggle full screen" +msgid "Find next" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:225 -msgid "Print" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:226 -msgid "Find previous" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:227 -msgid "Find previous occurrence" +msgid "Find next occurrence" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:229 -msgid "Toggle Paged mode" +msgid "Reference Mode" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:230 -msgid "Load theme" +msgid "Bookmark" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:231 +msgid "Toggle full screen" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:232 +msgid "Print" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:233 +msgid "Find previous" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:234 +msgid "Find previous occurrence" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:236 +msgid "Toggle Paged mode" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:237 +msgid "Load theme" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:238 msgid "Load a theme" msgstr "" @@ -17923,19 +17966,19 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/server/ajax.py:317 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:353 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:639 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:643 msgid "All books" msgstr "" #: /home/kovid/work/calibre/src/calibre/library/server/ajax.py:318 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:352 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:638 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:642 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:584 msgid "Newest" msgstr "" #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:65 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:511 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:515 msgid "Loading, please wait" msgstr "" @@ -17985,7 +18028,7 @@ msgid "home" msgstr "" #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:400 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:465 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:469 msgid "Browse books by" msgstr "" @@ -17993,56 +18036,56 @@ msgstr "" msgid "Choose a category to browse by:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:536 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:540 msgid "Browsing by" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:537 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:541 msgid "Up" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:674 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:678 msgid "in" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:677 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:681 msgid "Books in" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:771 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:775 msgid "Other formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:778 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:782 #, python-format msgid "Read %(title)s in the %(fmt)s format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:783 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:787 msgid "Get" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:796 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:800 msgid "Details" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:798 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:802 msgid "Permalink" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:799 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:803 msgid "A permanent link to this book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:811 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:815 msgid "This book has been deleted" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:909 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:913 msgid "in search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:911 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:915 msgid "Matching books" msgstr "" From daa0166315ac7d5bab4f5e58a9e20746c4717d09 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Jan 2013 10:55:11 +0530 Subject: [PATCH 65/67] ... --- src/calibre/ebooks/lrf/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/calibre/ebooks/lrf/__init__.py b/src/calibre/ebooks/lrf/__init__.py index 725338ead8..d99d914ed0 100644 --- a/src/calibre/ebooks/lrf/__init__.py +++ b/src/calibre/ebooks/lrf/__init__.py @@ -41,7 +41,6 @@ def find_custom_fonts(options, logger): if options.serif_family: f = family(options.serif_family) fonts['serif'] = font_scanner.legacy_fonts_for_family(f) - print (111111, fonts['serif']) if not fonts['serif']: logger.warn('Unable to find serif family %s'%f) if options.sans_family: From 65d51e305042520ca9281b232489d7c516b30531 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Jan 2013 11:00:26 +0530 Subject: [PATCH 66/67] ... --- Changelog.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.yaml b/Changelog.yaml index 0f4ecdd360..50ea7d1e6d 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -29,7 +29,7 @@ - title: "Show disabled device plugins in Preferences->Ignored Devices" - - title: "Get Books: Fix Smashwords, Google boks and B&N stores. Add Nook UK store" + - title: "Get Books: Fix Smashwords, Google books and B&N stores. Add Nook UK store" - title: "Allow series numbers lower than -100 for custom series columns." tickets: [1094475] From 22ee6226ce3f2506844fad50f394eacca305b02e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Jan 2013 11:37:00 +0530 Subject: [PATCH 67/67] Workaround for broken C libraries causing stdout redirection for broken libmtp causing stdout to become fully buffered --- src/calibre/devices/mtp/unix/libmtp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/devices/mtp/unix/libmtp.c b/src/calibre/devices/mtp/unix/libmtp.c index 920ddde3d1..5a274b2e89 100644 --- a/src/calibre/devices/mtp/unix/libmtp.c +++ b/src/calibre/devices/mtp/unix/libmtp.c @@ -734,6 +734,7 @@ initlibmtp(void) { // who designs a library without anyway to control/redirect the debugging // output, and hardcoded paths that cannot be changed? int bak, new; + fprintf(stdout, "\n"); // This is needed, without it, for some odd reason the code below causes stdout to buffer all output after it is restored, rather than using line buffering, and setlinebuf does not work. fflush(stdout); bak = dup(STDOUT_FILENO); new = open("/dev/null", O_WRONLY);