0.9.5
12
COPYRIGHT
@ -28,6 +28,12 @@ License: LGPL-2.1+
|
|||||||
The full text of the LGPL is distributed as in
|
The full text of the LGPL is distributed as in
|
||||||
/usr/share/common-licenses/LGPL-2.1 on Debian systems.
|
/usr/share/common-licenses/LGPL-2.1 on Debian systems.
|
||||||
|
|
||||||
|
Files: src/calibre/utils/fonts/woff/*
|
||||||
|
Copyright: Jonathan Kew?
|
||||||
|
License: LGPL-2.1
|
||||||
|
The full text of the LGPL is distributed as in
|
||||||
|
/usr/share/common-licenses/LGPL-2.1 on Debian systems.
|
||||||
|
|
||||||
Files: src/calibre/ebooks/hyphenate.py
|
Files: src/calibre/ebooks/hyphenate.py
|
||||||
Copyright: Copyright (C) 1990, 2004, 2005 Gerard D.C. Kuiken.
|
Copyright: Copyright (C) 1990, 2004, 2005 Gerard D.C. Kuiken.
|
||||||
License: other
|
License: other
|
||||||
@ -41,6 +47,12 @@ License: Apache 2.0
|
|||||||
The full text of the Apache 2.0 license is available at:
|
The full text of the Apache 2.0 license is available at:
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Files: src/sfntly/*
|
||||||
|
Copyright: Google Inc.
|
||||||
|
License: Apache 2.0
|
||||||
|
The full text of the Apache 2.0 license is available at:
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Files: resources/viewer/mathjax/*
|
Files: resources/viewer/mathjax/*
|
||||||
Copyright: Unknown
|
Copyright: Unknown
|
||||||
License: Apache 2.0
|
License: Apache 2.0
|
||||||
|
@ -19,6 +19,103 @@
|
|||||||
# new recipes:
|
# new recipes:
|
||||||
# - title:
|
# - title:
|
||||||
|
|
||||||
|
- version: 0.9.5
|
||||||
|
date: 2012-11-02
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Font embedding: Add support for the CSS 3 Fonts module, which means you can embed font families that have more that the usual four faces, with the full set of font-stretch and font-weight variations. Of course, whether the fonts actually show up on a reader will depend on the readers' support for CSS 3."
|
||||||
|
|
||||||
|
- title: "Sharing by email: Allow specifying an 'alias' or friendly name by which to identify each email recipient."
|
||||||
|
tickets: [1069076]
|
||||||
|
|
||||||
|
- title: "Embedding fonts: Allow adding ttf/otf font files to calibre directly to be used for embedding. That way the fonts do not have to be installed system wide. You can add a font to calibre via the 'Add fonts' button in the font chooser dialog for embedding fonts."
|
||||||
|
|
||||||
|
- title: "E-book viewer: Add the ability to rotate images to the popup image viewer."
|
||||||
|
tickets: [1073513]
|
||||||
|
|
||||||
|
- title: "Generate cover: Speedup searching the system for a font that can render special characters"
|
||||||
|
|
||||||
|
- title: "A new custom font scanner to locate all fonts on the system. Faster and less crash prone that fontconfig/freetype"
|
||||||
|
|
||||||
|
- title: "Font family chooser: Show the faces available for a family when clicking on the family"
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Get Books: Fix eHarlequin and Kobo stores."
|
||||||
|
tickets: [1072702]
|
||||||
|
|
||||||
|
- title: "Kobo driver: Fix a bug that could cause the on device book matching to fail in certain circumstances."
|
||||||
|
tickets: [1072437]
|
||||||
|
|
||||||
|
- title: "Kobo driver: When using a SD card do not delete shelves that contain on books on the card (there might be books in the shelf in the main memory)."
|
||||||
|
tickets: [1073792]
|
||||||
|
|
||||||
|
- title: "Workaround for bug in the windows API CreateHardLink function that breaks using calibre libraries on some networked filesystems."
|
||||||
|
|
||||||
|
- title: "Template editor: Use dummy metadata instead of blank/unknown values"
|
||||||
|
|
||||||
|
- title: "Windows: abort setting of title/author if any of the books' files are in use. Results in less surprising behavior than before, when the title/author would be changed, but the on disk location would not."
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- Financial Times UK
|
||||||
|
- Science AAAS
|
||||||
|
- The Atlantic
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: "Pravda in english, italian and portuguese"
|
||||||
|
author: Darko Miletic
|
||||||
|
|
||||||
|
- title: "Delco Times"
|
||||||
|
author: Krittika Goyal
|
||||||
|
|
||||||
|
|
||||||
|
- version: 0.9.4
|
||||||
|
date: 2012-10-26
|
||||||
|
|
||||||
|
new features:
|
||||||
|
- title: "Conversion: Add an option to embed a font family into the book."
|
||||||
|
description: "The embedded font is used as the base font for all text that does not specify its own font family in the input document. Works only with output formats that support font embedding, principally EPUB/AZW3. Option is found under Look & Feel in the conversion dialog. You can ensure that the font is used for all text, regardless of the input document's styles by filtering out font family styles via the Filter Style Information option in the Conversion dialog."
|
||||||
|
type: major
|
||||||
|
|
||||||
|
- title: "When changing the title/author of a book, use hard links instead of copying the books' files, for a large speedup. Only works on filesystems that support hardlinks."
|
||||||
|
|
||||||
|
- title: "Linux installer: Resume interrupted downloads and verify the SHA-512 signature of the downloaded file before installing it."
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Windows: Check if any of the files of a book are in use before changing the title/author, this prevents the creation of duplicate files if one of the files is open in another program"
|
||||||
|
|
||||||
|
- title: "Kobo driver: Fix the ondevice status for some books getting lost."
|
||||||
|
tickets: [1069403]
|
||||||
|
|
||||||
|
- title: "Catalogs: Fix regression that broke use of prefix rules."
|
||||||
|
tickets: [1070086]
|
||||||
|
|
||||||
|
- title: "Tag Browser: Fix sorting incorrect for accented letters"
|
||||||
|
tickets: [1069835]
|
||||||
|
|
||||||
|
- title: "Make the bundled Liberation fonts available on all platforms for embedding"
|
||||||
|
|
||||||
|
- title: "Use mimetype for fonts from the EPUB 3 specification"
|
||||||
|
|
||||||
|
- title: "Get Books: Handle website change that broke the SONY Store plugin"
|
||||||
|
|
||||||
|
- title: "Generate cover: If the default font cannot render characters in the metadata (for example for east asian languages) try to automatically find a font on the system that is capable of rendering the characters"
|
||||||
|
|
||||||
|
- title: "Fix regression that broke certain types of CSS selectors."
|
||||||
|
tickets: [1068937]
|
||||||
|
|
||||||
|
- title: "Use font-weight:bold instead of font-weight:bolder for the <b> and <strong> tags as ADE cant handle bolder when embedded fonts are used"
|
||||||
|
|
||||||
|
improved recipes:
|
||||||
|
- New York Post
|
||||||
|
- PC World
|
||||||
|
- TIME Magazine
|
||||||
|
- Associated Press
|
||||||
|
|
||||||
|
new recipes:
|
||||||
|
- title: Yazihane
|
||||||
|
author: A Erdogan
|
||||||
|
|
||||||
|
|
||||||
- version: 0.9.3
|
- version: 0.9.3
|
||||||
date: 2012-10-19
|
date: 2012-10-19
|
||||||
|
|
||||||
|
2862
imgsrc/font.svg
Normal file
After Width: | Height: | Size: 119 KiB |
@ -72,13 +72,21 @@ After installing Bazaar, you can get the |app| source code with the command::
|
|||||||
|
|
||||||
bzr branch lp:calibre
|
bzr branch lp:calibre
|
||||||
|
|
||||||
On Windows you will need the complete path name, that will be something like :file:`C:\\Program Files\\Bazaar\\bzr.exe`. To update a branch
|
On Windows you will need the complete path name, that will be something like :file:`C:\\Program Files\\Bazaar\\bzr.exe`.
|
||||||
to the latest code, use the command::
|
|
||||||
|
To update a branch to the latest code, use the command::
|
||||||
|
|
||||||
bzr merge
|
bzr merge
|
||||||
|
|
||||||
The calibre repository is huge so the branch operation above takes along time (about an hour). If you want to get the code faster, the sourcecode for the latest release is always available as an
|
|app| is a very large project with a very long source control history, so the
|
||||||
`archive <http://status.calibre-ebook.com/dist/src>`_.
|
above can take a while (10mins to an hour depending on your internet speed).
|
||||||
|
|
||||||
|
If you want to get the code faster, the sourcecode for the latest release is
|
||||||
|
always available as an `archive <http://status.calibre-ebook.com/dist/src>`_.
|
||||||
|
You can also use bzr to just download the source code, without the history,
|
||||||
|
using::
|
||||||
|
|
||||||
|
bzr branch --stacked lp:calibre
|
||||||
|
|
||||||
Submitting your changes to be included
|
Submitting your changes to be included
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -109,7 +117,7 @@ Whenever you commit changes to your branch with the command::
|
|||||||
bzr commit -m "Comment describing your change"
|
bzr commit -m "Comment describing your change"
|
||||||
|
|
||||||
Kovid can merge it directly from your branch into the main |app| source tree. You should also keep an eye on the |app|
|
Kovid can merge it directly from your branch into the main |app| source tree. You should also keep an eye on the |app|
|
||||||
`development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`. Before making major changes, you should
|
`development forum <http://www.mobileread.com/forums/forumdisplay.php?f=240>`_. Before making major changes, you should
|
||||||
discuss them in the forum or contact Kovid directly (his email address is all over the source code).
|
discuss them in the forum or contact Kovid directly (his email address is all over the source code).
|
||||||
|
|
||||||
Windows development environment
|
Windows development environment
|
||||||
|
@ -69,8 +69,8 @@ If you have a hand edited TOC in the input document, you can use the TOC detecti
|
|||||||
|
|
||||||
Finally, I encourage you to ditch the content TOC and only have a metadata TOC in your ebooks. Metadata TOCs will give the people reading your ebooks a much superior navigation experience (except on the Kindle, where they are essentially the same as a content TOC).
|
Finally, I encourage you to ditch the content TOC and only have a metadata TOC in your ebooks. Metadata TOCs will give the people reading your ebooks a much superior navigation experience (except on the Kindle, where they are essentially the same as a content TOC).
|
||||||
|
|
||||||
The covers for my MOBI files have stopped showing up in Kindle for PC/Kindle for Android/etc.
|
The covers for my MOBI files have stopped showing up in Kindle for PC/Kindle for Android/iPad etc.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This is caused by a bug in the Amazon software. You can work around it by going
|
This is caused by a bug in the Amazon software. You can work around it by going
|
||||||
to Preferences->Output Options->MOBI output and setting the "Enable sharing
|
to Preferences->Output Options->MOBI output and setting the "Enable sharing
|
||||||
@ -284,8 +284,8 @@ Use the 'Connect to iTunes' method in the 'Getting started' instructions in `Cal
|
|||||||
|
|
||||||
This method only works on Windows XP and higher, and OS X 10.5 and higher. Linux is not supported (iTunes is not available in linux) and OS X 10.4 is not supported.
|
This method only works on Windows XP and higher, and OS X 10.5 and higher. Linux is not supported (iTunes is not available in linux) and OS X 10.4 is not supported.
|
||||||
|
|
||||||
How do I use |app| with my Android phone/tablet?
|
How do I use |app| with my Android phone/tablet or Kindle Fire HD?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
There are two ways that you can connect your Android device to calibre. Using a USB cable -- or wirelessly, over the air.
|
There are two ways that you can connect your Android device to calibre. Using a USB cable -- or wirelessly, over the air.
|
||||||
The first step to using an Android device is installing an ebook reading
|
The first step to using an Android device is installing an ebook reading
|
||||||
@ -435,7 +435,7 @@ any |app| developers will ever feel motivated enough to support it. There is how
|
|||||||
that allows you to create collections on your Kindle from the |app| metadata. It is available
|
that allows you to create collections on your Kindle from the |app| metadata. It is available
|
||||||
`from here <http://www.mobileread.com/forums/showthread.php?t=118635>`_.
|
`from here <http://www.mobileread.com/forums/showthread.php?t=118635>`_.
|
||||||
|
|
||||||
.. note:: Amazon have removed the ability to manipulate collections completely in their newer models, like the Kindle Touch and Kindle Fire, making even the above plugin useless. If you really want the ability to manage collections on your Kindle via a USB connection, we encourage you to complain to Amazon about it, or get a reader where this is supported, like the SONY Readers.
|
.. note:: Amazon have removed the ability to manipulate collections completely in their newer models, like the Kindle Touch and Kindle Fire, making even the above plugin useless. If you really want the ability to manage collections on your Kindle via a USB connection, we encourage you to complain to Amazon about it, or get a reader where this is supported, like the SONY or Kobo Readers.
|
||||||
|
|
||||||
I am getting an error when I try to use |app| with my Kobo Touch?
|
I am getting an error when I try to use |app| with my Kobo Touch?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@ -557,6 +557,27 @@ There can be two reasons why |app| is showing a empty list of books:
|
|||||||
|
|
||||||
* Your metadata.db file was deleted/corrupted. In this case, you can ask |app| to rebuild the metadata.db from its backups. Right click the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Library maintenance->Restore database. |app| will automatically rebuild metadata.db.
|
* Your metadata.db file was deleted/corrupted. In this case, you can ask |app| to rebuild the metadata.db from its backups. Right click the |app| icon in the |app| toolbar (it will say 0 books underneath it) and select Library maintenance->Restore database. |app| will automatically rebuild metadata.db.
|
||||||
|
|
||||||
|
I am getting errors with my calibre library on a networked drive/NAS?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**Do not put your calibre library on a networked drive**.
|
||||||
|
|
||||||
|
A filesystem is a complex beast. Most network filesystems lack various
|
||||||
|
filesystem features that |app| uses. Some dont support file locking, some dont
|
||||||
|
support hardlinking, some are just flaky. Additionally, |app| is a single user
|
||||||
|
application, if you accidentally run two copies of |app| on the same networked
|
||||||
|
library, bad things will happen. Finally, different OSes impose different
|
||||||
|
limitations on filesystems, so if you share your networked drive across OSes,
|
||||||
|
once again, bad things *will happen*.
|
||||||
|
|
||||||
|
Consider using the |app| Content Server to make your books available on other
|
||||||
|
computers. Run |app| on a single computer and access it via the Content Server
|
||||||
|
or a Remote Desktop solution.
|
||||||
|
|
||||||
|
If you must share the actual library, use a file syncing tool like
|
||||||
|
DropBox or rsync or Microsoft SkyDrive instead of a networked drive. Even with
|
||||||
|
these tools there is danger of data corruption/loss, so only do this if you are
|
||||||
|
willing to live with that risk.
|
||||||
|
|
||||||
Content From The Web
|
Content From The Web
|
||||||
---------------------
|
---------------------
|
||||||
@ -638,6 +659,9 @@ There are three possible things I know of, that can cause this:
|
|||||||
|
|
||||||
* You are using a Wacom branded mouse. There is an incompatibility between Wacom mice and the graphics toolkit |app| uses. Try using a non-Wacom mouse.
|
* You are using a Wacom branded mouse. There is an incompatibility between Wacom mice and the graphics toolkit |app| uses. Try using a non-Wacom mouse.
|
||||||
|
|
||||||
|
* If you use RoboForm, it is known to cause |app| to crash. Add |app| to
|
||||||
|
the blacklist of programs inside RoboForm to fix this.
|
||||||
|
|
||||||
* Sometimes if some software has installed lots of new files in your fonts folder, |app| can crash until it finishes indexing them. Just start |app|, then leave it alone for about 20 minutes, without clicking on anything. After that you should be able to use |app| as normal.
|
* Sometimes if some software has installed lots of new files in your fonts folder, |app| can crash until it finishes indexing them. Just start |app|, then leave it alone for about 20 minutes, without clicking on anything. After that you should be able to use |app| as normal.
|
||||||
|
|
||||||
|
|
||||||
@ -674,7 +698,20 @@ If you still cannot get the installer to work and you are on windows, you can us
|
|||||||
My antivirus program claims |app| is a virus/trojan?
|
My antivirus program claims |app| is a virus/trojan?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Your antivirus program is wrong. Antivirus programs use heuristics, patterns of code that "looks suspicuous" to detect viruses. It's rather like racial profiling. |app| is a completely open source product. You can actually browse the source code yourself (or hire someone to do it for you) to verify that it is not a virus. Please report the false identification to whatever company you buy your antivirus software from. If the antivirus program is preventing you from downloading/installing |app|, disable it temporarily, install |app| and then re-enable it.
|
The first thing to check is that you are downloading |app| from the official
|
||||||
|
website: `<http://calibre-ebook.com/download>`_. |app| is a very popular program
|
||||||
|
and unscrupulous people try to setup websites offering it for download to fool
|
||||||
|
the unwary.
|
||||||
|
|
||||||
|
If you have the official download and your antivirus program is still claiming
|
||||||
|
|app| is a virus, then, your antivirus program is wrong. Antivirus programs use
|
||||||
|
heuristics, patterns of code that "look suspicious" to detect viruses. It's
|
||||||
|
rather like racial profiling. |app| is a completely open source product. You
|
||||||
|
can actually browse the source code yourself (or hire someone to do it for you)
|
||||||
|
to verify that it is not a virus. Please report the false identification to
|
||||||
|
whatever company you buy your antivirus software from. If the antivirus program
|
||||||
|
is preventing you from downloading/installing |app|, disable it temporarily,
|
||||||
|
install |app| and then re-enable it.
|
||||||
|
|
||||||
How do I backup |app|?
|
How do I backup |app|?
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Before Width: | Height: | Size: 85 KiB |
BIN
manual/images/sg_pref.png
Normal file
After Width: | Height: | Size: 70 KiB |
@ -65,7 +65,7 @@ You create the custom column in the usual way, using Preferences -> Add your own
|
|||||||
|
|
||||||
Then after restarting |app|, you must tell |app| that the column is to be treated as a hierarchy. Go to Preferences -> Look and Feel -> Tag Browser and enter the lookup name "#genre" into the "Categories with hierarchical items" box. Press Apply, and you are done with setting up.
|
Then after restarting |app|, you must tell |app| that the column is to be treated as a hierarchy. Go to Preferences -> Look and Feel -> Tag Browser and enter the lookup name "#genre" into the "Categories with hierarchical items" box. Press Apply, and you are done with setting up.
|
||||||
|
|
||||||
.. image:: images/sg_pref.jpg
|
.. image:: images/sg_pref.png
|
||||||
:align: center
|
:align: center
|
||||||
|
|
||||||
At the point there are no genres in the column. We are left with the last step: how to apply a genre to a book. A genre does not exist in |app| until it appears on at least one book. To learn how to apply a genre for the first time, we must go into some detail about what a genre looks like in the metadata for a book.
|
At the point there are no genres in the column. We are left with the last step: how to apply a genre to a book. A genre does not exist in |app| until it appears on at least one book. To learn how to apply a genre for the first time, we must go into some detail about what a genre looks like in the metadata for a book.
|
||||||
|
@ -10,6 +10,8 @@ class AssociatedPress(BasicNewsRecipe):
|
|||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
language = 'en'
|
language = 'en'
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
|
# auto_cleanup_keep = '//td[@class="ap-smallphoto-td-image"]'
|
||||||
max_articles_per_feed = 15
|
max_articles_per_feed = 15
|
||||||
|
|
||||||
|
|
||||||
@ -20,13 +22,13 @@ class AssociatedPress(BasicNewsRecipe):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
keep_only_tags = [ dict(name='div', attrs={'class':['body']}),
|
#keep_only_tags = [ dict(name='table', attrs={'class':['ap-story-table hnews hentry item']}),
|
||||||
dict(name='div', attrs={'class':['entry-content']}),
|
##dict(name='div', attrs={'class':['entry-content']}),
|
||||||
]
|
#]
|
||||||
remove_tags = [dict(name='table', attrs={'class':['ap-video-table','ap-htmlfragment-table','ap-htmltable-table']}),
|
#remove_tags = [dict(name='td', attrs={'class':['ap-mediabox-td']}),
|
||||||
dict(name='span', attrs={'class':['apCaption','tabletitle']}),
|
#dict(name='table', attrs={'class':['ap-htmltable-table', 'ap-htmltable-table', 'ap-mediabox-table']}),
|
||||||
dict(name='td', attrs={'bgcolor':['#333333']}),
|
##dict(name='td', attrs={'bgcolor':['#333333']}),
|
||||||
]
|
#]
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
.headline{font-family:Verdana,Arial,Helvetica,sans-serif;font-weight:bold;}
|
.headline{font-family:Verdana,Arial,Helvetica,sans-serif;font-weight:bold;}
|
||||||
.bline{color:#003366;}
|
.bline{color:#003366;}
|
||||||
|
@ -38,8 +38,10 @@ class TheAtlantic(BasicNewsRecipe):
|
|||||||
self.timefmt = ' [%s]'%ds
|
self.timefmt = ' [%s]'%ds
|
||||||
|
|
||||||
cover = soup.find('img', src=True, attrs={'class':'cover'})
|
cover = soup.find('img', src=True, attrs={'class':'cover'})
|
||||||
|
|
||||||
if cover is not None:
|
if cover is not None:
|
||||||
self.cover_url = cover['src'].replace(' ', '%20')
|
self.cover_url = re.sub('\s','%20',re.sub('jpg.*','jpg',cover['src']))
|
||||||
|
self.log(self.cover_url)
|
||||||
|
|
||||||
feeds = []
|
feeds = []
|
||||||
seen_titles = set([])
|
seen_titles = set([])
|
||||||
@ -47,18 +49,16 @@ class TheAtlantic(BasicNewsRecipe):
|
|||||||
section_title = self.tag_to_string(section.find('h2'))
|
section_title = self.tag_to_string(section.find('h2'))
|
||||||
self.log('Found section:', section_title)
|
self.log('Found section:', section_title)
|
||||||
articles = []
|
articles = []
|
||||||
for post in section.findAll('div', attrs={'class':lambda x : x and
|
for post in section.findAll('h3', attrs={'class':'headline'}):
|
||||||
'post' in x}):
|
a = post.find('a', href=True)
|
||||||
h = post.find(['h3', 'h4'])
|
title = self.tag_to_string(a)
|
||||||
title = self.tag_to_string(h)
|
|
||||||
if title in seen_titles:
|
if title in seen_titles:
|
||||||
continue
|
continue
|
||||||
seen_titles.add(title)
|
seen_titles.add(title)
|
||||||
a = post.find('a', href=True)
|
|
||||||
url = a['href']
|
url = a['href']
|
||||||
if url.startswith('/'):
|
if url.startswith('/'):
|
||||||
url = 'http://www.theatlantic.com'+url
|
url = 'http://www.theatlantic.com'+url
|
||||||
p = post.find('p', attrs={'class':'dek'})
|
p = post.parent.find('p', attrs={'class':'dek'})
|
||||||
desc = None
|
desc = None
|
||||||
self.log('\tFound article:', title, 'at', url)
|
self.log('\tFound article:', title, 'at', url)
|
||||||
if p is not None:
|
if p is not None:
|
||||||
@ -69,19 +69,29 @@ class TheAtlantic(BasicNewsRecipe):
|
|||||||
if articles:
|
if articles:
|
||||||
feeds.append((section_title, articles))
|
feeds.append((section_title, articles))
|
||||||
|
|
||||||
poems = []
|
rightContent=soup.find('div', attrs = {'class':'rightContent'})
|
||||||
self.log('Found section: Poems')
|
for module in rightContent.findAll('div', attrs={'class':'module'}):
|
||||||
pd = soup.find('h2', text='Poetry').parent.parent
|
section_title = self.tag_to_string(module.find('h2'))
|
||||||
for poem in pd.findAll('h4'):
|
articles = []
|
||||||
title = self.tag_to_string(poem)
|
for post in module.findAll('div', attrs={'class':'post'}):
|
||||||
url = poem.find('a')['href']
|
a = post.find('a', href=True)
|
||||||
if url.startswith('/'):
|
title = self.tag_to_string(a)
|
||||||
url = 'http://www.theatlantic.com' + url
|
if title in seen_titles:
|
||||||
self.log('\tFound article:', title, 'at', url)
|
continue
|
||||||
poems.append({'title':title, 'url':url, 'description':'',
|
seen_titles.add(title)
|
||||||
'date':''})
|
url = a['href']
|
||||||
if poems:
|
if url.startswith('/'):
|
||||||
feeds.append(('Poems', poems))
|
url = 'http://www.theatlantic.com'+url
|
||||||
|
p = post.parent.find('p', attrs={'class':'dek'})
|
||||||
|
desc = None
|
||||||
|
self.log('\tFound article:', title, 'at', url)
|
||||||
|
if p is not None:
|
||||||
|
desc = self.tag_to_string(p)
|
||||||
|
self.log('\t\t', desc)
|
||||||
|
articles.append({'title':title, 'url':url, 'description':desc, 'date':''})
|
||||||
|
if articles:
|
||||||
|
feeds.append((section_title, articles))
|
||||||
|
|
||||||
|
|
||||||
return feeds
|
return feeds
|
||||||
|
|
||||||
@ -100,4 +110,3 @@ class TheAtlantic(BasicNewsRecipe):
|
|||||||
table.replaceWith(div)
|
table.replaceWith(div)
|
||||||
|
|
||||||
return soup
|
return soup
|
||||||
|
|
||||||
|
@ -15,7 +15,8 @@ class BusinessStandard(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
delay = 1
|
delay = 1
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'cp1252'
|
auto_cleanup = True
|
||||||
|
encoding = 'utf-8'
|
||||||
publisher = 'Boston'
|
publisher = 'Boston'
|
||||||
category = 'news, boston, usa, world'
|
category = 'news, boston, usa, world'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
@ -30,23 +31,23 @@ class BusinessStandard(BasicNewsRecipe):
|
|||||||
,'publisher' : publisher
|
,'publisher' : publisher
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags = [dict(attrs={'id':['INDblogEntry','blogEntry','articleHeader','articleGraphs','galleryShell']})]
|
#keep_only_tags = [dict(attrs={'id':['INDblogEntry','blogEntry','articleHeader','articleGraphs','galleryShell']})]
|
||||||
remove_tags = [
|
#remove_tags = [
|
||||||
dict(name=['object','link','script','iframe'])
|
#dict(name=['object','link','script','iframe'])
|
||||||
,dict(attrs={'id':['blogheadTools','bdc_emailWidget','tools','relatedContent']})
|
#,dict(attrs={'id':['blogheadTools','bdc_emailWidget','tools','relatedContent']})
|
||||||
]
|
#]
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Top Stories' , u'http://feeds.boston.com/boston/topstories' )
|
(u'Top Stories' , u'http://feeds.boston.com/boston/topstories' )
|
||||||
,(u'Patriots news', u'http://feeds.boston.com/boston/sports/football/patriots')
|
,(u'Patriots news', u'http://feeds.boston.com/boston/sports/football/patriots/patriots_rss')
|
||||||
,(u'National news', u'http://feeds.boston.com/boston/news/nation' )
|
,(u'National news', u'http://feeds.boston.com/boston/news/nation' )
|
||||||
,(u'World news' , u'http://feeds.boston.com/boston/news/world' )
|
,(u'World news' , u'http://feeds.boston.com/boston/news/world' )
|
||||||
]
|
]
|
||||||
|
|
||||||
def print_version(self, url):
|
#def print_version(self, url):
|
||||||
return url + '?page=full'
|
#return url + '?page=full'
|
||||||
|
|
||||||
def get_article_url(self, article):
|
#def get_article_url(self, article):
|
||||||
rawarticle = article.get('guid', None)
|
#rawarticle = article.get('guid', None)
|
||||||
return rawarticle.rpartition('?')[0]
|
#return rawarticle.rpartition('?')[0]
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
description = 'News as provided by The Daily Mirror -UK'
|
description = 'News as provided by The Daily Mirror -UK'
|
||||||
|
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
# last updated 8/6/12
|
# last updated 19/10/12
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
#cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
#cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
|
||||||
|
|
||||||
@ -15,10 +15,12 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
|
|
||||||
|
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
max_articles_per_feed = 12
|
max_articles_per_feed = 1
|
||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
ignore_duplicate_articles = {'title'}
|
||||||
|
|
||||||
# auto_cleanup = True
|
# auto_cleanup = True
|
||||||
#conversion_options = { 'linearize_tables' : True }
|
#conversion_options = { 'linearize_tables' : True }
|
||||||
|
|
||||||
@ -60,11 +62,12 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
|
|
||||||
# example of commented out feed not needed ,(u'Travel','http://www.mirror.co.uk/advice/travel/rss.xml')
|
# example of commented out feed not needed ,(u'Travel','http://www.mirror.co.uk/advice/travel/rss.xml')
|
||||||
]
|
]
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
h1{ font-size:medium;}
|
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||||
body{ text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
|
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||||
img { display:block}
|
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||||
'''#
|
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||||
|
'''
|
||||||
|
|
||||||
def get_cover_url(self):
|
def get_cover_url(self):
|
||||||
soup = self.index_to_soup('http://www.politicshome.com/uk/latest_frontpage.html')
|
soup = self.index_to_soup('http://www.politicshome.com/uk/latest_frontpage.html')
|
||||||
@ -75,8 +78,10 @@ class AdvancedUserRecipe1306061239(BasicNewsRecipe):
|
|||||||
#cov2 now contains url of the page containing pic
|
#cov2 now contains url of the page containing pic
|
||||||
soup = self.index_to_soup(cov2)
|
soup = self.index_to_soup(cov2)
|
||||||
cov = soup.find(attrs={'id' : 'large'})
|
cov = soup.find(attrs={'id' : 'large'})
|
||||||
cov2 = str(cov)
|
cov=str(cov)
|
||||||
cov2=cov2[27:-18]
|
cov2 = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', cov)
|
||||||
|
cov2 = str(cov2)
|
||||||
|
cov2=cov2[2:len(cov2)-2]
|
||||||
#cov2 now is pic url, now go back to original function
|
#cov2 now is pic url, now go back to original function
|
||||||
br = browser()
|
br = browser()
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
|
26
recipes/delco_times.recipe
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class HindustanTimes(BasicNewsRecipe):
|
||||||
|
title = u'Delcoe Times'
|
||||||
|
language = 'en'
|
||||||
|
__author__ = 'Krittika Goyal'
|
||||||
|
oldest_article = 1 #days
|
||||||
|
max_articles_per_feed = 25
|
||||||
|
#encoding = 'cp1252'
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
('News',
|
||||||
|
'http://www.delcotimes.com/?rss=news'),
|
||||||
|
('Sports',
|
||||||
|
'http://www.delcotimes.com/?rss=sports'),
|
||||||
|
('Business',
|
||||||
|
'http://business-news.thestreet.com/the-delaware-county-daily-times/rss/109393'),
|
||||||
|
('Entertainment',
|
||||||
|
'http://www.delcotimes.com/?rss=entertainment'),
|
||||||
|
]
|
||||||
|
|
@ -55,7 +55,6 @@ class FinancialTimes(BasicNewsRecipe):
|
|||||||
,dict(name='div' , attrs={'class':'standfirst'})
|
,dict(name='div' , attrs={'class':'standfirst'})
|
||||||
,dict(name='div' , attrs={'id' :'storyContent'})
|
,dict(name='div' , attrs={'id' :'storyContent'})
|
||||||
,dict(name='div' , attrs={'class':['ft-story-body','index-detail']})
|
,dict(name='div' , attrs={'class':['ft-story-body','index-detail']})
|
||||||
,dict(name='div' , attrs={'class':['ft-story-body','index-detail']})
|
|
||||||
,dict(name='h2' , attrs={'class':'entry-title'} )
|
,dict(name='h2' , attrs={'class':'entry-title'} )
|
||||||
,dict(name='span', attrs={'class':lambda x: x and 'posted-on' in x.split()} )
|
,dict(name='span', attrs={'class':lambda x: x and 'posted-on' in x.split()} )
|
||||||
,dict(name='span', attrs={'class':'author_byline'} )
|
,dict(name='span', attrs={'class':'author_byline'} )
|
||||||
@ -91,7 +90,10 @@ class FinancialTimes(BasicNewsRecipe):
|
|||||||
url = rawlink
|
url = rawlink
|
||||||
if not rawlink.startswith('http://'):
|
if not rawlink.startswith('http://'):
|
||||||
url = self.PREFIX + rawlink
|
url = self.PREFIX + rawlink
|
||||||
urlverified = self.browser.open_novisit(url).geturl() # resolve redirect.
|
try:
|
||||||
|
urlverified = self.browser.open_novisit(url).geturl() # resolve redirect.
|
||||||
|
except:
|
||||||
|
continue
|
||||||
title = self.tag_to_string(item)
|
title = self.tag_to_string(item)
|
||||||
date = strftime(self.timefmt)
|
date = strftime(self.timefmt)
|
||||||
articles.append({
|
articles.append({
|
||||||
@ -175,3 +177,6 @@ class FinancialTimes(BasicNewsRecipe):
|
|||||||
tfile.close()
|
tfile.close()
|
||||||
self.temp_files.append(tfile)
|
self.temp_files.append(tfile)
|
||||||
return tfile.name
|
return tfile.name
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.browser.open('https://registration.ft.com/registration/login/logout?location=')
|
BIN
recipes/icons/pravda_en.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
recipes/icons/pravda_it.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
recipes/icons/pravda_por.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
recipes/icons/pravda_ru.png
Normal file
After Width: | Height: | Size: 538 B |
@ -17,6 +17,7 @@ class NYPost(BasicNewsRecipe):
|
|||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
encoding = 'utf8'
|
encoding = 'utf8'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
|
auto_cleanup = True
|
||||||
language = 'en'
|
language = 'en'
|
||||||
masthead_url = 'http://www.nypost.com/rw/SysConfig/WebPortal/nypost/images/nyp_logo_230x32.gif'
|
masthead_url = 'http://www.nypost.com/rw/SysConfig/WebPortal/nypost/images/nyp_logo_230x32.gif'
|
||||||
extra_css = ' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} '
|
extra_css = ' body{font-family: Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} '
|
||||||
@ -28,7 +29,7 @@ class NYPost(BasicNewsRecipe):
|
|||||||
, 'language' : language
|
, 'language' : language
|
||||||
}
|
}
|
||||||
|
|
||||||
keep_only_tags=[dict(name='div', attrs={'id':'story'})]
|
#keep_only_tags=[dict(name='div', attrs={'id':'story'})]
|
||||||
|
|
||||||
feeds = [(u'Articles', u'http://www.nypost.com/rss/all_section.xml')]
|
feeds = [(u'Articles', u'http://www.nypost.com/rss/all_section.xml')]
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
|
||||||
|
|
||||||
class NewYorkTimesBookReview(BasicNewsRecipe):
|
class NewYorkTimesBookReview(BasicNewsRecipe):
|
||||||
title = u'New York Times Book Review'
|
title = u'New York Times Book Review'
|
||||||
@ -7,50 +6,16 @@ class NewYorkTimesBookReview(BasicNewsRecipe):
|
|||||||
__author__ = 'Krittika Goyal'
|
__author__ = 'Krittika Goyal'
|
||||||
oldest_article = 8 #days
|
oldest_article = 8 #days
|
||||||
max_articles_per_feed = 1000
|
max_articles_per_feed = 1000
|
||||||
recursions = 2
|
#recursions = 2
|
||||||
#encoding = 'latin1'
|
#encoding = 'latin1'
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
|
|
||||||
remove_stylesheets = True
|
|
||||||
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
|
|
||||||
remove_tags_after = dict(name='div', attrs={'id':'authorId'})
|
|
||||||
remove_tags = [
|
|
||||||
dict(name='iframe'),
|
|
||||||
dict(name=['div', 'a'], attrs={'class':['enlargeThis', 'jumpLink']}),
|
|
||||||
dict(name='div', attrs={'id':['sidebarArticles', 'toolsRight']}),
|
|
||||||
#dict(name='ul', attrs={'class':'article-tools'}),
|
|
||||||
#dict(name='ul', attrs={'class':'articleTools'}),
|
|
||||||
]
|
|
||||||
match_regexps = [
|
|
||||||
r'http://www.nytimes.com/.+pagewanted=[2-9]+'
|
|
||||||
]
|
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
('New York Times Sunday Book Review',
|
('New York Times Sunday Book Review',
|
||||||
'http://feeds.nytimes.com/nyt/rss/SundayBookReview'),
|
'http://feeds.nytimes.com/nyt/rss/SundayBookReview'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
story = soup.find(name='div', attrs={'id':'article'})
|
|
||||||
#td = heading.findParent(name='td')
|
|
||||||
#td.extract()
|
|
||||||
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
|
||||||
body = soup.find(name='body')
|
|
||||||
body.insert(0, story)
|
|
||||||
#for x in soup.findAll(name='p', text=lambda x:x and '-->' in x):
|
|
||||||
#p = x.findParent('p')
|
|
||||||
#if p is not None:
|
|
||||||
#p.extract()
|
|
||||||
return soup
|
|
||||||
|
|
||||||
def postprocess_html(self, soup, first):
|
|
||||||
for div in soup.findAll(id='pageLinks'):
|
|
||||||
div.extract()
|
|
||||||
if not first:
|
|
||||||
h1 = soup.find('h1')
|
|
||||||
if h1 is not None:
|
|
||||||
h1.extract()
|
|
||||||
t = soup.find(attrs={'class':'timestamp'})
|
|
||||||
if t is not None:
|
|
||||||
t.extract()
|
|
||||||
return soup
|
|
||||||
|
@ -35,6 +35,7 @@ class pcWorld(BasicNewsRecipe):
|
|||||||
|
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
auto_cleanup = True
|
||||||
|
|
||||||
def get_obfuscated_article(self, url):
|
def get_obfuscated_article(self, url):
|
||||||
br = self.get_browser()
|
br = self.get_browser()
|
||||||
@ -48,16 +49,16 @@ class pcWorld(BasicNewsRecipe):
|
|||||||
self.temp_files[-1].close()
|
self.temp_files[-1].close()
|
||||||
return self.temp_files[-1].name
|
return self.temp_files[-1].name
|
||||||
|
|
||||||
keep_only_tags = [
|
#keep_only_tags = [
|
||||||
dict(name='div', attrs={'class':'article'})
|
#dict(name='div', attrs={'class':'article'})
|
||||||
]
|
#]
|
||||||
remove_tags = [
|
#remove_tags = [
|
||||||
dict(name='div', attrs={'class':['toolBar','mac_tags','toolBar btmTools','recommend longRecommend','recommend shortRecommend','textAds']}),
|
#dict(name='div', attrs={'class':['toolBar','mac_tags','toolBar btmTools','recommend longRecommend','recommend shortRecommend','textAds']}),
|
||||||
dict(name='div', attrs={'id':['sidebar','comments','mac_tags']}),
|
#dict(name='div', attrs={'id':['sidebar','comments','mac_tags']}),
|
||||||
dict(name='ul', attrs={'class':['tools', 'tools clearfix']}),
|
#dict(name='ul', attrs={'class':['tools', 'tools clearfix']}),
|
||||||
dict(name='li', attrs={'class':'sub'}),
|
#dict(name='li', attrs={'class':'sub'}),
|
||||||
dict(name='p', attrs={'id':'userDesire'})
|
#dict(name='p', attrs={'id':'userDesire'})
|
||||||
]
|
#]
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'PCWorld Headlines', u'http://feeds.pcworld.com/pcworld/latestnews'),
|
(u'PCWorld Headlines', u'http://feeds.pcworld.com/pcworld/latestnews'),
|
||||||
(u'How-To', u'http://feeds.pcworld.com/pcworld/update/howto'),
|
(u'How-To', u'http://feeds.pcworld.com/pcworld/update/howto'),
|
||||||
|
53
recipes/pravda_en.recipe
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
english.pravda.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Pravda_eng(BasicNewsRecipe):
|
||||||
|
title = 'Pravda in English'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia and rest of the world'
|
||||||
|
publisher = 'PRAVDA.Ru'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'en_RU'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://english.pravda.ru/pix/logo.gif'
|
||||||
|
extra_css = """
|
||||||
|
body{font-family: Arial,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_attributes=['lang', 'style']
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'World' , u'http://english.pravda.ru/world/export-articles.xml' )
|
||||||
|
,(u'Russia' , u'http://english.pravda.ru/russia/export-articles.xml' )
|
||||||
|
,(u'Society' , u'http://english.pravda.ru/society/export-articles.xml' )
|
||||||
|
,(u'Incidents', u'http://english.pravda.ru/hotspots/export-articles.xml' )
|
||||||
|
,(u'Opinion' , u'http://english.pravda.ru/opinion/export-articles.xml' )
|
||||||
|
,(u'Science' , u'http://english.pravda.ru/science/export-articles.xml' )
|
||||||
|
,(u'Business' , u'http://english.pravda.ru/business/export-articles.xml' )
|
||||||
|
,(u'Economics', u'http://english.pravda.ru/russia/economics/export-articles.xml')
|
||||||
|
,(u'Politics' , u'http://english.pravda.ru/russia/politics/export-articles.xml' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?mode=print'
|
52
recipes/pravda_it.recipe
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
italia.pravda.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Pravda_ita(BasicNewsRecipe):
|
||||||
|
title = 'Pravda in Italiano'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia and rest of the world'
|
||||||
|
publisher = 'PRAVDA.Ru'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'it'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://italia.pravda.ru/pix/logo.gif'
|
||||||
|
extra_css = """
|
||||||
|
body{font-family: Arial,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_attributes=['lang', 'style']
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Dal mondo' , u'http://italia.pravda.ru/world/export-articles.xml' )
|
||||||
|
,(u'Russia' , u'http://italia.pravda.ru/russia/export-articles.xml' )
|
||||||
|
,(u'Societa' , u'http://italia.pravda.ru/society/export-articles.xml' )
|
||||||
|
,(u'Avvenimenti', u'http://italia.pravda.ru/hotspots/export-articles.xml' )
|
||||||
|
,(u'Opinioni' , u'http://italia.pravda.ru/opinion/export-articles.xml' )
|
||||||
|
,(u'Scienza' , u'http://italia.pravda.ru/science/export-articles.xml' )
|
||||||
|
,(u'Economia' , u'http://italia.pravda.ru/russia/economics/export-articles.xml')
|
||||||
|
,(u'Politica' , u'http://italia.pravda.ru/russia/politics/export-articles.xml' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?mode=print'
|
51
recipes/pravda_por.recipe
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
port.pravda.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Pravda_port(BasicNewsRecipe):
|
||||||
|
title = u'Pravda em português'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from Russia and rest of the world'
|
||||||
|
publisher = 'PRAVDA.Ru'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'pt'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://port.pravda.ru/pix/logo.gif'
|
||||||
|
extra_css = """
|
||||||
|
body{font-family: Arial,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_attributes=['lang', 'style']
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
|
||||||
|
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Mundo' , u'http://port.pravda.ru/mundo/export-articles.xml' )
|
||||||
|
,(u'Russia' , u'http://port.pravda.ru/russa/export-articles.xml' )
|
||||||
|
,(u'Sociedade' , u'http://port.pravda.ru/sociedade/export-articles.xml' )
|
||||||
|
,(u'Cultura' , u'http://port.pravda.ru/sociedade/cultura/export-articles.xml')
|
||||||
|
,(u'Ciencia' , u'http://port.pravda.ru/science/export-articles.xml' )
|
||||||
|
,(u'Desporto' , u'http://port.pravda.ru/desporto/export-articles.xml' )
|
||||||
|
,(u'CPLP' , u'http://port.pravda.ru/cplp/export-articles.xml' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?mode=print'
|
50
recipes/pravda_ru.recipe
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
www.pravda.ru
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Pravda_ru(BasicNewsRecipe):
|
||||||
|
title = u'Правда'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = u'Правда.Ру: Аналитика и новости'
|
||||||
|
publisher = 'PRAVDA.Ru'
|
||||||
|
category = 'news, politics, Russia'
|
||||||
|
oldest_article = 2
|
||||||
|
max_articles_per_feed = 200
|
||||||
|
no_stylesheets = True
|
||||||
|
encoding = 'utf8'
|
||||||
|
use_embedded_content = False
|
||||||
|
language = 'ru'
|
||||||
|
remove_empty_feeds = True
|
||||||
|
publication_type = 'newspaper'
|
||||||
|
masthead_url = 'http://www.pravda.ru/pix/logo.gif'
|
||||||
|
extra_css = """
|
||||||
|
body{font-family: Arial,sans-serif }
|
||||||
|
img{margin-bottom: 0.4em; display:block}
|
||||||
|
"""
|
||||||
|
|
||||||
|
conversion_options = {
|
||||||
|
'comment' : description
|
||||||
|
, 'tags' : category
|
||||||
|
, 'publisher' : publisher
|
||||||
|
, 'language' : language
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_attributes=['lang', 'style']
|
||||||
|
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'Мир' , u'http://www.pravda.ru/world/export.xml' )
|
||||||
|
,(u'Религия' , u'http://www.pravda.ru/faith/export.xml' )
|
||||||
|
,(u'Общество' , u'http://www.pravda.ru/society/export.xml' )
|
||||||
|
,(u'Происшествия', u'http://www.pravda.ru/accidents/export.xml')
|
||||||
|
,(u'Наука' , u'http://www.pravda.ru/science/export.xml' )
|
||||||
|
,(u'Экономика' , u'http://www.pravda.ru/economics/export.xml')
|
||||||
|
,(u'Политика' , u'http://www.pravda.ru/politics/export.xml' )
|
||||||
|
]
|
||||||
|
|
||||||
|
def print_version(self, url):
|
||||||
|
return url + '?mode=print'
|
@ -27,7 +27,7 @@ class ScienceAAS(BasicNewsRecipe):
|
|||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser()
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
br.open(self.LOGIN)
|
br.open(self.LOGIN)
|
||||||
br.select_form(nr=1)
|
br.select_form(nr=0)
|
||||||
br['username'] = self.username
|
br['username'] = self.username
|
||||||
br['code' ] = self.password
|
br['code' ] = self.password
|
||||||
br.submit()
|
br.submit()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import random
|
import re, random
|
||||||
|
|
||||||
from calibre import browser
|
from calibre import browser
|
||||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||||
@ -8,7 +8,7 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
title = u'The Sun UK'
|
title = u'The Sun UK'
|
||||||
description = 'Articles from The Sun tabloid UK'
|
description = 'Articles from The Sun tabloid UK'
|
||||||
__author__ = 'Dave Asbury'
|
__author__ = 'Dave Asbury'
|
||||||
# last updated 12/10/12 added starsons remove article code
|
# last updated 19/10/12 better cover fetch
|
||||||
language = 'en_GB'
|
language = 'en_GB'
|
||||||
oldest_article = 1
|
oldest_article = 1
|
||||||
max_articles_per_feed = 15
|
max_articles_per_feed = 15
|
||||||
@ -19,7 +19,7 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
|
||||||
ignore_duplicate_articles = {'title'}
|
ignore_duplicate_articles = {'title','url'}
|
||||||
|
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
@ -72,9 +72,10 @@ class AdvancedUserRecipe1325006965(BasicNewsRecipe):
|
|||||||
#cov2 now contains url of the page containing pic
|
#cov2 now contains url of the page containing pic
|
||||||
soup = self.index_to_soup(cov2)
|
soup = self.index_to_soup(cov2)
|
||||||
cov = soup.find(attrs={'id' : 'large'})
|
cov = soup.find(attrs={'id' : 'large'})
|
||||||
cov2 = str(cov)
|
cov=str(cov)
|
||||||
cov2=cov2[27:-18]
|
cov2 = re.findall('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', cov)
|
||||||
#cov2 now is pic url, now go back to original function
|
cov2 = str(cov2)
|
||||||
|
cov2=cov2[2:len(cov2)-2]
|
||||||
br = browser()
|
br = browser()
|
||||||
br.set_handle_redirect(False)
|
br.set_handle_redirect(False)
|
||||||
try:
|
try:
|
||||||
|
@ -23,16 +23,15 @@ class Time(BasicNewsRecipe):
|
|||||||
|
|
||||||
keep_only_tags = [
|
keep_only_tags = [
|
||||||
{
|
{
|
||||||
'class':['tout1', 'entry-content', 'external-gallery-img', 'image-meta']
|
'class':['primary-col', 'tout1']
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
{'class':['thumbnail', 'button']},
|
{'class':['button', 'entry-sharing group', 'wp-paginate',
|
||||||
|
'moving-markup', 'entry-comments']},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
extra_css = '.entry-date { padding-left: 2ex }'
|
||||||
recursions = 10
|
|
||||||
match_regexps = [r'/[0-9,]+-(2|3|4|5|6|7|8|9)(,\d+){0,1}.html',r'http://www.time.com/time/specials/packages/article/.*']
|
|
||||||
|
|
||||||
preprocess_regexps = [(re.compile(
|
preprocess_regexps = [(re.compile(
|
||||||
r'<meta .+/>'), lambda m:'')]
|
r'<meta .+/>'), lambda m:'')]
|
||||||
@ -45,7 +44,7 @@ class Time(BasicNewsRecipe):
|
|||||||
br.select_form(predicate=lambda f: 'action' in f.attrs and f.attrs['action'] == 'https://auth.time.com/login.php')
|
br.select_form(predicate=lambda f: 'action' in f.attrs and f.attrs['action'] == 'https://auth.time.com/login.php')
|
||||||
br['username'] = self.username
|
br['username'] = self.username
|
||||||
br['password'] = self.password
|
br['password'] = self.password
|
||||||
br['magcode'] = ['TD']
|
# br['magcode'] = ['TD']
|
||||||
br.find_control('turl').readonly = False
|
br.find_control('turl').readonly = False
|
||||||
br['turl'] = 'http://www.time.com/time/magazine'
|
br['turl'] = 'http://www.time.com/time/magazine'
|
||||||
br.find_control('rurl').readonly = False
|
br.find_control('rurl').readonly = False
|
||||||
@ -104,7 +103,14 @@ class Time(BasicNewsRecipe):
|
|||||||
method='text').strip()
|
method='text').strip()
|
||||||
if not title: continue
|
if not title: continue
|
||||||
url = a[0].get('href')
|
url = a[0].get('href')
|
||||||
url = re.sub('/magazine/article/0,9171','/subscriber/printout/0,8816', url)
|
if url.startswith('/'):
|
||||||
|
url = 'http://www.time.com'+url
|
||||||
|
if '/article/0,' in url:
|
||||||
|
soup = self.index_to_soup(url)
|
||||||
|
a = soup.find('a', href=lambda x:x and '/printout/' in x)
|
||||||
|
url = a['href'].replace('/printout', '/subscriber/printout')
|
||||||
|
else:
|
||||||
|
url += 'print/' if url.endswith('/') else '/print/'
|
||||||
if url.startswith('/'):
|
if url.startswith('/'):
|
||||||
url = 'http://www.time.com'+url
|
url = 'http://www.time.com'+url
|
||||||
desc = ''
|
desc = ''
|
||||||
@ -112,10 +118,18 @@ class Time(BasicNewsRecipe):
|
|||||||
if p:
|
if p:
|
||||||
desc = html.tostring(p[0], encoding=unicode,
|
desc = html.tostring(p[0], encoding=unicode,
|
||||||
method='text')
|
method='text')
|
||||||
self.log('\t', title, ':\n\t\t', desc)
|
self.log('\t', title, ':\n\t\t', url)
|
||||||
yield {
|
yield {
|
||||||
'title' : title,
|
'title' : title,
|
||||||
'url' : url,
|
'url' : url,
|
||||||
'date' : '',
|
'date' : '',
|
||||||
'description' : desc
|
'description' : desc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def preprocess_html(self, soup):
|
||||||
|
for fig in soup.findAll('figure'):
|
||||||
|
img = fig.find('img')
|
||||||
|
if img is not None:
|
||||||
|
fig.replaceWith(img)
|
||||||
|
return soup
|
||||||
|
|
||||||
|
19
recipes/yazihane.recipe
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
import re
|
||||||
|
|
||||||
|
class AdvancedUserRecipe1350731826(BasicNewsRecipe):
|
||||||
|
title = u'Yazihane'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
__author__ = 'A Erdogan'
|
||||||
|
description = 'Sports Blog'
|
||||||
|
publisher = 'yazihaneden.com'
|
||||||
|
category = 'sports, basketball, nba, cycling, euroleague'
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
masthead_url = 'http://www.yazihaneden.com/wp-content/uploads/Untitled-1.png'
|
||||||
|
language = 'tr'
|
||||||
|
|
||||||
|
keep_only_tags = [ dict(name='div', attrs={'id':re.compile('(^|| )post-($|| )', re.DOTALL)})]
|
||||||
|
remove_tags_after = dict(name='div', attrs={'class':'post-footer clear'})
|
||||||
|
feeds = [(u'Yazihane', u'http://www.yazihaneden.com/feed/')]
|
@ -1,5 +1,4 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag
|
|
||||||
|
|
||||||
class YemenTimesRecipe(BasicNewsRecipe):
|
class YemenTimesRecipe(BasicNewsRecipe):
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
@ -13,7 +12,7 @@ class YemenTimesRecipe(BasicNewsRecipe):
|
|||||||
category = u'News, Opinion, Yemen'
|
category = u'News, Opinion, Yemen'
|
||||||
description = u'Award winning weekly from Yemen, promoting press freedom, professional journalism and the defense of human rights.'
|
description = u'Award winning weekly from Yemen, promoting press freedom, professional journalism and the defense of human rights.'
|
||||||
|
|
||||||
oldest_article = 7
|
oldest_article = 10
|
||||||
max_articles_per_feed = 100
|
max_articles_per_feed = 100
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
encoding = 'utf-8'
|
encoding = 'utf-8'
|
||||||
@ -21,27 +20,13 @@ class YemenTimesRecipe(BasicNewsRecipe):
|
|||||||
remove_empty_feeds = True
|
remove_empty_feeds = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
remove_javascript = True
|
remove_javascript = True
|
||||||
|
auto_cleanup = True
|
||||||
|
|
||||||
keep_only_tags = []
|
|
||||||
keep_only_tags.append(dict(name = 'div', attrs = {'id': 'ctl00_ContentPlaceHolder1_MAINNEWS0_Panel1',
|
|
||||||
'class': 'DMAIN2'}))
|
|
||||||
remove_attributes = ['style']
|
|
||||||
|
|
||||||
INDEX = 'http://www.yementimes.com/'
|
feeds = [
|
||||||
feeds = []
|
('News',
|
||||||
feeds.append((u'Our Viewpoint', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=6&pnm=OUR%20VIEWPOINT'))
|
'http://www.yementimes.com/?tpl=1341'),
|
||||||
feeds.append((u'Local News', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=3&pnm=Local%20news'))
|
]
|
||||||
feeds.append((u'Their News', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=80&pnm=Their%20News'))
|
|
||||||
feeds.append((u'Report', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=8&pnm=report'))
|
|
||||||
feeds.append((u'Health', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=51&pnm=health'))
|
|
||||||
feeds.append((u'Interview', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=77&pnm=interview'))
|
|
||||||
feeds.append((u'Opinion', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=7&pnm=opinion'))
|
|
||||||
feeds.append((u'Business', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=5&pnm=business'))
|
|
||||||
feeds.append((u'Op-Ed', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=81&pnm=Op-Ed'))
|
|
||||||
feeds.append((u'Culture', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=75&pnm=Culture'))
|
|
||||||
feeds.append((u'Readers View', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=4&pnm=Readers%20View'))
|
|
||||||
feeds.append((u'Variety', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=9&pnm=Variety'))
|
|
||||||
feeds.append((u'Education', u'http://www.yementimes.com/DEFAULTSUB.ASPX?pnc=57&pnm=Education'))
|
|
||||||
|
|
||||||
extra_css = '''
|
extra_css = '''
|
||||||
body {font-family:verdana, arial, helvetica, geneva, sans-serif;}
|
body {font-family:verdana, arial, helvetica, geneva, sans-serif;}
|
||||||
@ -53,73 +38,4 @@ class YemenTimesRecipe(BasicNewsRecipe):
|
|||||||
conversion_options = {'comments': description, 'tags': category, 'language': 'en',
|
conversion_options = {'comments': description, 'tags': category, 'language': 'en',
|
||||||
'publisher': publisher, 'linearize_tables': True}
|
'publisher': publisher, 'linearize_tables': True}
|
||||||
|
|
||||||
def get_browser(self):
|
|
||||||
br = BasicNewsRecipe.get_browser()
|
|
||||||
br.set_handle_gzip(True)
|
|
||||||
|
|
||||||
return br
|
|
||||||
|
|
||||||
def parse_index(self):
|
|
||||||
answer = []
|
|
||||||
for feed_title, feed in self.feeds:
|
|
||||||
soup = self.index_to_soup(feed)
|
|
||||||
|
|
||||||
newsbox = soup.find('div', 'newsbox')
|
|
||||||
main = newsbox.findNextSibling('table')
|
|
||||||
|
|
||||||
articles = []
|
|
||||||
for li in main.findAll('li'):
|
|
||||||
title = self.tag_to_string(li.a)
|
|
||||||
url = self.INDEX + li.a['href']
|
|
||||||
articles.append({'title': title, 'date': None, 'url': url, 'description': '<br/> '})
|
|
||||||
|
|
||||||
answer.append((feed_title, articles))
|
|
||||||
|
|
||||||
return answer
|
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
|
||||||
freshSoup = self.getFreshSoup(soup)
|
|
||||||
|
|
||||||
headline = soup.find('div', attrs = {'id': 'DVMTIT'})
|
|
||||||
if headline:
|
|
||||||
div = headline.findNext('div', attrs = {'id': 'DVTOP'})
|
|
||||||
img = None
|
|
||||||
if div:
|
|
||||||
img = div.find('img')
|
|
||||||
|
|
||||||
headline.name = 'h1'
|
|
||||||
freshSoup.body.append(headline)
|
|
||||||
if img is not None:
|
|
||||||
freshSoup.body.append(img)
|
|
||||||
|
|
||||||
byline = soup.find('div', attrs = {'id': 'DVTIT'})
|
|
||||||
if byline:
|
|
||||||
date_el = byline.find('span')
|
|
||||||
if date_el:
|
|
||||||
pub_date = self.tag_to_string(date_el)
|
|
||||||
date = Tag(soup, 'div', attrs = [('class', 'yemen_date')])
|
|
||||||
date.append(pub_date)
|
|
||||||
date_el.extract()
|
|
||||||
|
|
||||||
raw = '<br/>'.join(['%s' % (part) for part in byline.findAll(text = True)])
|
|
||||||
author = BeautifulSoup('<div class="yemen_byline">' + raw + '</div>')
|
|
||||||
|
|
||||||
if date is not None:
|
|
||||||
freshSoup.body.append(date)
|
|
||||||
freshSoup.body.append(author)
|
|
||||||
|
|
||||||
story = soup.find('div', attrs = {'id': 'DVDET'})
|
|
||||||
if story:
|
|
||||||
for table in story.findAll('table'):
|
|
||||||
if table.find('img'):
|
|
||||||
table['class'] = 'yemen_caption'
|
|
||||||
|
|
||||||
freshSoup.body.append(story)
|
|
||||||
|
|
||||||
return freshSoup
|
|
||||||
|
|
||||||
def getFreshSoup(self, oldSoup):
|
|
||||||
freshSoup = BeautifulSoup('<html><head><title></title></head><body></body></html>')
|
|
||||||
if oldSoup.head.title:
|
|
||||||
freshSoup.head.title.append(self.tag_to_string(oldSoup.head.title))
|
|
||||||
return freshSoup
|
|
||||||
|
BIN
resources/images/font.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
resources/images/rotate-right.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
resources/images/view-image.png
Normal file
After Width: | Height: | Size: 24 KiB |
@ -787,12 +787,10 @@ application/x-font-framemaker
|
|||||||
application/x-font-ghostscript gsf
|
application/x-font-ghostscript gsf
|
||||||
application/x-font-libgrx
|
application/x-font-libgrx
|
||||||
application/x-font-linux-psf psf
|
application/x-font-linux-psf psf
|
||||||
application/x-font-otf otf
|
|
||||||
application/x-font-pcf pcf
|
application/x-font-pcf pcf
|
||||||
application/x-font-snf snf
|
application/x-font-snf snf
|
||||||
application/x-font-speedo
|
application/x-font-speedo
|
||||||
application/x-font-sunos-news
|
application/x-font-sunos-news
|
||||||
application/x-font-ttf ttc ttf
|
|
||||||
application/x-font-type1 afm pfa pfb pfm
|
application/x-font-type1 afm pfa pfb pfm
|
||||||
application/x-font-vfont
|
application/x-font-vfont
|
||||||
application/x-freemind mm
|
application/x-freemind mm
|
||||||
@ -1368,8 +1366,6 @@ text/fb2+xml fb2
|
|||||||
text/x-sony-bbeb+xml lrs
|
text/x-sony-bbeb+xml lrs
|
||||||
application/x-sony-bbeb lrf lrx
|
application/x-sony-bbeb lrf lrx
|
||||||
application/adobe-page-template+xml xpgt
|
application/adobe-page-template+xml xpgt
|
||||||
application/x-font-opentype otf
|
|
||||||
application/x-font-truetype ttf
|
|
||||||
application/x-mobipocket-ebook mobi prc azw
|
application/x-mobipocket-ebook mobi prc azw
|
||||||
application/x-topaz-ebook tpz azw1
|
application/x-topaz-ebook tpz azw1
|
||||||
application/x-mobipocket-subscription pobi
|
application/x-mobipocket-subscription pobi
|
||||||
@ -1381,3 +1377,7 @@ application/x-cb7 cb7
|
|||||||
application/x-koboreader-ebook kobo
|
application/x-koboreader-ebook kobo
|
||||||
image/wmf wmf
|
image/wmf wmf
|
||||||
application/ereader pdb
|
application/ereader pdb
|
||||||
|
# See http://idpf.org/epub/30/spec/epub30-publications.html#sec-core-media-types
|
||||||
|
application/vnd.ms-opentype otf
|
||||||
|
application/font-woff woff
|
||||||
|
application/x-font-truetype ttf
|
||||||
|
@ -236,7 +236,8 @@ th {
|
|||||||
/* inlines */
|
/* inlines */
|
||||||
|
|
||||||
b, strong {
|
b, strong {
|
||||||
font-weight: bolder;
|
/* ADE doesn't support bolder with embedded fonts */
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
i, cite, em, var, dfn {
|
i, cite, em, var, dfn {
|
||||||
|
@ -8,7 +8,10 @@ let g:syntastic_cpp_include_dirs = [
|
|||||||
\'/usr/include/qt4/QtCore',
|
\'/usr/include/qt4/QtCore',
|
||||||
\'/usr/include/qt4/QtGui',
|
\'/usr/include/qt4/QtGui',
|
||||||
\'/usr/include/qt4',
|
\'/usr/include/qt4',
|
||||||
|
\'/usr/include/freetype2',
|
||||||
|
\'/usr/include/fontconfig',
|
||||||
\'src/qtcurve/common', 'src/qtcurve',
|
\'src/qtcurve/common', 'src/qtcurve',
|
||||||
|
\'src/sfntly/src', 'src/sfntly/src/sample',
|
||||||
\'/usr/include/ImageMagick',
|
\'/usr/include/ImageMagick',
|
||||||
\]
|
\]
|
||||||
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs
|
let g:syntastic_c_include_dirs = g:syntastic_cpp_include_dirs
|
||||||
|
@ -84,16 +84,18 @@ qt_inc = pyqt.qt_inc_dir
|
|||||||
qt_lib = pyqt.qt_lib_dir
|
qt_lib = pyqt.qt_lib_dir
|
||||||
ft_lib_dirs = []
|
ft_lib_dirs = []
|
||||||
ft_libs = []
|
ft_libs = []
|
||||||
|
ft_inc_dirs = []
|
||||||
jpg_libs = []
|
jpg_libs = []
|
||||||
jpg_lib_dirs = []
|
jpg_lib_dirs = []
|
||||||
fc_inc = '/usr/include/fontconfig'
|
|
||||||
fc_lib = '/usr/lib'
|
|
||||||
podofo_inc = '/usr/include/podofo'
|
podofo_inc = '/usr/include/podofo'
|
||||||
podofo_lib = '/usr/lib'
|
podofo_lib = '/usr/lib'
|
||||||
chmlib_inc_dirs = chmlib_lib_dirs = []
|
chmlib_inc_dirs = chmlib_lib_dirs = []
|
||||||
sqlite_inc_dirs = []
|
sqlite_inc_dirs = []
|
||||||
icu_inc_dirs = []
|
icu_inc_dirs = []
|
||||||
icu_lib_dirs = []
|
icu_lib_dirs = []
|
||||||
|
zlib_inc_dirs = []
|
||||||
|
zlib_lib_dirs = []
|
||||||
|
zlib_libs = ['z']
|
||||||
|
|
||||||
if iswindows:
|
if iswindows:
|
||||||
prefix = r'C:\cygwin\home\kovid\sw'
|
prefix = r'C:\cygwin\home\kovid\sw'
|
||||||
@ -103,8 +105,6 @@ if iswindows:
|
|||||||
'source', 'i18n')]
|
'source', 'i18n')]
|
||||||
icu_lib_dirs = [os.path.join(ICU, 'source', 'lib')]
|
icu_lib_dirs = [os.path.join(ICU, 'source', 'lib')]
|
||||||
sqlite_inc_dirs = [sw_inc_dir]
|
sqlite_inc_dirs = [sw_inc_dir]
|
||||||
fc_inc = os.path.join(sw_inc_dir, 'fontconfig')
|
|
||||||
fc_lib = sw_lib_dir
|
|
||||||
chmlib_inc_dirs = consolidate('CHMLIB_INC_DIR', os.path.join(prefix,
|
chmlib_inc_dirs = consolidate('CHMLIB_INC_DIR', os.path.join(prefix,
|
||||||
'build', 'chmlib-0.40', 'src'))
|
'build', 'chmlib-0.40', 'src'))
|
||||||
chmlib_lib_dirs = consolidate('CHMLIB_LIB_DIR', os.path.join(prefix,
|
chmlib_lib_dirs = consolidate('CHMLIB_LIB_DIR', os.path.join(prefix,
|
||||||
@ -116,6 +116,10 @@ if iswindows:
|
|||||||
jpg_libs = ['jpeg']
|
jpg_libs = ['jpeg']
|
||||||
ft_lib_dirs = [sw_lib_dir]
|
ft_lib_dirs = [sw_lib_dir]
|
||||||
ft_libs = ['freetype']
|
ft_libs = ['freetype']
|
||||||
|
ft_inc_dirs = [sw_inc_dir]
|
||||||
|
zlib_inc_dirs = [sw_inc_dir]
|
||||||
|
zlib_lib_dirs = [sw_lib_dir]
|
||||||
|
zlib_libs = ['zlib']
|
||||||
|
|
||||||
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.7.6')]
|
magick_inc_dirs = [os.path.join(prefix, 'build', 'ImageMagick-6.7.6')]
|
||||||
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
magick_lib_dirs = [os.path.join(magick_inc_dirs[0], 'VisualMagick', 'lib')]
|
||||||
@ -123,8 +127,6 @@ if iswindows:
|
|||||||
podofo_inc = os.path.join(sw_inc_dir, 'podofo')
|
podofo_inc = os.path.join(sw_inc_dir, 'podofo')
|
||||||
podofo_lib = sw_lib_dir
|
podofo_lib = sw_lib_dir
|
||||||
elif isosx:
|
elif isosx:
|
||||||
fc_inc = '/sw/include/fontconfig'
|
|
||||||
fc_lib = '/sw/lib'
|
|
||||||
podofo_inc = '/sw/podofo'
|
podofo_inc = '/sw/podofo'
|
||||||
podofo_lib = '/sw/lib'
|
podofo_lib = '/sw/lib'
|
||||||
magick_inc_dirs = consolidate('MAGICK_INC',
|
magick_inc_dirs = consolidate('MAGICK_INC',
|
||||||
@ -135,6 +137,8 @@ elif isosx:
|
|||||||
png_inc_dirs = consolidate('PNG_INC_DIR', '/sw/include')
|
png_inc_dirs = consolidate('PNG_INC_DIR', '/sw/include')
|
||||||
png_lib_dirs = consolidate('PNG_LIB_DIR', '/sw/lib')
|
png_lib_dirs = consolidate('PNG_LIB_DIR', '/sw/lib')
|
||||||
png_libs = ['png12']
|
png_libs = ['png12']
|
||||||
|
ft_libs = ['freetype']
|
||||||
|
ft_inc_dirs = ['/sw/include/freetype2']
|
||||||
else:
|
else:
|
||||||
# Include directories
|
# Include directories
|
||||||
png_inc_dirs = pkgconfig_include_dirs('libpng', 'PNG_INC_DIR',
|
png_inc_dirs = pkgconfig_include_dirs('libpng', 'PNG_INC_DIR',
|
||||||
@ -150,15 +154,12 @@ else:
|
|||||||
if not magick_libs:
|
if not magick_libs:
|
||||||
magick_libs = ['MagickWand', 'MagickCore']
|
magick_libs = ['MagickWand', 'MagickCore']
|
||||||
png_libs = ['png']
|
png_libs = ['png']
|
||||||
|
ft_inc_dirs = pkgconfig_include_dirs('freetype2', 'FT_INC_DIR',
|
||||||
|
'/usr/include/freetype2')
|
||||||
|
ft_lib_dirs = pkgconfig_lib_dirs('freetype2', 'FT_LIB_DIR', '/usr/lib')
|
||||||
|
ft_libs = pkgconfig_libs('freetype2', '', '')
|
||||||
|
|
||||||
|
|
||||||
fc_inc = os.environ.get('FC_INC_DIR', fc_inc)
|
|
||||||
fc_lib = os.environ.get('FC_LIB_DIR', fc_lib)
|
|
||||||
fc_error = None if os.path.exists(os.path.join(fc_inc, 'fontconfig.h')) else \
|
|
||||||
('fontconfig header files not found on your system. '
|
|
||||||
'Try setting the FC_INC_DIR and FC_LIB_DIR environment '
|
|
||||||
'variables.')
|
|
||||||
|
|
||||||
magick_error = None
|
magick_error = None
|
||||||
if not magick_inc_dirs or not os.path.exists(os.path.join(magick_inc_dirs[0],
|
if not magick_inc_dirs or not os.path.exists(os.path.join(magick_inc_dirs[0],
|
||||||
'wand')):
|
'wand')):
|
||||||
|
@ -13,11 +13,13 @@ from multiprocessing import cpu_count
|
|||||||
from PyQt4.pyqtconfig import QtGuiModuleMakefile
|
from PyQt4.pyqtconfig import QtGuiModuleMakefile
|
||||||
|
|
||||||
from setup import Command, islinux, isbsd, isosx, SRC, iswindows
|
from setup import Command, islinux, isbsd, isosx, SRC, iswindows
|
||||||
from setup.build_environment import (fc_inc, fc_lib, chmlib_inc_dirs, fc_error,
|
from setup.build_environment import (chmlib_inc_dirs,
|
||||||
podofo_inc, podofo_lib, podofo_error, pyqt, OSX_SDK, NMAKE, QMAKE,
|
podofo_inc, podofo_lib, podofo_error, pyqt, OSX_SDK, NMAKE, QMAKE,
|
||||||
msvc, MT, win_inc, win_lib, win_ddk, magick_inc_dirs, magick_lib_dirs,
|
msvc, MT, win_inc, win_lib, win_ddk, magick_inc_dirs, magick_lib_dirs,
|
||||||
magick_libs, chmlib_lib_dirs, sqlite_inc_dirs, icu_inc_dirs,
|
magick_libs, chmlib_lib_dirs, sqlite_inc_dirs, icu_inc_dirs,
|
||||||
icu_lib_dirs, win_ddk_lib_dirs)
|
icu_lib_dirs, win_ddk_lib_dirs, ft_libs, ft_lib_dirs, ft_inc_dirs,
|
||||||
|
zlib_libs, zlib_lib_dirs, zlib_inc_dirs)
|
||||||
|
from setup.sfntly import SfntlyBuilderMixin
|
||||||
MT
|
MT
|
||||||
isunix = islinux or isosx or isbsd
|
isunix = islinux or isosx or isbsd
|
||||||
|
|
||||||
@ -47,13 +49,12 @@ class Extension(object):
|
|||||||
self.optional = kwargs.get('optional', False)
|
self.optional = kwargs.get('optional', False)
|
||||||
self.needs_ddk = kwargs.get('needs_ddk', False)
|
self.needs_ddk = kwargs.get('needs_ddk', False)
|
||||||
|
|
||||||
|
def preflight(self, obj_dir, compiler, linker, builder, cflags, ldflags):
|
||||||
|
pass
|
||||||
|
|
||||||
reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp'))
|
reflow_sources = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.cpp'))
|
||||||
reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h'))
|
reflow_headers = glob.glob(os.path.join(SRC, 'calibre', 'ebooks', 'pdf', '*.h'))
|
||||||
|
|
||||||
pdfreflow_libs = []
|
|
||||||
if iswindows:
|
|
||||||
pdfreflow_libs = ['advapi32', 'User32', 'Gdi32', 'zlib']
|
|
||||||
|
|
||||||
icu_libs = ['icudata', 'icui18n', 'icuuc', 'icuio']
|
icu_libs = ['icudata', 'icui18n', 'icuuc', 'icuio']
|
||||||
icu_cflags = []
|
icu_cflags = []
|
||||||
if iswindows:
|
if iswindows:
|
||||||
@ -62,9 +63,26 @@ if isosx:
|
|||||||
icu_libs = ['icucore']
|
icu_libs = ['icucore']
|
||||||
icu_cflags = ['-DU_DISABLE_RENAMING'] # Needed to use system libicucore.dylib
|
icu_cflags = ['-DU_DISABLE_RENAMING'] # Needed to use system libicucore.dylib
|
||||||
|
|
||||||
|
class SfntlyExtension(Extension, SfntlyBuilderMixin):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
Extension.__init__(self, *args, **kwargs)
|
||||||
|
SfntlyBuilderMixin.__init__(self)
|
||||||
|
|
||||||
|
def preflight(self, *args, **kwargs):
|
||||||
|
self(*args, **kwargs)
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
|
|
||||||
|
SfntlyExtension('sfntly',
|
||||||
|
['calibre/utils/fonts/sfntly.cpp'],
|
||||||
|
headers= ['calibre/utils/fonts/sfntly.h'],
|
||||||
|
libraries=icu_libs,
|
||||||
|
lib_dirs=icu_lib_dirs,
|
||||||
|
inc_dirs=icu_inc_dirs,
|
||||||
|
cflags=icu_cflags
|
||||||
|
),
|
||||||
|
|
||||||
Extension('speedup',
|
Extension('speedup',
|
||||||
['calibre/utils/speedup.c'],
|
['calibre/utils/speedup.c'],
|
||||||
),
|
),
|
||||||
@ -119,12 +137,23 @@ extensions = [
|
|||||||
'calibre/utils/lzx/mspack.h'],
|
'calibre/utils/lzx/mspack.h'],
|
||||||
inc_dirs=['calibre/utils/lzx']),
|
inc_dirs=['calibre/utils/lzx']),
|
||||||
|
|
||||||
Extension('fontconfig',
|
Extension('freetype',
|
||||||
['calibre/utils/fonts/fontconfig.c'],
|
['calibre/utils/fonts/freetype.cpp'],
|
||||||
inc_dirs = [fc_inc],
|
inc_dirs = ft_inc_dirs,
|
||||||
libraries=['fontconfig'],
|
libraries=ft_libs,
|
||||||
lib_dirs=[fc_lib],
|
lib_dirs=ft_lib_dirs),
|
||||||
error=fc_error),
|
|
||||||
|
Extension('woff',
|
||||||
|
['calibre/utils/fonts/woff/main.c',
|
||||||
|
'calibre/utils/fonts/woff/woff.c'],
|
||||||
|
headers=[
|
||||||
|
'calibre/utils/fonts/woff/woff.h',
|
||||||
|
'calibre/utils/fonts/woff/woff-private.h'],
|
||||||
|
libraries=zlib_libs,
|
||||||
|
lib_dirs=zlib_lib_dirs,
|
||||||
|
inc_dirs=zlib_inc_dirs,
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
Extension('msdes',
|
Extension('msdes',
|
||||||
['calibre/utils/msdes/msdesmodule.c',
|
['calibre/utils/msdes/msdesmodule.c',
|
||||||
@ -228,6 +257,7 @@ if isunix:
|
|||||||
cc = os.environ.get('CC', 'gcc')
|
cc = os.environ.get('CC', 'gcc')
|
||||||
cxx = os.environ.get('CXX', 'g++')
|
cxx = os.environ.get('CXX', 'g++')
|
||||||
cflags = os.environ.get('OVERRIDE_CFLAGS',
|
cflags = os.environ.get('OVERRIDE_CFLAGS',
|
||||||
|
# '-Wall -DNDEBUG -ggdb -fno-strict-aliasing -pipe')
|
||||||
'-O3 -Wall -DNDEBUG -fno-strict-aliasing -pipe')
|
'-O3 -Wall -DNDEBUG -fno-strict-aliasing -pipe')
|
||||||
cflags = shlex.split(cflags) + ['-fPIC']
|
cflags = shlex.split(cflags) + ['-fPIC']
|
||||||
ldflags = os.environ.get('OVERRIDE_LDFLAGS', '-Wall')
|
ldflags = os.environ.get('OVERRIDE_LDFLAGS', '-Wall')
|
||||||
@ -290,9 +320,6 @@ class Build(Command):
|
|||||||
CFLAGS - Extra compiler flags
|
CFLAGS - Extra compiler flags
|
||||||
LDFLAGS - Extra linker flags
|
LDFLAGS - Extra linker flags
|
||||||
|
|
||||||
FC_INC_DIR - fontconfig header files
|
|
||||||
FC_LIB_DIR - fontconfig library
|
|
||||||
|
|
||||||
POPPLER_INC_DIR - poppler header files
|
POPPLER_INC_DIR - poppler header files
|
||||||
POPPLER_LIB_DIR - poppler-qt4 library
|
POPPLER_LIB_DIR - poppler-qt4 library
|
||||||
|
|
||||||
@ -358,8 +385,9 @@ class Build(Command):
|
|||||||
compiler = cxx if ext.needs_cxx else cc
|
compiler = cxx if ext.needs_cxx else cc
|
||||||
linker = msvc.linker if iswindows else compiler
|
linker = msvc.linker if iswindows else compiler
|
||||||
objects = []
|
objects = []
|
||||||
einc = self.inc_dirs_to_cflags(ext.inc_dirs)
|
|
||||||
obj_dir = self.j(self.obj_dir, ext.name)
|
obj_dir = self.j(self.obj_dir, ext.name)
|
||||||
|
ext.preflight(obj_dir, compiler, linker, self, cflags, ldflags)
|
||||||
|
einc = self.inc_dirs_to_cflags(ext.inc_dirs)
|
||||||
if ext.needs_ddk:
|
if ext.needs_ddk:
|
||||||
ddk_flags = ['-I'+x for x in win_ddk]
|
ddk_flags = ['-I'+x for x in win_ddk]
|
||||||
cflags.extend(ddk_flags)
|
cflags.extend(ddk_flags)
|
||||||
@ -380,7 +408,7 @@ class Build(Command):
|
|||||||
dest = self.dest(ext)
|
dest = self.dest(ext)
|
||||||
elib = self.lib_dirs_to_ldflags(ext.lib_dirs)
|
elib = self.lib_dirs_to_ldflags(ext.lib_dirs)
|
||||||
xlib = self.libraries_to_ldflags(ext.libraries)
|
xlib = self.libraries_to_ldflags(ext.libraries)
|
||||||
if self.newer(dest, objects):
|
if self.newer(dest, objects+ext.extra_objs):
|
||||||
print 'Linking', ext.name
|
print 'Linking', ext.name
|
||||||
cmd = [linker]
|
cmd = [linker]
|
||||||
if iswindows:
|
if iswindows:
|
||||||
|
@ -15,8 +15,8 @@ from setup import __version__ as VERSION, __appname__ as APPNAME, basenames, \
|
|||||||
LICENSE = open('LICENSE', 'rb').read()
|
LICENSE = open('LICENSE', 'rb').read()
|
||||||
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick'
|
MAGICK_HOME='@executable_path/../Frameworks/ImageMagick'
|
||||||
ENV = dict(
|
ENV = dict(
|
||||||
FC_CONFIG_DIR='@executable_path/../Resources/fonts',
|
FONTCONFIG_PATH='@executable_path/../Resources/fonts',
|
||||||
FC_CONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf',
|
FONTCONFIG_FILE='@executable_path/../Resources/fonts/fonts.conf',
|
||||||
MAGICK_CONFIGURE_PATH=MAGICK_HOME+'/config',
|
MAGICK_CONFIGURE_PATH=MAGICK_HOME+'/config',
|
||||||
MAGICK_CODER_MODULE_PATH=MAGICK_HOME+'/modules-Q16/coders',
|
MAGICK_CODER_MODULE_PATH=MAGICK_HOME+'/modules-Q16/coders',
|
||||||
MAGICK_CODER_FILTER_PATH=MAGICK_HOME+'/modules-Q16/filter',
|
MAGICK_CODER_FILTER_PATH=MAGICK_HOME+'/modules-Q16/filter',
|
||||||
@ -379,7 +379,7 @@ class Py2App(object):
|
|||||||
@flush
|
@flush
|
||||||
def add_poppler(self):
|
def add_poppler(self):
|
||||||
info('\nAdding poppler')
|
info('\nAdding poppler')
|
||||||
for x in ('libpoppler.27.dylib',):
|
for x in ('libpoppler.28.dylib',):
|
||||||
self.install_dylib(os.path.join(SW, 'lib', x))
|
self.install_dylib(os.path.join(SW, 'lib', x))
|
||||||
for x in ('pdftohtml', 'pdftoppm', 'pdfinfo'):
|
for x in ('pdftohtml', 'pdftoppm', 'pdfinfo'):
|
||||||
self.install_dylib(os.path.join(SW, 'bin', x), False)
|
self.install_dylib(os.path.join(SW, 'bin', x), False)
|
||||||
@ -411,7 +411,6 @@ class Py2App(object):
|
|||||||
raw = open(fc, 'rb').read()
|
raw = open(fc, 'rb').read()
|
||||||
raw = raw.replace('<dir>/usr/share/fonts</dir>', '''\
|
raw = raw.replace('<dir>/usr/share/fonts</dir>', '''\
|
||||||
<dir>/Library/Fonts</dir>
|
<dir>/Library/Fonts</dir>
|
||||||
<dir>/Network/Library/Fonts</dir>
|
|
||||||
<dir>/System/Library/Fonts</dir>
|
<dir>/System/Library/Fonts</dir>
|
||||||
<dir>/usr/X11R6/lib/X11/fonts</dir>
|
<dir>/usr/X11R6/lib/X11/fonts</dir>
|
||||||
<dir>/usr/share/fonts</dir>
|
<dir>/usr/share/fonts</dir>
|
||||||
|
@ -281,8 +281,6 @@ class Win32Freeze(Command, WixMixIn):
|
|||||||
for x in ('zlib1.dll', 'libxml2.dll'):
|
for x in ('zlib1.dll', 'libxml2.dll'):
|
||||||
shutil.copy2(self.j(bindir, x+'.manifest'), self.dll_dir)
|
shutil.copy2(self.j(bindir, x+'.manifest'), self.dll_dir)
|
||||||
|
|
||||||
shutil.copytree(os.path.join(SW, 'etc', 'fonts'),
|
|
||||||
os.path.join(self.base, 'fontconfig'))
|
|
||||||
# Copy ImageMagick
|
# Copy ImageMagick
|
||||||
for pat in ('*.dll', '*.xml'):
|
for pat in ('*.dll', '*.xml'):
|
||||||
for f in glob.glob(self.j(IMAGEMAGICK, pat)):
|
for f in glob.glob(self.j(IMAGEMAGICK, pat)):
|
||||||
|
@ -276,27 +276,6 @@ cp build/kdewin32-msvc-0.3.9/build/bin/Release/*.exp lib/
|
|||||||
cp -r build/kdewin32-msvc-0.3.9/include/msvc/ include/
|
cp -r build/kdewin32-msvc-0.3.9/include/msvc/ include/
|
||||||
cp build/kdewin32-msvc-0.3.9/include/*.h include/
|
cp build/kdewin32-msvc-0.3.9/include/*.h include/
|
||||||
|
|
||||||
fontconfig
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Get it from http://www.winkde.org/pub/kde/ports/win32/repository/win32libs/
|
|
||||||
mkdir build
|
|
||||||
Remove subdirectory test from the bottom of CMakeLists.txt
|
|
||||||
run cmake
|
|
||||||
|
|
||||||
Set build type to release and project config to dll
|
|
||||||
Right click on the fontconfig project and select properties. Add sw/include/msvc to the include paths
|
|
||||||
Build only fontconfig
|
|
||||||
|
|
||||||
cp build/fontconfig-msvc-2.4.2-3/build/src/Release/*.dll bin
|
|
||||||
cp build/fontconfig-msvc-2.4.2-3/build/src/Release/*.lib lib
|
|
||||||
cp build/fontconfig-msvc-2.4.2-3/build/src/Release/*.exp lib
|
|
||||||
cp -r build/fontconfig-msvc-2.4.2-3/fontconfig/ include/
|
|
||||||
|
|
||||||
Also install the etc files from the font-config-bin archive from kde win32libs
|
|
||||||
It contains correct fonts.conf etc.
|
|
||||||
|
|
||||||
|
|
||||||
poppler
|
poppler
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
||||||
"devel@lists.alioth.debian.org>\n"
|
"devel@lists.alioth.debian.org>\n"
|
||||||
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
||||||
"PO-Revision-Date: 2012-07-23 10:54+0000\n"
|
"PO-Revision-Date: 2012-10-28 09:28+0000\n"
|
||||||
"Last-Translator: jmontane <Unknown>\n"
|
"Last-Translator: Ferran Rius <frius64@hotmail.com>\n"
|
||||||
"Language-Team: Catalan <linux@softcatala.org>\n"
|
"Language-Team: Catalan <linux@softcatala.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-07-24 04:52+0000\n"
|
"X-Launchpad-Export-Date: 2012-10-29 04:59+0000\n"
|
||||||
"X-Generator: Launchpad (build 15668)\n"
|
"X-Generator: Launchpad (build 16194)\n"
|
||||||
"Language: ca\n"
|
"Language: ca\n"
|
||||||
|
|
||||||
#. name for aaa
|
#. name for aaa
|
||||||
@ -4744,7 +4744,7 @@ msgstr "Chachi"
|
|||||||
|
|
||||||
#. name for cbj
|
#. name for cbj
|
||||||
msgid "Ede Cabe"
|
msgid "Ede Cabe"
|
||||||
msgstr "Ede Cabe"
|
msgstr "Ede;Cabe"
|
||||||
|
|
||||||
#. name for cbk
|
#. name for cbk
|
||||||
msgid "Chavacano"
|
msgid "Chavacano"
|
||||||
@ -9936,7 +9936,7 @@ msgstr "Ibani"
|
|||||||
|
|
||||||
#. name for ica
|
#. name for ica
|
||||||
msgid "Ede Ica"
|
msgid "Ede Ica"
|
||||||
msgstr "Ede Ica"
|
msgstr "Ede;Ica"
|
||||||
|
|
||||||
#. name for ich
|
#. name for ich
|
||||||
msgid "Etkywan"
|
msgid "Etkywan"
|
||||||
@ -9964,7 +9964,7 @@ msgstr "Idon"
|
|||||||
|
|
||||||
#. name for idd
|
#. name for idd
|
||||||
msgid "Ede Idaca"
|
msgid "Ede Idaca"
|
||||||
msgstr "Ede Idaca"
|
msgstr "Ede;Idaca"
|
||||||
|
|
||||||
#. name for ide
|
#. name for ide
|
||||||
msgid "Idere"
|
msgid "Idere"
|
||||||
@ -9972,7 +9972,7 @@ msgstr "Idere"
|
|||||||
|
|
||||||
#. name for idi
|
#. name for idi
|
||||||
msgid "Idi"
|
msgid "Idi"
|
||||||
msgstr ""
|
msgstr "Idi"
|
||||||
|
|
||||||
#. name for ido
|
#. name for ido
|
||||||
msgid "Ido"
|
msgid "Ido"
|
||||||
@ -10032,35 +10032,35 @@ msgstr "Ebira"
|
|||||||
|
|
||||||
#. name for ige
|
#. name for ige
|
||||||
msgid "Igede"
|
msgid "Igede"
|
||||||
msgstr ""
|
msgstr "Igede"
|
||||||
|
|
||||||
#. name for igg
|
#. name for igg
|
||||||
msgid "Igana"
|
msgid "Igana"
|
||||||
msgstr ""
|
msgstr "Igana"
|
||||||
|
|
||||||
#. name for igl
|
#. name for igl
|
||||||
msgid "Igala"
|
msgid "Igala"
|
||||||
msgstr ""
|
msgstr "Igala"
|
||||||
|
|
||||||
#. name for igm
|
#. name for igm
|
||||||
msgid "Kanggape"
|
msgid "Kanggape"
|
||||||
msgstr ""
|
msgstr "Igom"
|
||||||
|
|
||||||
#. name for ign
|
#. name for ign
|
||||||
msgid "Ignaciano"
|
msgid "Ignaciano"
|
||||||
msgstr ""
|
msgstr "Ignaciano"
|
||||||
|
|
||||||
#. name for igo
|
#. name for igo
|
||||||
msgid "Isebe"
|
msgid "Isebe"
|
||||||
msgstr ""
|
msgstr "Isebe"
|
||||||
|
|
||||||
#. name for igs
|
#. name for igs
|
||||||
msgid "Interglossa"
|
msgid "Interglossa"
|
||||||
msgstr ""
|
msgstr "Interglossa"
|
||||||
|
|
||||||
#. name for igw
|
#. name for igw
|
||||||
msgid "Igwe"
|
msgid "Igwe"
|
||||||
msgstr ""
|
msgstr "Igwe"
|
||||||
|
|
||||||
#. name for ihb
|
#. name for ihb
|
||||||
msgid "Iha Based Pidgin"
|
msgid "Iha Based Pidgin"
|
||||||
@ -10068,175 +10068,175 @@ msgstr "Iha; parla mixta"
|
|||||||
|
|
||||||
#. name for ihi
|
#. name for ihi
|
||||||
msgid "Ihievbe"
|
msgid "Ihievbe"
|
||||||
msgstr ""
|
msgstr "Ihievbe"
|
||||||
|
|
||||||
#. name for ihp
|
#. name for ihp
|
||||||
msgid "Iha"
|
msgid "Iha"
|
||||||
msgstr ""
|
msgstr "Iha"
|
||||||
|
|
||||||
#. name for iii
|
#. name for iii
|
||||||
msgid "Yi; Sichuan"
|
msgid "Yi; Sichuan"
|
||||||
msgstr ""
|
msgstr "Yi; Sichuan"
|
||||||
|
|
||||||
#. name for ijc
|
#. name for ijc
|
||||||
msgid "Izon"
|
msgid "Izon"
|
||||||
msgstr ""
|
msgstr "Izon"
|
||||||
|
|
||||||
#. name for ije
|
#. name for ije
|
||||||
msgid "Biseni"
|
msgid "Biseni"
|
||||||
msgstr ""
|
msgstr "Biseni"
|
||||||
|
|
||||||
#. name for ijj
|
#. name for ijj
|
||||||
msgid "Ede Ije"
|
msgid "Ede Ije"
|
||||||
msgstr ""
|
msgstr "Ede;Ije"
|
||||||
|
|
||||||
#. name for ijn
|
#. name for ijn
|
||||||
msgid "Kalabari"
|
msgid "Kalabari"
|
||||||
msgstr ""
|
msgstr "Kalabari"
|
||||||
|
|
||||||
#. name for ijs
|
#. name for ijs
|
||||||
msgid "Ijo; Southeast"
|
msgid "Ijo; Southeast"
|
||||||
msgstr ""
|
msgstr "Izon;Ijo"
|
||||||
|
|
||||||
#. name for ike
|
#. name for ike
|
||||||
msgid "Inuktitut; Eastern Canadian"
|
msgid "Inuktitut; Eastern Canadian"
|
||||||
msgstr ""
|
msgstr "Inuktitut; Canadà oriental"
|
||||||
|
|
||||||
#. name for iki
|
#. name for iki
|
||||||
msgid "Iko"
|
msgid "Iko"
|
||||||
msgstr ""
|
msgstr "Iko"
|
||||||
|
|
||||||
#. name for ikk
|
#. name for ikk
|
||||||
msgid "Ika"
|
msgid "Ika"
|
||||||
msgstr ""
|
msgstr "Ika"
|
||||||
|
|
||||||
#. name for ikl
|
#. name for ikl
|
||||||
msgid "Ikulu"
|
msgid "Ikulu"
|
||||||
msgstr ""
|
msgstr "Ikulu"
|
||||||
|
|
||||||
#. name for iko
|
#. name for iko
|
||||||
msgid "Olulumo-Ikom"
|
msgid "Olulumo-Ikom"
|
||||||
msgstr ""
|
msgstr "Olulumo-Ikom"
|
||||||
|
|
||||||
#. name for ikp
|
#. name for ikp
|
||||||
msgid "Ikpeshi"
|
msgid "Ikpeshi"
|
||||||
msgstr ""
|
msgstr "Ikpeshi"
|
||||||
|
|
||||||
#. name for ikt
|
#. name for ikt
|
||||||
msgid "Inuktitut; Western Canadian"
|
msgid "Inuktitut; Western Canadian"
|
||||||
msgstr ""
|
msgstr "Inuktitut; Canadà occidental"
|
||||||
|
|
||||||
#. name for iku
|
#. name for iku
|
||||||
msgid "Inuktitut"
|
msgid "Inuktitut"
|
||||||
msgstr "inuktitut"
|
msgstr "Inuktitut"
|
||||||
|
|
||||||
#. name for ikv
|
#. name for ikv
|
||||||
msgid "Iku-Gora-Ankwa"
|
msgid "Iku-Gora-Ankwa"
|
||||||
msgstr ""
|
msgstr "Iku"
|
||||||
|
|
||||||
#. name for ikw
|
#. name for ikw
|
||||||
msgid "Ikwere"
|
msgid "Ikwere"
|
||||||
msgstr ""
|
msgstr "Ikwere"
|
||||||
|
|
||||||
#. name for ikx
|
#. name for ikx
|
||||||
msgid "Ik"
|
msgid "Ik"
|
||||||
msgstr ""
|
msgstr "Ik"
|
||||||
|
|
||||||
#. name for ikz
|
#. name for ikz
|
||||||
msgid "Ikizu"
|
msgid "Ikizu"
|
||||||
msgstr ""
|
msgstr "Ikizu"
|
||||||
|
|
||||||
#. name for ila
|
#. name for ila
|
||||||
msgid "Ile Ape"
|
msgid "Ile Ape"
|
||||||
msgstr ""
|
msgstr "Ile Ape"
|
||||||
|
|
||||||
#. name for ilb
|
#. name for ilb
|
||||||
msgid "Ila"
|
msgid "Ila"
|
||||||
msgstr ""
|
msgstr "Ila"
|
||||||
|
|
||||||
#. name for ile
|
#. name for ile
|
||||||
msgid "Interlingue"
|
msgid "Interlingue"
|
||||||
msgstr ""
|
msgstr "Interlingue"
|
||||||
|
|
||||||
#. name for ilg
|
#. name for ilg
|
||||||
msgid "Garig-Ilgar"
|
msgid "Garig-Ilgar"
|
||||||
msgstr ""
|
msgstr "Garig-Ilgar"
|
||||||
|
|
||||||
#. name for ili
|
#. name for ili
|
||||||
msgid "Ili Turki"
|
msgid "Ili Turki"
|
||||||
msgstr ""
|
msgstr "Ili turc"
|
||||||
|
|
||||||
#. name for ilk
|
#. name for ilk
|
||||||
msgid "Ilongot"
|
msgid "Ilongot"
|
||||||
msgstr ""
|
msgstr "Ilong"
|
||||||
|
|
||||||
#. name for ill
|
#. name for ill
|
||||||
msgid "Iranun"
|
msgid "Iranun"
|
||||||
msgstr ""
|
msgstr "Iranun"
|
||||||
|
|
||||||
#. name for ilo
|
#. name for ilo
|
||||||
msgid "Iloko"
|
msgid "Iloko"
|
||||||
msgstr ""
|
msgstr "Ilocà"
|
||||||
|
|
||||||
#. name for ils
|
#. name for ils
|
||||||
msgid "International Sign"
|
msgid "International Sign"
|
||||||
msgstr ""
|
msgstr "Signes internacional"
|
||||||
|
|
||||||
#. name for ilu
|
#. name for ilu
|
||||||
msgid "Ili'uun"
|
msgid "Ili'uun"
|
||||||
msgstr ""
|
msgstr "Iliun"
|
||||||
|
|
||||||
#. name for ilv
|
#. name for ilv
|
||||||
msgid "Ilue"
|
msgid "Ilue"
|
||||||
msgstr ""
|
msgstr "Ilue"
|
||||||
|
|
||||||
#. name for ilw
|
#. name for ilw
|
||||||
msgid "Talur"
|
msgid "Talur"
|
||||||
msgstr ""
|
msgstr "Talur"
|
||||||
|
|
||||||
#. name for ima
|
#. name for ima
|
||||||
msgid "Malasar; Mala"
|
msgid "Malasar; Mala"
|
||||||
msgstr ""
|
msgstr "Malasar; Mala"
|
||||||
|
|
||||||
#. name for ime
|
#. name for ime
|
||||||
msgid "Imeraguen"
|
msgid "Imeraguen"
|
||||||
msgstr ""
|
msgstr "Imeraguen"
|
||||||
|
|
||||||
#. name for imi
|
#. name for imi
|
||||||
msgid "Anamgura"
|
msgid "Anamgura"
|
||||||
msgstr ""
|
msgstr "Anamgura"
|
||||||
|
|
||||||
#. name for iml
|
#. name for iml
|
||||||
msgid "Miluk"
|
msgid "Miluk"
|
||||||
msgstr ""
|
msgstr "Miluk"
|
||||||
|
|
||||||
#. name for imn
|
#. name for imn
|
||||||
msgid "Imonda"
|
msgid "Imonda"
|
||||||
msgstr ""
|
msgstr "Imonda"
|
||||||
|
|
||||||
#. name for imo
|
#. name for imo
|
||||||
msgid "Imbongu"
|
msgid "Imbongu"
|
||||||
msgstr ""
|
msgstr "Imbongu"
|
||||||
|
|
||||||
#. name for imr
|
#. name for imr
|
||||||
msgid "Imroing"
|
msgid "Imroing"
|
||||||
msgstr ""
|
msgstr "Imroing"
|
||||||
|
|
||||||
#. name for ims
|
#. name for ims
|
||||||
msgid "Marsian"
|
msgid "Marsian"
|
||||||
msgstr ""
|
msgstr "Marsià"
|
||||||
|
|
||||||
#. name for imy
|
#. name for imy
|
||||||
msgid "Milyan"
|
msgid "Milyan"
|
||||||
msgstr ""
|
msgstr "Mili"
|
||||||
|
|
||||||
#. name for ina
|
#. name for ina
|
||||||
msgid "Interlingua (International Auxiliary Language Association)"
|
msgid "Interlingua (International Auxiliary Language Association)"
|
||||||
msgstr ""
|
msgstr "Interlingua"
|
||||||
|
|
||||||
#. name for inb
|
#. name for inb
|
||||||
msgid "Inga"
|
msgid "Inga"
|
||||||
msgstr ""
|
msgstr "Inga"
|
||||||
|
|
||||||
#. name for ind
|
#. name for ind
|
||||||
msgid "Indonesian"
|
msgid "Indonesian"
|
||||||
@ -10244,15 +10244,15 @@ msgstr "Indonesi"
|
|||||||
|
|
||||||
#. name for ing
|
#. name for ing
|
||||||
msgid "Degexit'an"
|
msgid "Degexit'an"
|
||||||
msgstr ""
|
msgstr "Degexitan"
|
||||||
|
|
||||||
#. name for inh
|
#. name for inh
|
||||||
msgid "Ingush"
|
msgid "Ingush"
|
||||||
msgstr ""
|
msgstr "Ingúix"
|
||||||
|
|
||||||
#. name for inj
|
#. name for inj
|
||||||
msgid "Inga; Jungle"
|
msgid "Inga; Jungle"
|
||||||
msgstr ""
|
msgstr "Inga; Jungla"
|
||||||
|
|
||||||
#. name for inl
|
#. name for inl
|
||||||
msgid "Indonesian Sign Language"
|
msgid "Indonesian Sign Language"
|
||||||
@ -10260,19 +10260,19 @@ msgstr "Llenguatge de signes indonesi"
|
|||||||
|
|
||||||
#. name for inm
|
#. name for inm
|
||||||
msgid "Minaean"
|
msgid "Minaean"
|
||||||
msgstr ""
|
msgstr "Minaeà"
|
||||||
|
|
||||||
#. name for inn
|
#. name for inn
|
||||||
msgid "Isinai"
|
msgid "Isinai"
|
||||||
msgstr ""
|
msgstr "Isinai"
|
||||||
|
|
||||||
#. name for ino
|
#. name for ino
|
||||||
msgid "Inoke-Yate"
|
msgid "Inoke-Yate"
|
||||||
msgstr ""
|
msgstr "Inoke-Yate"
|
||||||
|
|
||||||
#. name for inp
|
#. name for inp
|
||||||
msgid "Iñapari"
|
msgid "Iñapari"
|
||||||
msgstr ""
|
msgstr "Iñapari"
|
||||||
|
|
||||||
#. name for ins
|
#. name for ins
|
||||||
msgid "Indian Sign Language"
|
msgid "Indian Sign Language"
|
||||||
@ -10280,27 +10280,27 @@ msgstr "Llenguatge de signes indi"
|
|||||||
|
|
||||||
#. name for int
|
#. name for int
|
||||||
msgid "Intha"
|
msgid "Intha"
|
||||||
msgstr ""
|
msgstr "Intha"
|
||||||
|
|
||||||
#. name for inz
|
#. name for inz
|
||||||
msgid "Ineseño"
|
msgid "Ineseño"
|
||||||
msgstr ""
|
msgstr "Ineseño"
|
||||||
|
|
||||||
#. name for ior
|
#. name for ior
|
||||||
msgid "Inor"
|
msgid "Inor"
|
||||||
msgstr ""
|
msgstr "Inor"
|
||||||
|
|
||||||
#. name for iou
|
#. name for iou
|
||||||
msgid "Tuma-Irumu"
|
msgid "Tuma-Irumu"
|
||||||
msgstr ""
|
msgstr "Tuma-Irumu"
|
||||||
|
|
||||||
#. name for iow
|
#. name for iow
|
||||||
msgid "Iowa-Oto"
|
msgid "Iowa-Oto"
|
||||||
msgstr ""
|
msgstr "Iowa-Oto"
|
||||||
|
|
||||||
#. name for ipi
|
#. name for ipi
|
||||||
msgid "Ipili"
|
msgid "Ipili"
|
||||||
msgstr ""
|
msgstr "Ipili"
|
||||||
|
|
||||||
#. name for ipk
|
#. name for ipk
|
||||||
msgid "Inupiaq"
|
msgid "Inupiaq"
|
||||||
@ -10308,59 +10308,59 @@ msgstr "inupiak"
|
|||||||
|
|
||||||
#. name for ipo
|
#. name for ipo
|
||||||
msgid "Ipiko"
|
msgid "Ipiko"
|
||||||
msgstr ""
|
msgstr "Ipiko"
|
||||||
|
|
||||||
#. name for iqu
|
#. name for iqu
|
||||||
msgid "Iquito"
|
msgid "Iquito"
|
||||||
msgstr ""
|
msgstr "Iquito"
|
||||||
|
|
||||||
#. name for ire
|
#. name for ire
|
||||||
msgid "Iresim"
|
msgid "Iresim"
|
||||||
msgstr ""
|
msgstr "Iresim"
|
||||||
|
|
||||||
#. name for irh
|
#. name for irh
|
||||||
msgid "Irarutu"
|
msgid "Irarutu"
|
||||||
msgstr ""
|
msgstr "Irarutu"
|
||||||
|
|
||||||
#. name for iri
|
#. name for iri
|
||||||
msgid "Irigwe"
|
msgid "Irigwe"
|
||||||
msgstr ""
|
msgstr "Irigwe"
|
||||||
|
|
||||||
#. name for irk
|
#. name for irk
|
||||||
msgid "Iraqw"
|
msgid "Iraqw"
|
||||||
msgstr ""
|
msgstr "Iraqw"
|
||||||
|
|
||||||
#. name for irn
|
#. name for irn
|
||||||
msgid "Irántxe"
|
msgid "Irántxe"
|
||||||
msgstr ""
|
msgstr "Iranxe"
|
||||||
|
|
||||||
#. name for irr
|
#. name for irr
|
||||||
msgid "Ir"
|
msgid "Ir"
|
||||||
msgstr ""
|
msgstr "Ir"
|
||||||
|
|
||||||
#. name for iru
|
#. name for iru
|
||||||
msgid "Irula"
|
msgid "Irula"
|
||||||
msgstr ""
|
msgstr "Irula"
|
||||||
|
|
||||||
#. name for irx
|
#. name for irx
|
||||||
msgid "Kamberau"
|
msgid "Kamberau"
|
||||||
msgstr ""
|
msgstr "Kamberau"
|
||||||
|
|
||||||
#. name for iry
|
#. name for iry
|
||||||
msgid "Iraya"
|
msgid "Iraya"
|
||||||
msgstr ""
|
msgstr "Iraya"
|
||||||
|
|
||||||
#. name for isa
|
#. name for isa
|
||||||
msgid "Isabi"
|
msgid "Isabi"
|
||||||
msgstr ""
|
msgstr "Isabi"
|
||||||
|
|
||||||
#. name for isc
|
#. name for isc
|
||||||
msgid "Isconahua"
|
msgid "Isconahua"
|
||||||
msgstr ""
|
msgstr "Isconahua"
|
||||||
|
|
||||||
#. name for isd
|
#. name for isd
|
||||||
msgid "Isnag"
|
msgid "Isnag"
|
||||||
msgstr ""
|
msgstr "Isneg"
|
||||||
|
|
||||||
#. name for ise
|
#. name for ise
|
||||||
msgid "Italian Sign Language"
|
msgid "Italian Sign Language"
|
||||||
@ -10372,15 +10372,15 @@ msgstr "Llenguatge de signes irlandès"
|
|||||||
|
|
||||||
#. name for ish
|
#. name for ish
|
||||||
msgid "Esan"
|
msgid "Esan"
|
||||||
msgstr ""
|
msgstr "Esan"
|
||||||
|
|
||||||
#. name for isi
|
#. name for isi
|
||||||
msgid "Nkem-Nkum"
|
msgid "Nkem-Nkum"
|
||||||
msgstr ""
|
msgstr "Nkem-Nkum"
|
||||||
|
|
||||||
#. name for isk
|
#. name for isk
|
||||||
msgid "Ishkashimi"
|
msgid "Ishkashimi"
|
||||||
msgstr ""
|
msgstr "Ishkashimi"
|
||||||
|
|
||||||
#. name for isl
|
#. name for isl
|
||||||
msgid "Icelandic"
|
msgid "Icelandic"
|
||||||
@ -10388,15 +10388,15 @@ msgstr "Islandès"
|
|||||||
|
|
||||||
#. name for ism
|
#. name for ism
|
||||||
msgid "Masimasi"
|
msgid "Masimasi"
|
||||||
msgstr ""
|
msgstr "Masimasi"
|
||||||
|
|
||||||
#. name for isn
|
#. name for isn
|
||||||
msgid "Isanzu"
|
msgid "Isanzu"
|
||||||
msgstr ""
|
msgstr "Isanzu"
|
||||||
|
|
||||||
#. name for iso
|
#. name for iso
|
||||||
msgid "Isoko"
|
msgid "Isoko"
|
||||||
msgstr ""
|
msgstr "Isoco"
|
||||||
|
|
||||||
#. name for isr
|
#. name for isr
|
||||||
msgid "Israeli Sign Language"
|
msgid "Israeli Sign Language"
|
||||||
@ -10404,11 +10404,11 @@ msgstr "Llenguatge de signes israelià"
|
|||||||
|
|
||||||
#. name for ist
|
#. name for ist
|
||||||
msgid "Istriot"
|
msgid "Istriot"
|
||||||
msgstr ""
|
msgstr "Istri"
|
||||||
|
|
||||||
#. name for isu
|
#. name for isu
|
||||||
msgid "Isu (Menchum Division)"
|
msgid "Isu (Menchum Division)"
|
||||||
msgstr ""
|
msgstr "Isu (Divisió de Menchum)"
|
||||||
|
|
||||||
#. name for ita
|
#. name for ita
|
||||||
msgid "Italian"
|
msgid "Italian"
|
||||||
@ -10416,15 +10416,15 @@ msgstr "Italià"
|
|||||||
|
|
||||||
#. name for itb
|
#. name for itb
|
||||||
msgid "Itneg; Binongan"
|
msgid "Itneg; Binongan"
|
||||||
msgstr ""
|
msgstr "Itneg; Binongan"
|
||||||
|
|
||||||
#. name for ite
|
#. name for ite
|
||||||
msgid "Itene"
|
msgid "Itene"
|
||||||
msgstr ""
|
msgstr "Itene"
|
||||||
|
|
||||||
#. name for iti
|
#. name for iti
|
||||||
msgid "Itneg; Inlaod"
|
msgid "Itneg; Inlaod"
|
||||||
msgstr ""
|
msgstr "Itneg; Inlaod"
|
||||||
|
|
||||||
#. name for itk
|
#. name for itk
|
||||||
msgid "Judeo-Italian"
|
msgid "Judeo-Italian"
|
||||||
@ -10432,147 +10432,147 @@ msgstr "Judeo-italià"
|
|||||||
|
|
||||||
#. name for itl
|
#. name for itl
|
||||||
msgid "Itelmen"
|
msgid "Itelmen"
|
||||||
msgstr ""
|
msgstr "Itelmen"
|
||||||
|
|
||||||
#. name for itm
|
#. name for itm
|
||||||
msgid "Itu Mbon Uzo"
|
msgid "Itu Mbon Uzo"
|
||||||
msgstr ""
|
msgstr "Itu Mbon Uzo"
|
||||||
|
|
||||||
#. name for ito
|
#. name for ito
|
||||||
msgid "Itonama"
|
msgid "Itonama"
|
||||||
msgstr ""
|
msgstr "Itonama"
|
||||||
|
|
||||||
#. name for itr
|
#. name for itr
|
||||||
msgid "Iteri"
|
msgid "Iteri"
|
||||||
msgstr ""
|
msgstr "Iteri"
|
||||||
|
|
||||||
#. name for its
|
#. name for its
|
||||||
msgid "Isekiri"
|
msgid "Isekiri"
|
||||||
msgstr ""
|
msgstr "Isekiri"
|
||||||
|
|
||||||
#. name for itt
|
#. name for itt
|
||||||
msgid "Itneg; Maeng"
|
msgid "Itneg; Maeng"
|
||||||
msgstr ""
|
msgstr "Itneg; Maeng"
|
||||||
|
|
||||||
#. name for itv
|
#. name for itv
|
||||||
msgid "Itawit"
|
msgid "Itawit"
|
||||||
msgstr ""
|
msgstr "Itawit"
|
||||||
|
|
||||||
#. name for itw
|
#. name for itw
|
||||||
msgid "Ito"
|
msgid "Ito"
|
||||||
msgstr ""
|
msgstr "Ito"
|
||||||
|
|
||||||
#. name for itx
|
#. name for itx
|
||||||
msgid "Itik"
|
msgid "Itik"
|
||||||
msgstr ""
|
msgstr "Itik"
|
||||||
|
|
||||||
#. name for ity
|
#. name for ity
|
||||||
msgid "Itneg; Moyadan"
|
msgid "Itneg; Moyadan"
|
||||||
msgstr ""
|
msgstr "Itneg; Moyadan"
|
||||||
|
|
||||||
#. name for itz
|
#. name for itz
|
||||||
msgid "Itzá"
|
msgid "Itzá"
|
||||||
msgstr ""
|
msgstr "Itzà"
|
||||||
|
|
||||||
#. name for ium
|
#. name for ium
|
||||||
msgid "Mien; Iu"
|
msgid "Mien; Iu"
|
||||||
msgstr ""
|
msgstr "Mien; Iu"
|
||||||
|
|
||||||
#. name for ivb
|
#. name for ivb
|
||||||
msgid "Ibatan"
|
msgid "Ibatan"
|
||||||
msgstr ""
|
msgstr "Ibatan"
|
||||||
|
|
||||||
#. name for ivv
|
#. name for ivv
|
||||||
msgid "Ivatan"
|
msgid "Ivatan"
|
||||||
msgstr ""
|
msgstr "Ivatan"
|
||||||
|
|
||||||
#. name for iwk
|
#. name for iwk
|
||||||
msgid "I-Wak"
|
msgid "I-Wak"
|
||||||
msgstr ""
|
msgstr "Iwaak"
|
||||||
|
|
||||||
#. name for iwm
|
#. name for iwm
|
||||||
msgid "Iwam"
|
msgid "Iwam"
|
||||||
msgstr ""
|
msgstr "Iwam"
|
||||||
|
|
||||||
#. name for iwo
|
#. name for iwo
|
||||||
msgid "Iwur"
|
msgid "Iwur"
|
||||||
msgstr ""
|
msgstr "Iwur"
|
||||||
|
|
||||||
#. name for iws
|
#. name for iws
|
||||||
msgid "Iwam; Sepik"
|
msgid "Iwam; Sepik"
|
||||||
msgstr ""
|
msgstr "Iwam; Sepik"
|
||||||
|
|
||||||
#. name for ixc
|
#. name for ixc
|
||||||
msgid "Ixcatec"
|
msgid "Ixcatec"
|
||||||
msgstr ""
|
msgstr "Ixcatec"
|
||||||
|
|
||||||
#. name for ixl
|
#. name for ixl
|
||||||
msgid "Ixil"
|
msgid "Ixil"
|
||||||
msgstr ""
|
msgstr "Ixil"
|
||||||
|
|
||||||
#. name for iya
|
#. name for iya
|
||||||
msgid "Iyayu"
|
msgid "Iyayu"
|
||||||
msgstr ""
|
msgstr "Iyayu"
|
||||||
|
|
||||||
#. name for iyo
|
#. name for iyo
|
||||||
msgid "Mesaka"
|
msgid "Mesaka"
|
||||||
msgstr ""
|
msgstr "Mesaka"
|
||||||
|
|
||||||
#. name for iyx
|
#. name for iyx
|
||||||
msgid "Yaka (Congo)"
|
msgid "Yaka (Congo)"
|
||||||
msgstr ""
|
msgstr "Yaka (Congo)"
|
||||||
|
|
||||||
#. name for izh
|
#. name for izh
|
||||||
msgid "Ingrian"
|
msgid "Ingrian"
|
||||||
msgstr ""
|
msgstr "Ingri"
|
||||||
|
|
||||||
#. name for izi
|
#. name for izi
|
||||||
msgid "Izi-Ezaa-Ikwo-Mgbo"
|
msgid "Izi-Ezaa-Ikwo-Mgbo"
|
||||||
msgstr ""
|
msgstr "Izi-Ezaa-Ikwo-Mgbo"
|
||||||
|
|
||||||
#. name for izr
|
#. name for izr
|
||||||
msgid "Izere"
|
msgid "Izere"
|
||||||
msgstr ""
|
msgstr "Izere"
|
||||||
|
|
||||||
#. name for jaa
|
#. name for jaa
|
||||||
msgid "Jamamadí"
|
msgid "Jamamadí"
|
||||||
msgstr ""
|
msgstr "Jamamadí"
|
||||||
|
|
||||||
#. name for jab
|
#. name for jab
|
||||||
msgid "Hyam"
|
msgid "Hyam"
|
||||||
msgstr ""
|
msgstr "Ham"
|
||||||
|
|
||||||
#. name for jac
|
#. name for jac
|
||||||
msgid "Popti'"
|
msgid "Popti'"
|
||||||
msgstr ""
|
msgstr "Jacaltec"
|
||||||
|
|
||||||
#. name for jad
|
#. name for jad
|
||||||
msgid "Jahanka"
|
msgid "Jahanka"
|
||||||
msgstr ""
|
msgstr "Jahanka"
|
||||||
|
|
||||||
#. name for jae
|
#. name for jae
|
||||||
msgid "Yabem"
|
msgid "Yabem"
|
||||||
msgstr ""
|
msgstr "Yabem"
|
||||||
|
|
||||||
#. name for jaf
|
#. name for jaf
|
||||||
msgid "Jara"
|
msgid "Jara"
|
||||||
msgstr ""
|
msgstr "Jara"
|
||||||
|
|
||||||
#. name for jah
|
#. name for jah
|
||||||
msgid "Jah Hut"
|
msgid "Jah Hut"
|
||||||
msgstr ""
|
msgstr "Jah Hut"
|
||||||
|
|
||||||
#. name for jaj
|
#. name for jaj
|
||||||
msgid "Zazao"
|
msgid "Zazao"
|
||||||
msgstr ""
|
msgstr "Zazao"
|
||||||
|
|
||||||
#. name for jak
|
#. name for jak
|
||||||
msgid "Jakun"
|
msgid "Jakun"
|
||||||
msgstr ""
|
msgstr "Jakun"
|
||||||
|
|
||||||
#. name for jal
|
#. name for jal
|
||||||
msgid "Yalahatan"
|
msgid "Yalahatan"
|
||||||
msgstr ""
|
msgstr "Jalahatan"
|
||||||
|
|
||||||
#. name for jam
|
#. name for jam
|
||||||
msgid "Creole English; Jamaican"
|
msgid "Creole English; Jamaican"
|
||||||
@ -10580,31 +10580,31 @@ msgstr "Anglès crioll; Jamaica"
|
|||||||
|
|
||||||
#. name for jao
|
#. name for jao
|
||||||
msgid "Yanyuwa"
|
msgid "Yanyuwa"
|
||||||
msgstr ""
|
msgstr "Yanyula"
|
||||||
|
|
||||||
#. name for jaq
|
#. name for jaq
|
||||||
msgid "Yaqay"
|
msgid "Yaqay"
|
||||||
msgstr ""
|
msgstr "Yaqay"
|
||||||
|
|
||||||
#. name for jar
|
#. name for jar
|
||||||
msgid "Jarawa (Nigeria)"
|
msgid "Jarawa (Nigeria)"
|
||||||
msgstr ""
|
msgstr "Jarawa (Nigèria)"
|
||||||
|
|
||||||
#. name for jas
|
#. name for jas
|
||||||
msgid "Javanese; New Caledonian"
|
msgid "Javanese; New Caledonian"
|
||||||
msgstr ""
|
msgstr "Javanès; Nova Caledònia"
|
||||||
|
|
||||||
#. name for jat
|
#. name for jat
|
||||||
msgid "Jakati"
|
msgid "Jakati"
|
||||||
msgstr ""
|
msgstr "Jakati"
|
||||||
|
|
||||||
#. name for jau
|
#. name for jau
|
||||||
msgid "Yaur"
|
msgid "Yaur"
|
||||||
msgstr ""
|
msgstr "Yaur"
|
||||||
|
|
||||||
#. name for jav
|
#. name for jav
|
||||||
msgid "Javanese"
|
msgid "Javanese"
|
||||||
msgstr ""
|
msgstr "Javanès"
|
||||||
|
|
||||||
#. name for jax
|
#. name for jax
|
||||||
msgid "Malay; Jambi"
|
msgid "Malay; Jambi"
|
||||||
@ -10612,11 +10612,11 @@ msgstr "Malai; Jambi"
|
|||||||
|
|
||||||
#. name for jay
|
#. name for jay
|
||||||
msgid "Yan-nhangu"
|
msgid "Yan-nhangu"
|
||||||
msgstr ""
|
msgstr "Jarnango"
|
||||||
|
|
||||||
#. name for jaz
|
#. name for jaz
|
||||||
msgid "Jawe"
|
msgid "Jawe"
|
||||||
msgstr ""
|
msgstr "Jawe"
|
||||||
|
|
||||||
#. name for jbe
|
#. name for jbe
|
||||||
msgid "Judeo-Berber"
|
msgid "Judeo-Berber"
|
||||||
@ -10624,27 +10624,27 @@ msgstr "Judeo-berber"
|
|||||||
|
|
||||||
#. name for jbj
|
#. name for jbj
|
||||||
msgid "Arandai"
|
msgid "Arandai"
|
||||||
msgstr ""
|
msgstr "Arandai"
|
||||||
|
|
||||||
#. name for jbn
|
#. name for jbn
|
||||||
msgid "Nafusi"
|
msgid "Nafusi"
|
||||||
msgstr ""
|
msgstr "Djerbi"
|
||||||
|
|
||||||
#. name for jbo
|
#. name for jbo
|
||||||
msgid "Lojban"
|
msgid "Lojban"
|
||||||
msgstr ""
|
msgstr "Lojban"
|
||||||
|
|
||||||
#. name for jbr
|
#. name for jbr
|
||||||
msgid "Jofotek-Bromnya"
|
msgid "Jofotek-Bromnya"
|
||||||
msgstr ""
|
msgstr "Jofotek-Bromnya"
|
||||||
|
|
||||||
#. name for jbt
|
#. name for jbt
|
||||||
msgid "Jabutí"
|
msgid "Jabutí"
|
||||||
msgstr ""
|
msgstr "Jaboti"
|
||||||
|
|
||||||
#. name for jbu
|
#. name for jbu
|
||||||
msgid "Jukun Takum"
|
msgid "Jukun Takum"
|
||||||
msgstr ""
|
msgstr "Jukun Takum"
|
||||||
|
|
||||||
#. name for jcs
|
#. name for jcs
|
||||||
msgid "Jamaican Country Sign Language"
|
msgid "Jamaican Country Sign Language"
|
||||||
@ -10652,31 +10652,31 @@ msgstr "Llenguatge de signes del país jamaicà"
|
|||||||
|
|
||||||
#. name for jct
|
#. name for jct
|
||||||
msgid "Krymchak"
|
msgid "Krymchak"
|
||||||
msgstr ""
|
msgstr "Judeocrimeà"
|
||||||
|
|
||||||
#. name for jda
|
#. name for jda
|
||||||
msgid "Jad"
|
msgid "Jad"
|
||||||
msgstr ""
|
msgstr "Jad"
|
||||||
|
|
||||||
#. name for jdg
|
#. name for jdg
|
||||||
msgid "Jadgali"
|
msgid "Jadgali"
|
||||||
msgstr ""
|
msgstr "Jadgali"
|
||||||
|
|
||||||
#. name for jdt
|
#. name for jdt
|
||||||
msgid "Judeo-Tat"
|
msgid "Judeo-Tat"
|
||||||
msgstr ""
|
msgstr "Judeotat"
|
||||||
|
|
||||||
#. name for jeb
|
#. name for jeb
|
||||||
msgid "Jebero"
|
msgid "Jebero"
|
||||||
msgstr ""
|
msgstr "Jebero"
|
||||||
|
|
||||||
#. name for jee
|
#. name for jee
|
||||||
msgid "Jerung"
|
msgid "Jerung"
|
||||||
msgstr ""
|
msgstr "Jerung"
|
||||||
|
|
||||||
#. name for jeg
|
#. name for jeg
|
||||||
msgid "Jeng"
|
msgid "Jeng"
|
||||||
msgstr ""
|
msgstr "Jeng"
|
||||||
|
|
||||||
#. name for jeh
|
#. name for jeh
|
||||||
msgid "Jeh"
|
msgid "Jeh"
|
||||||
@ -11012,7 +11012,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for jvn
|
#. name for jvn
|
||||||
msgid "Javanese; Caribbean"
|
msgid "Javanese; Caribbean"
|
||||||
msgstr ""
|
msgstr "Javanès; Carib"
|
||||||
|
|
||||||
#. name for jwi
|
#. name for jwi
|
||||||
msgid "Jwira-Pepesa"
|
msgid "Jwira-Pepesa"
|
||||||
@ -18960,7 +18960,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for nqk
|
#. name for nqk
|
||||||
msgid "Ede Nago; Kura"
|
msgid "Ede Nago; Kura"
|
||||||
msgstr ""
|
msgstr "Ede;Nago Kura"
|
||||||
|
|
||||||
#. name for nqm
|
#. name for nqm
|
||||||
msgid "Ndom"
|
msgid "Ndom"
|
||||||
@ -25172,7 +25172,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for tis
|
#. name for tis
|
||||||
msgid "Itneg; Masadiit"
|
msgid "Itneg; Masadiit"
|
||||||
msgstr ""
|
msgstr "Itneg; Masadiit"
|
||||||
|
|
||||||
#. name for tit
|
#. name for tit
|
||||||
msgid "Tinigua"
|
msgid "Tinigua"
|
||||||
@ -29604,7 +29604,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for yix
|
#. name for yix
|
||||||
msgid "Yi; Axi"
|
msgid "Yi; Axi"
|
||||||
msgstr ""
|
msgstr "Yi; Axi"
|
||||||
|
|
||||||
#. name for yiy
|
#. name for yiy
|
||||||
msgid "Yir Yoront"
|
msgid "Yir Yoront"
|
||||||
@ -29688,7 +29688,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for ylo
|
#. name for ylo
|
||||||
msgid "Yi; Naluo"
|
msgid "Yi; Naluo"
|
||||||
msgstr ""
|
msgstr "Yi; Naluo"
|
||||||
|
|
||||||
#. name for ylr
|
#. name for ylr
|
||||||
msgid "Yalarnnga"
|
msgid "Yalarnnga"
|
||||||
@ -29764,7 +29764,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for ymr
|
#. name for ymr
|
||||||
msgid "Malasar"
|
msgid "Malasar"
|
||||||
msgstr ""
|
msgstr "Malasar"
|
||||||
|
|
||||||
#. name for yms
|
#. name for yms
|
||||||
msgid "Mysian"
|
msgid "Mysian"
|
||||||
@ -30104,7 +30104,7 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for ywq
|
#. name for ywq
|
||||||
msgid "Yi; Wuding-Luquan"
|
msgid "Yi; Wuding-Luquan"
|
||||||
msgstr ""
|
msgstr "Yi; Wuding-luqua"
|
||||||
|
|
||||||
#. name for ywr
|
#. name for ywr
|
||||||
msgid "Yawuru"
|
msgid "Yawuru"
|
||||||
|
@ -9,14 +9,14 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
||||||
"devel@lists.alioth.debian.org>\n"
|
"devel@lists.alioth.debian.org>\n"
|
||||||
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
||||||
"PO-Revision-Date: 2012-04-18 13:08+0000\n"
|
"PO-Revision-Date: 2012-10-29 14:16+0000\n"
|
||||||
"Last-Translator: Asier Iturralde Sarasola <Unknown>\n"
|
"Last-Translator: gorkaazk <gorkaazkarate@euskalerria.org>\n"
|
||||||
"Language-Team: Euskara <itzulpena@comtropos.com>\n"
|
"Language-Team: Euskara <itzulpena@comtropos.com>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-04-19 04:36+0000\n"
|
"X-Launchpad-Export-Date: 2012-10-30 04:44+0000\n"
|
||||||
"X-Generator: Launchpad (build 15108)\n"
|
"X-Generator: Launchpad (build 16206)\n"
|
||||||
"Language: eu\n"
|
"Language: eu\n"
|
||||||
|
|
||||||
#. name for aaa
|
#. name for aaa
|
||||||
@ -73,7 +73,7 @@ msgstr "Anambé"
|
|||||||
|
|
||||||
#. name for aao
|
#. name for aao
|
||||||
msgid "Arabic; Algerian Saharan"
|
msgid "Arabic; Algerian Saharan"
|
||||||
msgstr ""
|
msgstr "Arabiera, Aljeriako Saharakoa"
|
||||||
|
|
||||||
#. name for aap
|
#. name for aap
|
||||||
msgid "Arára; Pará"
|
msgid "Arára; Pará"
|
||||||
@ -181,31 +181,32 @@ msgstr "Abazera"
|
|||||||
|
|
||||||
#. name for abr
|
#. name for abr
|
||||||
msgid "Abron"
|
msgid "Abron"
|
||||||
msgstr ""
|
msgstr "Abron; (bono hizkuntza, Ghana)"
|
||||||
|
|
||||||
#. name for abs
|
#. name for abs
|
||||||
msgid "Malay; Ambonese"
|
msgid "Malay; Ambonese"
|
||||||
msgstr ""
|
msgstr "Malaysiera; (\"ambonese\" hizkuntza)"
|
||||||
|
|
||||||
#. name for abt
|
#. name for abt
|
||||||
msgid "Ambulas"
|
msgid "Ambulas"
|
||||||
msgstr ""
|
msgstr "Ambulas hizkuntza"
|
||||||
|
|
||||||
#. name for abu
|
#. name for abu
|
||||||
msgid "Abure"
|
msgid "Abure"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"Abure hizkuntza (edo abonwa; edo akaplass) (Niger, Kongo, Boli-kosta)"
|
||||||
|
|
||||||
#. name for abv
|
#. name for abv
|
||||||
msgid "Arabic; Baharna"
|
msgid "Arabic; Baharna"
|
||||||
msgstr ""
|
msgstr "Arabiera; baharna"
|
||||||
|
|
||||||
#. name for abw
|
#. name for abw
|
||||||
msgid "Pal"
|
msgid "Pal"
|
||||||
msgstr ""
|
msgstr "Pal hizkuntza (Papua)"
|
||||||
|
|
||||||
#. name for abx
|
#. name for abx
|
||||||
msgid "Inabaknon"
|
msgid "Inabaknon"
|
||||||
msgstr ""
|
msgstr "Inabaknon hizkuntza (edo abaknon, Filipina uharteak)"
|
||||||
|
|
||||||
#. name for aby
|
#. name for aby
|
||||||
msgid "Aneme Wake"
|
msgid "Aneme Wake"
|
||||||
|
@ -12,14 +12,14 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
||||||
"devel@lists.alioth.debian.org>\n"
|
"devel@lists.alioth.debian.org>\n"
|
||||||
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
||||||
"PO-Revision-Date: 2012-02-01 20:12+0000\n"
|
"PO-Revision-Date: 2012-10-24 18:16+0000\n"
|
||||||
"Last-Translator: drMerry <Unknown>\n"
|
"Last-Translator: drMerry <Unknown>\n"
|
||||||
"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
|
"Language-Team: Dutch <vertaling@vrijschrift.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-02-02 05:57+0000\n"
|
"X-Launchpad-Export-Date: 2012-10-25 05:35+0000\n"
|
||||||
"X-Generator: Launchpad (build 14738)\n"
|
"X-Generator: Launchpad (build 16179)\n"
|
||||||
"Language: nl\n"
|
"Language: nl\n"
|
||||||
|
|
||||||
#. name for aaa
|
#. name for aaa
|
||||||
@ -332,7 +332,7 @@ msgstr "Andegerebinha"
|
|||||||
|
|
||||||
#. name for adh
|
#. name for adh
|
||||||
msgid "Adhola"
|
msgid "Adhola"
|
||||||
msgstr ""
|
msgstr "Adhola"
|
||||||
|
|
||||||
#. name for adi
|
#. name for adi
|
||||||
msgid "Adi"
|
msgid "Adi"
|
||||||
@ -30436,11 +30436,11 @@ msgstr ""
|
|||||||
|
|
||||||
#. name for zma
|
#. name for zma
|
||||||
msgid "Manda (Australia)"
|
msgid "Manda (Australia)"
|
||||||
msgstr ""
|
msgstr "Manda (Australië)"
|
||||||
|
|
||||||
#. name for zmb
|
#. name for zmb
|
||||||
msgid "Zimba"
|
msgid "Zimba"
|
||||||
msgstr ""
|
msgstr "Zimba"
|
||||||
|
|
||||||
#. name for zmc
|
#. name for zmc
|
||||||
msgid "Margany"
|
msgid "Margany"
|
||||||
|
@ -13,14 +13,14 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
"Report-Msgid-Bugs-To: Debian iso-codes team <pkg-isocodes-"
|
||||||
"devel@lists.alioth.debian.org>\n"
|
"devel@lists.alioth.debian.org>\n"
|
||||||
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
"POT-Creation-Date: 2011-11-25 14:01+0000\n"
|
||||||
"PO-Revision-Date: 2012-06-14 09:06+0000\n"
|
"PO-Revision-Date: 2012-10-20 00:57+0000\n"
|
||||||
"Last-Translator: Eugene Marshal <Unknown>\n"
|
"Last-Translator: Ida Leter <iatheia@yandex.ru>\n"
|
||||||
"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
|
"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"X-Launchpad-Export-Date: 2012-06-15 04:42+0000\n"
|
"X-Launchpad-Export-Date: 2012-10-21 04:41+0000\n"
|
||||||
"X-Generator: Launchpad (build 15414)\n"
|
"X-Generator: Launchpad (build 16165)\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
|
|
||||||
#. name for aaa
|
#. name for aaa
|
||||||
@ -41,7 +41,7 @@ msgstr "Амал"
|
|||||||
|
|
||||||
#. name for aae
|
#. name for aae
|
||||||
msgid "Albanian; Arbëreshë"
|
msgid "Albanian; Arbëreshë"
|
||||||
msgstr ""
|
msgstr "Албанский; диалект Арбереши"
|
||||||
|
|
||||||
#. name for aaf
|
#. name for aaf
|
||||||
msgid "Aranadan"
|
msgid "Aranadan"
|
||||||
@ -53,7 +53,7 @@ msgstr "Амбрак"
|
|||||||
|
|
||||||
#. name for aah
|
#. name for aah
|
||||||
msgid "Arapesh; Abu'"
|
msgid "Arapesh; Abu'"
|
||||||
msgstr ""
|
msgstr "Арапешей; Абу'"
|
||||||
|
|
||||||
#. name for aai
|
#. name for aai
|
||||||
msgid "Arifama-Miniafia"
|
msgid "Arifama-Miniafia"
|
||||||
@ -77,15 +77,15 @@ msgstr "Анамбе"
|
|||||||
|
|
||||||
#. name for aao
|
#. name for aao
|
||||||
msgid "Arabic; Algerian Saharan"
|
msgid "Arabic; Algerian Saharan"
|
||||||
msgstr ""
|
msgstr "Арабский; Алжирская Сахара"
|
||||||
|
|
||||||
#. name for aap
|
#. name for aap
|
||||||
msgid "Arára; Pará"
|
msgid "Arára; Pará"
|
||||||
msgstr ""
|
msgstr "Арара; Пара"
|
||||||
|
|
||||||
#. name for aaq
|
#. name for aaq
|
||||||
msgid "Abnaki; Eastern"
|
msgid "Abnaki; Eastern"
|
||||||
msgstr ""
|
msgstr "Абенаки; Восточный"
|
||||||
|
|
||||||
#. name for aar
|
#. name for aar
|
||||||
msgid "Afar"
|
msgid "Afar"
|
||||||
@ -97,7 +97,7 @@ msgstr "Асакс"
|
|||||||
|
|
||||||
#. name for aat
|
#. name for aat
|
||||||
msgid "Albanian; Arvanitika"
|
msgid "Albanian; Arvanitika"
|
||||||
msgstr ""
|
msgstr "Албанский; Арнаутский диалект"
|
||||||
|
|
||||||
#. name for aau
|
#. name for aau
|
||||||
msgid "Abau"
|
msgid "Abau"
|
||||||
@ -125,7 +125,7 @@ msgstr "Банкон"
|
|||||||
|
|
||||||
#. name for abc
|
#. name for abc
|
||||||
msgid "Ayta; Ambala"
|
msgid "Ayta; Ambala"
|
||||||
msgstr ""
|
msgstr "Айта; Амбала"
|
||||||
|
|
||||||
#. name for abd
|
#. name for abd
|
||||||
msgid "Manide"
|
msgid "Manide"
|
||||||
@ -133,7 +133,7 @@ msgstr "Мэнайд"
|
|||||||
|
|
||||||
#. name for abe
|
#. name for abe
|
||||||
msgid "Abnaki; Western"
|
msgid "Abnaki; Western"
|
||||||
msgstr ""
|
msgstr "Абенаки; Западный"
|
||||||
|
|
||||||
#. name for abf
|
#. name for abf
|
||||||
msgid "Abai Sungai"
|
msgid "Abai Sungai"
|
||||||
@ -145,7 +145,7 @@ msgstr "Абага"
|
|||||||
|
|
||||||
#. name for abh
|
#. name for abh
|
||||||
msgid "Arabic; Tajiki"
|
msgid "Arabic; Tajiki"
|
||||||
msgstr ""
|
msgstr "Арабский; Таджи"
|
||||||
|
|
||||||
#. name for abi
|
#. name for abi
|
||||||
msgid "Abidji"
|
msgid "Abidji"
|
||||||
@ -177,7 +177,7 @@ msgstr "Абон"
|
|||||||
|
|
||||||
#. name for abp
|
#. name for abp
|
||||||
msgid "Ayta; Abellen"
|
msgid "Ayta; Abellen"
|
||||||
msgstr ""
|
msgstr "Айта; Абенлен"
|
||||||
|
|
||||||
#. name for abq
|
#. name for abq
|
||||||
msgid "Abaza"
|
msgid "Abaza"
|
||||||
|
93
setup/sfntly.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#!/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 <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import shlex, os
|
||||||
|
from glob import glob
|
||||||
|
|
||||||
|
from setup import iswindows
|
||||||
|
|
||||||
|
class Group(object):
|
||||||
|
|
||||||
|
def __init__(self, name, base, build_base, cflags):
|
||||||
|
self.name = name
|
||||||
|
self.cflags = cflags
|
||||||
|
self.headers = frozenset(glob(os.path.join(base, '*.h')))
|
||||||
|
self.src_files = glob(os.path.join(base, '*.cc'))
|
||||||
|
self.bdir = os.path.abspath(os.path.join(build_base, name))
|
||||||
|
if not os.path.exists(self.bdir):
|
||||||
|
os.makedirs(self.bdir)
|
||||||
|
self.objects = [os.path.join(self.bdir,
|
||||||
|
os.path.basename(x).rpartition('.')[0] + ('.obj' if iswindows else
|
||||||
|
'.o')) for x in self.src_files]
|
||||||
|
|
||||||
|
def __call__(self, compiler, linker, builder, all_headers):
|
||||||
|
for src, obj in zip(self.src_files, self.objects):
|
||||||
|
if builder.newer(obj, [src] + list(all_headers)):
|
||||||
|
sinc = ['/Tp'+src] if iswindows else ['-c', src]
|
||||||
|
oinc = ['/Fo'+obj] if iswindows else ['-o', obj]
|
||||||
|
cmd = [compiler] + self.cflags + sinc + oinc
|
||||||
|
builder.info(' '.join(cmd))
|
||||||
|
builder.check_call(cmd)
|
||||||
|
|
||||||
|
class SfntlyBuilderMixin(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.sfntly_cflags = [
|
||||||
|
'-DSFNTLY_NO_EXCEPTION',
|
||||||
|
'-DSFNTLY_EXPERIMENTAL',
|
||||||
|
]
|
||||||
|
if iswindows:
|
||||||
|
self.sfntly_cflags += [
|
||||||
|
'-D_UNICODE', '-DUNICODE',
|
||||||
|
] + shlex.split('/W4 /WX /Gm- /Gy /GR-')
|
||||||
|
self.cflags += ['-DWIN32']
|
||||||
|
else:
|
||||||
|
# Possibly add -fno-inline (slower, but more robust)
|
||||||
|
self.sfntly_cflags += [
|
||||||
|
'-Werror',
|
||||||
|
'-fno-exceptions',
|
||||||
|
]
|
||||||
|
if len(self.libraries) > 1:
|
||||||
|
self.libraries = ['icuuc']
|
||||||
|
if not iswindows:
|
||||||
|
self.libraries += ['pthread']
|
||||||
|
|
||||||
|
def __call__(self, obj_dir, compiler, linker, builder, cflags, ldflags):
|
||||||
|
self.sfntly_build_dir = os.path.join(obj_dir, 'sfntly')
|
||||||
|
if '/Ox' in cflags:
|
||||||
|
cflags.remove('/Ox')
|
||||||
|
if '-O3' in cflags:
|
||||||
|
cflags.remove('-O3')
|
||||||
|
if '/W3' in cflags:
|
||||||
|
cflags.remove('/W3')
|
||||||
|
if '-ggdb' not in cflags:
|
||||||
|
cflags.insert(0, '/O2' if iswindows else '-O2')
|
||||||
|
|
||||||
|
groups = []
|
||||||
|
all_headers = set()
|
||||||
|
all_objects = []
|
||||||
|
src_dir = self.absolutize([os.path.join('sfntly', 'src')])[0]
|
||||||
|
inc_dirs = [src_dir]
|
||||||
|
self.inc_dirs += inc_dirs
|
||||||
|
inc_flags = builder.inc_dirs_to_cflags(self.inc_dirs)
|
||||||
|
for loc in ('', 'port', 'data', 'math', 'table', 'table/bitmap',
|
||||||
|
'table/core', 'table/truetype'):
|
||||||
|
path = os.path.join(src_dir, 'sfntly', *loc.split('/'))
|
||||||
|
gr = Group(loc, path, self.sfntly_build_dir, cflags+
|
||||||
|
inc_flags+self.sfntly_cflags+self.cflags)
|
||||||
|
groups.append(gr)
|
||||||
|
all_headers |= gr.headers
|
||||||
|
all_objects.extend(gr.objects)
|
||||||
|
|
||||||
|
for group in groups:
|
||||||
|
group(compiler, linker, builder, all_headers)
|
||||||
|
|
||||||
|
self.extra_objs = all_objects
|
||||||
|
|
||||||
|
|
@ -329,6 +329,7 @@ def get_parsed_proxy(typ='http', debug=True):
|
|||||||
ans['port'] = int(ans['port'])
|
ans['port'] = int(ans['port'])
|
||||||
except:
|
except:
|
||||||
if debug:
|
if debug:
|
||||||
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
else:
|
else:
|
||||||
if debug:
|
if debug:
|
||||||
@ -689,29 +690,6 @@ def remove_bracketed_text(src,
|
|||||||
buf.append(char)
|
buf.append(char)
|
||||||
return u''.join(buf)
|
return u''.join(buf)
|
||||||
|
|
||||||
if isosx:
|
|
||||||
import glob, shutil
|
|
||||||
fdir = os.path.expanduser('~/.fonts')
|
|
||||||
try:
|
|
||||||
if not os.path.exists(fdir):
|
|
||||||
os.makedirs(fdir)
|
|
||||||
if not os.path.exists(os.path.join(fdir, 'LiberationSans_Regular.ttf')):
|
|
||||||
base = P('fonts/liberation/*.ttf')
|
|
||||||
for f in glob.glob(base):
|
|
||||||
shutil.copy2(f, fdir)
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
def load_builtin_fonts():
|
|
||||||
import glob
|
|
||||||
from PyQt4.Qt import QFontDatabase
|
|
||||||
base = P('fonts/liberation/*.ttf')
|
|
||||||
for f in glob.glob(base):
|
|
||||||
QFontDatabase.addApplicationFont(f)
|
|
||||||
return 'Liberation Serif', 'Liberation Sans', 'Liberation Mono'
|
|
||||||
|
|
||||||
|
|
||||||
def ipython(user_ns=None):
|
def ipython(user_ns=None):
|
||||||
from calibre.utils.ipython import ipython
|
from calibre.utils.ipython import ipython
|
||||||
ipython(user_ns=user_ns)
|
ipython(user_ns=user_ns)
|
||||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = u'calibre'
|
__appname__ = u'calibre'
|
||||||
numeric_version = (0, 9, 3)
|
numeric_version = (0, 9, 5)
|
||||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
@ -36,6 +36,7 @@ isunix = isosx or islinux
|
|||||||
isportable = os.environ.get('CALIBRE_PORTABLE_BUILD', None) is not None
|
isportable = os.environ.get('CALIBRE_PORTABLE_BUILD', None) is not None
|
||||||
ispy3 = sys.version_info.major > 2
|
ispy3 = sys.version_info.major > 2
|
||||||
isxp = iswindows and sys.getwindowsversion().major < 6
|
isxp = iswindows and sys.getwindowsversion().major < 6
|
||||||
|
isworker = os.environ.has_key('CALIBRE_WORKER') or os.environ.has_key('CALIBRE_SIMPLE_WORKER')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
preferred_encoding = locale.getpreferredencoding()
|
preferred_encoding = locale.getpreferredencoding()
|
||||||
@ -83,12 +84,14 @@ class Plugins(collections.Mapping):
|
|||||||
'magick',
|
'magick',
|
||||||
'podofo',
|
'podofo',
|
||||||
'cPalmdoc',
|
'cPalmdoc',
|
||||||
'fontconfig',
|
|
||||||
'progress_indicator',
|
'progress_indicator',
|
||||||
'chmlib',
|
'chmlib',
|
||||||
'chm_extra',
|
'chm_extra',
|
||||||
'icu',
|
'icu',
|
||||||
'speedup',
|
'speedup',
|
||||||
|
'freetype',
|
||||||
|
'woff',
|
||||||
|
'sfntly',
|
||||||
]
|
]
|
||||||
if iswindows:
|
if iswindows:
|
||||||
plugins.extend(['winutil', 'wpd', 'winfonts'])
|
plugins.extend(['winutil', 'wpd', 'winfonts'])
|
||||||
|
@ -1394,6 +1394,16 @@ class StoreEKnigiStore(StoreBase):
|
|||||||
formats = ['EPUB', 'PDF', 'HTML']
|
formats = ['EPUB', 'PDF', 'HTML']
|
||||||
affiliate = True
|
affiliate = True
|
||||||
|
|
||||||
|
class StoreEmpikStore(StoreBase):
|
||||||
|
name = 'Empik'
|
||||||
|
author = u'Tomasz Długosz'
|
||||||
|
description = u'Empik to marka o unikalnym dziedzictwie i legendarne miejsce, dawne “okno na świat”. Jest obecna w polskim krajobrazie kulturalnym od 60 lat (wcześniej jako Kluby Międzynarodowej Prasy i Książki).'
|
||||||
|
actual_plugin = 'calibre.gui2.store.stores.empik_plugin:EmpikStore'
|
||||||
|
|
||||||
|
headquarters = 'PL'
|
||||||
|
formats = ['EPUB', 'MOBI', 'PDF']
|
||||||
|
affiliate = True
|
||||||
|
|
||||||
class StoreEscapeMagazineStore(StoreBase):
|
class StoreEscapeMagazineStore(StoreBase):
|
||||||
name = 'EscapeMagazine'
|
name = 'EscapeMagazine'
|
||||||
author = u'Tomasz Długosz'
|
author = u'Tomasz Długosz'
|
||||||
@ -1661,6 +1671,7 @@ plugins += [
|
|||||||
StoreEbooksGratuitsStore,
|
StoreEbooksGratuitsStore,
|
||||||
StoreEHarlequinStore,
|
StoreEHarlequinStore,
|
||||||
StoreEKnigiStore,
|
StoreEKnigiStore,
|
||||||
|
StoreEmpikStore,
|
||||||
StoreEscapeMagazineStore,
|
StoreEscapeMagazineStore,
|
||||||
StoreFeedbooksStore,
|
StoreFeedbooksStore,
|
||||||
StoreFoylesUKStore,
|
StoreFoylesUKStore,
|
||||||
|
@ -19,6 +19,8 @@ Run an embedded python interpreter.
|
|||||||
''')
|
''')
|
||||||
parser.add_option('-c', '--command', help='Run python code.', default=None)
|
parser.add_option('-c', '--command', help='Run python code.', default=None)
|
||||||
parser.add_option('-e', '--exec-file', default=None, help='Run the python code in file.')
|
parser.add_option('-e', '--exec-file', default=None, help='Run the python code in file.')
|
||||||
|
parser.add_option('-f', '--subset-font', default=False,
|
||||||
|
action='store_true', help='Subset the specified font')
|
||||||
parser.add_option('-d', '--debug-device-driver', default=False, action='store_true',
|
parser.add_option('-d', '--debug-device-driver', default=False, action='store_true',
|
||||||
help='Debug the specified device driver.')
|
help='Debug the specified device driver.')
|
||||||
parser.add_option('-g', '--gui', default=False, action='store_true',
|
parser.add_option('-g', '--gui', default=False, action='store_true',
|
||||||
@ -209,6 +211,11 @@ def main(args=sys.argv):
|
|||||||
execfile(ef, g)
|
execfile(ef, g)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if len(args) > 1 and args[1] in ('-f', '--subset-font'):
|
||||||
|
from calibre.utils.fonts.subset import main
|
||||||
|
main(['subset-font']+args[2:])
|
||||||
|
return
|
||||||
|
|
||||||
opts, args = option_parser().parse_args(args)
|
opts, args = option_parser().parse_args(args)
|
||||||
if opts.gui:
|
if opts.gui:
|
||||||
from calibre.gui2.main import main
|
from calibre.gui2.main import main
|
||||||
|
@ -212,7 +212,7 @@ class ANDROID(USBMS):
|
|||||||
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
|
||||||
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD',
|
||||||
'PMP5097C', 'MASS', 'NOVO7', 'ZEKI', 'COBY', 'SXZ', 'USB_2.0',
|
'PMP5097C', 'MASS', 'NOVO7', 'ZEKI', 'COBY', 'SXZ', 'USB_2.0',
|
||||||
'COBY_MID', 'VS', 'AINOL', 'TOPWISE']
|
'COBY_MID', 'VS', 'AINOL', 'TOPWISE', 'PAD703']
|
||||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
|
||||||
|
@ -20,7 +20,7 @@ class Book(Book_):
|
|||||||
def __init__(self, prefix, lpath, title=None, authors=None, mime=None, date=None, ContentType=None,
|
def __init__(self, prefix, lpath, title=None, authors=None, mime=None, date=None, ContentType=None,
|
||||||
thumbnail_name=None, size=0, other=None):
|
thumbnail_name=None, size=0, other=None):
|
||||||
# debug_print('Book::__init__ - title=', title)
|
# debug_print('Book::__init__ - title=', title)
|
||||||
show_debug = title is not None and title.lower().find("magic kingdom") >= 0
|
show_debug = title is not None and title.lower().find("xxxxx") >= 0
|
||||||
if show_debug:
|
if show_debug:
|
||||||
debug_print("Book::__init__ - title=", title, 'authors=', authors)
|
debug_print("Book::__init__ - title=", title, 'authors=', authors)
|
||||||
debug_print("Book::__init__ - other=", other)
|
debug_print("Book::__init__ - other=", other)
|
||||||
@ -31,8 +31,8 @@ class Book(Book_):
|
|||||||
|
|
||||||
if authors is not None and len(authors) > 0:
|
if authors is not None and len(authors) > 0:
|
||||||
self.authors_from_string(authors)
|
self.authors_from_string(authors)
|
||||||
if self.author_sort is None or self.author_sort == "Unknown":
|
if self.author_sort is None or self.author_sort == "Unknown":
|
||||||
self.author_sort = author_to_author_sort(self.authors)
|
self.author_sort = author_to_author_sort(authors)
|
||||||
|
|
||||||
self.mime = mime
|
self.mime = mime
|
||||||
|
|
||||||
@ -58,7 +58,8 @@ class Book(Book_):
|
|||||||
self.datetime = time.gmtime()
|
self.datetime = time.gmtime()
|
||||||
|
|
||||||
self.contentID = None
|
self.contentID = None
|
||||||
self.current_collections = []
|
self.current_shelves = []
|
||||||
|
self.kobo_collections = []
|
||||||
|
|
||||||
if thumbnail_name is not None:
|
if thumbnail_name is not None:
|
||||||
self.thumbnail = ImageWrapper(thumbnail_name)
|
self.thumbnail = ImageWrapper(thumbnail_name)
|
||||||
@ -99,6 +100,10 @@ class KTCollectionsBookList(CollectionsBookList):
|
|||||||
lpath = getattr(book, 'lpath', None)
|
lpath = getattr(book, 'lpath', None)
|
||||||
if lpath is None:
|
if lpath is None:
|
||||||
continue
|
continue
|
||||||
|
# If the book is not in the current library, we don't want to use the metadtaa for the collections
|
||||||
|
if book.application_id is None:
|
||||||
|
# debug_print("KTCollectionsBookList:get_collections - Book not in current library")
|
||||||
|
continue
|
||||||
# Decide how we will build the collections. The default: leave the
|
# Decide how we will build the collections. The default: leave the
|
||||||
# book in all existing collections. Do not add any new ones.
|
# book in all existing collections. Do not add any new ones.
|
||||||
attrs = ['device_collections']
|
attrs = ['device_collections']
|
||||||
@ -115,7 +120,8 @@ class KTCollectionsBookList(CollectionsBookList):
|
|||||||
elif prefs['manage_device_metadata'] == 'on_connect':
|
elif prefs['manage_device_metadata'] == 'on_connect':
|
||||||
# For existing books, modify the collections only if the user
|
# For existing books, modify the collections only if the user
|
||||||
# specified 'on_connect'
|
# specified 'on_connect'
|
||||||
attrs += collection_attributes
|
attrs = collection_attributes
|
||||||
|
book.device_collections = []
|
||||||
if show_debug:
|
if show_debug:
|
||||||
debug_print("KTCollectionsBookList:get_collections - attrs=", attrs)
|
debug_print("KTCollectionsBookList:get_collections - attrs=", attrs)
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class KOBO(USBMS):
|
|||||||
gui_name = 'Kobo Reader'
|
gui_name = 'Kobo Reader'
|
||||||
description = _('Communicate with the Kobo Reader')
|
description = _('Communicate with the Kobo Reader')
|
||||||
author = 'Timothy Legge and David Forrester'
|
author = 'Timothy Legge and David Forrester'
|
||||||
version = (2, 0, 2)
|
version = (2, 0, 3)
|
||||||
|
|
||||||
dbversion = 0
|
dbversion = 0
|
||||||
fwversion = 0
|
fwversion = 0
|
||||||
@ -653,6 +653,7 @@ class KOBO(USBMS):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID):
|
def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID):
|
||||||
|
# debug_print("KOBO:book_from_path - title=%s"%title)
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
|
|
||||||
if cls.settings().read_metadata or cls.MUST_READ_METADATA:
|
if cls.settings().read_metadata or cls.MUST_READ_METADATA:
|
||||||
@ -850,7 +851,7 @@ class KOBO(USBMS):
|
|||||||
return collections
|
return collections
|
||||||
|
|
||||||
def sync_booklists(self, booklists, end_session=True):
|
def sync_booklists(self, booklists, end_session=True):
|
||||||
# debug_print('KOBO: started sync_booklists')
|
debug_print('KOBO: started sync_booklists')
|
||||||
paths = self.get_device_paths()
|
paths = self.get_device_paths()
|
||||||
|
|
||||||
blists = {}
|
blists = {}
|
||||||
@ -872,7 +873,7 @@ class KOBO(USBMS):
|
|||||||
self.update_device_database_collections(blist, collections, oncard)
|
self.update_device_database_collections(blist, collections, oncard)
|
||||||
|
|
||||||
USBMS.sync_booklists(self, booklists, end_session=end_session)
|
USBMS.sync_booklists(self, booklists, end_session=end_session)
|
||||||
#debug_print('KOBO: finished sync_booklists')
|
debug_print('KOBO: finished sync_booklists')
|
||||||
|
|
||||||
def rebuild_collections(self, booklist, oncard):
|
def rebuild_collections(self, booklist, oncard):
|
||||||
collections_attributes = []
|
collections_attributes = []
|
||||||
@ -1199,7 +1200,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
author = 'David Forrester'
|
author = 'David Forrester'
|
||||||
description = 'Communicate with the Kobo Touch, Glo and Mini firmware. Based on the existing Kobo driver by %s.' % (KOBO.author)
|
description = 'Communicate with the Kobo Touch, Glo and Mini firmware. Based on the existing Kobo driver by %s.' % (KOBO.author)
|
||||||
|
|
||||||
supported_dbversion = 62
|
supported_dbversion = 70
|
||||||
min_supported_dbversion = 53
|
min_supported_dbversion = 53
|
||||||
|
|
||||||
booklist_class = KTCollectionsBookList
|
booklist_class = KTCollectionsBookList
|
||||||
@ -1285,13 +1286,21 @@ class KOBOTOUCH(KOBO):
|
|||||||
|
|
||||||
# Image file name endings. Made up of: image size, min_dbversion, max_dbversion,
|
# Image file name endings. Made up of: image size, min_dbversion, max_dbversion,
|
||||||
COVER_FILE_ENDINGS = {
|
COVER_FILE_ENDINGS = {
|
||||||
' - N3_LIBRARY_FULL.parsed':[(355,530),0, 99,],
|
' - N3_LIBRARY_FULL.parsed':[(355,473),0, 99,], # Used for Details screen
|
||||||
# ' - N3_LIBRARY_FULL.parsed':[(600,800),0, 99,],
|
' - N3_LIBRARY_GRID.parsed':[(149,198),0, 99,], # Used for library lists
|
||||||
' - N3_LIBRARY_GRID.parsed':[(149,233),0, 99,],
|
' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,],
|
||||||
' - N3_LIBRARY_LIST.parsed':[(60,90),0, 99,],
|
# ' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,],
|
||||||
' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,],
|
' - N3_FULL.parsed':[(600,800),0, 99,], # Used for screensaver, home screen
|
||||||
' - N3_FULL.parsed':[(600,800),0, 52,],
|
|
||||||
}
|
}
|
||||||
|
#Following are the sizes used with pre2.1.4 firmware
|
||||||
|
# COVER_FILE_ENDINGS = {
|
||||||
|
# ' - N3_LIBRARY_FULL.parsed':[(355,530),0, 99,], # Used for Details screen
|
||||||
|
## ' - N3_LIBRARY_FULL.parsed':[(600,800),0, 99,],
|
||||||
|
# ' - N3_LIBRARY_GRID.parsed':[(149,233),0, 99,], # Used for library lists
|
||||||
|
# ' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,],
|
||||||
|
# ' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,],
|
||||||
|
# ' - N3_FULL.parsed':[(600,800),0, 99,], # Used for screensaver if "Full screen" is checked.
|
||||||
|
# }
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
super(KOBOTOUCH, self).initialize()
|
super(KOBOTOUCH, self).initialize()
|
||||||
@ -1400,9 +1409,14 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print("KoboTouch:update_booklist - have a deleted book")
|
debug_print("KoboTouch:update_booklist - have a deleted book")
|
||||||
# Label Previews
|
# Label Previews
|
||||||
if accessibility == 6:
|
if accessibility == 6:
|
||||||
playlist_map[lpath].append('Preview')
|
if isdownloaded == 'false':
|
||||||
|
playlist_map[lpath].append('Recommendation')
|
||||||
|
else:
|
||||||
|
playlist_map[lpath].append('Preview')
|
||||||
elif accessibility == 4:
|
elif accessibility == 4:
|
||||||
playlist_map[lpath].append('Recommendation')
|
playlist_map[lpath].append('Recommendation')
|
||||||
|
|
||||||
|
kobo_collections = playlist_map[lpath][:]
|
||||||
|
|
||||||
if len(bookshelves) > 0:
|
if len(bookshelves) > 0:
|
||||||
playlist_map[lpath].extend(bookshelves)
|
playlist_map[lpath].extend(bookshelves)
|
||||||
@ -1420,12 +1434,15 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print('KoboTouch:update_booklist - bl[idx].device_collections=', bl[idx].device_collections)
|
debug_print('KoboTouch:update_booklist - bl[idx].device_collections=', bl[idx].device_collections)
|
||||||
debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map)
|
debug_print('KoboTouch:update_booklist - playlist_map=', playlist_map)
|
||||||
debug_print('KoboTouch:update_booklist - bookshelves=', bookshelves)
|
debug_print('KoboTouch:update_booklist - bookshelves=', bookshelves)
|
||||||
|
debug_print('KoboTouch:update_booklist - kobo_collections=', kobo_collections)
|
||||||
debug_print('KoboTouch:update_booklist - series="%s"' % bl[idx].series)
|
debug_print('KoboTouch:update_booklist - series="%s"' % bl[idx].series)
|
||||||
debug_print('KoboTouch:update_booklist - the book=', bl[idx])
|
debug_print('KoboTouch:update_booklist - the book=', bl[idx])
|
||||||
|
debug_print('KoboTouch:update_booklist - the authors=', bl[idx].authors)
|
||||||
debug_print('KoboTouch:update_booklist - application_id=', bl[idx].application_id)
|
debug_print('KoboTouch:update_booklist - application_id=', bl[idx].application_id)
|
||||||
bl_cache[lpath] = None
|
bl_cache[lpath] = None
|
||||||
if bl[idx].title_sort is not None:
|
# removed to allow recognizing of ePub with an UUID inside
|
||||||
bl[idx].title = bl[idx].title_sort
|
# if bl[idx].title_sort is not None:
|
||||||
|
# bl[idx].title = bl[idx].title_sort
|
||||||
if ImageID is not None:
|
if ImageID is not None:
|
||||||
imagename = self.imagefilename_from_imageID(ImageID)
|
imagename = self.imagefilename_from_imageID(ImageID)
|
||||||
if imagename is not None:
|
if imagename is not None:
|
||||||
@ -1438,19 +1455,15 @@ class KOBOTOUCH(KOBO):
|
|||||||
else:
|
else:
|
||||||
debug_print(" Strange: The file: ", prefix, lpath, " does not exist!")
|
debug_print(" Strange: The file: ", prefix, lpath, " does not exist!")
|
||||||
debug_print("KoboTouch:update_booklist - book size=", bl[idx].size)
|
debug_print("KoboTouch:update_booklist - book size=", bl[idx].size)
|
||||||
# if lpath in playlist_map and \
|
|
||||||
# playlist_map[lpath] not in bl[idx].device_collections:
|
|
||||||
# bl[idx].device_collections = playlist_map.get(lpath,[])
|
|
||||||
# changed = True
|
|
||||||
|
|
||||||
|
|
||||||
if show_debug:
|
if show_debug:
|
||||||
debug_print("KoboTouch:update_booklist - ContentID='%s'"%ContentID)
|
debug_print("KoboTouch:update_booklist - ContentID='%s'"%ContentID)
|
||||||
bl[idx].contentID = ContentID
|
bl[idx].contentID = ContentID
|
||||||
|
|
||||||
if lpath in playlist_map:
|
if lpath in playlist_map:
|
||||||
bl[idx].device_collections = playlist_map.get(lpath,[])
|
bl[idx].device_collections = playlist_map.get(lpath,[])
|
||||||
bl[idx].current_collections = bl[idx].device_collections
|
bl[idx].current_shelves = bookshelves
|
||||||
|
bl[idx].kobo_collections = kobo_collections
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
if show_debug:
|
if show_debug:
|
||||||
@ -1482,12 +1495,16 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print('KoboTouch:update_booklist - class:', book.__class__)
|
debug_print('KoboTouch:update_booklist - class:', book.__class__)
|
||||||
# debug_print(' resolution:', book.__class__.__mro__)
|
# debug_print(' resolution:', book.__class__.__mro__)
|
||||||
debug_print(" contentid:'%s'"%book.contentID)
|
debug_print(" contentid:'%s'"%book.contentID)
|
||||||
debug_print(book)
|
debug_print(" title:'%s'"%book.title)
|
||||||
|
debug_print(" the book:", book)
|
||||||
debug_print(" author_sort:'%s'"%book.author_sort)
|
debug_print(" author_sort:'%s'"%book.author_sort)
|
||||||
|
debug_print(" bookshelves:", bookshelves)
|
||||||
|
debug_print(" kobo_collections:", kobo_collections)
|
||||||
|
|
||||||
# print 'Update booklist'
|
# print 'Update booklist'
|
||||||
book.device_collections = playlist_map.get(lpath,[])# if lpath in playlist_map else []
|
book.device_collections = playlist_map.get(lpath,[])# if lpath in playlist_map else []
|
||||||
book.current_collections = bl[idx].device_collections
|
book.current_shelves = bookshelves
|
||||||
|
book.kobo_collections = kobo_collections
|
||||||
book.contentID = ContentID
|
book.contentID = ContentID
|
||||||
# debug_print('KoboTouch:update_booklist - title=', title, 'book.device_collections', book.device_collections)
|
# debug_print('KoboTouch:update_booklist - title=', title, 'book.device_collections', book.device_collections)
|
||||||
|
|
||||||
@ -1526,16 +1543,16 @@ class KOBOTOUCH(KOBO):
|
|||||||
# return bytestrings if the content cannot the decoded as unicode
|
# return bytestrings if the content cannot the decoded as unicode
|
||||||
connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")
|
connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")
|
||||||
|
|
||||||
self.bookshelvelist = self.get_bookshelflist(connection)
|
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
cursor.execute('select version from dbversion')
|
cursor.execute('select version from dbversion')
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
self.dbversion = result[0]
|
self.dbversion = result[0]
|
||||||
|
|
||||||
debug_print("Database Version=%d"%self.dbversion)
|
debug_print("Database Version=%d"%self.dbversion)
|
||||||
|
|
||||||
|
self.bookshelvelist = self.get_bookshelflist(connection)
|
||||||
|
debug_print("KoboTouch:books - shelf list:", self.bookshelvelist)
|
||||||
|
|
||||||
opts = self.settings()
|
opts = self.settings()
|
||||||
if self.dbversion >= 33:
|
if self.dbversion >= 33:
|
||||||
query= ('select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
|
query= ('select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ' \
|
||||||
@ -1623,14 +1640,16 @@ class KOBOTOUCH(KOBO):
|
|||||||
|
|
||||||
#print "count found in cache: %d, count of files in metadata: %d, need_sync: %s" % \
|
#print "count found in cache: %d, count of files in metadata: %d, need_sync: %s" % \
|
||||||
# (len(bl_cache), len(bl), need_sync)
|
# (len(bl_cache), len(bl), need_sync)
|
||||||
if need_sync: #self.count_found_in_bl != len(bl) or need_sync:
|
# Bypassing the KOBO sync_booklists as that does things we don't need to do
|
||||||
|
# Also forcing sync to see if this solves issues with updating shelves and matching books.
|
||||||
|
if need_sync or True: #self.count_found_in_bl != len(bl) or need_sync:
|
||||||
debug_print("KoboTouch:books - about to sync_booklists")
|
debug_print("KoboTouch:books - about to sync_booklists")
|
||||||
if oncard == 'cardb':
|
if oncard == 'cardb':
|
||||||
self.sync_booklists((None, None, bl))
|
USBMS.sync_booklists(self, (None, None, bl))
|
||||||
elif oncard == 'carda':
|
elif oncard == 'carda':
|
||||||
self.sync_booklists((None, bl, None))
|
USBMS.sync_booklists(self, (None, bl, None))
|
||||||
else:
|
else:
|
||||||
self.sync_booklists((bl, None, None))
|
USBMS.sync_booklists(self, (bl, None, None))
|
||||||
|
|
||||||
self.report_progress(1.0, _('Getting list of books on device...'))
|
self.report_progress(1.0, _('Getting list of books on device...'))
|
||||||
debug_print("KoboTouch:books - end - oncard='%s'"%oncard)
|
debug_print("KoboTouch:books - end - oncard='%s'"%oncard)
|
||||||
@ -1941,7 +1960,6 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print("Booklists=", booklists)
|
debug_print("Booklists=", booklists)
|
||||||
if self.dbversion < 53:
|
if self.dbversion < 53:
|
||||||
self.reset_readstatus(connection, oncard)
|
self.reset_readstatus(connection, oncard)
|
||||||
self.remove_from_bookshelves(connection, oncard)
|
|
||||||
if self.dbversion >= 14:
|
if self.dbversion >= 14:
|
||||||
debug_print("No Collections - reseting FavouritesIndex")
|
debug_print("No Collections - reseting FavouritesIndex")
|
||||||
self.reset_favouritesindex(connection, oncard)
|
self.reset_favouritesindex(connection, oncard)
|
||||||
@ -1954,6 +1972,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
if book.application_id is not None:
|
if book.application_id is not None:
|
||||||
# debug_print("KoboTouch:update_device_database_collections - about to remove a book from shelves book.title=%s" % book.title)
|
# debug_print("KoboTouch:update_device_database_collections - about to remove a book from shelves book.title=%s" % book.title)
|
||||||
self.remove_book_from_device_bookshelves(connection, book)
|
self.remove_book_from_device_bookshelves(connection, book)
|
||||||
|
book.device_collections.extend(book.kobo_collections)
|
||||||
if not prefs['manage_device_metadata'] == 'manual' and delete_empty_shelves:
|
if not prefs['manage_device_metadata'] == 'manual' and delete_empty_shelves:
|
||||||
debug_print("KoboTouch:update_device_database_collections - about to clear empty bookshelves")
|
debug_print("KoboTouch:update_device_database_collections - about to clear empty bookshelves")
|
||||||
self.delete_empty_bookshelves(connection)
|
self.delete_empty_bookshelves(connection)
|
||||||
@ -2088,13 +2107,14 @@ class KOBOTOUCH(KOBO):
|
|||||||
|
|
||||||
def remove_book_from_device_bookshelves(self, connection, book):
|
def remove_book_from_device_bookshelves(self, connection, book):
|
||||||
show_debug = self.is_debugging_title(book.title)# or True
|
show_debug = self.is_debugging_title(book.title)# or True
|
||||||
|
|
||||||
remove_shelf_list = set(book.current_collections) - set(book.device_collections) - set(["Im_Reading", "Read", "Closed"])
|
remove_shelf_list = set(book.current_shelves) - set(book.device_collections)
|
||||||
|
|
||||||
if show_debug:
|
if show_debug:
|
||||||
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.application_id="%s"'%book.application_id)
|
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.application_id="%s"'%book.application_id)
|
||||||
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.contentID="%s"'%book.contentID)
|
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.contentID="%s"'%book.contentID)
|
||||||
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.device_collections=', book.device_collections)
|
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.device_collections=', book.device_collections)
|
||||||
|
debug_print('KoboTouch:remove_book_from_device_bookshelves - book.current_shelves=', book.current_shelves)
|
||||||
debug_print('KoboTouch:remove_book_from_device_bookshelves - remove_shelf_list=', remove_shelf_list)
|
debug_print('KoboTouch:remove_book_from_device_bookshelves - remove_shelf_list=', remove_shelf_list)
|
||||||
|
|
||||||
if len(remove_shelf_list) == 0:
|
if len(remove_shelf_list) == 0:
|
||||||
@ -2155,14 +2175,14 @@ class KOBOTOUCH(KOBO):
|
|||||||
if not self.supports_bookshelves():
|
if not self.supports_bookshelves():
|
||||||
return bookshelves
|
return bookshelves
|
||||||
|
|
||||||
query = 'SELECT Name FROM Shelf'
|
query = 'SELECT Name FROM Shelf WHERE _IsDeleted = "false"'
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(query)
|
cursor.execute(query)
|
||||||
# count_bookshelves = 0
|
# count_bookshelves = 0
|
||||||
for i, row in enumerate(cursor):
|
for i, row in enumerate(cursor):
|
||||||
bookshelves.append(row[0])
|
bookshelves.append(row[0])
|
||||||
# count_bookshelves = i + 1
|
# count_bookshelves = i + 1
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
# debug_print("KoboTouch:get_bookshelflist - count bookshelves=" + unicode(count_bookshelves))
|
# debug_print("KoboTouch:get_bookshelflist - count bookshelves=" + unicode(count_bookshelves))
|
||||||
@ -2204,11 +2224,8 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print('KoboTouch:check_for_bookshelf bookshelf_name="%s"'%bookshelf_name)
|
debug_print('KoboTouch:check_for_bookshelf bookshelf_name="%s"'%bookshelf_name)
|
||||||
test_query = 'SELECT InternalName, Name, _IsDeleted FROM Shelf WHERE Name = ?'
|
test_query = 'SELECT InternalName, Name, _IsDeleted FROM Shelf WHERE Name = ?'
|
||||||
test_values = (bookshelf_name, )
|
test_values = (bookshelf_name, )
|
||||||
addquery = 'INSERT INTO "main"."Shelf"'\
|
addquery = 'INSERT INTO "main"."Shelf"'
|
||||||
' ("CreationDate","InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced")'\
|
add_values = (time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
|
||||||
' VALUES (?, ?, ?, ?, ?, ?, ?)'
|
|
||||||
add_values = (
|
|
||||||
time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
|
|
||||||
bookshelf_name,
|
bookshelf_name,
|
||||||
time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
|
time.strftime(self.TIMESTAMP_STRING, time.gmtime()),
|
||||||
bookshelf_name,
|
bookshelf_name,
|
||||||
@ -2216,6 +2233,17 @@ class KOBOTOUCH(KOBO):
|
|||||||
"true",
|
"true",
|
||||||
"false",
|
"false",
|
||||||
)
|
)
|
||||||
|
if self.dbversion < 64:
|
||||||
|
addquery += ' ("CreationDate","InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced")'\
|
||||||
|
' VALUES (?, ?, ?, ?, ?, ?, ?)'
|
||||||
|
else:
|
||||||
|
addquery += ' ("CreationDate", "InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced", "Id")'\
|
||||||
|
' VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
|
||||||
|
add_values = add_values +(bookshelf_name,)
|
||||||
|
|
||||||
|
if show_debug:
|
||||||
|
debug_print('KoboTouch:check_for_bookshelf addquery=', addquery)
|
||||||
|
debug_print('KoboTouch:check_for_bookshelf add_values=', add_values)
|
||||||
updatequery = 'UPDATE Shelf SET _IsDeleted = "false" WHERE Name = ?'
|
updatequery = 'UPDATE Shelf SET _IsDeleted = "false" WHERE Name = ?'
|
||||||
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
@ -2232,6 +2260,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
connection.commit()
|
connection.commit()
|
||||||
cursor.close()
|
cursor.close()
|
||||||
|
|
||||||
|
# Update the bookshelf list.
|
||||||
self.bookshelvelist = self.get_bookshelflist(connection)
|
self.bookshelvelist = self.get_bookshelflist(connection)
|
||||||
|
|
||||||
# debug_print("KoboTouch:set_bookshelf - end")
|
# debug_print("KoboTouch:set_bookshelf - end")
|
||||||
|
@ -114,7 +114,7 @@ class MTP_DEVICE(BASE):
|
|||||||
except:
|
except:
|
||||||
prints('Failed to load existing driveinfo.calibre file, with error:')
|
prints('Failed to load existing driveinfo.calibre file, with error:')
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
dinfo = None
|
dinfo = {}
|
||||||
if dinfo.get('device_store_uuid', None) is None:
|
if dinfo.get('device_store_uuid', None) is None:
|
||||||
dinfo['device_store_uuid'] = unicode(uuid.uuid4())
|
dinfo['device_store_uuid'] = unicode(uuid.uuid4())
|
||||||
if dinfo.get('device_name', None) is None:
|
if dinfo.get('device_name', None) is None:
|
||||||
|
@ -178,18 +178,41 @@ def normalize(x):
|
|||||||
|
|
||||||
def calibre_cover(title, author_string, series_string=None,
|
def calibre_cover(title, author_string, series_string=None,
|
||||||
output_format='jpg', title_size=46, author_size=36, logo_path=None):
|
output_format='jpg', title_size=46, author_size=36, logo_path=None):
|
||||||
|
from calibre.utils.config_base import tweaks
|
||||||
title = normalize(title)
|
title = normalize(title)
|
||||||
author_string = normalize(author_string)
|
author_string = normalize(author_string)
|
||||||
series_string = normalize(series_string)
|
series_string = normalize(series_string)
|
||||||
from calibre.utils.magick.draw import create_cover_page, TextLine
|
from calibre.utils.magick.draw import create_cover_page, TextLine
|
||||||
lines = [TextLine(title, title_size), TextLine(author_string, author_size)]
|
text = title + author_string + (series_string or u'')
|
||||||
|
font_path = tweaks['generate_cover_title_font']
|
||||||
|
if font_path is None:
|
||||||
|
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
||||||
|
|
||||||
|
from calibre.utils.fonts.utils import get_font_for_text
|
||||||
|
font = open(font_path, 'rb').read()
|
||||||
|
c = get_font_for_text(text, font)
|
||||||
|
cleanup = False
|
||||||
|
if c is not None and c != font:
|
||||||
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
pt = PersistentTemporaryFile('.ttf')
|
||||||
|
pt.write(c)
|
||||||
|
pt.close()
|
||||||
|
font_path = pt.name
|
||||||
|
cleanup = True
|
||||||
|
|
||||||
|
lines = [TextLine(title, title_size, font_path=font_path),
|
||||||
|
TextLine(author_string, author_size, font_path=font_path)]
|
||||||
if series_string:
|
if series_string:
|
||||||
lines.append(TextLine(series_string, author_size))
|
lines.append(TextLine(series_string, author_size, font_path=font_path))
|
||||||
if logo_path is None:
|
if logo_path is None:
|
||||||
logo_path = I('library.png')
|
logo_path = I('library.png')
|
||||||
return create_cover_page(lines, logo_path, output_format='jpg',
|
try:
|
||||||
|
return create_cover_page(lines, logo_path, output_format='jpg',
|
||||||
texture_opacity=0.3, texture_data=I('cover_texture.png',
|
texture_opacity=0.3, texture_data=I('cover_texture.png',
|
||||||
data=True))
|
data=True))
|
||||||
|
finally:
|
||||||
|
if cleanup:
|
||||||
|
os.remove(font_path)
|
||||||
|
|
||||||
UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|ex|en|px|mm|cm|in|pt|pc)$')
|
UNIT_RE = re.compile(r'^(-*[0-9]*[.]?[0-9]*)\s*(%|em|ex|en|px|mm|cm|in|pt|pc)$')
|
||||||
|
|
||||||
@ -231,7 +254,6 @@ def unit_convert(value, base, font, dpi):
|
|||||||
|
|
||||||
def generate_masthead(title, output_path=None, width=600, height=60):
|
def generate_masthead(title, output_path=None, width=600, height=60):
|
||||||
from calibre.ebooks.conversion.config import load_defaults
|
from calibre.ebooks.conversion.config import load_defaults
|
||||||
from calibre.utils.fonts import fontconfig
|
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks
|
||||||
fp = tweaks['generate_cover_title_font']
|
fp = tweaks['generate_cover_title_font']
|
||||||
if not fp:
|
if not fp:
|
||||||
@ -241,11 +263,10 @@ def generate_masthead(title, output_path=None, width=600, height=60):
|
|||||||
masthead_font_family = recs.get('masthead_font', 'Default')
|
masthead_font_family = recs.get('masthead_font', 'Default')
|
||||||
|
|
||||||
if masthead_font_family != 'Default':
|
if masthead_font_family != 'Default':
|
||||||
masthead_font = fontconfig.files_for_family(masthead_font_family)
|
from calibre.utils.fonts.scanner import font_scanner
|
||||||
# Assume 'normal' always in dict, else use default
|
faces = font_scanner.fonts_for_family(masthead_font_family)
|
||||||
# {'normal': (path_to_font, friendly name)}
|
if faces:
|
||||||
if 'normal' in masthead_font:
|
font_path = faces[0]['path']
|
||||||
font_path = masthead_font['normal'][0]
|
|
||||||
|
|
||||||
if not font_path or not os.access(font_path, os.R_OK):
|
if not font_path or not os.access(font_path, os.R_OK):
|
||||||
font_path = default_font
|
font_path = default_font
|
||||||
|
@ -132,7 +132,7 @@ def add_pipeline_options(parser, plumber):
|
|||||||
_('Options to control the look and feel of the output'),
|
_('Options to control the look and feel of the output'),
|
||||||
[
|
[
|
||||||
'base_font_size', 'disable_font_rescaling',
|
'base_font_size', 'disable_font_rescaling',
|
||||||
'font_size_mapping',
|
'font_size_mapping', 'embed_font_family',
|
||||||
'line_height', 'minimum_line_height',
|
'line_height', 'minimum_line_height',
|
||||||
'linearize_tables',
|
'linearize_tables',
|
||||||
'extra_css', 'filter_css',
|
'extra_css', 'filter_css',
|
||||||
|
@ -193,6 +193,17 @@ OptionRecommendation(name='line_height',
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
OptionRecommendation(name='embed_font_family',
|
||||||
|
recommended_value=None, level=OptionRecommendation.LOW,
|
||||||
|
help=_(
|
||||||
|
'Embed the specified font family into the book. This specifies '
|
||||||
|
'the "base" font used for the book. If the input document '
|
||||||
|
'specifies its own fonts, they may override this base font. '
|
||||||
|
'You can use the filter style information option to remove fonts from the '
|
||||||
|
'input document. Note that font embedding only works '
|
||||||
|
'with some output formats, principally EPUB and AZW3.')
|
||||||
|
),
|
||||||
|
|
||||||
OptionRecommendation(name='linearize_tables',
|
OptionRecommendation(name='linearize_tables',
|
||||||
recommended_value=False, level=OptionRecommendation.LOW,
|
recommended_value=False, level=OptionRecommendation.LOW,
|
||||||
help=_('Some badly designed documents use tables to control the '
|
help=_('Some badly designed documents use tables to control the '
|
||||||
|
@ -34,24 +34,24 @@ class PRS500_PROFILE(object):
|
|||||||
name = 'prs500'
|
name = 'prs500'
|
||||||
|
|
||||||
def find_custom_fonts(options, logger):
|
def find_custom_fonts(options, logger):
|
||||||
from calibre.utils.fonts import fontconfig
|
from calibre.utils.fonts.scanner import font_scanner
|
||||||
files_for_family = fontconfig.files_for_family
|
|
||||||
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
|
fonts = {'serif' : None, 'sans' : None, 'mono' : None}
|
||||||
def family(cmd):
|
def family(cmd):
|
||||||
return cmd.split(',')[-1].strip()
|
return cmd.split(',')[-1].strip()
|
||||||
if options.serif_family:
|
if options.serif_family:
|
||||||
f = family(options.serif_family)
|
f = family(options.serif_family)
|
||||||
fonts['serif'] = files_for_family(f)
|
fonts['serif'] = font_scanner.legacy_fonts_for_family(f)
|
||||||
|
print (111111, fonts['serif'])
|
||||||
if not fonts['serif']:
|
if not fonts['serif']:
|
||||||
logger.warn('Unable to find serif family %s'%f)
|
logger.warn('Unable to find serif family %s'%f)
|
||||||
if options.sans_family:
|
if options.sans_family:
|
||||||
f = family(options.sans_family)
|
f = family(options.sans_family)
|
||||||
fonts['sans'] = files_for_family(f)
|
fonts['sans'] = font_scanner.legacy_fonts_for_family(f)
|
||||||
if not fonts['sans']:
|
if not fonts['sans']:
|
||||||
logger.warn('Unable to find sans family %s'%f)
|
logger.warn('Unable to find sans family %s'%f)
|
||||||
if options.mono_family:
|
if options.mono_family:
|
||||||
f = family(options.mono_family)
|
f = family(options.mono_family)
|
||||||
fonts['mono'] = files_for_family(f)
|
fonts['mono'] = font_scanner.legacy_fonts_for_family(f)
|
||||||
if not fonts['mono']:
|
if not fonts['mono']:
|
||||||
logger.warn('Unable to find mono family %s'%f)
|
logger.warn('Unable to find mono family %s'%f)
|
||||||
return fonts
|
return fonts
|
||||||
|
@ -379,6 +379,10 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
|||||||
|
|
||||||
stream.seek(0)
|
stream.seek(0)
|
||||||
stream.truncate()
|
stream.truncate()
|
||||||
|
# Apparently there exists FB2 reading software that chokes on the use of
|
||||||
|
# single quotes in xml declaration. Sigh. See
|
||||||
|
# http://www.mobileread.com/forums/showthread.php?p=2273184#post2273184
|
||||||
|
stream.write(b'<?xml version="1.0" encoding="UTF-8"?>\n')
|
||||||
stream.write(etree.tostring(root, method='xml', encoding='utf-8',
|
stream.write(etree.tostring(root, method='xml', encoding='utf-8',
|
||||||
xml_declaration=True))
|
xml_declaration=False))
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ TEMPLATE = '''
|
|||||||
a {{ text-decoration: none }}
|
a {{ text-decoration: none }}
|
||||||
a:hover {{ color: red }}
|
a:hover {{ color: red }}
|
||||||
{extra_css}
|
{extra_css}
|
||||||
|
{embed_css}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body id="calibre_generated_inline_toc">
|
<body id="calibre_generated_inline_toc">
|
||||||
@ -64,8 +65,16 @@ class TOCAdder(object):
|
|||||||
|
|
||||||
self.log('\tGenerating in-line ToC')
|
self.log('\tGenerating in-line ToC')
|
||||||
|
|
||||||
|
embed_css = ''
|
||||||
|
s = getattr(oeb, 'store_embed_font_rules', None)
|
||||||
|
if getattr(s, 'body_font_family', None):
|
||||||
|
css = [x.cssText for x in s.rules] + [
|
||||||
|
'body { font-family: %s }'%s.body_font_family]
|
||||||
|
embed_css = '\n\n'.join(css)
|
||||||
|
|
||||||
root = etree.fromstring(TEMPLATE.format(xhtmlns=XHTML_NS,
|
root = etree.fromstring(TEMPLATE.format(xhtmlns=XHTML_NS,
|
||||||
title=self.title, extra_css=(opts.extra_css or '')))
|
title=self.title, embed_css=embed_css,
|
||||||
|
extra_css=(opts.extra_css or '')))
|
||||||
parent = XPath('//h:ul')(root)[0]
|
parent = XPath('//h:ul')(root)[0]
|
||||||
parent.text = '\n\t'
|
parent.text = '\n\t'
|
||||||
for child in self.oeb.toc:
|
for child in self.oeb.toc:
|
||||||
|
@ -258,7 +258,7 @@ OPF_MIME = types_map['.opf']
|
|||||||
PAGE_MAP_MIME = 'application/oebps-page-map+xml'
|
PAGE_MAP_MIME = 'application/oebps-page-map+xml'
|
||||||
OEB_DOC_MIME = 'text/x-oeb1-document'
|
OEB_DOC_MIME = 'text/x-oeb1-document'
|
||||||
OEB_CSS_MIME = 'text/x-oeb1-css'
|
OEB_CSS_MIME = 'text/x-oeb1-css'
|
||||||
OPENTYPE_MIME = 'application/x-font-opentype'
|
OPENTYPE_MIME = types_map['.otf']
|
||||||
GIF_MIME = types_map['.gif']
|
GIF_MIME = types_map['.gif']
|
||||||
JPEG_MIME = types_map['.jpeg']
|
JPEG_MIME = types_map['.jpeg']
|
||||||
PNG_MIME = types_map['.png']
|
PNG_MIME = types_map['.png']
|
||||||
|
@ -22,7 +22,6 @@ from calibre.utils.logging import default_log
|
|||||||
from calibre import (guess_type, prepare_string_for_xml,
|
from calibre import (guess_type, prepare_string_for_xml,
|
||||||
xml_replace_entities)
|
xml_replace_entities)
|
||||||
from calibre.ebooks.oeb.transforms.cover import CoverManager
|
from calibre.ebooks.oeb.transforms.cover import CoverManager
|
||||||
|
|
||||||
from calibre.ebooks.oeb.iterator.spine import (SpineItem, create_indexing_data)
|
from calibre.ebooks.oeb.iterator.spine import (SpineItem, create_indexing_data)
|
||||||
from calibre.ebooks.oeb.iterator.bookmarks import BookmarksMixin
|
from calibre.ebooks.oeb.iterator.bookmarks import BookmarksMixin
|
||||||
|
|
||||||
@ -76,7 +75,8 @@ class EbookIterator(BookmarksMixin):
|
|||||||
return i
|
return i
|
||||||
|
|
||||||
def __enter__(self, processed=False, only_input_plugin=False,
|
def __enter__(self, processed=False, only_input_plugin=False,
|
||||||
run_char_count=True, read_anchor_map=True):
|
run_char_count=True, read_anchor_map=True,
|
||||||
|
extract_embedded_fonts_for_qt=False):
|
||||||
''' Convert an ebook file into an exploded OEB book suitable for
|
''' Convert an ebook file into an exploded OEB book suitable for
|
||||||
display in viewers/preprocessing etc. '''
|
display in viewers/preprocessing etc. '''
|
||||||
|
|
||||||
@ -174,6 +174,16 @@ class EbookIterator(BookmarksMixin):
|
|||||||
|
|
||||||
self.read_bookmarks()
|
self.read_bookmarks()
|
||||||
|
|
||||||
|
if extract_embedded_fonts_for_qt:
|
||||||
|
from calibre.ebooks.oeb.iterator.extract_fonts import extract_fonts
|
||||||
|
try:
|
||||||
|
extract_fonts(self.opf, self.log)
|
||||||
|
except:
|
||||||
|
ol = self.log.filter_level
|
||||||
|
self.log.filter_level = self.log.DEBUG
|
||||||
|
self.log.exception('Failed to extract fonts')
|
||||||
|
self.log.filter_level = ol
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
|
110
src/calibre/ebooks/oeb/iterator/extract_fonts.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#!/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 <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import re, os, logging
|
||||||
|
from functools import partial
|
||||||
|
from future_builtins import map
|
||||||
|
|
||||||
|
class FamilyMap(dict):
|
||||||
|
|
||||||
|
def __init__(self, log):
|
||||||
|
dict.__init__(self)
|
||||||
|
self.replace_map = {}
|
||||||
|
self.added_fonts = set()
|
||||||
|
self.log = log
|
||||||
|
|
||||||
|
def __call__(self, basedir, match):
|
||||||
|
self.read_font_fule(basedir, match.group())
|
||||||
|
return b''
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
if self.replace_map:
|
||||||
|
self.pat = re.compile(br'(font-family.*?)(' +
|
||||||
|
b'|'.join([re.escape(x) for x in
|
||||||
|
self.replace_map.iterkeys()])+b')', re.I)
|
||||||
|
|
||||||
|
def replace_font_families(self, raw):
|
||||||
|
if self.replace_map:
|
||||||
|
def sub(m):
|
||||||
|
k = m.group(2).lower()
|
||||||
|
for q, val in self.replace_map.iteritems():
|
||||||
|
if q.lower() == k.lower():
|
||||||
|
return m.group().replace(m.group(2), val)
|
||||||
|
return m.group()
|
||||||
|
|
||||||
|
return self.pat.sub(sub, raw)
|
||||||
|
|
||||||
|
def read_font_fule(self, basedir, css):
|
||||||
|
from PyQt4.Qt import QFontDatabase
|
||||||
|
import cssutils
|
||||||
|
cssutils.log.setLevel(logging.ERROR)
|
||||||
|
try:
|
||||||
|
sheet = cssutils.parseString(css, validate=False)
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
for rule in sheet.cssRules:
|
||||||
|
try:
|
||||||
|
s = rule.style
|
||||||
|
src = s.getProperty('src').propertyValue[0].uri
|
||||||
|
font_family = s.getProperty('font-family').propertyValue[0].value
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
if not src or not font_family:
|
||||||
|
continue
|
||||||
|
font_file = os.path.normcase(os.path.abspath(os.path.join(basedir,
|
||||||
|
src)))
|
||||||
|
if font_file not in self.added_fonts:
|
||||||
|
self.added_fonts.add(font_file)
|
||||||
|
if os.path.exists(font_file):
|
||||||
|
with open(font_file, 'rb') as f:
|
||||||
|
idx = QFontDatabase.addApplicationFontFromData(f.read())
|
||||||
|
if idx > -1:
|
||||||
|
family = map(unicode,
|
||||||
|
QFontDatabase.applicationFontFamilies(idx)).next()
|
||||||
|
self.log('Extracted embedded font:', family, 'from',
|
||||||
|
os.path.basename(font_file))
|
||||||
|
if (family and family != font_family and
|
||||||
|
family not in self.replace_map):
|
||||||
|
self.log('Replacing font family value:',
|
||||||
|
font_family, 'with', family)
|
||||||
|
self.replace_map[font_family.encode('utf-8')] = \
|
||||||
|
family.encode('utf-8')
|
||||||
|
|
||||||
|
def extract_fonts(opf, log):
|
||||||
|
'''
|
||||||
|
Extract embedded fonts from the ebook and add them explicitly to the Qt
|
||||||
|
font database to workaround https://bugs.webkit.org/show_bug.cgi?id=29433
|
||||||
|
|
||||||
|
Only works if the font-face and font-family rules are all contained in the
|
||||||
|
CSS files (Also processing the HTML files would be too much of a
|
||||||
|
performance hit, to do robustly).
|
||||||
|
'''
|
||||||
|
css_files = {}
|
||||||
|
font_family_map = FamilyMap(log)
|
||||||
|
pat = re.compile(br'^\s*@font-face\s*{[^}]+}', re.M)
|
||||||
|
|
||||||
|
for item in opf.manifest:
|
||||||
|
if item.mime_type and item.mime_type.lower() in {
|
||||||
|
'text/css', 'text/x-oeb1-css', 'text/x-oeb-css'}:
|
||||||
|
try:
|
||||||
|
with open(item.path, 'rb') as f:
|
||||||
|
raw = f.read()
|
||||||
|
except EnvironmentError:
|
||||||
|
continue
|
||||||
|
css_files[item.path] = pat.sub(partial(font_family_map,
|
||||||
|
os.path.dirname(item.path)), raw)
|
||||||
|
|
||||||
|
font_family_map.finalize()
|
||||||
|
|
||||||
|
if font_family_map.added_fonts:
|
||||||
|
for path, raw in css_files.iteritems():
|
||||||
|
with open(path, 'wb') as f:
|
||||||
|
nraw = font_family_map.replace_font_families(raw) or raw
|
||||||
|
f.write(nraw)
|
||||||
|
|
@ -126,6 +126,17 @@ class CaseInsensitiveAttributesTranslator(HTMLTranslator):
|
|||||||
|
|
||||||
ci_css_to_xpath = CaseInsensitiveAttributesTranslator().css_to_xpath
|
ci_css_to_xpath = CaseInsensitiveAttributesTranslator().css_to_xpath
|
||||||
|
|
||||||
|
NULL_NAMESPACE_REGEX = re.compile(ur'''(name\(\) = ['"])h:''')
|
||||||
|
def fix_namespace(raw):
|
||||||
|
'''
|
||||||
|
cssselect uses name() = 'h:p' to select tags for some CSS selectors (e.g.
|
||||||
|
h|p+h|p).
|
||||||
|
However, since for us the XHTML namespace is the default namespace (with no
|
||||||
|
prefix), name() is the same as local-name(). So this is a hack to
|
||||||
|
workaround the problem.
|
||||||
|
'''
|
||||||
|
return NULL_NAMESPACE_REGEX.sub(ur'\1', raw)
|
||||||
|
|
||||||
class CSSSelector(object):
|
class CSSSelector(object):
|
||||||
|
|
||||||
def __init__(self, css, log=None, namespaces=XPNSMAP):
|
def __init__(self, css, log=None, namespaces=XPNSMAP):
|
||||||
@ -136,7 +147,7 @@ class CSSSelector(object):
|
|||||||
|
|
||||||
def build_selector(self, css, log, func=css_to_xpath):
|
def build_selector(self, css, log, func=css_to_xpath):
|
||||||
try:
|
try:
|
||||||
return etree.XPath(func(css), namespaces=self.namespaces)
|
return etree.XPath(fix_namespace(func(css)), namespaces=self.namespaces)
|
||||||
except:
|
except:
|
||||||
if log is not None:
|
if log is not None:
|
||||||
log.exception('Failed to parse CSS selector: %r'%css)
|
log.exception('Failed to parse CSS selector: %r'%css)
|
||||||
|
@ -14,9 +14,11 @@ from lxml import etree
|
|||||||
import cssutils
|
import cssutils
|
||||||
from cssutils.css import Property
|
from cssutils.css import Property
|
||||||
|
|
||||||
|
from calibre import guess_type
|
||||||
from calibre.ebooks.oeb.base import (XHTML, XHTML_NS, CSS_MIME, OEB_STYLES,
|
from calibre.ebooks.oeb.base import (XHTML, XHTML_NS, CSS_MIME, OEB_STYLES,
|
||||||
namespace, barename, XPath)
|
namespace, barename, XPath)
|
||||||
from calibre.ebooks.oeb.stylizer import Stylizer
|
from calibre.ebooks.oeb.stylizer import Stylizer
|
||||||
|
from calibre.utils.filenames import ascii_filename
|
||||||
|
|
||||||
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
COLLAPSE = re.compile(r'[ \t\r\n\v]+')
|
||||||
STRIPNUM = re.compile(r'[-0-9]+$')
|
STRIPNUM = re.compile(r'[-0-9]+$')
|
||||||
@ -101,6 +103,22 @@ def FontMapper(sbase=None, dbase=None, dkey=None):
|
|||||||
else:
|
else:
|
||||||
return NullMapper()
|
return NullMapper()
|
||||||
|
|
||||||
|
class EmbedFontsCSSRules(object):
|
||||||
|
|
||||||
|
def __init__(self, body_font_family, rules):
|
||||||
|
self.body_font_family, self.rules = body_font_family, rules
|
||||||
|
self.href = None
|
||||||
|
|
||||||
|
def __call__(self, oeb):
|
||||||
|
if not self.body_font_family: return None
|
||||||
|
if not self.href:
|
||||||
|
iid, href = oeb.manifest.generate(u'page_styles', u'page_styles.css')
|
||||||
|
rules = [x.cssText for x in self.rules]
|
||||||
|
rules = u'\n\n'.join(rules)
|
||||||
|
sheet = cssutils.parseString(rules, validate=False)
|
||||||
|
self.href = oeb.manifest.add(iid, href, guess_type(href)[0],
|
||||||
|
data=sheet).href
|
||||||
|
return self.href
|
||||||
|
|
||||||
class CSSFlattener(object):
|
class CSSFlattener(object):
|
||||||
def __init__(self, fbase=None, fkey=None, lineh=None, unfloat=False,
|
def __init__(self, fbase=None, fkey=None, lineh=None, unfloat=False,
|
||||||
@ -144,11 +162,61 @@ class CSSFlattener(object):
|
|||||||
cssutils.replaceUrls(item.data, item.abshref,
|
cssutils.replaceUrls(item.data, item.abshref,
|
||||||
ignoreImportRules=True)
|
ignoreImportRules=True)
|
||||||
|
|
||||||
|
self.body_font_family, self.embed_font_rules = self.get_embed_font_info(
|
||||||
|
self.opts.embed_font_family)
|
||||||
|
# Store for use in output plugins/transforms that generate content,
|
||||||
|
# like the AZW3 output inline ToC.
|
||||||
|
self.oeb.store_embed_font_rules = EmbedFontsCSSRules(self.body_font_family,
|
||||||
|
self.embed_font_rules)
|
||||||
self.stylize_spine()
|
self.stylize_spine()
|
||||||
self.sbase = self.baseline_spine() if self.fbase else None
|
self.sbase = self.baseline_spine() if self.fbase else None
|
||||||
self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
|
self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
|
||||||
self.flatten_spine()
|
self.flatten_spine()
|
||||||
|
|
||||||
|
def get_embed_font_info(self, family, failure_critical=True):
|
||||||
|
efi = []
|
||||||
|
body_font_family = None
|
||||||
|
if not family:
|
||||||
|
return body_font_family, efi
|
||||||
|
from calibre.utils.fonts.scanner import font_scanner
|
||||||
|
from calibre.utils.fonts.utils import panose_to_css_generic_family
|
||||||
|
faces = font_scanner.fonts_for_family(family)
|
||||||
|
if not faces:
|
||||||
|
msg = (u'No embeddable fonts found for family: %r'%self.opts.embed_font_family)
|
||||||
|
if failure_critical:
|
||||||
|
raise ValueError(msg)
|
||||||
|
self.oeb.log.warn(msg)
|
||||||
|
return body_font_family, efi
|
||||||
|
|
||||||
|
for i, font in enumerate(faces):
|
||||||
|
ext = 'otf' if font['is_otf'] else 'ttf'
|
||||||
|
fid, href = self.oeb.manifest.generate(id=u'font',
|
||||||
|
href=u'%s.%s'%(ascii_filename(font['full_name']).replace(u' ', u'-'), ext))
|
||||||
|
item = self.oeb.manifest.add(fid, href,
|
||||||
|
guess_type('dummy.'+ext)[0],
|
||||||
|
data=font_scanner.get_font_data(font))
|
||||||
|
item.unload_data_from_memory()
|
||||||
|
|
||||||
|
cfont = {
|
||||||
|
u'font-family':u'"%s"'%font['font-family'],
|
||||||
|
u'panose-1': u' '.join(map(unicode, font['panose'])),
|
||||||
|
u'src': u'url(%s)'%item.href,
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
generic_family = panose_to_css_generic_family(font['panose'])
|
||||||
|
body_font_family = u"'%s',%s"%(font['font-family'], generic_family)
|
||||||
|
self.oeb.log(u'Embedding font: %s'%font['font-family'])
|
||||||
|
for k in (u'font-weight', u'font-style', u'font-stretch'):
|
||||||
|
if font[k] != u'normal':
|
||||||
|
cfont[k] = font[k]
|
||||||
|
rule = '@font-face { %s }'%('; '.join(u'%s:%s'%(k, v) for k, v in
|
||||||
|
cfont.iteritems()))
|
||||||
|
rule = cssutils.parseString(rule)
|
||||||
|
efi.append(rule)
|
||||||
|
|
||||||
|
return body_font_family, efi
|
||||||
|
|
||||||
def stylize_spine(self):
|
def stylize_spine(self):
|
||||||
self.stylizers = {}
|
self.stylizers = {}
|
||||||
profile = self.context.source
|
profile = self.context.source
|
||||||
@ -170,6 +238,8 @@ class CSSFlattener(object):
|
|||||||
bs.extend(['page-break-before: always'])
|
bs.extend(['page-break-before: always'])
|
||||||
if self.context.change_justification != 'original':
|
if self.context.change_justification != 'original':
|
||||||
bs.append('text-align: '+ self.context.change_justification)
|
bs.append('text-align: '+ self.context.change_justification)
|
||||||
|
if self.body_font_family:
|
||||||
|
bs.append(u'font-family: '+self.body_font_family)
|
||||||
body.set('style', '; '.join(bs))
|
body.set('style', '; '.join(bs))
|
||||||
stylizer = Stylizer(html, item.href, self.oeb, self.context, profile,
|
stylizer = Stylizer(html, item.href, self.oeb, self.context, profile,
|
||||||
user_css=self.context.extra_css,
|
user_css=self.context.extra_css,
|
||||||
@ -450,7 +520,8 @@ class CSSFlattener(object):
|
|||||||
items.sort()
|
items.sort()
|
||||||
css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
|
css = ';\n'.join("%s: %s" % (key, val) for key, val in items)
|
||||||
css = ('@page {\n%s\n}\n'%css) if items else ''
|
css = ('@page {\n%s\n}\n'%css) if items else ''
|
||||||
rules = [r.cssText for r in stylizer.font_face_rules]
|
rules = [r.cssText for r in stylizer.font_face_rules +
|
||||||
|
self.embed_font_rules]
|
||||||
raw = '\n\n'.join(rules)
|
raw = '\n\n'.join(rules)
|
||||||
css += '\n\n' + raw
|
css += '\n\n' + raw
|
||||||
global_css[css].append(item)
|
global_css[css].append(item)
|
||||||
|
@ -73,6 +73,7 @@ class Split(object):
|
|||||||
|
|
||||||
def find_page_breaks(self, item):
|
def find_page_breaks(self, item):
|
||||||
if self.page_break_selectors is None:
|
if self.page_break_selectors is None:
|
||||||
|
from calibre.ebooks.oeb.stylizer import fix_namespace
|
||||||
css_to_xpath = HTMLTranslator().css_to_xpath
|
css_to_xpath = HTMLTranslator().css_to_xpath
|
||||||
self.page_break_selectors = set([])
|
self.page_break_selectors = set([])
|
||||||
stylesheets = [x.data for x in self.oeb.manifest if x.media_type in
|
stylesheets = [x.data for x in self.oeb.manifest if x.media_type in
|
||||||
@ -84,7 +85,7 @@ class Split(object):
|
|||||||
'page-break-after'), 'cssText', '').strip().lower()
|
'page-break-after'), 'cssText', '').strip().lower()
|
||||||
try:
|
try:
|
||||||
if before and before not in {'avoid', 'auto', 'inherit'}:
|
if before and before not in {'avoid', 'auto', 'inherit'}:
|
||||||
self.page_break_selectors.add((XPath(css_to_xpath(rule.selectorText)),
|
self.page_break_selectors.add((XPath(fix_namespace(css_to_xpath(rule.selectorText))),
|
||||||
True))
|
True))
|
||||||
if self.remove_css_pagebreaks:
|
if self.remove_css_pagebreaks:
|
||||||
rule.style.removeProperty('page-break-before')
|
rule.style.removeProperty('page-break-before')
|
||||||
@ -92,7 +93,7 @@ class Split(object):
|
|||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
if after and after not in {'avoid', 'auto', 'inherit'}:
|
if after and after not in {'avoid', 'auto', 'inherit'}:
|
||||||
self.page_break_selectors.add((XPath(css_to_xpath(rule.selectorText)),
|
self.page_break_selectors.add((XPath(fix_namespace(css_to_xpath(rule.selectorText))),
|
||||||
False))
|
False))
|
||||||
if self.remove_css_pagebreaks:
|
if self.remove_css_pagebreaks:
|
||||||
rule.style.removeProperty('page-break-after')
|
rule.style.removeProperty('page-break-after')
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
""" The GUI """
|
""" The GUI """
|
||||||
import os, sys, Queue, threading
|
import os, sys, Queue, threading, glob
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
from urllib import unquote
|
from urllib import unquote
|
||||||
from PyQt4.Qt import (QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt,
|
from PyQt4.Qt import (QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt,
|
||||||
QByteArray, QTranslator, QCoreApplication, QThread,
|
QByteArray, QTranslator, QCoreApplication, QThread,
|
||||||
QEvent, QTimer, pyqtSignal, QDateTime, QDesktopServices,
|
QEvent, QTimer, pyqtSignal, QDateTime, QDesktopServices,
|
||||||
QFileDialog, QFileIconProvider, QSettings, QColor,
|
QFileDialog, QFileIconProvider, QSettings, QColor,
|
||||||
QIcon, QApplication, QDialog, QUrl, QFont, QPalette)
|
QIcon, QApplication, QDialog, QUrl, QFont, QPalette,
|
||||||
|
QFontDatabase)
|
||||||
|
|
||||||
ORG_NAME = 'KovidsBrain'
|
ORG_NAME = 'KovidsBrain'
|
||||||
APP_UID = 'libprs500'
|
APP_UID = 'libprs500'
|
||||||
@ -791,6 +792,29 @@ class Application(QApplication):
|
|||||||
self.redirect_notify = True
|
self.redirect_notify = True
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def load_builtin_fonts(self, scan_for_fonts=False):
|
||||||
|
global _rating_font
|
||||||
|
if scan_for_fonts:
|
||||||
|
from calibre.utils.fonts.scanner import font_scanner
|
||||||
|
# Start scanning the users computer for fonts
|
||||||
|
font_scanner
|
||||||
|
|
||||||
|
# Load the builtin fonts and any fonts added to calibre by the user to
|
||||||
|
# Qt
|
||||||
|
for ff in glob.glob(P('fonts/liberation/*.?tf')) + \
|
||||||
|
[P('fonts/calibreSymbols.otf')] + \
|
||||||
|
glob.glob(os.path.join(config_dir, 'fonts', '*.?tf')):
|
||||||
|
if ff.rpartition('.')[-1].lower() in {'ttf', 'otf'}:
|
||||||
|
with open(ff, 'rb') as s:
|
||||||
|
# Windows requires font files to be executable for them to be
|
||||||
|
# loaded successfully, so we use the in memory loader
|
||||||
|
fid = QFontDatabase.addApplicationFontFromData(s.read())
|
||||||
|
if fid > -1:
|
||||||
|
fam = QFontDatabase.applicationFontFamilies(fid)
|
||||||
|
fam = set(map(unicode, fam))
|
||||||
|
if u'calibre Symbols' in fam:
|
||||||
|
_rating_font = u'calibre Symbols'
|
||||||
|
|
||||||
def load_calibre_style(self):
|
def load_calibre_style(self):
|
||||||
# On OS X QtCurve resets the palette, so we preserve it explicitly
|
# On OS X QtCurve resets the palette, so we preserve it explicitly
|
||||||
orig_pal = QPalette(self.palette())
|
orig_pal = QPalette(self.palette())
|
||||||
@ -963,22 +987,9 @@ def is_gui_thread():
|
|||||||
global gui_thread
|
global gui_thread
|
||||||
return gui_thread is QThread.currentThread()
|
return gui_thread is QThread.currentThread()
|
||||||
|
|
||||||
_rating_font = None
|
_rating_font = 'Arial Unicode MS' if iswindows else 'sans-serif'
|
||||||
def rating_font():
|
def rating_font():
|
||||||
global _rating_font
|
global _rating_font
|
||||||
if _rating_font is None:
|
|
||||||
from PyQt4.Qt import QFontDatabase
|
|
||||||
_rating_font = 'Arial Unicode MS' if iswindows else 'sans-serif'
|
|
||||||
fontid = QFontDatabase.addApplicationFont(
|
|
||||||
#P('fonts/liberation/LiberationSerif-Regular.ttf')
|
|
||||||
P('fonts/calibreSymbols.otf')
|
|
||||||
)
|
|
||||||
if fontid > -1:
|
|
||||||
try:
|
|
||||||
_rating_font = unicode(list(
|
|
||||||
QFontDatabase.applicationFontFamilies(fontid))[0])
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return _rating_font
|
return _rating_font
|
||||||
|
|
||||||
def find_forms(srcdir):
|
def find_forms(srcdir):
|
||||||
|
@ -120,17 +120,19 @@ class ShareConnMenu(QMenu): # {{{
|
|||||||
for account in keys:
|
for account in keys:
|
||||||
formats, auto, default = opts.accounts[account]
|
formats, auto, default = opts.accounts[account]
|
||||||
subject = opts.subjects.get(account, '')
|
subject = opts.subjects.get(account, '')
|
||||||
|
alias = opts.aliases.get(account, '')
|
||||||
dest = 'mail:'+account+';'+formats+';'+subject
|
dest = 'mail:'+account+';'+formats+';'+subject
|
||||||
action1 = DeviceAction(dest, False, False, I('mail.png'),
|
action1 = DeviceAction(dest, False, False, I('mail.png'),
|
||||||
account)
|
alias or account)
|
||||||
action2 = DeviceAction(dest, True, False, I('mail.png'),
|
action2 = DeviceAction(dest, True, False, I('mail.png'),
|
||||||
account + ' ' + _('(delete from library)'))
|
(alias or account) + ' ' + _('(delete from library)'))
|
||||||
self.email_to_menu.addAction(action1)
|
self.email_to_menu.addAction(action1)
|
||||||
self.email_to_and_delete_menu.addAction(action2)
|
self.email_to_and_delete_menu.addAction(action2)
|
||||||
map(self.memory.append, (action1, action2))
|
map(self.memory.append, (action1, action2))
|
||||||
if default:
|
if default:
|
||||||
ac = DeviceAction(dest, False, False,
|
ac = DeviceAction(dest, False, False,
|
||||||
I('mail.png'), _('Email to') + ' ' +account)
|
I('mail.png'), _('Email to') + ' ' +(alias or
|
||||||
|
account))
|
||||||
self.addAction(ac)
|
self.addAction(ac)
|
||||||
self.email_actions.append(ac)
|
self.email_actions.append(ac)
|
||||||
ac.a_s.connect(sync_menu.action_triggered)
|
ac.a_s.connect(sync_menu.action_triggered)
|
||||||
|
@ -191,6 +191,8 @@ class Widget(QWidget):
|
|||||||
elif isinstance(g, (XPathEdit, RegexEdit)):
|
elif isinstance(g, (XPathEdit, RegexEdit)):
|
||||||
g.edit.editTextChanged.connect(f)
|
g.edit.editTextChanged.connect(f)
|
||||||
g.edit.currentIndexChanged.connect(f)
|
g.edit.currentIndexChanged.connect(f)
|
||||||
|
elif isinstance(g, FontFamilyChooser):
|
||||||
|
g.family_changed.connect(f)
|
||||||
else:
|
else:
|
||||||
raise Exception('Can\'t connect %s'%type(g))
|
raise Exception('Can\'t connect %s'%type(g))
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ class LookAndFeelWidget(Widget, Ui_Form):
|
|||||||
Widget.__init__(self, parent,
|
Widget.__init__(self, parent,
|
||||||
['change_justification', 'extra_css', 'base_font_size',
|
['change_justification', 'extra_css', 'base_font_size',
|
||||||
'font_size_mapping', 'line_height', 'minimum_line_height',
|
'font_size_mapping', 'line_height', 'minimum_line_height',
|
||||||
|
'embed_font_family',
|
||||||
'smarten_punctuation', 'unsmarten_punctuation',
|
'smarten_punctuation', 'unsmarten_punctuation',
|
||||||
'disable_font_rescaling', 'insert_blank_line',
|
'disable_font_rescaling', 'insert_blank_line',
|
||||||
'remove_paragraph_spacing',
|
'remove_paragraph_spacing',
|
||||||
|
@ -7,27 +7,53 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>655</width>
|
<width>655</width>
|
||||||
<height>522</height>
|
<height>619</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="3" column="4">
|
||||||
<widget class="QCheckBox" name="opt_disable_font_rescaling">
|
<widget class="QDoubleSpinBox" name="opt_line_height">
|
||||||
<property name="text">
|
<property name="suffix">
|
||||||
<string>&Disable font size rescaling</string>
|
<string> pt</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="3" column="3">
|
||||||
<widget class="QLabel" name="label_18">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Base &font size:</string>
|
<string>Line &height:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>opt_base_font_size</cstring>
|
<cstring>opt_line_height</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Minimum &line height:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_minimum_line_height</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="opt_minimum_line_height">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> %</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>900.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -97,49 +123,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>Minimum &line height:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_minimum_line_height</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="opt_minimum_line_height">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> %</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<double>900.000000000000000</double>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Line &height:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_line_height</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QDoubleSpinBox" name="opt_line_height">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> pt</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -157,14 +140,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2">
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="QCheckBox" name="opt_remove_paragraph_spacing">
|
<widget class="QCheckBox" name="opt_remove_paragraph_spacing">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Remove &spacing between paragraphs</string>
|
<string>Remove &spacing between paragraphs</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="3">
|
<item row="7" column="3">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Indent size:</string>
|
<string>&Indent size:</string>
|
||||||
@ -177,7 +160,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="4">
|
<item row="7" column="4">
|
||||||
<widget class="QDoubleSpinBox" name="opt_remove_paragraph_spacing_indent_size">
|
<widget class="QDoubleSpinBox" name="opt_remove_paragraph_spacing_indent_size">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><p>When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent.</string>
|
<string><p>When calibre removes inter paragraph spacing, it automatically sets a paragraph indent, to ensure that paragraphs can be easily distinguished. This option controls the width of that indent.</string>
|
||||||
@ -199,85 +182,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
<item row="12" column="0" colspan="5">
|
||||||
<widget class="QCheckBox" name="opt_insert_blank_line">
|
|
||||||
<property name="text">
|
|
||||||
<string>Insert &blank line between paragraphs</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="3">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Line size:</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_insert_blank_line_size</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="4">
|
|
||||||
<widget class="QDoubleSpinBox" name="opt_insert_blank_line_size">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> em</string>
|
|
||||||
</property>
|
|
||||||
<property name="decimals">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>Text &justification:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>opt_change_justification</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="2" colspan="3">
|
|
||||||
<widget class="QComboBox" name="opt_change_justification"/>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="0">
|
|
||||||
<widget class="QCheckBox" name="opt_smarten_punctuation">
|
|
||||||
<property name="text">
|
|
||||||
<string>Smarten &punctuation</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="1" colspan="4">
|
|
||||||
<widget class="QCheckBox" name="opt_asciiize">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Transliterate unicode characters to ASCII</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="0">
|
|
||||||
<widget class="QCheckBox" name="opt_unsmarten_punctuation">
|
|
||||||
<property name="text">
|
|
||||||
<string>&UnSmarten punctuation</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="1" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="opt_keep_ligatures">
|
|
||||||
<property name="text">
|
|
||||||
<string>Keep &ligatures</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="10" column="3">
|
|
||||||
<widget class="QCheckBox" name="opt_linearize_tables">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Linearize tables</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="11" column="0" colspan="5">
|
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -378,10 +283,131 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="8" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_insert_blank_line">
|
||||||
|
<property name="text">
|
||||||
|
<string>Insert &blank line between paragraphs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="4">
|
||||||
|
<widget class="QDoubleSpinBox" name="opt_insert_blank_line_size">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> em</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Text &justification:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_change_justification</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="2" colspan="3">
|
||||||
|
<widget class="QComboBox" name="opt_change_justification"/>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_smarten_punctuation">
|
||||||
|
<property name="text">
|
||||||
|
<string>Smarten &punctuation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="1" colspan="4">
|
||||||
|
<widget class="QCheckBox" name="opt_asciiize">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Transliterate unicode characters to ASCII</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_unsmarten_punctuation">
|
||||||
|
<property name="text">
|
||||||
|
<string>&UnSmarten punctuation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_keep_ligatures">
|
||||||
|
<property name="text">
|
||||||
|
<string>Keep &ligatures</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="3">
|
||||||
|
<widget class="QCheckBox" name="opt_linearize_tables">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Linearize tables</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>Base &font size:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_base_font_size</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="3">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Line size:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_insert_blank_line_size</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Embed font family:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_embed_font_family</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="5">
|
||||||
|
<widget class="QCheckBox" name="opt_disable_font_rescaling">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Disable font size rescaling</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1" colspan="2">
|
||||||
|
<widget class="FontFamilyChooser" name="opt_embed_font_family"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
@ -390,6 +416,11 @@
|
|||||||
<extends>QComboBox</extends>
|
<extends>QComboBox</extends>
|
||||||
<header>widgets.h</header>
|
<header>widgets.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>FontFamilyChooser</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>calibre/gui2/font_family_chooser.h</header>
|
||||||
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../../../resources/images.qrc"/>
|
<include location="../../../../resources/images.qrc"/>
|
||||||
|
@ -205,7 +205,7 @@
|
|||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>FontFamilyChooser</class>
|
<class>FontFamilyChooser</class>
|
||||||
<extends>QComboBox</extends>
|
<extends>QWidget</extends>
|
||||||
<header>calibre/gui2/font_family_chooser.h</header>
|
<header>calibre/gui2/font_family_chooser.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
|
@ -29,38 +29,8 @@ class PluginWidget(Widget, Ui_Form):
|
|||||||
)
|
)
|
||||||
self.db, self.book_id = db, book_id
|
self.db, self.book_id = db, book_id
|
||||||
|
|
||||||
'''
|
|
||||||
from calibre.utils.fonts import fontconfig
|
|
||||||
|
|
||||||
global font_family_model
|
|
||||||
if font_family_model is None:
|
|
||||||
font_family_model = FontFamilyModel()
|
|
||||||
try:
|
|
||||||
font_family_model.families = fontconfig.find_font_families(allowed_extensions=['ttf'])
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
font_family_model.families = []
|
|
||||||
print 'WARNING: Could not load fonts'
|
|
||||||
traceback.print_exc()
|
|
||||||
font_family_model.families.sort()
|
|
||||||
font_family_model.families[:0] = [_('Default')]
|
|
||||||
|
|
||||||
self.font_family_model = font_family_model
|
|
||||||
self.opt_masthead_font.setModel(self.font_family_model)
|
|
||||||
'''
|
|
||||||
self.opt_mobi_file_type.addItems(['old', 'both', 'new'])
|
self.opt_mobi_file_type.addItems(['old', 'both', 'new'])
|
||||||
|
|
||||||
self.initialize_options(get_option, get_help, db, book_id)
|
self.initialize_options(get_option, get_help, db, book_id)
|
||||||
|
|
||||||
'''
|
|
||||||
def set_value_handler(self, g, val):
|
|
||||||
if unicode(g.objectName()) in 'opt_masthead_font':
|
|
||||||
idx = -1
|
|
||||||
if val:
|
|
||||||
idx = g.findText(val, Qt.MatchFixedString)
|
|
||||||
if idx < 0:
|
|
||||||
idx = 0
|
|
||||||
g.setCurrentIndex(idx)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
'''
|
|
||||||
|
@ -211,6 +211,9 @@ class RegexEdit(QWidget, Ui_Edit):
|
|||||||
self.button.clicked.connect(self.builder)
|
self.button.clicked.connect(self.builder)
|
||||||
|
|
||||||
def builder(self):
|
def builder(self):
|
||||||
|
if self.db is None:
|
||||||
|
self.doc_cache = _('Click the Open button below to open a '
|
||||||
|
'ebook to use for testing.')
|
||||||
bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self.doc_cache, self)
|
bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self.doc_cache, self)
|
||||||
if bld.cancelled:
|
if bld.cancelled:
|
||||||
return
|
return
|
||||||
|
@ -504,7 +504,11 @@ from the value in the box</string>
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="restore_original">
|
<widget class="QCheckBox" name="restore_original">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>When doing a same format to same format conversion, for e.g., EPUB to EPUB, calibre saves the original EPUB as ORIGINAL_EPUB. This option tells calibre to restore the EPUB from ORIGINAL_EPUB. Useful if you did a bulk conversion of a large number of books and something went wrong.</string>
|
<string>When doing a same format to same format conversion,
|
||||||
|
for e.g., EPUB to EPUB, calibre saves the original EPUB
|
||||||
|
as ORIGINAL_EPUB. This option tells calibre to restore
|
||||||
|
the EPUB from ORIGINAL_EPUB. Useful if you did a bulk
|
||||||
|
conversion of a large number of books and something went wrong.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Restore pre conversion &originals, if available</string>
|
<string>Restore pre conversion &originals, if available</string>
|
||||||
|
@ -219,7 +219,13 @@ class TemplateDialog(QDialog, Ui_TemplateDialog):
|
|||||||
if mi:
|
if mi:
|
||||||
self.mi = mi
|
self.mi = mi
|
||||||
else:
|
else:
|
||||||
self.mi = Metadata(None, None)
|
self.mi = Metadata(_('Title'), [_('Author')])
|
||||||
|
self.mi.author_sort = _('Author Sort')
|
||||||
|
self.mi.series = _('Series')
|
||||||
|
self.mi.series_index = 3
|
||||||
|
self.mi.rating = 4.0
|
||||||
|
self.mi.tags = [_('Tag 1'), _('Tag 2')]
|
||||||
|
self.mi.languages = ['eng']
|
||||||
|
|
||||||
# Remove help icon on title bar
|
# Remove help icon on title bar
|
||||||
icon = self.windowIcon()
|
icon = self.windowIcon()
|
||||||
|
@ -7,11 +7,16 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from PyQt4.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
|
import os, shutil
|
||||||
QStyledItemDelegate, QSize, QStyle, QComboBox, QStringListModel,
|
|
||||||
QDialog, QVBoxLayout, QApplication, QFontComboBox)
|
|
||||||
|
|
||||||
from calibre.utils.icu import sort_key
|
from PyQt4.Qt import (QFontInfo, QFontMetrics, Qt, QFont, QFontDatabase, QPen,
|
||||||
|
QStyledItemDelegate, QSize, QStyle, QStringListModel, pyqtSignal,
|
||||||
|
QDialog, QVBoxLayout, QApplication, QFontComboBox, QPushButton,
|
||||||
|
QToolButton, QGridLayout, QListView, QWidget, QDialogButtonBox, QIcon,
|
||||||
|
QHBoxLayout, QLabel, QModelIndex, QLineEdit)
|
||||||
|
|
||||||
|
from calibre.constants import config_dir
|
||||||
|
from calibre.gui2 import choose_files, error_dialog, info_dialog
|
||||||
|
|
||||||
def writing_system_for_font(font):
|
def writing_system_for_font(font):
|
||||||
has_latin = True
|
has_latin = True
|
||||||
@ -55,6 +60,12 @@ def writing_system_for_font(font):
|
|||||||
class FontFamilyDelegate(QStyledItemDelegate):
|
class FontFamilyDelegate(QStyledItemDelegate):
|
||||||
|
|
||||||
def sizeHint(self, option, index):
|
def sizeHint(self, option, index):
|
||||||
|
try:
|
||||||
|
return self.do_size_hint(option, index)
|
||||||
|
except:
|
||||||
|
return QSize(300, 50)
|
||||||
|
|
||||||
|
def do_size_hint(self, option, index):
|
||||||
text = index.data(Qt.DisplayRole).toString()
|
text = index.data(Qt.DisplayRole).toString()
|
||||||
font = QFont(option.font)
|
font = QFont(option.font)
|
||||||
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
||||||
@ -62,6 +73,15 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
return QSize(m.width(text), m.height())
|
return QSize(m.width(text), m.height())
|
||||||
|
|
||||||
def paint(self, painter, option, index):
|
def paint(self, painter, option, index):
|
||||||
|
QStyledItemDelegate.paint(self, painter, option, QModelIndex())
|
||||||
|
painter.save()
|
||||||
|
try:
|
||||||
|
self.do_paint(painter, option, index)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
painter.restore()
|
||||||
|
|
||||||
|
def do_paint(self, painter, option, index):
|
||||||
text = unicode(index.data(Qt.DisplayRole).toString())
|
text = unicode(index.data(Qt.DisplayRole).toString())
|
||||||
font = QFont(option.font)
|
font = QFont(option.font)
|
||||||
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
font.setPointSize(QFontInfo(font).pointSize() * 1.5)
|
||||||
@ -75,10 +95,6 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
r = option.rect
|
r = option.rect
|
||||||
|
|
||||||
if option.state & QStyle.State_Selected:
|
if option.state & QStyle.State_Selected:
|
||||||
painter.save()
|
|
||||||
painter.setBrush(option.palette.highlight())
|
|
||||||
painter.setPen(Qt.NoPen)
|
|
||||||
painter.drawRect(option.rect)
|
|
||||||
painter.setPen(QPen(option.palette.highlightedText(), 0))
|
painter.setPen(QPen(option.palette.highlightedText(), 0))
|
||||||
|
|
||||||
if (option.direction == Qt.RightToLeft):
|
if (option.direction == Qt.RightToLeft):
|
||||||
@ -86,7 +102,6 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
else:
|
else:
|
||||||
r.setLeft(r.left() + 4)
|
r.setLeft(r.left() + 4)
|
||||||
|
|
||||||
old = painter.font()
|
|
||||||
painter.setFont(font)
|
painter.setFont(font)
|
||||||
painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, text)
|
painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, text)
|
||||||
|
|
||||||
@ -100,69 +115,248 @@ class FontFamilyDelegate(QStyledItemDelegate):
|
|||||||
r.setLeft(r.left() + w)
|
r.setLeft(r.left() + w)
|
||||||
painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, sample)
|
painter.drawText(r, Qt.AlignVCenter|Qt.AlignLeading|Qt.TextSingleLine, sample)
|
||||||
|
|
||||||
painter.setFont(old)
|
class Typefaces(QLabel):
|
||||||
|
|
||||||
if (option.state & QStyle.State_Selected):
|
|
||||||
painter.restore()
|
|
||||||
|
|
||||||
class FontFamilyChooser(QComboBox):
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QComboBox.__init__(self, parent)
|
QLabel.__init__(self, parent)
|
||||||
from calibre.utils.fonts import fontconfig
|
self.setMinimumWidth(400)
|
||||||
|
self.base_msg = '<h3>'+_('Choose a font family')+'</h3>'
|
||||||
|
self.setText(self.base_msg)
|
||||||
|
self.setWordWrap(True)
|
||||||
|
|
||||||
|
def show_family(self, family, faces):
|
||||||
|
if not family:
|
||||||
|
self.setText(self.base_msg)
|
||||||
|
return
|
||||||
|
msg = '''
|
||||||
|
<h3>%s</h3>
|
||||||
|
<dl style="font-size: smaller">
|
||||||
|
{0}
|
||||||
|
</dl>
|
||||||
|
'''%(_('Available faces for %s')%family)
|
||||||
|
entries = []
|
||||||
|
for font in faces:
|
||||||
|
sf = (font['wws_subfamily_name'] or font['preferred_subfamily_name']
|
||||||
|
or font['subfamily_name'])
|
||||||
|
entries.append('''
|
||||||
|
<dt><b>{sf}</b></dt>
|
||||||
|
<dd>font-stretch: <i>{width}</i> font-weight: <i>{weight}</i> font-style:
|
||||||
|
<i>{style}</i></dd>
|
||||||
|
|
||||||
|
'''.format(sf=sf, width=font['font-stretch'],
|
||||||
|
weight=font['font-weight'], style=font['font-style']))
|
||||||
|
msg = msg.format('\n\n'.join(entries))
|
||||||
|
self.setText(msg)
|
||||||
|
|
||||||
|
class FontsView(QListView):
|
||||||
|
|
||||||
|
changed = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QListView.__init__(self, parent)
|
||||||
|
self.setSelectionMode(self.SingleSelection)
|
||||||
|
self.setAlternatingRowColors(True)
|
||||||
|
self.d = FontFamilyDelegate(self)
|
||||||
|
self.setItemDelegate(self.d)
|
||||||
|
|
||||||
|
def currentChanged(self, current, previous):
|
||||||
|
self.changed.emit()
|
||||||
|
QListView.currentChanged(self, current, previous)
|
||||||
|
|
||||||
|
|
||||||
|
class FontFamilyDialog(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, current_family, parent=None):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self.setWindowTitle(_('Choose font family'))
|
||||||
|
self.setWindowIcon(QIcon(I('font.png')))
|
||||||
|
from calibre.utils.fonts.scanner import font_scanner
|
||||||
|
self.font_scanner = font_scanner
|
||||||
|
|
||||||
|
self.m = QStringListModel(self)
|
||||||
|
self.build_font_list()
|
||||||
|
self.l = l = QGridLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
self.view = FontsView(self)
|
||||||
|
self.view.setModel(self.m)
|
||||||
|
self.view.setCurrentIndex(self.m.index(0))
|
||||||
|
if current_family:
|
||||||
|
for i, val in enumerate(self.families):
|
||||||
|
if icu_lower(val) == icu_lower(current_family):
|
||||||
|
self.view.setCurrentIndex(self.m.index(i))
|
||||||
|
break
|
||||||
|
self.view.doubleClicked.connect(self.accept, type=Qt.QueuedConnection)
|
||||||
|
self.view.changed.connect(self.current_changed,
|
||||||
|
type=Qt.QueuedConnection)
|
||||||
|
self.faces = Typefaces(self)
|
||||||
|
self.bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||||
|
self.bb.accepted.connect(self.accept)
|
||||||
|
self.bb.rejected.connect(self.reject)
|
||||||
|
self.add_fonts_button = afb = self.bb.addButton(_('Add &fonts'),
|
||||||
|
self.bb.ActionRole)
|
||||||
|
afb.setIcon(QIcon(I('plus.png')))
|
||||||
|
afb.clicked.connect(self.add_fonts)
|
||||||
|
self.ml = QLabel(_('Choose a font family from the list below:'))
|
||||||
|
self.search = QLineEdit(self)
|
||||||
|
self.search.setPlaceholderText(_('Search'))
|
||||||
|
self.search.returnPressed.connect(self.find)
|
||||||
|
self.nb = QToolButton(self)
|
||||||
|
self.nb.setIcon(QIcon(I('arrow-down.png')))
|
||||||
|
self.nb.setToolTip(_('Find Next'))
|
||||||
|
self.pb = QToolButton(self)
|
||||||
|
self.pb.setIcon(QIcon(I('arrow-up.png')))
|
||||||
|
self.pb.setToolTip(_('Find Previous'))
|
||||||
|
self.nb.clicked.connect(self.find_next)
|
||||||
|
self.pb.clicked.connect(self.find_previous)
|
||||||
|
|
||||||
|
l.addWidget(self.ml, 0, 0, 1, 4)
|
||||||
|
l.addWidget(self.search, 1, 0, 1, 1)
|
||||||
|
l.addWidget(self.nb, 1, 1, 1, 1)
|
||||||
|
l.addWidget(self.pb, 1, 2, 1, 1)
|
||||||
|
l.addWidget(self.view, 2, 0, 1, 3)
|
||||||
|
l.addWidget(self.faces, 1, 3, 2, 1)
|
||||||
|
l.addWidget(self.bb, 3, 0, 1, 4)
|
||||||
|
l.setAlignment(self.faces, Qt.AlignTop)
|
||||||
|
|
||||||
|
self.resize(800, 600)
|
||||||
|
|
||||||
|
def set_current(self, i):
|
||||||
|
self.view.setCurrentIndex(self.m.index(i))
|
||||||
|
|
||||||
|
def keyPressEvent(self, e):
|
||||||
|
if e.key() == Qt.Key_Return:
|
||||||
|
return
|
||||||
|
return QDialog.keyPressEvent(self, e)
|
||||||
|
|
||||||
|
def find(self, backwards=False):
|
||||||
|
i = self.view.currentIndex().row()
|
||||||
|
if i < 0: i = 0
|
||||||
|
q = icu_lower(unicode(self.search.text())).strip()
|
||||||
|
if not q: return
|
||||||
|
r = (xrange(i-1, -1, -1) if backwards else xrange(i+1,
|
||||||
|
len(self.families)))
|
||||||
|
for j in r:
|
||||||
|
f = self.families[j]
|
||||||
|
if q in icu_lower(f):
|
||||||
|
self.set_current(j)
|
||||||
|
return
|
||||||
|
|
||||||
|
def find_next(self):
|
||||||
|
self.find()
|
||||||
|
|
||||||
|
def find_previous(self):
|
||||||
|
self.find(backwards=True)
|
||||||
|
|
||||||
|
def build_font_list(self):
|
||||||
try:
|
try:
|
||||||
self.families = fontconfig.find_font_families()
|
self.families = list(self.font_scanner.find_font_families())
|
||||||
except:
|
except:
|
||||||
self.families = []
|
self.families = []
|
||||||
print ('WARNING: Could not load fonts')
|
print ('WARNING: Could not load fonts')
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
# Restrict to Qt families as we need the font to be available in
|
|
||||||
# QFontDatabase
|
|
||||||
qt_families = set([unicode(x) for x in QFontDatabase().families()])
|
|
||||||
self.families = list(qt_families.intersection(set(self.families)))
|
|
||||||
self.families.sort(key=sort_key)
|
|
||||||
self.families.insert(0, _('None'))
|
self.families.insert(0, _('None'))
|
||||||
|
self.m.setStringList(self.families)
|
||||||
|
|
||||||
self.m = QStringListModel(self.families)
|
def add_fonts(self):
|
||||||
self.setModel(self.m)
|
from calibre.utils.fonts.metadata import FontMetadata
|
||||||
self.d = FontFamilyDelegate(self)
|
files = choose_files(self, 'add fonts to calibre',
|
||||||
self.setItemDelegate(self.d)
|
_('Select font files'), filters=[(_('TrueType/OpenType Fonts'),
|
||||||
self.setCurrentIndex(0)
|
['ttf', 'otf'])], all_files=False)
|
||||||
|
if not files: return
|
||||||
|
families = set()
|
||||||
|
for f in files:
|
||||||
|
try:
|
||||||
|
with open(f, 'rb') as stream:
|
||||||
|
fm = FontMetadata(stream)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
error_dialog(self, _('Corrupt font'),
|
||||||
|
_('Failed to read metadata from the font file: %s')%
|
||||||
|
f, det_msg=traceback.format_exc(), show=True)
|
||||||
|
return
|
||||||
|
families.add(fm.font_family)
|
||||||
|
families = sorted(families)
|
||||||
|
|
||||||
def event(self, e):
|
dest = os.path.join(config_dir, 'fonts')
|
||||||
if e.type() == e.Resize:
|
for f in files:
|
||||||
view = self.view()
|
shutil.copyfile(f, os.path.join(dest, os.path.basename(f)))
|
||||||
view.window().setFixedWidth(self.width() * 5/3)
|
self.font_scanner.do_scan()
|
||||||
return QComboBox.event(self, e)
|
self.build_font_list()
|
||||||
|
self.m.reset()
|
||||||
|
self.view.setCurrentIndex(self.m.index(0))
|
||||||
|
if families:
|
||||||
|
for i, val in enumerate(self.families):
|
||||||
|
if icu_lower(val) == icu_lower(families[0]):
|
||||||
|
self.view.setCurrentIndex(self.m.index(i))
|
||||||
|
break
|
||||||
|
|
||||||
def sizeHint(self):
|
info_dialog(self, _('Added fonts'),
|
||||||
ans = QComboBox.sizeHint(self)
|
_('Added font families: %s')%(
|
||||||
ans.setWidth(QFontMetrics(self.font()).width('m'*14))
|
', '.join(families)), show=True)
|
||||||
return ans
|
|
||||||
|
@property
|
||||||
|
def font_family(self):
|
||||||
|
idx = self.view.currentIndex().row()
|
||||||
|
if idx == 0: return None
|
||||||
|
return self.families[idx]
|
||||||
|
|
||||||
|
def current_changed(self):
|
||||||
|
fam = self.font_family
|
||||||
|
self.faces.show_family(fam, self.font_scanner.fonts_for_family(fam)
|
||||||
|
if fam else None)
|
||||||
|
|
||||||
|
class FontFamilyChooser(QWidget):
|
||||||
|
|
||||||
|
family_changed = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.l = l = QHBoxLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
self.button = QPushButton(self)
|
||||||
|
self.button.setIcon(QIcon(I('font.png')))
|
||||||
|
l.addWidget(self.button)
|
||||||
|
self.default_text = _('Choose &font family')
|
||||||
|
self.font_family = None
|
||||||
|
self.button.clicked.connect(self.show_chooser)
|
||||||
|
self.clear_button = QToolButton(self)
|
||||||
|
self.clear_button.setIcon(QIcon(I('clear_left.png')))
|
||||||
|
self.clear_button.clicked.connect(self.clear_family)
|
||||||
|
l.addWidget(self.clear_button)
|
||||||
|
self.setToolTip = self.button.setToolTip
|
||||||
|
self.toolTip = self.button.toolTip
|
||||||
|
self.clear_button.setToolTip(_('Clear the font family'))
|
||||||
|
|
||||||
|
def clear_family(self):
|
||||||
|
self.font_family = None
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
def font_family(self):
|
def font_family(self):
|
||||||
def fget(self):
|
def fget(self):
|
||||||
idx= self.currentIndex()
|
return self._current_family
|
||||||
if idx == 0: return None
|
|
||||||
return self.families[idx]
|
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
if not val:
|
if not val:
|
||||||
idx = 0
|
val = None
|
||||||
try:
|
self._current_family = val
|
||||||
idx = self.families.index(type(u'')(val))
|
self.button.setText(val or self.default_text)
|
||||||
except ValueError:
|
self.family_changed.emit(val)
|
||||||
idx = 0
|
|
||||||
self.setCurrentIndex(idx)
|
|
||||||
return property(fget=fget, fset=fset)
|
return property(fget=fget, fset=fset)
|
||||||
|
|
||||||
|
def show_chooser(self):
|
||||||
|
d = FontFamilyDialog(self.font_family, self)
|
||||||
|
if d.exec_() == d.Accepted:
|
||||||
|
self.font_family = d.font_family
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def test():
|
||||||
app = QApplication([])
|
app = QApplication([])
|
||||||
|
app
|
||||||
d = QDialog()
|
d = QDialog()
|
||||||
d.setLayout(QVBoxLayout())
|
d.setLayout(QVBoxLayout())
|
||||||
d.layout().addWidget(FontFamilyChooser(d))
|
d.layout().addWidget(FontFamilyChooser(d))
|
||||||
d.layout().addWidget(QFontComboBox(d))
|
d.layout().addWidget(QFontComboBox(d))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
||||||
|
|
||||||
|
@ -871,12 +871,18 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
try:
|
try:
|
||||||
return self._set_data(index, value)
|
return self._set_data(index, value)
|
||||||
except (IOError, OSError) as err:
|
except (IOError, OSError) as err:
|
||||||
|
import traceback
|
||||||
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
|
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
|
||||||
import traceback
|
fname = getattr(err, 'filename', None)
|
||||||
|
p = 'Locked file: %s\n\n'%fname if fname else ''
|
||||||
error_dialog(get_gui(), _('Permission denied'),
|
error_dialog(get_gui(), _('Permission denied'),
|
||||||
_('Could not change the on disk location of this'
|
_('Could not change the on disk location of this'
|
||||||
' book. Is it open in another program?'),
|
' book. Is it open in another program?'),
|
||||||
det_msg=traceback.format_exc(), show=True)
|
det_msg=p+traceback.format_exc(), show=True)
|
||||||
|
return False
|
||||||
|
error_dialog(get_gui(), _('Failed to set data'),
|
||||||
|
_('Could not set data, click Show Details to see why.'),
|
||||||
|
det_msg=traceback.format_exc(), show=True)
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@ -1368,6 +1374,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
|||||||
return QVariant(authors_to_string(au))
|
return QVariant(authors_to_string(au))
|
||||||
elif cname == 'size':
|
elif cname == 'size':
|
||||||
size = self.db[self.map[row]].size
|
size = self.db[self.map[row]].size
|
||||||
|
if not isinstance(size, (float, int)):
|
||||||
|
size = 0
|
||||||
return QVariant(human_readable(size))
|
return QVariant(human_readable(size))
|
||||||
elif cname == 'timestamp':
|
elif cname == 'timestamp':
|
||||||
dt = self.db[self.map[row]].datetime
|
dt = self.db[self.map[row]].datetime
|
||||||
|
@ -291,6 +291,7 @@ def run_in_debug_mode(logpath=None):
|
|||||||
|
|
||||||
def run_gui(opts, args, actions, listener, app, gui_debug=None):
|
def run_gui(opts, args, actions, listener, app, gui_debug=None):
|
||||||
initialize_file_icon_provider()
|
initialize_file_icon_provider()
|
||||||
|
app.load_builtin_fonts(scan_for_fonts=True)
|
||||||
if not dynamic.get('welcome_wizard_was_run', False):
|
if not dynamic.get('welcome_wizard_was_run', False):
|
||||||
from calibre.gui2.wizard import wizard
|
from calibre.gui2.wizard import wizard
|
||||||
wizard().exec_()
|
wizard().exec_()
|
||||||
|
@ -139,3 +139,5 @@ class MainWindow(QMainWindow):
|
|||||||
show=True)
|
show=True)
|
||||||
except BaseException:
|
except BaseException:
|
||||||
pass
|
pass
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
@ -91,22 +91,26 @@ class TitleEdit(EnLineEdit):
|
|||||||
|
|
||||||
def commit(self, db, id_):
|
def commit(self, db, id_):
|
||||||
title = self.current_val
|
title = self.current_val
|
||||||
try:
|
if title != self.original_val:
|
||||||
if self.COMMIT:
|
# Only try to commit if changed. This allow setting of other fields
|
||||||
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False)
|
# to work even if some of the book files are opened in windows.
|
||||||
else:
|
try:
|
||||||
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False,
|
if self.COMMIT:
|
||||||
commit=False)
|
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False)
|
||||||
except (IOError, OSError) as err:
|
else:
|
||||||
if getattr(err, 'errno', -1) == errno.EACCES: # Permission denied
|
getattr(db, 'set_'+ self.TITLE_ATTR)(id_, title, notify=False,
|
||||||
import traceback
|
commit=False)
|
||||||
fname = err.filename if err.filename else 'file'
|
except (IOError, OSError) as err:
|
||||||
error_dialog(self, _('Permission denied'),
|
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
|
||||||
_('Could not open %s. Is it being used by another'
|
import traceback
|
||||||
' program?')%fname, det_msg=traceback.format_exc(),
|
fname = getattr(err, 'filename', None)
|
||||||
show=True)
|
p = 'Locked file: %s\n\n'%fname if fname else ''
|
||||||
return False
|
error_dialog(self, _('Permission denied'),
|
||||||
raise
|
_('Could not change the on disk location of this'
|
||||||
|
' book. Is it open in another program?'),
|
||||||
|
det_msg=p+traceback.format_exc(), show=True)
|
||||||
|
return False
|
||||||
|
raise
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
@ -262,19 +266,23 @@ class AuthorsEdit(EditWithComplete):
|
|||||||
|
|
||||||
def commit(self, db, id_):
|
def commit(self, db, id_):
|
||||||
authors = self.current_val
|
authors = self.current_val
|
||||||
try:
|
if authors != self.original_val:
|
||||||
self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
|
# Only try to commit if changed. This allow setting of other fields
|
||||||
allow_case_change=True)
|
# to work even if some of the book files are opened in windows.
|
||||||
except (IOError, OSError) as err:
|
try:
|
||||||
if getattr(err, 'errno', -1) == errno.EACCES: # Permission denied
|
self.books_to_refresh |= db.set_authors(id_, authors, notify=False,
|
||||||
import traceback
|
allow_case_change=True)
|
||||||
fname = err.filename if err.filename else 'file'
|
except (IOError, OSError) as err:
|
||||||
error_dialog(self, _('Permission denied'),
|
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
|
||||||
_('Could not open %s. Is it being used by another'
|
import traceback
|
||||||
' program?')%fname, det_msg=traceback.format_exc(),
|
fname = getattr(err, 'filename', None)
|
||||||
show=True)
|
p = 'Locked file: %s\n\n'%fname if fname else ''
|
||||||
return False
|
error_dialog(self, _('Permission denied'),
|
||||||
raise
|
_('Could not change the on disk location of this'
|
||||||
|
' book. Is it open in another program?'),
|
||||||
|
det_msg=p+traceback.format_exc(), show=True)
|
||||||
|
return False
|
||||||
|
raise
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@dynamic_property
|
@dynamic_property
|
||||||
|
@ -322,6 +322,7 @@ class MetadataSingleDialogBase(ResizableDialog):
|
|||||||
' program?')%fname, det_msg=traceback.format_exc(),
|
' program?')%fname, det_msg=traceback.format_exc(),
|
||||||
show=True)
|
show=True)
|
||||||
return
|
return
|
||||||
|
raise
|
||||||
if mi is None:
|
if mi is None:
|
||||||
return
|
return
|
||||||
cdata = None
|
cdata = None
|
||||||
@ -444,11 +445,12 @@ class MetadataSingleDialogBase(ResizableDialog):
|
|||||||
except (IOError, OSError) as err:
|
except (IOError, OSError) as err:
|
||||||
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
|
if getattr(err, 'errno', None) == errno.EACCES: # Permission denied
|
||||||
import traceback
|
import traceback
|
||||||
fname = err.filename if err.filename else 'file'
|
fname = getattr(err, 'filename', None)
|
||||||
|
p = 'Locked file: %s\n\n'%fname if fname else ''
|
||||||
error_dialog(self, _('Permission denied'),
|
error_dialog(self, _('Permission denied'),
|
||||||
_('Could not open %s. Is it being used by another'
|
_('Could not change the on disk location of this'
|
||||||
' program?')%fname, det_msg=traceback.format_exc(),
|
' book. Is it open in another program?'),
|
||||||
show=True)
|
det_msg=p+traceback.format_exc(), show=True)
|
||||||
return False
|
return False
|
||||||
raise
|
raise
|
||||||
for widget in getattr(self, 'custom_metadata_widgets', []):
|
for widget in getattr(self, 'custom_metadata_widgets', []):
|
||||||
|
@ -156,7 +156,7 @@ Author matching is exact.</string>
|
|||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>If set, this option will causes calibre to check if a file
|
<string>If set, this option will causes calibre to check if a file
|
||||||
being auto-added is already in the calibre library.
|
being auto-added is already in the calibre library.
|
||||||
If it is, a meesage will pop up asking you whether
|
If it is, a message will pop up asking you whether
|
||||||
you want to add it anyway.</string>
|
you want to add it anyway.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -19,12 +19,14 @@ from calibre.utils.smtp import config as smtp_prefs
|
|||||||
|
|
||||||
class EmailAccounts(QAbstractTableModel): # {{{
|
class EmailAccounts(QAbstractTableModel): # {{{
|
||||||
|
|
||||||
def __init__(self, accounts, subjects):
|
def __init__(self, accounts, subjects, aliases={}):
|
||||||
QAbstractTableModel.__init__(self)
|
QAbstractTableModel.__init__(self)
|
||||||
self.accounts = accounts
|
self.accounts = accounts
|
||||||
self.subjects = subjects
|
self.subjects = subjects
|
||||||
|
self.aliases = aliases
|
||||||
self.account_order = sorted(self.accounts.keys())
|
self.account_order = sorted(self.accounts.keys())
|
||||||
self.headers = map(QVariant, [_('Email'), _('Formats'), _('Subject'), _('Auto send')])
|
self.headers = map(QVariant, [_('Email'), _('Formats'), _('Subject'),
|
||||||
|
_('Auto send'), _('Alias')])
|
||||||
self.default_font = QFont()
|
self.default_font = QFont()
|
||||||
self.default_font.setBold(True)
|
self.default_font.setBold(True)
|
||||||
self.default_font = QVariant(self.default_font)
|
self.default_font = QVariant(self.default_font)
|
||||||
@ -36,7 +38,9 @@ class EmailAccounts(QAbstractTableModel): # {{{
|
|||||||
'{author_sort} can be used here.'),
|
'{author_sort} can be used here.'),
|
||||||
'<p>'+_('If checked, downloaded news will be automatically '
|
'<p>'+_('If checked, downloaded news will be automatically '
|
||||||
'mailed <br>to this email address '
|
'mailed <br>to this email address '
|
||||||
'(provided it is in one of the listed formats).')])))
|
'(provided it is in one of the listed formats).'),
|
||||||
|
_('Friendly name to use for this email address')
|
||||||
|
])))
|
||||||
|
|
||||||
def rowCount(self, *args):
|
def rowCount(self, *args):
|
||||||
return len(self.account_order)
|
return len(self.account_order)
|
||||||
@ -67,6 +71,8 @@ class EmailAccounts(QAbstractTableModel): # {{{
|
|||||||
return QVariant(self.accounts[account][0])
|
return QVariant(self.accounts[account][0])
|
||||||
if col == 2:
|
if col == 2:
|
||||||
return QVariant(self.subjects.get(account, ''))
|
return QVariant(self.subjects.get(account, ''))
|
||||||
|
if col == 4:
|
||||||
|
return QVariant(self.aliases.get(account, ''))
|
||||||
if role == Qt.FontRole and self.accounts[account][2]:
|
if role == Qt.FontRole and self.accounts[account][2]:
|
||||||
return self.default_font
|
return self.default_font
|
||||||
if role == Qt.CheckStateRole and col == 3:
|
if role == Qt.CheckStateRole and col == 3:
|
||||||
@ -88,6 +94,11 @@ class EmailAccounts(QAbstractTableModel): # {{{
|
|||||||
self.accounts[account][1] ^= True
|
self.accounts[account][1] ^= True
|
||||||
elif col == 2:
|
elif col == 2:
|
||||||
self.subjects[account] = unicode(value.toString())
|
self.subjects[account] = unicode(value.toString())
|
||||||
|
elif col == 4:
|
||||||
|
self.aliases.pop(account, None)
|
||||||
|
aval = unicode(value.toString()).strip()
|
||||||
|
if aval:
|
||||||
|
self.aliases[account] = aval
|
||||||
elif col == 1:
|
elif col == 1:
|
||||||
self.accounts[account][0] = unicode(value.toString()).upper()
|
self.accounts[account][0] = unicode(value.toString()).upper()
|
||||||
elif col == 0:
|
elif col == 0:
|
||||||
@ -156,7 +167,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.send_email_widget.initialize(self.preferred_to_address)
|
self.send_email_widget.initialize(self.preferred_to_address)
|
||||||
self.send_email_widget.changed_signal.connect(self.changed_signal.emit)
|
self.send_email_widget.changed_signal.connect(self.changed_signal.emit)
|
||||||
opts = self.send_email_widget.smtp_opts
|
opts = self.send_email_widget.smtp_opts
|
||||||
self._email_accounts = EmailAccounts(opts.accounts, opts.subjects)
|
self._email_accounts = EmailAccounts(opts.accounts, opts.subjects,
|
||||||
|
opts.aliases)
|
||||||
self._email_accounts.dataChanged.connect(lambda x,y:
|
self._email_accounts.dataChanged.connect(lambda x,y:
|
||||||
self.changed_signal.emit())
|
self.changed_signal.emit())
|
||||||
self.email_view.setModel(self._email_accounts)
|
self.email_view.setModel(self._email_accounts)
|
||||||
@ -184,6 +196,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
raise AbortCommit('abort')
|
raise AbortCommit('abort')
|
||||||
self.proxy['accounts'] = self._email_accounts.accounts
|
self.proxy['accounts'] = self._email_accounts.accounts
|
||||||
self.proxy['subjects'] = self._email_accounts.subjects
|
self.proxy['subjects'] = self._email_accounts.subjects
|
||||||
|
self.proxy['aliases'] = self._email_accounts.aliases
|
||||||
|
|
||||||
return ConfigWidgetBase.commit(self)
|
return ConfigWidgetBase.commit(self)
|
||||||
|
|
||||||
|
@ -403,7 +403,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
return
|
return
|
||||||
|
|
||||||
all_locations = OrderedDict(ConfigWidget.LOCATIONS)
|
all_locations = OrderedDict(ConfigWidget.LOCATIONS)
|
||||||
plugin_action = plugin.load_actual_plugin(self.gui)
|
try:
|
||||||
|
plugin_action = plugin.load_actual_plugin(self.gui)
|
||||||
|
except:
|
||||||
|
# Broken plugin, fails to initialize. Given that, it's probably
|
||||||
|
# already configured, so we can just quit.
|
||||||
|
return
|
||||||
installed_actions = OrderedDict([
|
installed_actions = OrderedDict([
|
||||||
(key, list(gprefs.get('action-layout-'+key, [])))
|
(key, list(gprefs.get('action-layout-'+key, [])))
|
||||||
for key in all_locations])
|
for key in all_locations])
|
||||||
|
@ -127,35 +127,14 @@ class AmazonKindleStore(StorePlugin):
|
|||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read().decode('latin-1', 'replace'))
|
doc = html.fromstring(f.read().decode('latin-1', 'replace'))
|
||||||
|
|
||||||
# Amazon has two results pages.
|
data_xpath = '//div[contains(@class, "prod")]'
|
||||||
is_shot = doc.xpath('boolean(//div[@id="shotgunMainResults"])')
|
format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()'
|
||||||
# Horizontal grid of books. Search "Paolo Bacigalupi"
|
asin_xpath = './/div[@class="image"]/a[1]'
|
||||||
if is_shot:
|
cover_xpath = './/img[@class="productImage"]/@src'
|
||||||
data_xpath = '//div[contains(@class, "result")]'
|
title_xpath = './/h3[@class="newaps"]/a//text()'
|
||||||
format_xpath = './/div[@class="productTitle"]//text()'
|
author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()'
|
||||||
asin_xpath = './/div[@class="productTitle"]//a'
|
price_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and contains(@class, "bld")]/text()'
|
||||||
cover_xpath = './/div[@class="productTitle"]//img/@src'
|
|
||||||
title_xpath = './/div[@class="productTitle"]/a//text()'
|
|
||||||
price_xpath = './/div[@class="newPrice"]/span/text()'
|
|
||||||
# Vertical list of books.
|
|
||||||
else:
|
|
||||||
# New style list. Search "Paolo Bacigalupi"
|
|
||||||
if doc.xpath('boolean(//div[@class="image"])'):
|
|
||||||
data_xpath = '//div[contains(@class, "results")]//div[contains(@class, "result")]'
|
|
||||||
format_xpath = './/span[@class="binding"]//text()'
|
|
||||||
asin_xpath = './/div[@class="image"]/a[1]'
|
|
||||||
cover_xpath = './/img[@class="productImage"]/@src'
|
|
||||||
title_xpath = './/a[@class="title"]/text()'
|
|
||||||
price_xpath = './/span[contains(@class, "price")]/text()'
|
|
||||||
# Old style list. Search "martin"
|
|
||||||
else:
|
|
||||||
data_xpath = '//div[contains(@class, "result")]'
|
|
||||||
format_xpath = './/span[@class="format"]//text()'
|
|
||||||
asin_xpath = './/div[@class="productImage"]/a[1]'
|
|
||||||
cover_xpath = './/div[@class="productImage"]//img/@src'
|
|
||||||
title_xpath = './/div[@class="productTitle"]/a/text()'
|
|
||||||
price_xpath = './/div[@class="newPrice"]//span//text()'
|
|
||||||
|
|
||||||
for data in doc.xpath(data_xpath):
|
for data in doc.xpath(data_xpath):
|
||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
@ -186,14 +165,14 @@ class AmazonKindleStore(StorePlugin):
|
|||||||
cover_url = ''.join(data.xpath(cover_xpath))
|
cover_url = ''.join(data.xpath(cover_xpath))
|
||||||
|
|
||||||
title = ''.join(data.xpath(title_xpath))
|
title = ''.join(data.xpath(title_xpath))
|
||||||
|
author = ''.join(data.xpath(author_xpath))
|
||||||
|
try:
|
||||||
|
author = author.split('by ', 1)[1].split(" (")[0]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
price = ''.join(data.xpath(price_xpath))
|
price = ''.join(data.xpath(price_xpath))
|
||||||
|
|
||||||
if is_shot:
|
|
||||||
author = format.split(' by ')[-1]
|
|
||||||
else:
|
|
||||||
author = ''.join(data.xpath('.//span[@class="ptBrand"]/text()'))
|
|
||||||
author = author.split('by ')[-1]
|
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
|
@ -6,7 +6,6 @@ __license__ = 'GPL 3'
|
|||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import random
|
|
||||||
import re
|
import re
|
||||||
import urllib2
|
import urllib2
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
@ -25,23 +24,12 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
|||||||
class EHarlequinStore(BasicStoreConfig, StorePlugin):
|
class EHarlequinStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
m_url = 'http://www.dpbolvw.net/'
|
url = 'http://www.harlequin.com/'
|
||||||
h_click = 'click-4879827-534091'
|
|
||||||
d_click = 'click-4879827-10375439'
|
|
||||||
# Use Kovid's affiliate id 30% of the time.
|
|
||||||
if random.randint(1, 10) in (1, 2, 3):
|
|
||||||
h_click = 'click-4913808-534091'
|
|
||||||
d_click = 'click-4913808-10375439'
|
|
||||||
|
|
||||||
url = m_url + h_click
|
|
||||||
detail_url = None
|
|
||||||
if detail_item:
|
|
||||||
detail_url = m_url + d_click + detail_item
|
|
||||||
|
|
||||||
if external or self.config.get('open_external', False):
|
if external or self.config.get('open_external', False):
|
||||||
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
|
||||||
else:
|
else:
|
||||||
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
d = WebStoreDialog(self.gui, url, parent, detail_item)
|
||||||
d.setWindowTitle(self.name)
|
d.setWindowTitle(self.name)
|
||||||
d.set_tags(self.config.get('tags', ''))
|
d.set_tags(self.config.get('tags', ''))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
@ -74,7 +62,7 @@ class EHarlequinStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
s.author = author.strip()
|
s.author = author.strip()
|
||||||
s.price = price.strip()
|
s.price = price.strip()
|
||||||
s.detail_item = '?url=http://ebooks.eharlequin.com/' + id.strip()
|
s.detail_item = 'http://ebooks.eharlequin.com/' + id.strip()
|
||||||
s.formats = 'EPUB'
|
s.formats = 'EPUB'
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
80
src/calibre/gui2/store/stores/empik_plugin.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL 3'
|
||||||
|
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import re
|
||||||
|
import urllib
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
|
from lxml import html
|
||||||
|
|
||||||
|
from PyQt4.Qt import QUrl
|
||||||
|
|
||||||
|
from calibre import browser, url_slash_cleaner
|
||||||
|
from calibre.gui2 import open_url
|
||||||
|
from calibre.gui2.store import StorePlugin
|
||||||
|
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||||
|
from calibre.gui2.store.search_result import SearchResult
|
||||||
|
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||||
|
|
||||||
|
class EmpikStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
|
plain_url = 'http://www.empik.com/ebooki'
|
||||||
|
url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,23c7f,,,?u=(' + plain_url + ')'
|
||||||
|
detail_url = None
|
||||||
|
|
||||||
|
if detail_item:
|
||||||
|
detail_url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,23c7f,,,?u=(' + detail_item + ')'
|
||||||
|
|
||||||
|
if external or self.config.get('open_external', False):
|
||||||
|
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
||||||
|
else:
|
||||||
|
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
||||||
|
d.setWindowTitle(self.name)
|
||||||
|
d.set_tags(self.config.get('tags', ''))
|
||||||
|
d.exec_()
|
||||||
|
|
||||||
|
def search(self, query, max_results=10, timeout=60):
|
||||||
|
url = 'http://www.empik.com/szukaj/produkt?c=ebooki-ebooki&q=' + urllib.quote(query) + '&qtype=basicForm&start=1&catalogType=pl&searchCategory=3501&resultsPP=' + str(max_results)
|
||||||
|
|
||||||
|
br = browser()
|
||||||
|
|
||||||
|
counter = max_results
|
||||||
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
|
doc = html.fromstring(f.read())
|
||||||
|
for data in doc.xpath('//div[@class="productsSet"]/div'):
|
||||||
|
if counter <= 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
id = ''.join(data.xpath('.//a[@class="productBox-450Title"]/@href'))
|
||||||
|
if not id:
|
||||||
|
continue
|
||||||
|
|
||||||
|
cover_url = ''.join(data.xpath('.//div[@class="productBox-450Pic"]/a/img/@src'))
|
||||||
|
title = ''.join(data.xpath('.//a[@class="productBox-450Title"]/text()'))
|
||||||
|
title = re.sub(r' \(ebook\)', '', title)
|
||||||
|
author = ''.join(data.xpath('.//div[@class="productBox-450Author"]/a/text()'))
|
||||||
|
price = ''.join(data.xpath('.//div[@class="actPrice"]/text()'))
|
||||||
|
formats = ''.join(data.xpath('.//div[@class="productBox-450Type"]/text()'))
|
||||||
|
formats = re.sub(r'Ebook *,? *','', formats)
|
||||||
|
formats = re.sub(r'\(.*\)','', formats)
|
||||||
|
drm = data.xpath('boolean(.//div[@class="productBox-450Type" and contains(text(), "ADE")])')
|
||||||
|
|
||||||
|
counter -= 1
|
||||||
|
|
||||||
|
s = SearchResult()
|
||||||
|
s.cover_url = cover_url
|
||||||
|
s.title = title.strip() + ' ' + formats
|
||||||
|
s.author = author.strip()
|
||||||
|
s.price = price
|
||||||
|
s.detail_item = 'http://empik.com' + id.strip()
|
||||||
|
s.formats = formats.upper().strip()
|
||||||
|
s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED
|
||||||
|
|
||||||
|
yield s
|
||||||
|
|
@ -68,10 +68,10 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
title = ''.join(data.xpath('.//h3/a//text()'))
|
title = ''.join(data.xpath('.//h3/a//text()'))
|
||||||
authors = data.xpath('.//span[@class="f"]//a//text()')
|
authors = data.xpath('.//div[@class="f"]//a//text()')
|
||||||
if authors and authors[-1].strip().lower() in ('preview', 'read'):
|
while authors and authors[-1].strip().lower() in ('preview', 'read', 'more editions'):
|
||||||
authors = authors[:-1]
|
authors = authors[:-1]
|
||||||
else:
|
if not authors:
|
||||||
continue
|
continue
|
||||||
author = ', '.join(authors)
|
author = ', '.join(authors)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ __copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
@ -24,23 +25,24 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
|||||||
class KoboStore(BasicStoreConfig, StorePlugin):
|
class KoboStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
m_url = 'http://www.dpbolvw.net/'
|
pub_id = 'sHa5EXvYOwA'
|
||||||
h_click = 'click-4879827-10762497'
|
|
||||||
d_click = 'click-4879827-10772898'
|
|
||||||
# Use Kovid's affiliate id 30% of the time.
|
# Use Kovid's affiliate id 30% of the time.
|
||||||
if random.randint(1, 10) in (1, 2, 3):
|
if random.randint(1, 10) in (1, 2, 3):
|
||||||
h_click = 'click-4913808-10762497'
|
pub_id = '0dsO3kDu/AU'
|
||||||
d_click = 'click-4913808-10772898'
|
|
||||||
|
murl = 'http://click.linksynergy.com/fs-bin/click?id=%s&offerid=268429.4&type=3&subid=0' % pub_id
|
||||||
|
|
||||||
url = m_url + h_click
|
|
||||||
detail_url = None
|
|
||||||
if detail_item:
|
if detail_item:
|
||||||
detail_url = m_url + d_click + detail_item
|
purl = 'http://click.linksynergy.com/link?id=%s&offerid=268429&type=2&murl=%s' % (pub_id, urllib.quote_plus(detail_item))
|
||||||
|
url = purl
|
||||||
|
else:
|
||||||
|
purl = None
|
||||||
|
url = murl
|
||||||
|
|
||||||
if external or self.config.get('open_external', False):
|
if external or self.config.get('open_external', False):
|
||||||
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
open_url(QUrl(url_slash_cleaner(url)))
|
||||||
else:
|
else:
|
||||||
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
d = WebStoreDialog(self.gui, murl, parent, purl)
|
||||||
d.setWindowTitle(self.name)
|
d.setWindowTitle(self.name)
|
||||||
d.set_tags(self.config.get('tags', ''))
|
d.set_tags(self.config.get('tags', ''))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
@ -60,15 +62,19 @@ class KoboStore(BasicStoreConfig, StorePlugin):
|
|||||||
id = ''.join(data.xpath('.//div[@class="SearchImageContainer"]/a[1]/@href'))
|
id = ''.join(data.xpath('.//div[@class="SearchImageContainer"]/a[1]/@href'))
|
||||||
if not id:
|
if not id:
|
||||||
continue
|
continue
|
||||||
|
try:
|
||||||
|
id = id.split('?', 1)[0]
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
price = ''.join(data.xpath('.//span[@class="OurPrice"]/strong/text()'))
|
price = ''.join(data.xpath('.//span[@class="KV2OurPrice"]/strong/text()'))
|
||||||
if not price:
|
if not price:
|
||||||
price = '$0.00'
|
price = '$0.00'
|
||||||
|
|
||||||
cover_url = ''.join(data.xpath('.//div[@class="SearchImageContainer"]//img[1]/@src'))
|
cover_url = ''.join(data.xpath('.//div[@class="SearchImageContainer"]//img[1]/@src'))
|
||||||
|
|
||||||
title = ''.join(data.xpath('.//div[@class="SCItemHeader"]/h1/a[1]/text()'))
|
title = ''.join(data.xpath('.//div[@class="SCItemHeader"]//a[1]/text()'))
|
||||||
author = ', '.join(data.xpath('.//div[@class="SCItemSummary"]//span//a/text()'))
|
author = ', '.join(data.xpath('.//div[@class="SCItemSummary"]//span[contains(@class, "Author")]//a/text()'))
|
||||||
drm = data.xpath('boolean(.//span[@class="SCAvailibilityFormatsText" and not(contains(text(), "DRM-Free"))])')
|
drm = data.xpath('boolean(.//span[@class="SCAvailibilityFormatsText" and not(contains(text(), "DRM-Free"))])')
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
@ -78,7 +84,7 @@ class KoboStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
s.author = author.strip()
|
s.author = author.strip()
|
||||||
s.price = price.strip()
|
s.price = price.strip()
|
||||||
s.detail_item = '?url=http://www.kobobooks.com/' + id.strip()
|
s.detail_item = 'http://www.kobobooks.com/' + id.strip()
|
||||||
s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED
|
s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED
|
||||||
s.formats = 'EPUB'
|
s.formats = 'EPUB'
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@ class SonyStore(BasicStoreConfig, StorePlugin):
|
|||||||
detail_url = ''.join(item.xpath('descendant::h3[@class="item"]'
|
detail_url = ''.join(item.xpath('descendant::h3[@class="item"]'
|
||||||
'/descendant::a[@class="fn" and @href]/@href'))
|
'/descendant::a[@class="fn" and @href]/@href'))
|
||||||
if not detail_url: continue
|
if not detail_url: continue
|
||||||
|
if detail_url.startswith('/'):
|
||||||
|
detail_url = 'http:'+detail_url
|
||||||
s.detail_item = detail_url
|
s.detail_item = detail_url
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
@ -415,10 +415,10 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
if not tag.sort:
|
if not tag.sort:
|
||||||
c = ' '
|
c = ' '
|
||||||
else:
|
else:
|
||||||
c = tag.sort
|
c = icu_upper(tag.sort)
|
||||||
ordnum, ordlen = collation_order(c)
|
ordnum, ordlen = collation_order(c)
|
||||||
if last_ordnum != ordnum:
|
if last_ordnum != ordnum:
|
||||||
last_c = icu_upper(c[0:ordlen])
|
last_c = c[0:ordlen]
|
||||||
last_ordnum = ordnum
|
last_ordnum = ordnum
|
||||||
cl_list[idx] = last_c
|
cl_list[idx] = last_c
|
||||||
top_level_component = 'z' + data[key][0].original_name
|
top_level_component = 'z' + data[key][0].original_name
|
||||||
|
@ -191,10 +191,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
self.content_server = None
|
self.content_server = None
|
||||||
self.spare_servers = []
|
self.spare_servers = []
|
||||||
self.must_restart_before_config = False
|
self.must_restart_before_config = False
|
||||||
# Initialize fontconfig in a separate thread as this can be a lengthy
|
|
||||||
# process if run for the first time on this machine
|
|
||||||
from calibre.utils.fonts import fontconfig
|
|
||||||
self.fc = fontconfig
|
|
||||||
self.listener = Listener(listener)
|
self.listener = Listener(listener)
|
||||||
self.check_messages_timer = QTimer()
|
self.check_messages_timer = QTimer()
|
||||||
self.connect(self.check_messages_timer, SIGNAL('timeout()'),
|
self.connect(self.check_messages_timer, SIGNAL('timeout()'),
|
||||||
|
@ -16,7 +16,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
|||||||
|
|
||||||
from calibre.gui2.viewer.flip import SlideFlip
|
from calibre.gui2.viewer.flip import SlideFlip
|
||||||
from calibre.gui2.shortcuts import Shortcuts
|
from calibre.gui2.shortcuts import Shortcuts
|
||||||
from calibre import prints, load_builtin_fonts
|
from calibre import prints
|
||||||
from calibre.customize.ui import all_viewer_plugins
|
from calibre.customize.ui import all_viewer_plugins
|
||||||
from calibre.gui2.viewer.keys import SHORTCUTS
|
from calibre.gui2.viewer.keys import SHORTCUTS
|
||||||
from calibre.gui2.viewer.javascript import JavaScriptLoader
|
from calibre.gui2.viewer.javascript import JavaScriptLoader
|
||||||
@ -86,7 +86,6 @@ class Document(QWebPage): # {{{
|
|||||||
settings = self.settings()
|
settings = self.settings()
|
||||||
|
|
||||||
# Fonts
|
# Fonts
|
||||||
load_builtin_fonts()
|
|
||||||
self.all_viewer_plugins = tuple(all_viewer_plugins())
|
self.all_viewer_plugins = tuple(all_viewer_plugins())
|
||||||
for pl in self.all_viewer_plugins:
|
for pl in self.all_viewer_plugins:
|
||||||
pl.load_fonts()
|
pl.load_fonts()
|
||||||
@ -486,7 +485,7 @@ class DocumentView(QWebView): # {{{
|
|||||||
self.dictionary_action.triggered.connect(self.lookup)
|
self.dictionary_action.triggered.connect(self.lookup)
|
||||||
self.addAction(self.dictionary_action)
|
self.addAction(self.dictionary_action)
|
||||||
self.image_popup = ImagePopup(self)
|
self.image_popup = ImagePopup(self)
|
||||||
self.view_image_action = QAction(_('View &image...'), self)
|
self.view_image_action = QAction(QIcon(I('view-image.png')), _('View &image...'), self)
|
||||||
self.view_image_action.triggered.connect(self.image_popup)
|
self.view_image_action.triggered.connect(self.image_popup)
|
||||||
self.search_action = QAction(QIcon(I('dictionary.png')),
|
self.search_action = QAction(QIcon(I('dictionary.png')),
|
||||||
_('&Search for next occurrence'), self)
|
_('&Search for next occurrence'), self)
|
||||||
|
@ -8,7 +8,8 @@ __copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from PyQt4.Qt import (QDialog, QPixmap, QUrl, QScrollArea, QLabel, QSizePolicy,
|
from PyQt4.Qt import (QDialog, QPixmap, QUrl, QScrollArea, QLabel, QSizePolicy,
|
||||||
QDialogButtonBox, QVBoxLayout, QPalette, QApplication, QSize, QIcon, Qt)
|
QDialogButtonBox, QVBoxLayout, QPalette, QApplication, QSize, QIcon,
|
||||||
|
Qt, QTransform)
|
||||||
|
|
||||||
from calibre.gui2 import choose_save_file, gprefs
|
from calibre.gui2 import choose_save_file, gprefs
|
||||||
|
|
||||||
@ -37,12 +38,15 @@ class ImageView(QDialog):
|
|||||||
self.zi_button = zi = bb.addButton(_('Zoom &in'), bb.ActionRole)
|
self.zi_button = zi = bb.addButton(_('Zoom &in'), bb.ActionRole)
|
||||||
self.zo_button = zo = bb.addButton(_('Zoom &out'), bb.ActionRole)
|
self.zo_button = zo = bb.addButton(_('Zoom &out'), bb.ActionRole)
|
||||||
self.save_button = so = bb.addButton(_('&Save as'), bb.ActionRole)
|
self.save_button = so = bb.addButton(_('&Save as'), bb.ActionRole)
|
||||||
|
self.rotate_button = ro = bb.addButton(_('&Rotate'), bb.ActionRole)
|
||||||
zi.setIcon(QIcon(I('plus.png')))
|
zi.setIcon(QIcon(I('plus.png')))
|
||||||
zo.setIcon(QIcon(I('minus.png')))
|
zo.setIcon(QIcon(I('minus.png')))
|
||||||
so.setIcon(QIcon(I('save.png')))
|
so.setIcon(QIcon(I('save.png')))
|
||||||
|
ro.setIcon(QIcon(I('rotate-right.png')))
|
||||||
zi.clicked.connect(self.zoom_in)
|
zi.clicked.connect(self.zoom_in)
|
||||||
zo.clicked.connect(self.zoom_out)
|
zo.clicked.connect(self.zoom_out)
|
||||||
so.clicked.connect(self.save_image)
|
so.clicked.connect(self.save_image)
|
||||||
|
ro.clicked.connect(self.rotate_image)
|
||||||
|
|
||||||
self.l = l = QVBoxLayout()
|
self.l = l = QVBoxLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
@ -76,6 +80,14 @@ class ImageView(QDialog):
|
|||||||
self.scrollarea.verticalScrollBar()):
|
self.scrollarea.verticalScrollBar()):
|
||||||
sb.setValue(int(factor*sb.value()) + ((factor - 1) * sb.pageStep()/2))
|
sb.setValue(int(factor*sb.value()) + ((factor - 1) * sb.pageStep()/2))
|
||||||
|
|
||||||
|
def rotate_image(self):
|
||||||
|
pm = self.label.pixmap()
|
||||||
|
t = QTransform()
|
||||||
|
t.rotate(90)
|
||||||
|
pm = pm.transformed(t)
|
||||||
|
self.label.setPixmap(pm)
|
||||||
|
self.label.adjustSize()
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
geom = self.avail_geom
|
geom = self.avail_geom
|
||||||
self.label.setPixmap(self.current_img)
|
self.label.setPixmap(self.current_img)
|
||||||
@ -93,6 +105,14 @@ class ImageView(QDialog):
|
|||||||
gprefs['viewer_image_popup_geometry'] = bytearray(self.saveGeometry())
|
gprefs['viewer_image_popup_geometry'] = bytearray(self.saveGeometry())
|
||||||
return QDialog.done(self, e)
|
return QDialog.done(self, e)
|
||||||
|
|
||||||
|
def wheelEvent(self, event):
|
||||||
|
if event.delta() < -14:
|
||||||
|
self.zoom_out()
|
||||||
|
event.accept()
|
||||||
|
elif event.delta() > 14:
|
||||||
|
event.accept()
|
||||||
|
self.zoom_in()
|
||||||
|
|
||||||
class ImagePopup(object):
|
class ImagePopup(object):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@ -114,3 +134,12 @@ class ImagePopup(object):
|
|||||||
if not d.isVisible():
|
if not d.isVisible():
|
||||||
self.dialogs.remove(d)
|
self.dialogs.remove(d)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
app = QApplication([])
|
||||||
|
p = QPixmap()
|
||||||
|
p.load(sys.argv[-1])
|
||||||
|
u = QUrl.fromLocalFile(sys.argv[-1])
|
||||||
|
d = ImageView(None, p, u)
|
||||||
|
d()
|
||||||
|
app.exec_()
|
||||||
|
@ -963,7 +963,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
self.iterator.__exit__()
|
self.iterator.__exit__()
|
||||||
self.iterator = EbookIterator(pathtoebook)
|
self.iterator = EbookIterator(pathtoebook)
|
||||||
self.open_progress_indicator(_('Loading ebook...'))
|
self.open_progress_indicator(_('Loading ebook...'))
|
||||||
worker = Worker(target=self.iterator.__enter__)
|
worker = Worker(target=partial(self.iterator.__enter__,
|
||||||
|
extract_embedded_fonts_for_qt=True))
|
||||||
worker.start()
|
worker.start()
|
||||||
while worker.isAlive():
|
while worker.isAlive():
|
||||||
worker.join(0.1)
|
worker.join(0.1)
|
||||||
@ -1136,6 +1137,7 @@ def main(args=sys.argv):
|
|||||||
if pid <= 0:
|
if pid <= 0:
|
||||||
override = 'calibre-ebook-viewer' if islinux else None
|
override = 'calibre-ebook-viewer' if islinux else None
|
||||||
app = Application(args, override_program_name=override)
|
app = Application(args, override_program_name=override)
|
||||||
|
app.load_builtin_fonts()
|
||||||
app.setWindowIcon(QIcon(I('viewer.png')))
|
app.setWindowIcon(QIcon(I('viewer.png')))
|
||||||
QApplication.setOrganizationName(ORG_NAME)
|
QApplication.setOrganizationName(ORG_NAME)
|
||||||
QApplication.setApplicationName(APP_UID)
|
QApplication.setApplicationName(APP_UID)
|
||||||
|
@ -3,18 +3,16 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
'''
|
'''
|
||||||
Miscellaneous widgets used in the GUI
|
Miscellaneous widgets used in the GUI
|
||||||
'''
|
'''
|
||||||
import re, traceback, os
|
import re, os
|
||||||
|
|
||||||
from PyQt4.Qt import (QIcon, QFont, QLabel, QListWidget, QAction,
|
from PyQt4.Qt import (QIcon, QFont, QLabel, QListWidget, QAction,
|
||||||
QListWidgetItem, QTextCharFormat, QApplication, QSyntaxHighlighter,
|
QListWidgetItem, QTextCharFormat, QApplication, QSyntaxHighlighter,
|
||||||
QCursor, QColor, QWidget, QPixmap, QSplitterHandle, QToolButton,
|
QCursor, QColor, QWidget, QPixmap, QSplitterHandle, QToolButton,
|
||||||
QAbstractListModel, QVariant, Qt, SIGNAL, pyqtSignal, QRegExp, QSize,
|
QVariant, Qt, SIGNAL, pyqtSignal, QRegExp, QSize, QSplitter, QPainter,
|
||||||
QSplitter, QPainter, QLineEdit, QComboBox, QPen, QGraphicsScene, QMenu,
|
QLineEdit, QComboBox, QPen, QGraphicsScene, QMenu, QStringListModel,
|
||||||
QStringListModel, QCompleter, QStringList, QTimer, QRect,
|
QCompleter, QStringList, QTimer, QRect, QGraphicsView, QByteArray)
|
||||||
QFontDatabase, QGraphicsView, QByteArray)
|
|
||||||
|
|
||||||
from calibre.constants import iswindows
|
from calibre.gui2 import (error_dialog, pixmap_to_data, gprefs,
|
||||||
from calibre.gui2 import (NONE, error_dialog, pixmap_to_data, gprefs,
|
|
||||||
warning_dialog)
|
warning_dialog)
|
||||||
from calibre.gui2.filename_pattern_ui import Ui_Form
|
from calibre.gui2.filename_pattern_ui import Ui_Form
|
||||||
from calibre import fit_image
|
from calibre import fit_image
|
||||||
@ -348,46 +346,6 @@ class CoverView(QGraphicsView, ImageDropMixin): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class FontFamilyModel(QAbstractListModel): # {{{
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
QAbstractListModel.__init__(self, *args)
|
|
||||||
from calibre.utils.fonts import fontconfig
|
|
||||||
try:
|
|
||||||
self.families = fontconfig.find_font_families()
|
|
||||||
except:
|
|
||||||
self.families = []
|
|
||||||
print 'WARNING: Could not load fonts'
|
|
||||||
traceback.print_exc()
|
|
||||||
# Restrict to Qt families as Qt tends to crash
|
|
||||||
qt_families = set([unicode(x) for x in QFontDatabase().families()])
|
|
||||||
self.families = list(qt_families.intersection(set(self.families)))
|
|
||||||
self.families.sort()
|
|
||||||
self.families[:0] = [_('None')]
|
|
||||||
self.font = QFont('Arial' if iswindows else 'sansserif')
|
|
||||||
|
|
||||||
def rowCount(self, *args):
|
|
||||||
return len(self.families)
|
|
||||||
|
|
||||||
def data(self, index, role):
|
|
||||||
try:
|
|
||||||
family = self.families[index.row()]
|
|
||||||
except:
|
|
||||||
traceback.print_exc()
|
|
||||||
return NONE
|
|
||||||
if role == Qt.DisplayRole:
|
|
||||||
return QVariant(family)
|
|
||||||
if role == Qt.FontRole:
|
|
||||||
# If a user chooses some non standard font as the interface font,
|
|
||||||
# rendering some font names causes Qt to crash, so return what is
|
|
||||||
# hopefully a "safe" font
|
|
||||||
return QVariant(self.font)
|
|
||||||
return NONE
|
|
||||||
|
|
||||||
def index_of(self, family):
|
|
||||||
return self.families.index(family.strip())
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
# BasicList {{{
|
# BasicList {{{
|
||||||
class BasicListItem(QListWidgetItem):
|
class BasicListItem(QListWidgetItem):
|
||||||
|
|
||||||
|
@ -569,12 +569,12 @@ class CatalogBuilder(object):
|
|||||||
prefix (str): matched a prefix_rule
|
prefix (str): matched a prefix_rule
|
||||||
None: no match
|
None: no match
|
||||||
"""
|
"""
|
||||||
def _log_prefix_rule_match_info(rule, record, field_contents):
|
def _log_prefix_rule_match_info(rule, record, matched):
|
||||||
self.opts.log.info(" %s '%s' by %s (%s: '%s' contains '%s')" %
|
self.opts.log.info(" %s '%s' by %s (%s: '%s' contains '%s')" %
|
||||||
(rule['prefix'],record['title'],
|
(rule['prefix'],record['title'],
|
||||||
record['authors'][0], rule['name'],
|
record['authors'][0], rule['name'],
|
||||||
self.db.metadata_for_field(rule['field'])['name'],
|
self.db.metadata_for_field(rule['field'])['name'],
|
||||||
field_contents))
|
matched))
|
||||||
|
|
||||||
# Compare the record to each rule looking for a match
|
# Compare the record to each rule looking for a match
|
||||||
for rule in self.prefix_rules:
|
for rule in self.prefix_rules:
|
||||||
@ -582,7 +582,7 @@ class CatalogBuilder(object):
|
|||||||
if rule['field'].lower() == 'tags':
|
if rule['field'].lower() == 'tags':
|
||||||
if rule['pattern'].lower() in map(unicode.lower,record['tags']):
|
if rule['pattern'].lower() in map(unicode.lower,record['tags']):
|
||||||
if self.opts.verbose:
|
if self.opts.verbose:
|
||||||
_log_prefix_rule_match_info(rule, record)
|
_log_prefix_rule_match_info(rule, record, rule['pattern'])
|
||||||
return rule['prefix']
|
return rule['prefix']
|
||||||
|
|
||||||
# Regex match for custom field
|
# Regex match for custom field
|
||||||
@ -649,7 +649,6 @@ class CatalogBuilder(object):
|
|||||||
|
|
||||||
cl_list = [None] * len(item_list)
|
cl_list = [None] * len(item_list)
|
||||||
last_ordnum = 0
|
last_ordnum = 0
|
||||||
last_c = u''
|
|
||||||
|
|
||||||
for idx, item in enumerate(item_list):
|
for idx, item in enumerate(item_list):
|
||||||
if key:
|
if key:
|
||||||
@ -659,9 +658,10 @@ class CatalogBuilder(object):
|
|||||||
|
|
||||||
ordnum, ordlen = collation_order(c)
|
ordnum, ordlen = collation_order(c)
|
||||||
if isosx and platform.mac_ver()[0] < '10.8':
|
if isosx and platform.mac_ver()[0] < '10.8':
|
||||||
|
# Hackhackhackhackhack
|
||||||
# icu returns bogus results with curly apostrophes, maybe others under OS X 10.6.x
|
# icu returns bogus results with curly apostrophes, maybe others under OS X 10.6.x
|
||||||
# When we see the magic combo of 0/-1 for ordnum/ordlen, special case the logic
|
# When we see the magic combo of 0/-1 for ordnum/ordlen, special case the logic
|
||||||
|
last_c = u''
|
||||||
if ordnum == 0 and ordlen == -1:
|
if ordnum == 0 and ordlen == -1:
|
||||||
if icu_upper(c[0]) != last_c:
|
if icu_upper(c[0]) != last_c:
|
||||||
last_c = icu_upper(c[0])
|
last_c = icu_upper(c[0])
|
||||||
@ -2757,7 +2757,6 @@ class CatalogBuilder(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from calibre.ebooks.conversion.config import load_defaults
|
from calibre.ebooks.conversion.config import load_defaults
|
||||||
from calibre.utils.fonts import fontconfig
|
|
||||||
|
|
||||||
MI_WIDTH = 600
|
MI_WIDTH = 600
|
||||||
MI_HEIGHT = 60
|
MI_HEIGHT = 60
|
||||||
@ -2767,11 +2766,10 @@ class CatalogBuilder(object):
|
|||||||
masthead_font_family = recs.get('masthead_font', 'Default')
|
masthead_font_family = recs.get('masthead_font', 'Default')
|
||||||
|
|
||||||
if masthead_font_family != 'Default':
|
if masthead_font_family != 'Default':
|
||||||
masthead_font = fontconfig.files_for_family(masthead_font_family)
|
from calibre.utils.fonts.scanner import font_scanner
|
||||||
# Assume 'normal' always in dict, else use default
|
faces = font_scanner.fonts_for_family(masthead_font_family)
|
||||||
# {'normal': (path_to_font, friendly name)}
|
if faces:
|
||||||
if 'normal' in masthead_font:
|
font_path = faces[0]['path']
|
||||||
font_path = masthead_font['normal'][0]
|
|
||||||
|
|
||||||
if not font_path or not os.access(font_path, os.R_OK):
|
if not font_path or not os.access(font_path, os.R_OK):
|
||||||
font_path = default_font
|
font_path = default_font
|
||||||
|
@ -30,7 +30,8 @@ from calibre.ptempfile import (PersistentTemporaryFile,
|
|||||||
base_dir, SpooledTemporaryFile)
|
base_dir, SpooledTemporaryFile)
|
||||||
from calibre.customize.ui import run_plugins_on_import
|
from calibre.customize.ui import run_plugins_on_import
|
||||||
from calibre import isbytestring
|
from calibre import isbytestring
|
||||||
from calibre.utils.filenames import ascii_filename, samefile
|
from calibre.utils.filenames import (ascii_filename, samefile,
|
||||||
|
WindowsAtomicFolderMove, hardlink_file)
|
||||||
from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp,
|
from calibre.utils.date import (utcnow, now as nowf, utcfromtimestamp,
|
||||||
parse_only_date, UNDEFINED_DATE)
|
parse_only_date, UNDEFINED_DATE)
|
||||||
from calibre.utils.config import prefs, tweaks, from_json, to_json
|
from calibre.utils.config import prefs, tweaks, from_json, to_json
|
||||||
@ -640,38 +641,46 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
if name and name != fname:
|
if name and name != fname:
|
||||||
changed = True
|
changed = True
|
||||||
break
|
break
|
||||||
tpath = os.path.join(self.library_path, *path.split('/'))
|
|
||||||
if not os.path.exists(tpath):
|
|
||||||
os.makedirs(tpath)
|
|
||||||
if path == current_path and not changed:
|
if path == current_path and not changed:
|
||||||
return
|
return
|
||||||
|
|
||||||
spath = os.path.join(self.library_path, *current_path.split('/'))
|
spath = os.path.join(self.library_path, *current_path.split('/'))
|
||||||
|
tpath = os.path.join(self.library_path, *path.split('/'))
|
||||||
|
|
||||||
if current_path and os.path.exists(spath): # Migrate existing files
|
source_ok = current_path and os.path.exists(spath)
|
||||||
cdata = self.cover(id, index_is_id=True)
|
wam = WindowsAtomicFolderMove(spath) if iswindows and source_ok else None
|
||||||
if cdata is not None:
|
try:
|
||||||
with lopen(os.path.join(tpath, 'cover.jpg'), 'wb') as f:
|
if not os.path.exists(tpath):
|
||||||
f.write(cdata)
|
os.makedirs(tpath)
|
||||||
for format in formats:
|
|
||||||
copy_function = functools.partial(self.copy_format_to, id,
|
if source_ok: # Migrate existing files
|
||||||
format, index_is_id=True)
|
self.copy_cover_to(id, os.path.join(tpath, 'cover.jpg'),
|
||||||
try:
|
index_is_id=True, windows_atomic_move=wam,
|
||||||
self.add_format(id, format, None, index_is_id=True,
|
use_hardlink=True)
|
||||||
path=tpath, notify=False, copy_function=copy_function)
|
for format in formats:
|
||||||
except NoSuchFormat:
|
copy_function = functools.partial(self.copy_format_to, id,
|
||||||
continue
|
format, index_is_id=True, windows_atomic_move=wam,
|
||||||
self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
|
use_hardlink=True)
|
||||||
self.dirtied([id], commit=False)
|
try:
|
||||||
self.conn.commit()
|
self.add_format(id, format, None, index_is_id=True,
|
||||||
self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True)
|
path=tpath, notify=False, copy_function=copy_function)
|
||||||
# Delete not needed directories
|
except NoSuchFormat:
|
||||||
if current_path and os.path.exists(spath):
|
continue
|
||||||
if not samefile(spath, tpath):
|
self.conn.execute('UPDATE books SET path=? WHERE id=?', (path, id))
|
||||||
self.rmtree(spath, permanent=True)
|
self.dirtied([id], commit=False)
|
||||||
parent = os.path.dirname(spath)
|
self.conn.commit()
|
||||||
if len(os.listdir(parent)) == 0:
|
self.data.set(id, self.FIELD_MAP['path'], path, row_is_id=True)
|
||||||
self.rmtree(parent, permanent=True)
|
# Delete not needed directories
|
||||||
|
if source_ok:
|
||||||
|
if not samefile(spath, tpath):
|
||||||
|
if wam is not None:
|
||||||
|
wam.delete_originals()
|
||||||
|
self.rmtree(spath, permanent=True)
|
||||||
|
parent = os.path.dirname(spath)
|
||||||
|
if len(os.listdir(parent)) == 0:
|
||||||
|
self.rmtree(parent, permanent=True)
|
||||||
|
finally:
|
||||||
|
if wam is not None:
|
||||||
|
wam.close_handles()
|
||||||
|
|
||||||
curpath = self.library_path
|
curpath = self.library_path
|
||||||
c1, c2 = current_path.split('/'), path.split('/')
|
c1, c2 = current_path.split('/'), path.split('/')
|
||||||
@ -1340,26 +1349,97 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
return None
|
return None
|
||||||
return fmt_path
|
return fmt_path
|
||||||
|
|
||||||
def copy_format_to(self, index, fmt, dest, index_is_id=False):
|
def copy_format_to(self, index, fmt, dest, index_is_id=False,
|
||||||
|
windows_atomic_move=None, use_hardlink=False):
|
||||||
'''
|
'''
|
||||||
Copy the format ``fmt`` to the file like object ``dest``. If the
|
Copy the format ``fmt`` to the file like object ``dest``. If the
|
||||||
specified format does not exist, raises :class:`NoSuchFormat` error.
|
specified format does not exist, raises :class:`NoSuchFormat` error.
|
||||||
dest can also be a path, in which case the format is copied to it, iff
|
dest can also be a path, in which case the format is copied to it, iff
|
||||||
the path is different from the current path (taking case sensitivity
|
the path is different from the current path (taking case sensitivity
|
||||||
into account).
|
into account).
|
||||||
|
|
||||||
|
If use_hardlink is True, a hard link will be created instead of the
|
||||||
|
file being copied. Use with care, because a hard link means that
|
||||||
|
modifying any one file will cause both files to be modified.
|
||||||
|
|
||||||
|
windows_atomic_move is an internally used parameter. You should not use
|
||||||
|
it in any code outside this module.
|
||||||
'''
|
'''
|
||||||
path = self.format_abspath(index, fmt, index_is_id=index_is_id)
|
path = self.format_abspath(index, fmt, index_is_id=index_is_id)
|
||||||
if path is None:
|
if path is None:
|
||||||
id_ = index if index_is_id else self.id(index)
|
id_ = index if index_is_id else self.id(index)
|
||||||
raise NoSuchFormat('Record %d has no %s file'%(id_, fmt))
|
raise NoSuchFormat('Record %d has no %s file'%(id_, fmt))
|
||||||
if hasattr(dest, 'write'):
|
if windows_atomic_move is not None:
|
||||||
with lopen(path, 'rb') as f:
|
if not isinstance(dest, basestring):
|
||||||
shutil.copyfileobj(f, dest)
|
raise Exception("Error, you must pass the dest as a path when"
|
||||||
if hasattr(dest, 'flush'):
|
" using windows_atomic_move")
|
||||||
dest.flush()
|
if dest and not samefile(dest, path):
|
||||||
elif dest and not samefile(dest, path):
|
windows_atomic_move.copy_path_to(path, dest)
|
||||||
with lopen(path, 'rb') as f, lopen(dest, 'wb') as d:
|
else:
|
||||||
shutil.copyfileobj(f, d)
|
if hasattr(dest, 'write'):
|
||||||
|
with lopen(path, 'rb') as f:
|
||||||
|
shutil.copyfileobj(f, dest)
|
||||||
|
if hasattr(dest, 'flush'):
|
||||||
|
dest.flush()
|
||||||
|
elif dest and not samefile(dest, path):
|
||||||
|
if use_hardlink:
|
||||||
|
try:
|
||||||
|
hardlink_file(path, dest)
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
with lopen(path, 'rb') as f, lopen(dest, 'wb') as d:
|
||||||
|
shutil.copyfileobj(f, d)
|
||||||
|
|
||||||
|
def copy_cover_to(self, index, dest, index_is_id=False,
|
||||||
|
windows_atomic_move=None, use_hardlink=False):
|
||||||
|
'''
|
||||||
|
Copy the cover to the file like object ``dest``. Returns False
|
||||||
|
if no cover exists or dest is the same file as the current cover.
|
||||||
|
dest can also be a path in which case the cover is
|
||||||
|
copied to it iff the path is different from the current path (taking
|
||||||
|
case sensitivity into account).
|
||||||
|
|
||||||
|
If use_hardlink is True, a hard link will be created instead of the
|
||||||
|
file being copied. Use with care, because a hard link means that
|
||||||
|
modifying any one file will cause both files to be modified.
|
||||||
|
|
||||||
|
windows_atomic_move is an internally used parameter. You should not use
|
||||||
|
it in any code outside this module.
|
||||||
|
'''
|
||||||
|
id = index if index_is_id else self.id(index)
|
||||||
|
path = os.path.join(self.library_path, self.path(id, index_is_id=True), 'cover.jpg')
|
||||||
|
if windows_atomic_move is not None:
|
||||||
|
if not isinstance(dest, basestring):
|
||||||
|
raise Exception("Error, you must pass the dest as a path when"
|
||||||
|
" using windows_atomic_move")
|
||||||
|
if os.access(path, os.R_OK) and dest and not samefile(dest, path):
|
||||||
|
windows_atomic_move.copy_path_to(path, dest)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if os.access(path, os.R_OK):
|
||||||
|
try:
|
||||||
|
f = lopen(path, 'rb')
|
||||||
|
except (IOError, OSError):
|
||||||
|
time.sleep(0.2)
|
||||||
|
f = lopen(path, 'rb')
|
||||||
|
with f:
|
||||||
|
if hasattr(dest, 'write'):
|
||||||
|
shutil.copyfileobj(f, dest)
|
||||||
|
if hasattr(dest, 'flush'):
|
||||||
|
dest.flush()
|
||||||
|
return True
|
||||||
|
elif dest and not samefile(dest, path):
|
||||||
|
if use_hardlink:
|
||||||
|
try:
|
||||||
|
hardlink_file(path, dest)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
with lopen(dest, 'wb') as d:
|
||||||
|
shutil.copyfileobj(f, d)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def format(self, index, format, index_is_id=False, as_file=False,
|
def format(self, index, format, index_is_id=False, as_file=False,
|
||||||
mode='r+b', as_path=False, preserve_filename=False):
|
mode='r+b', as_path=False, preserve_filename=False):
|
||||||
@ -2125,13 +2205,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
|
|
||||||
def set(self, row, column, val, allow_case_change=False):
|
def set(self, row, column, val, allow_case_change=False):
|
||||||
'''
|
'''
|
||||||
Convenience method for setting the title, authors, publisher or rating
|
Convenience method for setting the title, authors, publisher, tags or
|
||||||
|
rating
|
||||||
'''
|
'''
|
||||||
id = self.data[row][0]
|
id = self.data[row][0]
|
||||||
col = {'title':1, 'authors':2, 'publisher':3, 'rating':4, 'tags':7}[column]
|
col = self.FIELD_MAP[column]
|
||||||
|
|
||||||
books_to_refresh = set()
|
books_to_refresh = set()
|
||||||
self.data.set(row, col, val)
|
set_args = (row, col, val)
|
||||||
if column == 'authors':
|
if column == 'authors':
|
||||||
val = string_to_authors(val)
|
val = string_to_authors(val)
|
||||||
books_to_refresh |= self.set_authors(id, val, notify=False,
|
books_to_refresh |= self.set_authors(id, val, notify=False,
|
||||||
@ -2147,6 +2228,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
books_to_refresh |= \
|
books_to_refresh |= \
|
||||||
self.set_tags(id, [x.strip() for x in val.split(',') if x.strip()],
|
self.set_tags(id, [x.strip() for x in val.split(',') if x.strip()],
|
||||||
append=False, notify=False, allow_case_change=allow_case_change)
|
append=False, notify=False, allow_case_change=allow_case_change)
|
||||||
|
self.data.set(*set_args)
|
||||||
self.data.refresh_ids(self, [id])
|
self.data.refresh_ids(self, [id])
|
||||||
self.set_path(id, True)
|
self.set_path(id, True)
|
||||||
self.notify('metadata', [id])
|
self.notify('metadata', [id])
|
||||||
@ -2394,6 +2476,23 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
self.clean_standard_field('authors', commit=True)
|
self.clean_standard_field('authors', commit=True)
|
||||||
return books_to_refresh
|
return books_to_refresh
|
||||||
|
|
||||||
|
def windows_check_if_files_in_use(self, book_id):
|
||||||
|
'''
|
||||||
|
Raises an EACCES IOError if any of the files in the folder of book_id
|
||||||
|
are opened in another program on windows.
|
||||||
|
'''
|
||||||
|
if iswindows:
|
||||||
|
path = self.path(book_id, index_is_id=True)
|
||||||
|
if path:
|
||||||
|
spath = os.path.join(self.library_path, *path.split('/'))
|
||||||
|
wam = None
|
||||||
|
if os.path.exists(spath):
|
||||||
|
try:
|
||||||
|
wam = WindowsAtomicFolderMove(spath)
|
||||||
|
finally:
|
||||||
|
if wam is not None:
|
||||||
|
wam.close_handles()
|
||||||
|
|
||||||
def set_authors(self, id, authors, notify=True, commit=True,
|
def set_authors(self, id, authors, notify=True, commit=True,
|
||||||
allow_case_change=False):
|
allow_case_change=False):
|
||||||
'''
|
'''
|
||||||
@ -2402,6 +2501,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
|
|
||||||
:param authors: A list of authors.
|
:param authors: A list of authors.
|
||||||
'''
|
'''
|
||||||
|
self.windows_check_if_files_in_use(id)
|
||||||
books_to_refresh = self._set_authors(id, authors,
|
books_to_refresh = self._set_authors(id, authors,
|
||||||
allow_case_change=allow_case_change)
|
allow_case_change=allow_case_change)
|
||||||
self.dirtied(set([id])|books_to_refresh, commit=False)
|
self.dirtied(set([id])|books_to_refresh, commit=False)
|
||||||
@ -2452,6 +2552,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
Note that even if commit is False, the db will still be committed to
|
Note that even if commit is False, the db will still be committed to
|
||||||
because this causes the location of files to change
|
because this causes the location of files to change
|
||||||
'''
|
'''
|
||||||
|
self.windows_check_if_files_in_use(id)
|
||||||
if not self._set_title(id, title):
|
if not self._set_title(id, title):
|
||||||
return
|
return
|
||||||
self.set_path(id, index_is_id=True)
|
self.set_path(id, index_is_id=True)
|
||||||
|
@ -23,16 +23,16 @@ class Severity(ctypes.c_long):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class String(ctypes.c_char_p):
|
class String(ctypes.c_char_p):
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
_libwand.MagickRelinquishMemory(self)
|
_libwand.MagickRelinquishMemory(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
if _libwand is not None:
|
if _libwand is not None:
|
||||||
_libwand.MagickGetException.argtypes = [ctypes.c_void_p, ctypes.POINTER(Severity)]
|
_libwand.MagickGetException.argtypes = [ctypes.c_void_p, ctypes.POINTER(Severity)]
|
||||||
_libwand.MagickGetException.restype = String
|
_libwand.MagickGetException.restype = String
|
||||||
|
|
||||||
def get_exception(wand):
|
def get_exception(wand):
|
||||||
severity = Severity()
|
severity = Severity()
|
||||||
@ -52,4 +52,4 @@ def convert(source, dest):
|
|||||||
if not _libwand.MagickWriteImage(wand, dest):
|
if not _libwand.MagickWriteImage(wand, dest):
|
||||||
raise WandException('Cannot write image to file %s: %s'%(source, get_exception(wand)))
|
raise WandException('Cannot write image to file %s: %s'%(source, get_exception(wand)))
|
||||||
_libwand.DestroyMagickWand(wand)
|
_libwand.DestroyMagickWand(wand)
|
||||||
_libwand.MagickWandTerminus()
|
_libwand.MagickWandTerminus()
|
||||||
|