Merge from trunk
173
Changelog.yaml
@ -4,6 +4,175 @@
|
||||
# for important features/bug fixes.
|
||||
# Also, each release can have new and improved recipes.
|
||||
|
||||
- version: 0.7.20
|
||||
date: 2010-09-24
|
||||
|
||||
|
||||
new features:
|
||||
- title: "Tweak epub feature."
|
||||
type: major
|
||||
description: >
|
||||
"Now you can conveniently browse the contents of an epub, tweak them and rebuild the epub within your calibre library
|
||||
by right clicking on the book and selecting Tweak ePub. See http://www.mobileread.com/forums/showthread.php?t=99875
|
||||
for details."
|
||||
|
||||
- title: "Add button to Edit metadata dialog to trim borders from the cover"
|
||||
|
||||
- title: "Kobo driver: Add support for setting the ReadStatus to Read and correctly deal with empty collections"
|
||||
|
||||
- title: "Improved algorithm for removal of hyphens during pre-processing"
|
||||
|
||||
- title: "EPUB metadata: Don't read timestamp value from epubs as I am sick of closing bugs about adding books and having the Date not be today."
|
||||
|
||||
- title: "After bulk edit metadata, reselect previously selected books."
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix regression in 0.7.19 that broke the By Author and By Title category listing in Stanza/Aldiko feeds."
|
||||
|
||||
- title: "MOBI Output: Fix regression that broke sections list in downloaded periodicals on Kindle for non-english news sources"
|
||||
|
||||
- title: "News download: Rationalize cover processing."
|
||||
tickets: [6852]
|
||||
|
||||
- title: "Cover cache: load images only in the GUI thread to prevent stale files being leftover by set_path due to Windows file locking"
|
||||
|
||||
- title: "Database: Make renaming of folders on case change more robust"
|
||||
tickets: [6914]
|
||||
|
||||
- title: "When adding/replacing files to/in EPUB files, set the GPF bit for all files in the archive, to prevent unzip from complaining in linux"
|
||||
tickets: [6363]
|
||||
|
||||
- title: "Plugin loading: Handle encoding declarations in .py files correctly"
|
||||
|
||||
- title: "MOBI input: Another corner case"
|
||||
tickets: [6909]
|
||||
|
||||
- title: "IPC: Store results file in the calibre temp dir and also dont die if for some reason removing result file fails. Should make adding/saving more robust"
|
||||
|
||||
- title: "Database: Fix regression that caused has_cover to create empty directories unneccessarily"
|
||||
|
||||
- title: "Detection of Alex on unix"
|
||||
tickets: [5900]
|
||||
|
||||
- title: "News download: Don't add inline table of contents when downloading news for the Kindle"
|
||||
|
||||
- title: "Add prologue and epilogue to default chapter detection regex"
|
||||
|
||||
- title: "Kobo driver: Fix issue where books that are read were getting their status reset to Unread"
|
||||
|
||||
- title: "Device drivers: Fix occassional false positive when matching books on device with books in the calibre library"
|
||||
|
||||
- title: "Content server: Making serving of large files more efficient."
|
||||
|
||||
- title: "GUI device detection: Handle case when user yanks connected device before device connection handler is called."
|
||||
tickets: [6864]
|
||||
|
||||
- title: "Strip leading/trailing whitespace when setting metadata using the edit metadata dialog"
|
||||
tickets: [6854]
|
||||
|
||||
- title: "KOBO: Editing the Im_Reading list with SD Card installed fixed"
|
||||
tickets: [6850]
|
||||
|
||||
new recipes:
|
||||
- title: "Neal's Nuze and Popular Science"
|
||||
author: Tony Stegall
|
||||
|
||||
- title: "Rmf24.pl"
|
||||
author: "Tomasz Dlugosz"
|
||||
|
||||
- title: "Gazeta Pomorska"
|
||||
author: "Richard"
|
||||
|
||||
- title: "Le Journal de Montreal and superesportes"
|
||||
author: "Luciano Furtado"
|
||||
|
||||
- title: "The Marker"
|
||||
author: Marbs
|
||||
|
||||
- title: "Tagesanzeiger"
|
||||
author: noxxx
|
||||
|
||||
|
||||
improved recipes:
|
||||
- Danas
|
||||
- Harvard Business Review
|
||||
|
||||
- version: 0.7.19
|
||||
date: 2010-09-17
|
||||
|
||||
new features:
|
||||
- title: "The ability to perform search and replace via regular expressions in the Bulk Edit metadata dialog"
|
||||
type: major
|
||||
|
||||
- title: "Add an option to have calibre automatically convert straight quotes to curly quotes. Also handles em/en-dashes and ellipses. Found under 'Look & Feel' in the conversion options"
|
||||
type: major
|
||||
tickets: [6808]
|
||||
|
||||
- title: "Greatly improve sorting performance on large libraries."
|
||||
type: major
|
||||
|
||||
- title: "Drivers for the SONY PRS-350/PRS-650 and the Sovos E-reader"
|
||||
|
||||
- title: "Kobo driver: Add management of the I'm Reading list on Kobo via an Im_Reading tag in calibre. See http://www.mobileread.com/forums/showthread.php?t=98906 for details"
|
||||
|
||||
- title: "Conversion pipeline: Add an option to control how hard line breaks are removed during preprocessing. See the Structure Detection section in the conversion options"
|
||||
|
||||
- title: "In the Edit metadata dialog, indicate whether the author sort value matches the author value, using a background color"
|
||||
|
||||
- title: "Add an option to split the toolbar into two toolbars in Preferences->Interface->Look and Feel"
|
||||
|
||||
- title: "EPUB Output: Improved design of the 'jacket' page created by calibre when using the 'Insert metadata at start of book' option"
|
||||
|
||||
- title: "PDF Input: Improve line unwrapping, handling of hyphens/dashes and quotes. Also handle more specially encoded non ASCII characters"
|
||||
|
||||
bug fixes:
|
||||
- title: "Fix regression in filename shortening that caused loss of filename extension"
|
||||
|
||||
- title: "Fix various regressions that could be triggered when using search restrictions and/or multi-sorts and connecting a device"
|
||||
|
||||
- title: "Database: Fix possible race condition in windows when changing title/author during move of book files, that could lead to old files not being deleted"
|
||||
|
||||
- title: "Conversion pipeline: Don't die if rescaling of image raises an exception, just ignore and continue"
|
||||
|
||||
- title: "Database: Update has_cover cache when setting/removing covers so that the search returns correct results. Also fix an exception that could occur when adding books with a db that has been upgraded from very old SQL."
|
||||
|
||||
- title: "Workaround for bug that affects some windows installs causing white backgrounds on default covers to be rendered as yellow"
|
||||
|
||||
- title: "Fix handling of non-ASCII chars when rendering series in default EPUB cover"
|
||||
|
||||
- title: "Fix --start-in-tray switch displays hidden windows in metacity, xfwm4 and compiz"
|
||||
tickets: [6806]
|
||||
|
||||
- title: "Conversion pipeline: When setting margins on <body> explicitly set padding to 0 to override and existing padding in the input document"
|
||||
|
||||
- title: "CHM Input: Ignore missing image files in the input document"
|
||||
tickets: [6773]
|
||||
|
||||
- title: "News download: Fix bug that could break some downloads in non ASCII locales"
|
||||
|
||||
- title: "TXT Output: When using preserve spaces, output tab characters as a sequence of four non-breaking spaces as some readers dont handle the 09 char code."
|
||||
|
||||
- title: "PDB Input: Fix bug in conversion of TOC in some PML files"
|
||||
|
||||
|
||||
new recipes:
|
||||
- title: "taz.de RSS"
|
||||
author: Alexander Schremmer
|
||||
|
||||
- title: "Brand Eins"
|
||||
author: Constantin Hofstetter
|
||||
|
||||
improved recipes:
|
||||
- Harpers (free)
|
||||
- Danas
|
||||
- Novosti
|
||||
- ESPN
|
||||
- Taz Digiabo
|
||||
- Slate
|
||||
- AJC
|
||||
- Infobae
|
||||
- NSPM
|
||||
|
||||
- version: 0.7.18
|
||||
date: 2010-09-10
|
||||
|
||||
@ -197,7 +366,7 @@
|
||||
new features:
|
||||
- title: "Multiple library support: Various improvements to make using multiple calibre libraries easier."
|
||||
type: major
|
||||
desc: >
|
||||
description: >
|
||||
"Now, when you switch libraries using the Choose Library button on the toolbar, entries are created in the menu of that button to easily switch to that library in the
|
||||
future. Also, you can now right click on a book in the calibre library and use the 'Copy to library' action to copy the book to another library,
|
||||
that you have switched to at least once. The name of the current library is shown in the titlebar.
|
||||
@ -205,7 +374,7 @@
|
||||
|
||||
- title: "Content server: Allow setting a restriction so that the server shares only some of the books in the library."
|
||||
type: major
|
||||
desc: >
|
||||
description: >
|
||||
"You can now use a Saved Search as a restiction for the content server, via Preferences->Content Server. This will cause the
|
||||
server to share only those books that match the saved search.
|
||||
"
|
||||
|
589
imgsrc/rating.svg
Normal file
@ -0,0 +1,589 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://web.resource.org/cc/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.45+devel"
|
||||
version="1.0"
|
||||
sodipodi:docbase="/Users/david/Progetti/oxygen-svn/theme/svg/actions"
|
||||
sodipodi:docname="rating.svgz"
|
||||
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
|
||||
inkscape:export-filename="/home/pinheiro/pics/oxygen/scalable/actions/rating.png"
|
||||
inkscape:export-xdpi="11.25"
|
||||
inkscape:export-ydpi="11.25">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
id="linearGradient3946">
|
||||
<stop
|
||||
style="stop-color:#552b00;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3948" />
|
||||
<stop
|
||||
style="stop-color:#673400;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3950" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3844">
|
||||
<stop
|
||||
style="stop-color:#faff64;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3846" />
|
||||
<stop
|
||||
style="stop-color:#faff64;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3848" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3379">
|
||||
<stop
|
||||
style="stop-color:#fffc07;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3381" />
|
||||
<stop
|
||||
style="stop-color:#fffc07;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3383" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3363">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3365" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3367" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3309">
|
||||
<stop
|
||||
style="stop-color:#f8ff8a;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3311" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3313" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient26907"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="-84.002403"
|
||||
y1="-383.9971"
|
||||
x2="-12.0029"
|
||||
y2="-383.9971"
|
||||
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
id="stop26909" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#2e3436;stop-opacity:1;"
|
||||
id="stop26911" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
|
||||
y2="-383.9975"
|
||||
x2="-23.516129"
|
||||
y1="-383.9971"
|
||||
x1="-84.002403"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3711">
|
||||
<stop
|
||||
id="stop3713"
|
||||
style="stop-color:white;stop-opacity:1;"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3715"
|
||||
style="stop-color:white;stop-opacity:0;"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3081">
|
||||
<stop
|
||||
id="stop3083"
|
||||
offset="0"
|
||||
style="stop-color:#28691f;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3085"
|
||||
offset="1"
|
||||
style="stop-color:#00bf00;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3290">
|
||||
<stop
|
||||
style="stop-color:yellow;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3292" />
|
||||
<stop
|
||||
style="stop-color:#f07800;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3294" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3638">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3640" />
|
||||
<stop
|
||||
id="stop3661"
|
||||
offset="0.06868132"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3659"
|
||||
offset="0.5"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3642" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient1563">
|
||||
<stop
|
||||
id="stop1565"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop1567"
|
||||
offset="1"
|
||||
style="stop-color:white;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3273">
|
||||
<stop
|
||||
id="stop3275"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:0.55035973;" />
|
||||
<stop
|
||||
id="stop3277"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient12948">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop12950" />
|
||||
<stop
|
||||
style="stop-color:#c0c0c0;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop12952" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.111111,0,138.1081)"
|
||||
r="64.796692"
|
||||
fy="177.29686"
|
||||
fx="80.738739"
|
||||
cy="155.37218"
|
||||
cx="80.738739"
|
||||
id="radialGradient5079"
|
||||
xlink:href="#linearGradient5073"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient5073"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop5075"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop5077"
|
||||
offset="1"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<foreignObject
|
||||
id="foreignObject7221"
|
||||
height="1"
|
||||
width="1"
|
||||
y="0"
|
||||
x="0"
|
||||
requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/">
|
||||
<i:pgfRef
|
||||
xlink:href="#adobe_illustrator_pgf" />
|
||||
</foreignObject>
|
||||
<linearGradient
|
||||
id="XMLID_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="95.693398"
|
||||
y1="141.1738"
|
||||
x2="32.308601"
|
||||
y2="77.789001">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#ffd50a;stop-opacity:1;"
|
||||
id="stop7227" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#8d4000;stop-opacity:1;"
|
||||
id="stop7233" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="XMLID_3_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="63.9995"
|
||||
y1="92.865196"
|
||||
x2="63.9995"
|
||||
y2="120.8652"
|
||||
gradientTransform="translate(175.0067,11.74752)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#888A85"
|
||||
id="stop7261" />
|
||||
<stop
|
||||
offset="0.3226"
|
||||
style="stop-color:#A6A7A3"
|
||||
id="stop7263" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#EEEEEC"
|
||||
id="stop7265" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="XMLID_4_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="64.000504"
|
||||
y1="108.8652"
|
||||
x2="64.000504"
|
||||
y2="92.865196">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#EEEEEC"
|
||||
id="stop7270" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop7272" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3081"
|
||||
id="linearGradient2149"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="62.112335"
|
||||
y1="90.513916"
|
||||
x2="67.887672"
|
||||
y2="39.095695" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient26907"
|
||||
id="linearGradient3226"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0,1,-1,0,-39.9985,140.0029)"
|
||||
x1="-70.002899"
|
||||
y1="-383.9971"
|
||||
x2="-11.91648"
|
||||
y2="-383.9971" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3711"
|
||||
id="radialGradient3228"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
cx="343.99899"
|
||||
cy="92"
|
||||
fx="343.99899"
|
||||
fy="92"
|
||||
r="36" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3711"
|
||||
id="linearGradient3230"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0,1.022977,-1.022977,0,111.9686,137.8125)"
|
||||
x1="-88.058083"
|
||||
y1="-131.93112"
|
||||
x2="-45.096584"
|
||||
y2="-131.93112" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_1_"
|
||||
id="linearGradient2898"
|
||||
x1="64.07962"
|
||||
y1="-14.227339"
|
||||
x2="64.07962"
|
||||
y2="120.44466"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3290"
|
||||
id="radialGradient2906"
|
||||
cx="69.526619"
|
||||
cy="60.115833"
|
||||
fx="69.526619"
|
||||
fy="89.655701"
|
||||
r="111.65377"
|
||||
gradientTransform="matrix(0.9439139,-0.3301918,0.332644,0.9509241,-16.097695,27.249949)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3290"
|
||||
id="radialGradient3304"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.5227399,0,-1.554444e-8,0.5266221,349.81061,60.575712)"
|
||||
cx="69.526619"
|
||||
cy="60.115833"
|
||||
fx="69.526619"
|
||||
fy="60.115833"
|
||||
r="111.65377" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3309"
|
||||
id="linearGradient3315"
|
||||
x1="219.22163"
|
||||
y1="11.902248"
|
||||
x2="219.22163"
|
||||
y2="136.85997"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-170.08594,0)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1563"
|
||||
id="linearGradient3345"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-37.771032,-0.1213203)"
|
||||
x1="278.47162"
|
||||
y1="77.652245"
|
||||
x2="200.17728"
|
||||
y2="31.10997" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3363"
|
||||
id="linearGradient3369"
|
||||
x1="177.42397"
|
||||
y1="22.377773"
|
||||
x2="177.60074"
|
||||
y2="93.022789"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3379"
|
||||
id="linearGradient3385"
|
||||
x1="216.88614"
|
||||
y1="122.5867"
|
||||
x2="216.88614"
|
||||
y2="37.969955"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-152,0)" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3391">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.55939545"
|
||||
id="feGaussianBlur3393" />
|
||||
</filter>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3401">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.11157909"
|
||||
id="feGaussianBlur3403" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3363"
|
||||
id="linearGradient3800"
|
||||
x1="63.948792"
|
||||
y1="12.034382"
|
||||
x2="67.219337"
|
||||
y2="12.034382"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="reflect" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3838"
|
||||
x="-0.17816916"
|
||||
width="1.3563383"
|
||||
y="-0.15506857"
|
||||
height="1.3101371">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.46259975"
|
||||
id="feGaussianBlur3840" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3844"
|
||||
id="linearGradient3850"
|
||||
x1="28.637825"
|
||||
y1="120.84999"
|
||||
x2="31.289474"
|
||||
y2="122.08743"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="reflect" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3928">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.18346262"
|
||||
id="feGaussianBlur3930" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3844"
|
||||
id="linearGradient3934"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="reflect"
|
||||
x1="28.637825"
|
||||
y1="120.84999"
|
||||
x2="31.289474"
|
||||
y2="122.08743" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3946"
|
||||
id="radialGradient3956"
|
||||
cx="64.07962"
|
||||
cy="114.47154"
|
||||
fx="64.07962"
|
||||
fy="114.47154"
|
||||
r="60.700505"
|
||||
gradientTransform="matrix(0.2787307,0,0,0.2689969,46.218665,81.520439)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3975">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.2948866"
|
||||
id="feGaussianBlur3977" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.4142136"
|
||||
inkscape:cx="-57.231582"
|
||||
inkscape:cy="95.226607"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:window-width="1247"
|
||||
inkscape:window-height="816"
|
||||
inkscape:window-x="388"
|
||||
inkscape:window-y="110"
|
||||
showgrid="true"
|
||||
gridspacingx="4px"
|
||||
gridspacingy="4px"
|
||||
gridempspacing="0"
|
||||
inkscape:grid-points="true">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2302"
|
||||
spacingx="4px"
|
||||
spacingy="4px"
|
||||
empspacing="2" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
id="path3961"
|
||||
d="M 64.03125 8 C 56.162818 8.0100117 46.828561 34.554451 40.46875 39.1875 C 34.10894 43.820548 5.9844574 44.576082 3.5625 52.0625 C 1.1405426 59.548917 23.465249 76.613524 25.90625 84.09375 C 28.347251 91.57398 20.40967 118.5394 26.78125 123.15625 C 33.15283 127.7731 56.287818 111.82251 64.15625 111.8125 C 72.024682 111.80249 95.202691 127.69555 101.5625 123.0625 C 107.92231 118.42945 99.890544 91.486414 102.3125 84 C 104.73446 76.513583 127.03475 59.38648 124.59375 51.90625 C 122.15275 44.426021 94.027829 43.741849 87.65625 39.125 C 81.28467 34.508152 71.899685 7.9899879 64.03125 8 z M 64.03125 11.90625 C 64.208046 12.045423 65.56776 12.712264 67.15625 14.65625 C 68.97167 16.877947 71.031426 20.210059 73.0625 23.75 C 75.093573 27.28994 77.113982 31.048819 79.09375 34.3125 C 81.073519 37.576182 82.75512 40.328991 85.40625 42.25 C 88.057376 44.171009 91.18831 44.91637 94.90625 45.78125 C 98.624192 46.646129 102.81606 47.391152 106.8125 48.21875 C 110.80894 49.046347 114.60465 49.966787 117.28125 51 C 119.62327 51.904061 120.71845 53.000764 120.90625 53.125 C 120.82618 53.333062 120.57672 54.794782 119.21875 56.90625 C 117.66679 59.319356 115.1453 62.318181 112.40625 65.34375 C 109.66721 68.369316 106.71091 71.452346 104.21875 74.34375 C 101.72659 77.235155 99.632744 79.697501 98.625 82.8125 C 97.617256 85.927495 97.892393 89.134266 98.21875 92.9375 C 98.545107 96.740738 99.114622 100.97466 99.5625 105.03125 C 100.01038 109.08783 100.31178 112.97888 100.15625 115.84375 C 100.02016 118.35052 99.34151 119.69095 99.28125 119.90625 C 99.057443 119.89786 97.552762 120.17027 95.125 119.53125 C 92.350417 118.80093 88.723899 117.29504 85 115.625 C 81.276103 113.95497 77.426259 112.10169 73.90625 110.625 C 70.386242 109.1483 67.4302 107.93334 64.15625 107.9375 C 60.882303 107.94167 57.891241 109.1706 54.375 110.65625 C 50.858761 112.1419 47.032137 114.00799 43.3125 115.6875 C 39.592862 117.367 35.960216 118.85638 33.1875 119.59375 C 30.761373 120.23895 29.286908 119.99088 29.0625 120 C 29.004012 119.7864 28.29872 118.4439 28.15625 115.9375 C 27.993428 113.07303 28.281199 109.18271 28.71875 105.125 C 29.156299 101.0673 29.714573 96.835302 30.03125 93.03125 C 30.347928 89.227198 30.609418 85.987425 29.59375 82.875 C 28.578082 79.762573 26.468263 77.322553 23.96875 74.4375 C 21.469238 71.552452 18.527988 68.487339 15.78125 65.46875 C 13.034512 62.450158 10.495601 59.471649 8.9375 57.0625 C 7.5741618 54.954496 7.3592053 53.457399 7.28125 53.25 C 7.2962039 53.337785 8.2681026 52.126785 10.84375 51.125 C 13.517705 50.084977 17.34943 49.150265 21.34375 48.3125 C 25.33807 47.474737 29.534272 46.749339 33.25 45.875 C 36.96573 45.000663 40.103767 44.24025 42.75 42.3125 C 45.396234 40.384748 47.059794 37.612458 49.03125 34.34375 C 51.002705 31.075042 53.009191 27.326347 55.03125 23.78125 C 57.053308 20.236153 59.096493 16.88256 60.90625 14.65625 C 62.489787 12.708229 63.857465 12.044552 64.03125 11.90625 z "
|
||||
style="opacity:1;fill:url(#linearGradient2898);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269000000059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3975)" />
|
||||
<path
|
||||
transform="matrix(0.4934214,0.1726044,-0.1726044,0.4934214,42.377875,49.908537)"
|
||||
d="M 153.09403,94.713757 C 144.53658,107.09689 92.616372,93.013297 78.414631,98.001518 C 64.21289,102.98974 32.50348,146.4474 18.082028,142.13539 C 3.6605746,137.82337 1.0106378,84.092245 -8.1220219,72.127031 C -17.254681,60.161818 -68.384124,43.433534 -68.739625,28.385431 C -69.095125,13.337327 -18.812666,-5.7867426 -10.255219,-18.169872 C -1.697772,-30.553002 -1.5880954,-84.349316 12.613645,-89.337536 C 26.815387,-94.325757 60.541592,-52.41396 74.963045,-48.101941 C 89.384498,-43.789923 140.58172,-60.30959 149.71438,-48.344376 C 158.84704,-36.379162 129.40853,8.6478227 129.76403,23.695927 C 130.11953,38.74403 161.65148,82.330628 153.09403,94.713757 z"
|
||||
inkscape:randomized="0"
|
||||
inkscape:rounded="0.20136392"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:arg2="1.2330172"
|
||||
sodipodi:arg1="0.60469864"
|
||||
sodipodi:r2="76.832565"
|
||||
sodipodi:r1="121.72647"
|
||||
sodipodi:cy="25.510532"
|
||||
sodipodi:cx="52.952892"
|
||||
sodipodi:sides="5"
|
||||
id="path3574"
|
||||
style="opacity:1;fill:url(#radialGradient2906);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
||||
sodipodi:type="star" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient2898);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269000000059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
||||
d="M 64.03125 8 C 56.162818 8.0100117 46.828561 34.554451 40.46875 39.1875 C 34.10894 43.820548 5.9844574 44.576082 3.5625 52.0625 C 1.1405426 59.548917 23.465249 76.613524 25.90625 84.09375 C 28.347251 91.57398 20.40967 118.5394 26.78125 123.15625 C 33.15283 127.7731 56.287818 111.82251 64.15625 111.8125 C 72.024682 111.80249 95.202691 127.69555 101.5625 123.0625 C 107.92231 118.42945 99.890544 91.486414 102.3125 84 C 104.73446 76.513583 127.03475 59.38648 124.59375 51.90625 C 122.15275 44.426021 94.027829 43.741849 87.65625 39.125 C 81.28467 34.508152 71.899685 7.9899879 64.03125 8 z M 64.03125 11.90625 C 64.208046 12.045423 65.56776 12.712264 67.15625 14.65625 C 68.97167 16.877947 71.031426 20.210059 73.0625 23.75 C 75.093573 27.28994 77.113982 31.048819 79.09375 34.3125 C 81.073519 37.576182 82.75512 40.328991 85.40625 42.25 C 88.057376 44.171009 91.18831 44.91637 94.90625 45.78125 C 98.624192 46.646129 102.81606 47.391152 106.8125 48.21875 C 110.80894 49.046347 114.60465 49.966787 117.28125 51 C 119.62327 51.904061 120.71845 53.000764 120.90625 53.125 C 120.82618 53.333062 120.57672 54.794782 119.21875 56.90625 C 117.66679 59.319356 115.1453 62.318181 112.40625 65.34375 C 109.66721 68.369316 106.71091 71.452346 104.21875 74.34375 C 101.72659 77.235155 99.632744 79.697501 98.625 82.8125 C 97.617256 85.927495 97.892393 89.134266 98.21875 92.9375 C 98.545107 96.740738 99.114622 100.97466 99.5625 105.03125 C 100.01038 109.08783 100.31178 112.97888 100.15625 115.84375 C 100.02016 118.35052 99.34151 119.69095 99.28125 119.90625 C 99.057443 119.89786 97.552762 120.17027 95.125 119.53125 C 92.350417 118.80093 88.723899 117.29504 85 115.625 C 81.276103 113.95497 77.426259 112.10169 73.90625 110.625 C 70.386242 109.1483 67.4302 107.93334 64.15625 107.9375 C 60.882303 107.94167 57.891241 109.1706 54.375 110.65625 C 50.858761 112.1419 47.032137 114.00799 43.3125 115.6875 C 39.592862 117.367 35.960216 118.85638 33.1875 119.59375 C 30.761373 120.23895 29.286908 119.99088 29.0625 120 C 29.004012 119.7864 28.29872 118.4439 28.15625 115.9375 C 27.993428 113.07303 28.281199 109.18271 28.71875 105.125 C 29.156299 101.0673 29.714573 96.835302 30.03125 93.03125 C 30.347928 89.227198 30.609418 85.987425 29.59375 82.875 C 28.578082 79.762573 26.468263 77.322553 23.96875 74.4375 C 21.469238 71.552452 18.527988 68.487339 15.78125 65.46875 C 13.034512 62.450158 10.495601 59.471649 8.9375 57.0625 C 7.5741618 54.954496 7.3592053 53.457399 7.28125 53.25 C 7.2962039 53.337785 8.2681026 52.126785 10.84375 51.125 C 13.517705 50.084977 17.34943 49.150265 21.34375 48.3125 C 25.33807 47.474737 29.534272 46.749339 33.25 45.875 C 36.96573 45.000663 40.103767 44.24025 42.75 42.3125 C 45.396234 40.384748 47.059794 37.612458 49.03125 34.34375 C 51.002705 31.075042 53.009191 27.326347 55.03125 23.78125 C 57.053308 20.236153 59.096493 16.88256 60.90625 14.65625 C 62.489787 12.708229 63.857465 12.044552 64.03125 11.90625 z "
|
||||
id="path2304" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3800);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3838)"
|
||||
d="M 60.98796,9.471226 C 62.846491,8.2143022 64.889907,8.0204702 67.219338,9.471226 L 64.037358,15.614216 L 60.98796,9.471226 z"
|
||||
id="path3409"
|
||||
sodipodi:nodetypes="cccc" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#linearGradient3315);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
||||
d="M 64.039064,11.90625 C 63.865274,12.044552 62.497594,12.708229 60.914064,14.65625 C 59.104304,16.88256 57.061124,20.236153 55.039064,23.78125 C 53.017004,27.326347 51.010514,31.075042 49.039064,34.34375 C 47.067604,37.612458 45.404044,40.384748 42.757814,42.3125 C 40.111574,44.24025 36.973544,45.000663 33.257814,45.875 C 29.542084,46.749339 25.345884,47.474737 21.351564,48.3125 C 17.357244,49.150265 13.525514,50.084977 10.851564,51.125 C 8.2759131,52.126785 7.3040131,53.337785 7.2890631,53.25 C 7.3670131,53.457399 7.5819731,54.954496 8.9453131,57.0625 C 10.503414,59.471649 13.042324,62.450158 15.789064,65.46875 C 18.535804,68.487339 21.477054,71.552452 23.976564,74.4375 C 26.476074,77.322553 28.585894,79.762573 29.601564,82.875 C 29.865144,83.682722 30.019904,84.511238 30.132814,85.34375 C 32.540654,85.431079 34.961934,85.5 37.414064,85.5 C 64.456484,85.5 88.974124,80.107134 106.91406,71.34375 C 108.71383,69.370041 110.60784,67.338911 112.41406,65.34375 C 115.15311,62.318181 117.67459,59.319356 119.22656,56.90625 C 120.58453,54.794782 120.83398,53.333062 120.91406,53.125 C 120.72626,53.000764 119.63107,51.904061 117.28906,51 C 114.61246,49.966787 110.81674,49.046347 106.82031,48.21875 C 102.82387,47.391152 98.631994,46.646129 94.914064,45.78125 C 91.196124,44.91637 88.065184,44.171009 85.414064,42.25 C 82.762934,40.328991 81.081334,37.576182 79.101564,34.3125 C 77.121794,31.048819 75.101384,27.28994 73.070314,23.75 C 71.039234,20.210059 68.979484,16.877947 67.164064,14.65625 C 65.575574,12.712264 64.215854,12.045423 64.039064,11.90625 z"
|
||||
id="path2910" />
|
||||
<g
|
||||
id="g3339"
|
||||
transform="translate(-132.29928,0)">
|
||||
<path
|
||||
id="path3317"
|
||||
d="M 196.34375,11.90625 C 196.16996,12.044552 194.80228,12.708229 193.21875,14.65625 C 191.40899,16.88256 189.36581,20.236153 187.34375,23.78125 C 185.32169,27.326347 183.3152,31.075042 181.34375,34.34375 C 179.37229,37.612458 177.70873,40.384748 175.0625,42.3125 C 172.41626,44.24025 169.27823,45.000663 165.5625,45.875 C 161.84677,46.749339 157.65057,47.474737 153.65625,48.3125 C 149.66193,49.150265 145.8302,50.084977 143.15625,51.125 C 140.5806,52.126785 139.6087,53.337785 139.59375,53.25 C 139.62377,53.329884 139.71528,53.638731 139.84375,54.0625 C 140.2595,53.69998 141.25985,52.862595 143.15625,52.125 C 145.8302,51.084977 149.66193,50.150265 153.65625,49.3125 C 157.65057,48.474737 161.84677,47.749339 165.5625,46.875 C 169.27823,46.000663 172.41626,45.24025 175.0625,43.3125 C 177.70873,41.384748 179.37229,38.612458 181.34375,35.34375 C 183.3152,32.075042 185.32169,28.326347 187.34375,24.78125 C 189.36581,21.236153 191.40899,17.88256 193.21875,15.65625 C 194.80228,13.708229 196.16996,13.044552 196.34375,12.90625 C 196.52054,13.045423 197.88026,13.712264 199.46875,15.65625 C 201.28417,17.877947 203.34392,21.210059 205.375,24.75 C 207.40607,28.28994 209.42648,32.048819 211.40625,35.3125 C 213.38602,38.576182 215.06762,41.328991 217.71875,43.25 C 220.36987,45.171009 223.50081,45.91637 227.21875,46.78125 C 230.93668,47.646129 235.12856,48.391152 239.125,49.21875 C 243.12143,50.046347 246.91715,50.966787 249.59375,52 C 251.51448,52.74144 252.56925,53.579608 253,53.9375 C 253.13371,53.522484 253.18802,53.204851 253.21875,53.125 C 253.03095,53.000764 251.93576,51.904061 249.59375,51 C 246.91715,49.966787 243.12143,49.046347 239.125,48.21875 C 235.12856,47.391152 230.93668,46.646129 227.21875,45.78125 C 223.50081,44.91637 220.36987,44.171009 217.71875,42.25 C 215.06762,40.328991 213.38602,37.576182 211.40625,34.3125 C 209.42648,31.048819 207.40607,27.28994 205.375,23.75 C 203.34392,20.210059 201.28417,16.877947 199.46875,14.65625 C 197.88026,12.712264 196.52054,12.045423 196.34375,11.90625 z"
|
||||
style="opacity:1;fill:url(#linearGradient3369);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="cscsscsccscsscsc"
|
||||
id="path3325"
|
||||
d="M 246.78125,49.937636 C 247.42469,50.142466 248.03845,50.379526 248.59375,50.593886 C 250.93576,51.497946 252.03095,52.594656 252.21875,52.718886 C 252.13867,52.926956 251.88922,54.388676 250.53125,56.500136 C 248.97928,58.913246 246.4578,61.912066 243.71875,64.937636 C 241.91253,66.932796 240.01852,68.963926 238.21875,70.937636 C 220.27881,79.701026 195.76117,85.093886 168.71875,85.093886 C 166.59433,85.093886 164.49568,85.039506 162.40625,84.968886 C 162.4184,85.051736 162.42625,85.135936 162.4375,85.218886 C 164.84534,85.306216 167.26662,85.375136 169.71875,85.375136 C 196.76117,85.375136 221.27881,79.982276 239.21875,71.218886 C 241.01852,69.245176 242.91253,67.214046 244.71875,65.218886 C 247.4578,62.193316 249.97928,59.194496 251.53125,56.781386 C 252.88922,54.669926 253.13867,53.208206 253.21875,53.000136 C 253.03095,52.875906 251.93576,51.779196 249.59375,50.875136 C 248.75868,50.552786 247.80629,50.238636 246.78125,49.937636 z"
|
||||
style="opacity:1;fill:url(#linearGradient3345);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80892944;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:url(#linearGradient3850);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:0.77153558000000000;filter:url(#filter3928)"
|
||||
d="M 25.190679,119.77989 C 26.414679,122.74238 27.241162,124.11897 31.289475,123.31542 L 30.638356,120.21008 L 29.079766,120.3986 L 28.261711,118.57341 L 25.190679,119.77989 z"
|
||||
id="path3842"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3385);stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3391)"
|
||||
d="M 64.125001,11.90625 C 63.951211,12.044552 62.583531,12.708229 61.000001,14.65625 C 59.190241,16.88256 57.147061,20.236153 55.125001,23.78125 C 53.102941,27.326347 51.096451,31.075042 49.125001,34.34375 C 47.153541,37.612458 45.489981,40.384748 42.843751,42.3125 C 40.197511,44.24025 37.059481,45.000663 33.343751,45.875 C 29.628021,46.749339 25.431821,47.474737 21.437501,48.3125 C 17.443181,49.150265 13.611451,50.084977 10.937501,51.125 C 8.3618506,52.126785 7.3899506,53.337785 7.3750006,53.25 C 7.4529506,53.457399 7.6679106,54.954496 9.0312506,57.0625 C 10.589351,59.471649 13.128261,62.450158 15.875001,65.46875 C 18.621741,68.487339 21.562991,71.552452 24.062501,74.4375 C 26.562011,77.322553 28.671831,79.762573 29.687501,82.875 C 30.703171,85.987425 30.441681,89.227198 30.125001,93.03125 C 29.808321,96.835302 29.250051,101.0673 28.812501,105.125 C 28.374951,109.18271 28.087181,113.07303 28.250001,115.9375 C 28.392471,118.4439 29.097761,119.7864 29.156251,120 C 29.380661,119.99088 30.855121,120.23895 33.281251,119.59375 C 36.053961,118.85638 39.686611,117.367 43.406251,115.6875 C 47.125881,114.00799 50.952511,112.1419 54.468751,110.65625 C 57.984991,109.1706 60.976051,107.94167 64.250001,107.9375 C 67.523951,107.93334 70.479991,109.1483 74.000001,110.625 C 77.520011,112.10169 81.369851,113.95497 85.093751,115.625 C 88.817651,117.29504 92.444151,118.80093 95.218751,119.53125 C 97.646511,120.17027 99.151181,119.89786 99.375001,119.90625 C 99.435261,119.69095 100.1139,118.35052 100.25,115.84375 C 100.40553,112.97888 100.10412,109.08783 99.656251,105.03125 C 99.208371,100.97466 98.638841,96.740738 98.312501,92.9375 C 97.986141,89.134266 97.710991,85.927495 98.718751,82.8125 C 99.726491,79.697501 101.82033,77.235155 104.3125,74.34375 C 106.80466,71.452346 109.76095,68.369316 112.5,65.34375 C 115.23905,62.318181 117.76053,59.319356 119.3125,56.90625 C 120.67047,54.794782 120.91992,53.333062 121,53.125 C 120.8122,53.000764 119.71701,51.904061 117.375,51 C 114.6984,49.966787 110.90268,49.046347 106.90625,48.21875 C 102.90981,47.391152 98.717931,46.646129 95.000001,45.78125 C 91.282061,44.91637 88.151121,44.171009 85.500001,42.25 C 82.848871,40.328991 81.167271,37.576182 79.187501,34.3125 C 77.207731,31.048819 75.187321,27.28994 73.156251,23.75 C 71.125171,20.210059 69.065421,16.877947 67.250001,14.65625 C 65.661511,12.712264 64.301791,12.045423 64.125001,11.90625 z"
|
||||
id="path3375"
|
||||
sodipodi:nodetypes="cssssssssssssssscssssssscssssssscsssssssc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cssssssssssssssscssssssscssssssscsssssssc"
|
||||
id="path3395"
|
||||
d="M 64.125001,11.90625 C 63.951211,12.044552 62.583531,12.708229 61.000001,14.65625 C 59.190241,16.88256 57.147061,20.236153 55.125001,23.78125 C 53.102941,27.326347 51.096451,31.075042 49.125001,34.34375 C 47.153541,37.612458 45.489981,40.384748 42.843751,42.3125 C 40.197511,44.24025 37.059481,45.000663 33.343751,45.875 C 29.628021,46.749339 25.431821,47.474737 21.437501,48.3125 C 17.443181,49.150265 13.611451,50.084977 10.937501,51.125 C 8.3618506,52.126785 7.3899506,53.337785 7.3750006,53.25 C 7.4529506,53.457399 7.6679106,54.954496 9.0312506,57.0625 C 10.589351,59.471649 13.128261,62.450158 15.875001,65.46875 C 18.621741,68.487339 21.562991,71.552452 24.062501,74.4375 C 26.562011,77.322553 28.671831,79.762573 29.687501,82.875 C 30.703171,85.987425 30.441681,89.227198 30.125001,93.03125 C 29.808321,96.835302 29.250051,101.0673 28.812501,105.125 C 28.374951,109.18271 28.087181,113.07303 28.250001,115.9375 C 28.392471,118.4439 29.097761,119.7864 29.156251,120 C 29.380661,119.99088 30.855121,120.23895 33.281251,119.59375 C 36.053961,118.85638 39.686611,117.367 43.406251,115.6875 C 47.125881,114.00799 50.952511,112.1419 54.468751,110.65625 C 57.984991,109.1706 60.976051,107.94167 64.250001,107.9375 C 67.523951,107.93334 70.479991,109.1483 74.000001,110.625 C 77.520011,112.10169 81.369851,113.95497 85.093751,115.625 C 88.817651,117.29504 92.444151,118.80093 95.218751,119.53125 C 97.646511,120.17027 99.151181,119.89786 99.375001,119.90625 C 99.435261,119.69095 100.1139,118.35052 100.25,115.84375 C 100.40553,112.97888 100.10412,109.08783 99.656251,105.03125 C 99.208371,100.97466 98.638841,96.740738 98.312501,92.9375 C 97.986141,89.134266 97.710991,85.927495 98.718751,82.8125 C 99.726491,79.697501 101.82033,77.235155 104.3125,74.34375 C 106.80466,71.452346 109.76095,68.369316 112.5,65.34375 C 115.23905,62.318181 117.76053,59.319356 119.3125,56.90625 C 120.67047,54.794782 120.91992,53.333062 121,53.125 C 120.8122,53.000764 119.71701,51.904061 117.375,51 C 114.6984,49.966787 110.90268,49.046347 106.90625,48.21875 C 102.90981,47.391152 98.717931,46.646129 95.000001,45.78125 C 91.282061,44.91637 88.151121,44.171009 85.500001,42.25 C 82.848871,40.328991 81.167271,37.576182 79.187501,34.3125 C 77.207731,31.048819 75.187321,27.28994 73.156251,23.75 C 71.125171,20.210059 69.065421,16.877947 67.250001,14.65625 C 65.661511,12.712264 64.301791,12.045423 64.125001,11.90625 z"
|
||||
style="opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3385);stroke-width:0.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1;filter:url(#filter3401)" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccc"
|
||||
id="path3932"
|
||||
d="M 25.190679,119.77989 C 26.414679,122.74238 27.241162,124.11897 31.289475,123.31542 L 30.638356,120.21008 L 29.079766,120.3986 L 28.261711,118.57341 L 25.190679,119.77989 z"
|
||||
style="opacity:0.7715356;fill:url(#linearGradient3934);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3928)"
|
||||
transform="matrix(-1,0,0,1,128.10515,0)" />
|
||||
<path
|
||||
style="opacity:1;fill:url(#radialGradient3956);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:14.80851269000000059;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
||||
d="M 26.03125 94.25 C 24.983755 105.1142 22.21942 119.85075 26.78125 123.15625 C 33.15283 127.7731 56.287818 111.82251 64.15625 111.8125 C 72.024682 111.80249 95.202691 127.69555 101.5625 123.0625 C 106.10279 119.75495 103.30815 105.10184 102.21875 94.25 L 98.34375 94.25 C 98.677864 97.707156 99.164649 101.42777 99.5625 105.03125 C 100.01038 109.08783 100.31178 112.97888 100.15625 115.84375 C 100.02016 118.35052 99.34151 119.69095 99.28125 119.90625 C 99.057443 119.89786 97.552762 120.17027 95.125 119.53125 C 92.350417 118.80093 88.723899 117.29504 85 115.625 C 81.276103 113.95497 77.426259 112.10169 73.90625 110.625 C 70.386242 109.1483 67.4302 107.93334 64.15625 107.9375 C 60.882303 107.94167 57.891241 109.1706 54.375 110.65625 C 50.858761 112.1419 47.032137 114.00799 43.3125 115.6875 C 39.592862 117.367 35.960216 118.85638 33.1875 119.59375 C 30.761373 120.23895 29.286908 119.99088 29.0625 120 C 29.004012 119.7864 28.29872 118.4439 28.15625 115.9375 C 27.993428 113.07303 28.281199 109.18271 28.71875 105.125 C 29.110886 101.48845 29.580993 97.733027 29.90625 94.25 L 26.03125 94.25 z "
|
||||
id="path3936" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 36 KiB |
688
imgsrc/trim.svg
Normal file
@ -0,0 +1,688 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg1307"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46+devel"
|
||||
version="1.0"
|
||||
sodipodi:docname="transform-crop.svgz"
|
||||
inkscape:export-filename="/home/pinheiro/pics/oxygen-icons/scalable/actions/transform-crop.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:output_extension="org.inkscape.output.svgz.inkscape">
|
||||
<defs
|
||||
id="defs1309">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2594">
|
||||
<stop
|
||||
style="stop-color:#fafafa;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2596" />
|
||||
<stop
|
||||
style="stop-color:#fafafa;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop2598" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3969">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3971" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3973" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2783">
|
||||
<stop
|
||||
style="stop-color:#323232;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2785" />
|
||||
<stop
|
||||
id="stop2787"
|
||||
offset="0.07692308"
|
||||
style="stop-color:#dfe1e1;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#b6b1b1;stop-opacity:1;"
|
||||
offset="0.26289096"
|
||||
id="stop2799" />
|
||||
<stop
|
||||
id="stop2789"
|
||||
offset="0.5"
|
||||
style="stop-color:#8d8282;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0.78201604"
|
||||
id="stop2791" />
|
||||
<stop
|
||||
style="stop-color:#dfd9df;stop-opacity:1;"
|
||||
offset="0.9005897"
|
||||
id="stop2793" />
|
||||
<stop
|
||||
style="stop-color:#3a3a3a;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2795" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2222"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop2224"
|
||||
offset="0"
|
||||
style="stop-color:#0066ff;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop2226"
|
||||
offset="1"
|
||||
style="stop-color:#80b3ff;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3314"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3316"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3318"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient2431">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop2433" />
|
||||
<stop
|
||||
id="stop2435"
|
||||
offset="0.42597079"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop2437"
|
||||
offset="0.5892781"
|
||||
style="stop-color:#f1f1f1;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#eaeaea;stop-opacity:1;"
|
||||
offset="0.80219781"
|
||||
id="stop2439" />
|
||||
<stop
|
||||
style="stop-color:#dfdfdf;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop2441" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7422">
|
||||
<stop
|
||||
style="stop-color:#b4b4b6;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop7424" />
|
||||
<stop
|
||||
id="stop5348"
|
||||
offset="0.5"
|
||||
style="stop-color:#9c9ca1;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop7426"
|
||||
offset="1"
|
||||
style="stop-color:#cdcdd1;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3310"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3312"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3314"
|
||||
offset="1"
|
||||
style="stop-color:#ffffff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
x="-0.21138181"
|
||||
width="1.4227636"
|
||||
y="-0.21047288"
|
||||
height="1.4209458"
|
||||
id="filter9723">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.4336041"
|
||||
id="feGaussianBlur9725" />
|
||||
</filter>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath10698">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
||||
d="M -128.2008,-3.392377 L -104.45558,6.3360672 L -102.43766,6.1757677 L -103.81912,-4.5678172 L -105.75454,-5.8316609 L -124.96922,-4.4459394 L -128.2008,-3.392377 z "
|
||||
id="path10700"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</clipPath>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2783"
|
||||
id="radialGradient3418"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9728905,-8.15107,-18.526373,-2.211261,1957.2342,725.31677)"
|
||||
cx="53.235302"
|
||||
cy="106.0573"
|
||||
fx="53.235302"
|
||||
fy="106.0573"
|
||||
r="9.1025209" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2594"
|
||||
id="radialGradient3420"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.5808473,-2.8009276,-6.4965168,-1.3472267,701.00301,348.75795)"
|
||||
cx="53.347126"
|
||||
cy="104.68401"
|
||||
fx="53.347126"
|
||||
fy="104.68401"
|
||||
r="9.1025209" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3314"
|
||||
id="radialGradient3422"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-2.9339535,-1.0170467,-1.1904108,3.4340702,323.071,-252.78281)"
|
||||
cx="49.110855"
|
||||
cy="105.43803"
|
||||
fx="49.110855"
|
||||
fy="105.43803"
|
||||
r="10.20672" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2783"
|
||||
id="linearGradient3425"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.2608955,0,0,1.9345479,-550.58555,-317.90247)"
|
||||
x1="190.03462"
|
||||
y1="90.22673"
|
||||
x2="208.7153"
|
||||
y2="90.22673" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3969"
|
||||
id="linearGradient3430"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.2608955,0,0,1.9345479,-497.11778,-432.24104)"
|
||||
x1="98.411324"
|
||||
y1="185.68851"
|
||||
x2="166.32983"
|
||||
y2="155.59846" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient7422"
|
||||
id="linearGradient3525"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(6.0715756e-2,0,0,9.7589526e-2,24.201706,-45.627655)"
|
||||
x1="399.77466"
|
||||
y1="1164.6696"
|
||||
x2="399.77466"
|
||||
y2="549.06134" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2431"
|
||||
id="linearGradient3527"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.5415355,0,0,0.7222225,23.477667,-8.2222193)"
|
||||
x1="119.57646"
|
||||
y1="23.792561"
|
||||
x2="15.999996"
|
||||
y2="109.6508" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3310"
|
||||
id="linearGradient3529"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0,-1.5975038,-2,0,96,199.26848)"
|
||||
x1="102.31124"
|
||||
y1="-5.8302126"
|
||||
x2="74.330322"
|
||||
y2="32" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient2222"
|
||||
id="linearGradient3538"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.7476489,0,0,0.7476489,0,-19.999999)"
|
||||
x1="8.2386189"
|
||||
y1="-13.864992"
|
||||
x2="8.2386189"
|
||||
y2="-1.4047648" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter4420">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="3.0486726"
|
||||
id="feGaussianBlur4422" />
|
||||
</filter>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask3562">
|
||||
<rect
|
||||
ry="1.4444447"
|
||||
rx="1.1997639"
|
||||
y="8"
|
||||
x="-4.0000005"
|
||||
height="116.00001"
|
||||
width="124"
|
||||
id="rect3564"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter4420)"
|
||||
transform="matrix(1.1453342,0,0,1.1453342,15.087799,-38.432604)" />
|
||||
</mask>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.2136483"
|
||||
inkscape:cx="77.317692"
|
||||
inkscape:cy="55.850409"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
guidetolerance="4"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="840"
|
||||
inkscape:window-x="223"
|
||||
inkscape:window-y="37"
|
||||
objecttolerance="4"
|
||||
gridtolerance="4">
|
||||
<sodipodi:guide
|
||||
orientation="horizontal"
|
||||
position="-32.073749"
|
||||
id="guide2204" />
|
||||
<inkscape:grid
|
||||
id="GridFromPre046Settings"
|
||||
type="xygrid"
|
||||
originx="0px"
|
||||
originy="0px"
|
||||
spacingx="4px"
|
||||
spacingy="4px"
|
||||
color="#0000ff"
|
||||
empcolor="#0000ff"
|
||||
opacity="0.2"
|
||||
empopacity="0.4"
|
||||
empspacing="4"
|
||||
visible="true"
|
||||
enabled="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata1312">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" />
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>Oxygen team</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/LGPL/2.1/">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/Notice" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/ShareAlike" />
|
||||
<cc:requires
|
||||
rdf:resource="http://web.resource.org/cc/SourceCode" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<rect
|
||||
ry="0.1870501"
|
||||
rx="0.1537565"
|
||||
y="28.129654"
|
||||
x="8"
|
||||
height="92"
|
||||
width="92"
|
||||
id="rect3226"
|
||||
style="fill:#618fd2;fill-opacity:0.09195401;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1" />
|
||||
<g
|
||||
id="g3520"
|
||||
transform="translate(32,-0.1296539)">
|
||||
<rect
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename="/home/pinheiro/Desktop/mock2.png"
|
||||
style="opacity:0.75;fill:url(#linearGradient3525);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="rect3281"
|
||||
width="92"
|
||||
height="92"
|
||||
x="28.129654"
|
||||
y="-24"
|
||||
inkscape:r_cx="true"
|
||||
inkscape:r_cy="true"
|
||||
ry="3.9616783"
|
||||
rx="3.9616783"
|
||||
transform="matrix(0,1,1,0,0,0)" />
|
||||
<rect
|
||||
ry="1.4444447"
|
||||
rx="1.1997639"
|
||||
y="-20"
|
||||
x="32.129654"
|
||||
height="84"
|
||||
width="84"
|
||||
id="rect3283"
|
||||
style="fill:url(#linearGradient3527);fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
transform="matrix(0,1,1,0,0,0)" />
|
||||
<path
|
||||
id="path3285"
|
||||
d="M 64,53.096891 C 45.143834,70.163928 24.748768,86.162699 -2.0000002e-07,96.129654 L -2.0000002e-07,52.647595 C 23.693959,50.212248 45.09831,42.609775 64,32.129654 L 64,53.096891 z"
|
||||
style="fill:url(#linearGradient3529);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(-16,20.129654)"
|
||||
style="fill:#7193c6;fill-opacity:1"
|
||||
id="g2250">
|
||||
<rect
|
||||
ry="1.3512546"
|
||||
rx="0.077153668"
|
||||
y="-116"
|
||||
x="16"
|
||||
height="4"
|
||||
width="4"
|
||||
id="rect3210"
|
||||
style="opacity:1;fill:#7193c6;fill-opacity:1;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1"
|
||||
transform="matrix(0,1,-1,0,0,0)"
|
||||
inkscape:tile-w="8"
|
||||
inkscape:tile-h="8"
|
||||
inkscape:tile-cx="124"
|
||||
inkscape:tile-cy="28" />
|
||||
<use
|
||||
style="fill:#7193c6;fill-opacity:1"
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect3210"
|
||||
xlink:href="#rect3210"
|
||||
transform="translate(0,8)"
|
||||
id="use2236"
|
||||
width="128"
|
||||
height="128" />
|
||||
<use
|
||||
style="fill:#7193c6;fill-opacity:1"
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect3210"
|
||||
xlink:href="#rect3210"
|
||||
transform="translate(0,16)"
|
||||
id="use2240"
|
||||
width="128"
|
||||
height="128" />
|
||||
<use
|
||||
style="fill:#7193c6;fill-opacity:1"
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect3210"
|
||||
xlink:href="#rect3210"
|
||||
transform="translate(0,24)"
|
||||
id="use2244"
|
||||
width="128"
|
||||
height="128" />
|
||||
<use
|
||||
style="fill:#7193c6;fill-opacity:1"
|
||||
x="0"
|
||||
y="0"
|
||||
inkscape:tiled-clone-of="#rect3210"
|
||||
xlink:href="#rect3210"
|
||||
transform="translate(0,32)"
|
||||
id="use2248"
|
||||
width="128"
|
||||
height="128" />
|
||||
<use
|
||||
height="88"
|
||||
width="88"
|
||||
transform="translate(0,24)"
|
||||
id="use3220"
|
||||
xlink:href="#use2240"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="88"
|
||||
width="88"
|
||||
transform="translate(0,24)"
|
||||
id="use3222"
|
||||
xlink:href="#use2244"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(0,32)"
|
||||
id="use2230"
|
||||
xlink:href="#use2244"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(0,32)"
|
||||
id="use2232"
|
||||
xlink:href="#use2248"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(0,32)"
|
||||
id="use2234"
|
||||
xlink:href="#use3220"
|
||||
y="0"
|
||||
x="0" />
|
||||
</g>
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="matrix(8.5712909e-8,-0.9999999,0.9999999,8.5712909e-8,-20.129659,128.12964)"
|
||||
id="use2258"
|
||||
xlink:href="#g2250"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(-88,0)"
|
||||
id="use2314"
|
||||
xlink:href="#g2250"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="matrix(8.5712909e-8,-0.9999999,0.9999999,8.5712909e-8,-20.129651,216.12964)"
|
||||
id="use2316"
|
||||
xlink:href="#g2250"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(96,0.1296547)"
|
||||
id="use3300"
|
||||
xlink:href="#rect3222"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(7.4990672e-6,96.129662)"
|
||||
id="use3302"
|
||||
xlink:href="#rect3222"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
height="128"
|
||||
width="128"
|
||||
transform="translate(96,96.129652)"
|
||||
id="use3304"
|
||||
xlink:href="#rect3222"
|
||||
y="0"
|
||||
x="0" />
|
||||
<rect
|
||||
ry="0.18696606"
|
||||
rx="0.15479258"
|
||||
y="-32"
|
||||
x="0"
|
||||
height="12"
|
||||
width="12"
|
||||
id="rect3222"
|
||||
style="fill:url(#linearGradient3538);fill-opacity:1;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1"
|
||||
transform="scale(1,-1)" />
|
||||
<rect
|
||||
transform="scale(1,-1)"
|
||||
style="fill:#bfd9ff;fill-opacity:1;stroke:none;stroke-width:0.86699998;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.08779998;stroke-opacity:1"
|
||||
id="rect2225"
|
||||
width="4"
|
||||
height="4"
|
||||
x="4"
|
||||
y="-28"
|
||||
rx="0.15479258"
|
||||
ry="0.18696606" />
|
||||
<use
|
||||
style="fill:#a4c0e4"
|
||||
height="88"
|
||||
width="88"
|
||||
transform="translate(96,0.1296539)"
|
||||
id="use3226"
|
||||
xlink:href="#rect2225"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
style="fill:#a4c0e4"
|
||||
height="88"
|
||||
width="88"
|
||||
transform="translate(7.5e-6,96.129661)"
|
||||
id="use3228"
|
||||
xlink:href="#rect2225"
|
||||
y="0"
|
||||
x="0" />
|
||||
<use
|
||||
style="fill:#a4c0e4"
|
||||
height="88"
|
||||
width="88"
|
||||
transform="translate(96,96.129654)"
|
||||
id="use3230"
|
||||
xlink:href="#rect2225"
|
||||
y="0"
|
||||
x="0" />
|
||||
<rect
|
||||
style="opacity:0.57786889;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.63199997;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4;stroke-opacity:1"
|
||||
id="rect1327"
|
||||
width="1"
|
||||
height="0"
|
||||
x="15.057414"
|
||||
y="-308.20486" />
|
||||
<g
|
||||
id="g3407"
|
||||
transform="matrix(0.8731076,0,0,0.8731076,-13.173272,33.555799)"
|
||||
mask="url(#mask3562)">
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path3836"
|
||||
d="m 29.733826,93.557578 76.565594,-35.724313 3.74271,-5.050163 -27.964957,-18.69067 -6.907623,1.950856 -41.307066,47.80066 -4.128658,9.71363 z"
|
||||
style="fill:url(#radialGradient3418);fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<path
|
||||
style="fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 107.32508,50.938663 -74.427424,35.613119 -3.008197,6.986785 76.368201,-35.710168 3.7845,-5.046004 -2.71708,-1.843732 z"
|
||||
id="path8241"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="opacity:0.10688836;fill:url(#radialGradient3420);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 29.733826,93.557578 76.565594,-35.724313 3.74271,-5.050163 -27.964957,-18.69067 -6.907623,1.950856 -41.307066,47.80066 -4.128658,9.71363 z"
|
||||
id="path11683"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
id="path17921"
|
||||
d="m 29.733826,93.557578 76.565594,-35.724313 3.74271,-5.050163 -27.964957,-18.69067 -6.907623,1.950856 -41.307066,47.80066 -4.128658,9.71363 z"
|
||||
style="fill:none;stroke:url(#radialGradient3422);stroke-width:0.86455041;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:4" />
|
||||
<rect
|
||||
style="fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="rect8239"
|
||||
width="39.714981"
|
||||
height="37.454777"
|
||||
x="27.310663"
|
||||
y="81.415123"
|
||||
transform="matrix(0.6571695,-0.7537428,0.7537428,0.6571695,0,0)"
|
||||
rx="3.8771732"
|
||||
ry="3.8771732" />
|
||||
<rect
|
||||
transform="matrix(-0.7651682,-0.6438304,-0.6438304,0.7651682,0,0)"
|
||||
style="fill:url(#linearGradient3425);fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="rect2803"
|
||||
width="40.499767"
|
||||
height="122.13765"
|
||||
x="-120.93575"
|
||||
y="-157.97318"
|
||||
rx="0"
|
||||
ry="0" />
|
||||
<rect
|
||||
transform="matrix(-0.7651682,-0.6438304,-0.6438304,0.7651682,0,0)"
|
||||
y="-161.84383"
|
||||
x="-119.89533"
|
||||
height="126.00658"
|
||||
width="39.223213"
|
||||
id="rect3967"
|
||||
style="fill:url(#linearGradient3430);fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<rect
|
||||
transform="matrix(-0.6438304,0.7651682,0.7651682,0.6438304,0,0)"
|
||||
y="80.243172"
|
||||
x="-155.77248"
|
||||
height="40.591759"
|
||||
width="100.57008"
|
||||
id="rect1851"
|
||||
style="opacity:0.52459011;fill:#e0e0e0;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
<rect
|
||||
ry="1.2485937"
|
||||
rx="1.2485937"
|
||||
transform="matrix(2.0406638,-2.3405465,2.3405465,2.0406638,304.62828,-199.57966)"
|
||||
y="-5.487061"
|
||||
x="-104.11894"
|
||||
height="12.061829"
|
||||
width="12.789698"
|
||||
id="rect8248"
|
||||
style="fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;filter:url(#filter9723)"
|
||||
clip-path="url(#clipPath10698)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 21 KiB |
@ -26,7 +26,7 @@ var current_library_request = null;
|
||||
|
||||
////////////////////////////// GET BOOK LIST //////////////////////////////
|
||||
|
||||
var LIBRARY_FETCH_TIMEOUT = 30000; // milliseconds
|
||||
var LIBRARY_FETCH_TIMEOUT = 5*60000; // milliseconds
|
||||
|
||||
function create_table_headers() {
|
||||
var thead = $('table#book_list thead tr');
|
||||
@ -54,7 +54,7 @@ function render_book(book) {
|
||||
formats = book.attr("formats").split(",");
|
||||
if (formats.length > 0) {
|
||||
for (i=0; i < formats.length; i++) {
|
||||
title += '<a title="Download in '+formats[i]+' format" class="format" href="'+format_url(formats[i], id, book.attr("title"))+'">'+formats[i]+'</a>, ';
|
||||
title += '<a title="Download in '+formats[i]+' format" class="format" href="'+format_url(formats[i], id, book.attr("safe_title"))+'">'+formats[i]+'</a>, ';
|
||||
}
|
||||
title = title.slice(0, title.length-2);
|
||||
title += ' ({0} MB) '.format(size);
|
||||
|
BIN
resources/images/news/ajc.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/images/news/boortz.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
resources/images/news/fronda.png
Normal file
After Width: | Height: | Size: 646 B |
BIN
resources/images/news/gazeta_pomorska.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
resources/images/news/howtogeek.png
Normal file
After Width: | Height: | Size: 922 B |
BIN
resources/images/news/jpost_fr.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
resources/images/news/legeartis.png
Normal file
After Width: | Height: | Size: 634 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1017 B |
BIN
resources/images/news/popscience.png
Normal file
After Width: | Height: | Size: 737 B |
BIN
resources/images/news/rmf24_ESKN.png
Normal file
After Width: | Height: | Size: 722 B |
BIN
resources/images/news/rmf24_fakty.png
Normal file
After Width: | Height: | Size: 722 B |
BIN
resources/images/rating.png
Normal file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 1.7 KiB |
BIN
resources/images/trim.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
116
resources/jacket/stylesheet.css
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
** Book Jacket generation
|
||||
**
|
||||
** The template for Book Jackets is template.xhtml
|
||||
** This CSS is inserted into the generated HTML at conversion time
|
||||
**
|
||||
** Users can control parts of the presentation of a generated book jacket by
|
||||
** editing this file and template.xhtml
|
||||
**
|
||||
** The general form of a generated Book Jacket:
|
||||
**
|
||||
** Title
|
||||
** Series: series [series_index]
|
||||
** Published: year_of_publication
|
||||
** Rating: #_of_stars
|
||||
** Tags: tag1, tag2, tag3 ...
|
||||
**
|
||||
** Comments
|
||||
**
|
||||
** If a book does not have Series information, a date of publication, a rating or tags
|
||||
** the corresponding row is automatically removed from the generated book jacket.
|
||||
*/
|
||||
|
||||
/*
|
||||
** Banner
|
||||
** Only affects EPUB, kindle ignores this type of formatting
|
||||
*/
|
||||
.cbj_banner {
|
||||
background: #eee;
|
||||
border: thin solid black;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
-webkit-border-radius:8px;
|
||||
}
|
||||
|
||||
/*
|
||||
** Title
|
||||
*/
|
||||
.cbj_title {
|
||||
font-size: x-large;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table containing Series, Publication Year, Rating and Tags
|
||||
*/
|
||||
table.cbj_header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
** General formatting for banner labels
|
||||
*/
|
||||
table.cbj_header td.cbj_label {
|
||||
font-family: sans-serif;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
/*
|
||||
** General formatting for banner content
|
||||
*/
|
||||
table.cbj_header td.cbj_content {
|
||||
font-family: sans-serif;
|
||||
text-align: left;
|
||||
width:60%;
|
||||
}
|
||||
|
||||
/*
|
||||
** To skip a banner item (Series|Published|Rating|Tags),
|
||||
** edit the appropriate CSS rule below.
|
||||
*/
|
||||
table.cbj_header tr.cbj_series {
|
||||
/* Uncomment the next line to remove 'Series' from banner section */
|
||||
/* display:none; */
|
||||
}
|
||||
|
||||
table.cbj_header tr.cbj_pubdate {
|
||||
/* Uncomment the next line to remove 'Published' from banner section */
|
||||
/* display:none; */
|
||||
}
|
||||
|
||||
table.cbj_header tr.cbj_rating {
|
||||
/* Uncomment the next line to remove 'Rating' from banner section */
|
||||
/* display:none; */
|
||||
}
|
||||
|
||||
table.cbj_header tr.cbj_tags {
|
||||
/* Uncomment the next line to remove 'Tags' from banner section */
|
||||
/* display:none; */
|
||||
}
|
||||
|
||||
hr {
|
||||
/* This rule controls formatting for any hr elements contained in the jacket */
|
||||
border-top: 0px solid white;
|
||||
border-right: 0px solid white;
|
||||
border-bottom: 2px solid black;
|
||||
border-left: 0px solid white;
|
||||
margin-left: 10%;
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.cbj_footer {
|
||||
font-family: sans-serif;
|
||||
font-size: small;
|
||||
margin-top: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.cbj_smallcaps {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.cbj_comments {
|
||||
font-family: sans-serif;
|
||||
}
|
34
resources/jacket/template.xhtml
Normal file
@ -0,0 +1,34 @@
|
||||
<html xmlns="{xmlns}">
|
||||
<head>
|
||||
<title>{title_str}</title>
|
||||
<meta name="calibre-content" content="jacket"/>
|
||||
<style type="text/css" media="screen">{css}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cbj_banner">
|
||||
<div class="cbj_title">{title}</div>
|
||||
<table class="cbj_header">
|
||||
<tr class="cbj_series">
|
||||
<td class="cbj_label">{series_label}:</td>
|
||||
<td class="cbj_content">{series}</td>
|
||||
</tr>
|
||||
<tr class="cbj_pubdate">
|
||||
<td class="cbj_label">{pubdate_label}:</td>
|
||||
<td class="cbj_content">{pubdate}</td>
|
||||
</tr>
|
||||
<tr class="cbj_rating">
|
||||
<td class="cbj_label">{rating_label}:</td>
|
||||
<td class="cbj_content">{rating}</td>
|
||||
</tr>
|
||||
<tr class="cbj_tags">
|
||||
<td class="cbj_label">{tags_label}:</td>
|
||||
<td class="cbj_content">{tags}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="cbj_footer">{footer}</div>
|
||||
</div>
|
||||
<hr class="cbj_kindle_banner_hr" />
|
||||
<div class="cbj_comments">{comments}</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.adventuregamers.com
|
||||
'''
|
||||
@ -11,13 +9,10 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class AdventureGamers(BasicNewsRecipe):
|
||||
title = u'Adventure Gamers'
|
||||
language = 'en'
|
||||
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Adventure games portal'
|
||||
publisher = 'Adventure Gamers'
|
||||
category = 'news, games, adventure, technology'
|
||||
language = 'en'
|
||||
|
||||
oldest_article = 10
|
||||
delay = 10
|
||||
max_articles_per_feed = 100
|
||||
@ -26,14 +21,25 @@ class AdventureGamers(BasicNewsRecipe):
|
||||
remove_javascript = True
|
||||
use_embedded_content = False
|
||||
INDEX = u'http://www.adventuregamers.com'
|
||||
extra_css = """
|
||||
.pageheader_type{font-size: x-large; font-weight: bold; color: #828D74}
|
||||
.pageheader_title{font-size: xx-large; color: #394128}
|
||||
.pageheader_byline{font-size: small; font-weight: bold; color: #394128}
|
||||
.score_bg {display: inline; width: 100%; margin-bottom: 2em}
|
||||
.score_column_1{ padding-left: 10px; font-size: small; width: 50%}
|
||||
.score_column_2{ padding-left: 10px; font-size: small; width: 50%}
|
||||
.score_column_3{ padding-left: 10px; font-size: small; width: 50%}
|
||||
.score_header{font-size: large; color: #50544A}
|
||||
.bodytext{display: block}
|
||||
body{font-family: Helvetica,Arial,sans-serif}
|
||||
"""
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"'
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':'content_middle'})
|
||||
@ -45,6 +51,7 @@ class AdventureGamers(BasicNewsRecipe):
|
||||
]
|
||||
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'toolbar_fat'})]
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [(u'Articles', u'http://feeds2.feedburner.com/AdventureGamers')]
|
||||
|
||||
@ -66,12 +73,12 @@ class AdventureGamers(BasicNewsRecipe):
|
||||
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Language" content="en-US"/>\n<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll('div', attrs={'class':'floatright'}):
|
||||
item.extract()
|
||||
self.append_page(soup, soup.body, 3)
|
||||
pager = soup.find('div',attrs={'class':'toolbar_fat'})
|
||||
if pager:
|
||||
pager.extract()
|
||||
return soup
|
||||
return self.adeify_images(soup)
|
||||
|
@ -10,12 +10,31 @@ class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
oldest_article = 1
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
extra_css = '.headline {font-size: x-large;} \n .fact { padding-top: 10pt }'
|
||||
|
||||
masthead_url = 'http://gawand.org/wp-content/uploads/2010/06/ajc-logo.gif'
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||
'''
|
||||
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'id':['cxArticleContent']})
|
||||
,dict(attrs={'id':['cxArticleText','cxArticleBodyText']})
|
||||
dict(name='div', attrs={'class':['cxArticleHeader']})
|
||||
,dict(attrs={'id':['cxArticleText']})
|
||||
]
|
||||
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div' , attrs={'class':'cxArticleList' })
|
||||
,dict(name='div' , attrs={'class':'cxFeedTease' })
|
||||
,dict(name='div' , attrs={'class':'cxElementEnlarge' })
|
||||
,dict(name='div' , attrs={'id':'cxArticleTools' })
|
||||
]
|
||||
|
||||
|
||||
|
||||
feeds = [
|
||||
('Breaking News', 'http://www.ajc.com/genericList-rss.do?source=61499'),
|
||||
# -------------------------------------------------------------------
|
||||
@ -23,7 +42,7 @@ class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
# read by simply removing the pound sign from it. I currently have it
|
||||
# set to only get the Cobb area
|
||||
# --------------------------------------------------------------------
|
||||
('Atlanta & Fulton', 'http://www.ajc.com/section-rss.do?source=atlanta'),
|
||||
#('Atlanta & Fulton', 'http://www.ajc.com/section-rss.do?source=atlanta'),
|
||||
#('Clayton', 'http://www.ajc.com/section-rss.do?source=clayton'),
|
||||
#('DeKalb', 'http://www.ajc.com/section-rss.do?source=dekalb'),
|
||||
#('Gwinnett', 'http://www.ajc.com/section-rss.do?source=gwinnett'),
|
||||
@ -41,7 +60,7 @@ class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
# but again
|
||||
# You can enable which ever team you like by removing the pound sign
|
||||
# ------------------------------------------------------------------------
|
||||
('Sports News', 'http://www.ajc.com/genericList-rss.do?source=61510'),
|
||||
#('Sports News', 'http://www.ajc.com/genericList-rss.do?source=61510'),
|
||||
#('Braves', 'http://www.ajc.com/genericList-rss.do?source=61457'),
|
||||
('Falcons', 'http://www.ajc.com/genericList-rss.do?source=61458'),
|
||||
#('Hawks', 'http://www.ajc.com/genericList-rss.do?source=61522'),
|
||||
@ -52,11 +71,16 @@ class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
('Music', 'http://www.accessatlanta.com/section-rss.do?source=music'),
|
||||
]
|
||||
|
||||
def postprocess_html(self, soup, first):
|
||||
for credit_tag in soup.findAll('span', attrs={'class':['imageCredit rightFloat']}):
|
||||
credit_tag.name ='p'
|
||||
|
||||
return soup
|
||||
|
||||
#def print_version(self, url):
|
||||
# return url.partition('?')[0] +'?printArticle=y'
|
||||
|
||||
|
||||
|
||||
def print_version(self, url):
|
||||
return url.partition('?')[0] +'?printArticle=y'
|
||||
|
||||
|
||||
|
||||
|
44
resources/recipes/boortz.recipe
Normal file
@ -0,0 +1,44 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, re
|
||||
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
title = 'Nealz Nuze'
|
||||
language = 'en'
|
||||
__author__ = 'TonytheBookworm'
|
||||
description = 'Neal Boortz Show Radio Notes'
|
||||
publisher = 'Neal Boortz'
|
||||
category = 'news, politics, USA, talkshow'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
linearize_tables = True
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
|
||||
masthead_url = 'http://boortz.com/images/nuze_logo.gif'
|
||||
keep_only_tags = [
|
||||
dict(name='td', attrs={'id':['contentWellCell']})
|
||||
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='a', attrs={'class':['blogPermalink']}),
|
||||
dict(name='span', attrs={'class':['blogBylineSeparator']}),
|
||||
dict(name='td', attrs={'id':['nealztitle']}),
|
||||
]
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'blogEntryBody'}),]
|
||||
feeds = [
|
||||
('NUZE', 'http://boortz.com/nealz_nuze_rss/rss.xml')
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
125
resources/recipes/brand_eins.recipe
Normal file
@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Constantin Hofstetter <consti at consti.de>'
|
||||
__version__ = '0.95'
|
||||
|
||||
''' http://brandeins.de - Wirtschaftsmagazin '''
|
||||
import re
|
||||
import string
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class BrandEins(BasicNewsRecipe):
|
||||
|
||||
title = u'Brand Eins'
|
||||
__author__ = 'Constantin Hofstetter'
|
||||
description = u'Wirtschaftsmagazin'
|
||||
publisher ='brandeins.de'
|
||||
category = 'politics, business, wirtschaft, Germany'
|
||||
use_embedded_content = False
|
||||
lang = 'de-DE'
|
||||
no_stylesheets = True
|
||||
encoding = 'utf-8'
|
||||
language = 'de'
|
||||
|
||||
# 2 is the last full magazine (default)
|
||||
# 1 is the newest (but not full)
|
||||
# 3 is one before 2 etc.
|
||||
which_ausgabe = 2
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'theContent'}), dict(name='div', attrs={'id':'sidebar'}), dict(name='div', attrs={'class':'intro'}), dict(name='p', attrs={'class':'bodytext'}), dict(name='div', attrs={'class':'single_image'})]
|
||||
|
||||
'''
|
||||
brandeins.de
|
||||
'''
|
||||
|
||||
def postprocess_html(self, soup,first):
|
||||
|
||||
# Move the image of the sidebar right below the h3
|
||||
first_h3 = soup.find(name='div', attrs={'id':'theContent'}).find('h3')
|
||||
for imgdiv in soup.findAll(name='div', attrs={'class':'single_image'}):
|
||||
if len(first_h3.findNextSiblings('div', {'class':'intro'})) >= 1:
|
||||
# first_h3.parent.insert(2, imgdiv)
|
||||
first_h3.findNextSiblings('div', {'class':'intro'})[0].parent.insert(4, imgdiv)
|
||||
else:
|
||||
first_h3.parent.insert(2, imgdiv)
|
||||
|
||||
# Now, remove the sidebar
|
||||
soup.find(name='div', attrs={'id':'sidebar'}).extract()
|
||||
|
||||
# Remove the rating-image (stars) from the h3
|
||||
for img in first_h3.findAll(name='img'):
|
||||
img.extract()
|
||||
|
||||
# Mark the intro texts as italic
|
||||
for div in soup.findAll(name='div', attrs={'class':'intro'}):
|
||||
for p in div.findAll('p'):
|
||||
content = self.tag_to_string(p)
|
||||
new_p = "<p><i>"+ content +"</i></p>"
|
||||
p.replaceWith(new_p)
|
||||
|
||||
return soup
|
||||
|
||||
def parse_index(self):
|
||||
feeds = []
|
||||
|
||||
archive = "http://www.brandeins.de/archiv.html"
|
||||
|
||||
soup = self.index_to_soup(archive)
|
||||
latest_jahrgang = soup.findAll('div', attrs={'class': re.compile(r'\bjahrgang-latest\b') })[0].findAll('ul')[0]
|
||||
pre_latest_issue = latest_jahrgang.findAll('a')[len(latest_jahrgang.findAll('a'))-self.which_ausgabe]
|
||||
url = pre_latest_issue.get('href', False)
|
||||
# Get the title for the magazin - build it out of the title of the cover - take the issue and year;
|
||||
self.title = "Brand Eins "+ re.search(r"(?P<date>\d\d\/\d\d\d\d+)", pre_latest_issue.find('img').get('title', False)).group('date')
|
||||
url = 'http://brandeins.de/'+url
|
||||
|
||||
# url = "http://www.brandeins.de/archiv/magazin/tierisch.html"
|
||||
titles_and_articles = self.brand_eins_parse_latest_issue(url)
|
||||
if titles_and_articles:
|
||||
for title, articles in titles_and_articles:
|
||||
feeds.append((title, articles))
|
||||
return feeds
|
||||
|
||||
def brand_eins_parse_latest_issue(self, url):
|
||||
soup = self.index_to_soup(url)
|
||||
article_lists = [soup.find('div', attrs={'class':'subColumnLeft articleList'}), soup.find('div', attrs={'class':'subColumnRight articleList'})]
|
||||
|
||||
titles_and_articles = []
|
||||
current_articles = []
|
||||
chapter_title = "Editorial"
|
||||
self.log('Found Chapter:', chapter_title)
|
||||
|
||||
# Remove last list of links (thats just the impressum and the 'gewinnspiel')
|
||||
article_lists[1].findAll('ul')[len(article_lists[1].findAll('ul'))-1].extract()
|
||||
|
||||
for article_list in article_lists:
|
||||
for chapter in article_list.findAll('ul'):
|
||||
if len(chapter.findPreviousSiblings('h3')) >= 1:
|
||||
new_chapter_title = string.capwords(self.tag_to_string(chapter.findPreviousSiblings('h3')[0]))
|
||||
if new_chapter_title != chapter_title:
|
||||
titles_and_articles.append([chapter_title, current_articles])
|
||||
current_articles = []
|
||||
self.log('Found Chapter:', new_chapter_title)
|
||||
chapter_title = new_chapter_title
|
||||
for li in chapter.findAll('li'):
|
||||
a = li.find('a', href = True)
|
||||
if a is None:
|
||||
continue
|
||||
title = self.tag_to_string(a)
|
||||
url = a.get('href', False)
|
||||
if not url or not title:
|
||||
continue
|
||||
url = 'http://brandeins.de/'+url
|
||||
if len(a.parent.findNextSiblings('p')) >= 1:
|
||||
description = self.tag_to_string(a.parent.findNextSiblings('p')[0])
|
||||
else:
|
||||
description = ''
|
||||
|
||||
self.log('\t\tFound article:', title)
|
||||
self.log('\t\t\t', url)
|
||||
self.log('\t\t\t', description)
|
||||
|
||||
current_articles.append({'title': title, 'url': url, 'description': description, 'date':''})
|
||||
titles_and_articles.append([chapter_title, current_articles])
|
||||
return titles_and_articles
|
@ -20,16 +20,27 @@ class Danas(BasicNewsRecipe):
|
||||
encoding = 'utf-8'
|
||||
masthead_url = 'http://www.danas.rs/images/basic/danas.gif'
|
||||
language = 'sr'
|
||||
remove_javascript = True
|
||||
publication_type = 'newspaper'
|
||||
remove_empty_feeds = True
|
||||
extra_css = """ @font-face {font-family: "serif1";src:url(res:///opt/sony/ebook/FONT/tt0011m_.ttf)}
|
||||
@font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||
.article_description,body,.lokacija{font-family: Tahoma,Arial,Helvetica,sans1,sans-serif}
|
||||
.nadNaslov,h1,.preamble{font-family: Georgia,"Times New Roman",Times,serif1,serif}
|
||||
.antrfileText{border-left: 2px solid #999999; margin-left: 0.8em; padding-left: 1.2em;
|
||||
margin-bottom: 0; margin-top: 0} h2,.datum,.lokacija,.autor{font-size: small}
|
||||
.antrfileNaslov{border-left: 2px solid #999999; margin-left: 0.8em; padding-left: 1.2em;
|
||||
font-weight:bold; margin-bottom: 0; margin-top: 0} img{margin-bottom: 0.8em} """
|
||||
.antrfileText{border-left: 2px solid #999999;
|
||||
margin-left: 0.8em;
|
||||
padding-left: 1.2em;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0}
|
||||
h2,.datum,.lokacija,.autor{font-size: small}
|
||||
.antrfileNaslov{border-left: 2px solid #999999;
|
||||
margin-left: 0.8em;
|
||||
padding-left: 1.2em;
|
||||
font-weight:bold;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0}
|
||||
img{margin-bottom: 0.8em}
|
||||
"""
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
@ -38,14 +49,25 @@ class Danas(BasicNewsRecipe):
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
preprocess_regexps = [
|
||||
(re.compile(u'\u0110'), lambda match: u'\u00D0')
|
||||
,(re.compile(u'\u2018'), lambda match: '‘') # left single quotation mark
|
||||
,(re.compile(u'\u2019'), lambda match: '’') # right single quotation mark
|
||||
,(re.compile(u'\u201a'), lambda match: '‘') # single low-9 quotation mark
|
||||
,(re.compile(u'\u201b'), lambda match: '’') # single high-reversed-9 quotation mark
|
||||
,(re.compile(u'\u201c'), lambda match: '“') # left double quotation mark
|
||||
,(re.compile(u'\u201d'), lambda match: '”') # right double quotation mark
|
||||
,(re.compile(u'\u201e'), lambda match: '“') # double low-9 quotation mark
|
||||
,(re.compile(u'\u201f'), lambda match: '”') # double high-reversed-9 quotation mark
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'left'})]
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':['width_1_4','metaClanka','baner']})
|
||||
,dict(name='div', attrs={'id':'comments'})
|
||||
,dict(name=['object','link','iframe'])
|
||||
,dict(name=['object','link','iframe','meta'])
|
||||
]
|
||||
remove_attributes = ['w:st','st']
|
||||
|
||||
feeds = [
|
||||
(u'Politika' , u'http://www.danas.rs/rss/rss.asp?column_id=27')
|
||||
@ -74,12 +96,23 @@ class Danas(BasicNewsRecipe):
|
||||
,(u'Vostani Serbie' , u'http://www.danas.rs/rss/rss.asp?column_id=57')
|
||||
,(u'Med&Jad-a' , u'http://www.danas.rs/rss/rss.asp?column_id=58')
|
||||
,(u'Svetlosti pozornice' , u'http://www.danas.rs/rss/rss.asp?column_id=59')
|
||||
,(u'Dva cvancika' , u'http://www.danas.rs/rss/rss.asp?column_id=65')
|
||||
,(u'Iz kornera' , u'http://www.danas.rs/rss/rss.asp?column_id=64')
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for tagn in ['st1:place','st1:city','st1:country-region','st1:state']:
|
||||
for item in soup.body.findAll(tagn):
|
||||
item.name='span'
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return self.adeify_images(soup)
|
||||
for item in soup.findAll('a'):
|
||||
if item.has_key('name'):
|
||||
item.extract()
|
||||
for item in soup.findAll('img'):
|
||||
if not item.has_key('alt'):
|
||||
item['alt'] = 'image'
|
||||
return soup
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '&action=print'
|
||||
|
@ -8,6 +8,7 @@ espn.com
|
||||
'''
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ptempfile import TemporaryFile
|
||||
|
||||
class ESPN(BasicNewsRecipe):
|
||||
|
||||
@ -78,12 +79,19 @@ class ESPN(BasicNewsRecipe):
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.set_handle_refresh(False)
|
||||
if self.username is not None and self.password is not None:
|
||||
br.open('http://espn.com')#('http://espn.go.com/#myespn')
|
||||
br.select_form(nr=1)
|
||||
url = ('https://r.espn.go.com/members/v3_1/login')
|
||||
raw = br.open(url).read()
|
||||
raw = re.sub(r'(?s)<form>.*?id="regsigninbtn".*?</form>', '', raw)
|
||||
with TemporaryFile(suffix='.htm') as fname:
|
||||
with open(fname, 'wb') as f:
|
||||
f.write(raw)
|
||||
br.open_local_file(fname)
|
||||
|
||||
br.form = br.forms().next()
|
||||
br.form.find_control(name='username', type='text').value = self.username
|
||||
br.form['password'] = self.password
|
||||
br.submit()
|
||||
br.submit().read()
|
||||
br.open('http://espn.go.com').read()
|
||||
br.set_handle_refresh(True)
|
||||
return br
|
||||
|
||||
|
104
resources/recipes/gazeta_pomorska.recipe
Normal file
@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# # Przed uzyciem przeczytaj komentarz w sekcji "feeds"
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2010, Richard z forum.eksiazki.org'
|
||||
'''pomorska.pl'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class GazetaPomorska(BasicNewsRecipe):
|
||||
title = u'Gazeta Pomorska'
|
||||
publisher = u'Gazeta Pomorska'
|
||||
description = u'Kujawy i Pomorze - wiadomo\u015bci'
|
||||
language = 'pl'
|
||||
__author__ = u'Richard z forum.eksiazki.org'
|
||||
# # (dziekuje t3d z forum.eksiazki.org za testy)
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 20
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'<a href="http://maps.google[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
|
||||
(re.compile(r'[<Bb >]*Poznaj opinie[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
|
||||
(re.compile(r'[<Bb >]*Przeczytaj[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
|
||||
(re.compile(r'[<Bb >]*Wi.cej informacji[^<]*[</Bb >]*[^<]*<a href[^>]*>[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
|
||||
(re.compile(r'<a href[^>]*>[<Bb >]*Wideo[^<]*[</Bb >]*[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: ''),
|
||||
(re.compile(r'<a href[^>]*>[<Bb >]*KLIKNIJ TUTAJ[^<]*[</Bb >]*[^<]*</a>\.*', re.DOTALL|re.IGNORECASE), lambda m: '')
|
||||
]
|
||||
|
||||
feeds = [
|
||||
# # Tutaj jest wymieniona lista kategorii jakie mozemy otrzymywac z Gazety
|
||||
# # Pomorskiej, po jednej kategorii w wierszu. Jesli na poczatku danego wiersza
|
||||
# # znajduje sie jeden znak "#", oznacza to ze kategoria jest zakomentowana
|
||||
# # i nie bedziemy jej otrzymywac. Jesli chcemy ja otrzymywac nalezy usunac
|
||||
# # znak # z jej wiersza.
|
||||
# # Jesli subskrybujemy wiecej niz jedna kategorie, na koncu wiersza z kazda
|
||||
# # kategoria musi sie znajdowac niezakomentowany przecinek, z wyjatkiem
|
||||
# # ostatniego wiersza - ma byc bez przecinka na koncu.
|
||||
# # Rekomendowane opcje wyboru kategorii:
|
||||
# # 1. PomorskaRSS - wiadomosci kazdego typu, lub
|
||||
# # 2. Region + wybrane miasta, lub
|
||||
# # 3. Wiadomosci tematyczne.
|
||||
# # Lista kategorii:
|
||||
|
||||
# # PomorskaRSS - wiadomosci kazdego typu, zakomentuj znakiem "#"
|
||||
# # przed odkomentowaniem wiadomosci wybranego typu:
|
||||
(u'PomorskaRSS', u'http://www.pomorska.pl/rss.xml')
|
||||
|
||||
# # wiadomosci z regionu nie przypisane do okreslonego miasta:
|
||||
# (u'Region', u'http://www.pomorska.pl/region.xml'),
|
||||
|
||||
# # wiadomosci przypisane do miast:
|
||||
# (u'Bydgoszcz', u'http://www.pomorska.pl/bydgoszcz.xml'),
|
||||
# (u'Nak\u0142o', u'http://www.pomorska.pl/naklo.xml'),
|
||||
# (u'Koronowo', u'http://www.pomorska.pl/koronowo.xml'),
|
||||
# (u'Solec Kujawski', u'http://www.pomorska.pl/soleckujawski.xml'),
|
||||
# (u'Grudzi\u0105dz', u'http://www.pomorska.pl/grudziadz.xml'),
|
||||
# (u'Inowroc\u0142aw', u'http://www.pomorska.pl/inowroclaw.xml'),
|
||||
# (u'Toru\u0144', u'http://www.pomorska.pl/torun.xml'),
|
||||
# (u'W\u0142oc\u0142awek', u'http://www.pomorska.pl/wloclawek.xml'),
|
||||
# (u'Aleksandr\u00f3w Kujawski', u'http://www.pomorska.pl/aleksandrow.xml'),
|
||||
# (u'Brodnica', u'http://www.pomorska.pl/brodnica.xml'),
|
||||
# (u'Che\u0142mno', u'http://www.pomorska.pl/chelmno.xml'),
|
||||
# (u'Chojnice', u'http://www.pomorska.pl/chojnice.xml'),
|
||||
# (u'Ciechocinek', u'http://www.pomorska.pl/ciechocinek.xml'),
|
||||
# (u'Golub Dobrzy\u0144', u'http://www.pomorska.pl/golubdobrzyn.xml'),
|
||||
# (u'Mogilno', u'http://www.pomorska.pl/mogilno.xml'),
|
||||
# (u'Radziej\u00f3w', u'http://www.pomorska.pl/radziejow.xml'),
|
||||
# (u'Rypin', u'http://www.pomorska.pl/rypin.xml'),
|
||||
# (u'S\u0119p\u00f3lno', u'http://www.pomorska.pl/sepolno.xml'),
|
||||
# (u'\u015awiecie', u'http://www.pomorska.pl/swiecie.xml'),
|
||||
# (u'Tuchola', u'http://www.pomorska.pl/tuchola.xml'),
|
||||
# (u'\u017bnin', u'http://www.pomorska.pl/znin.xml')
|
||||
|
||||
# # wiadomosci tematyczne (redundancja z region/miasta):
|
||||
# (u'Sport', u'http://www.pomorska.pl/sport.xml'),
|
||||
# (u'Zdrowie', u'http://www.pomorska.pl/zdrowie.xml'),
|
||||
# (u'Auto', u'http://www.pomorska.pl/moto.xml'),
|
||||
# (u'Dom', u'http://www.pomorska.pl/dom.xml'),
|
||||
# (u'Reporta\u017c', u'http://www.pomorska.pl/reportaz.xml'),
|
||||
# (u'Gospodarka', u'http://www.pomorska.pl/gospodarka.xml')
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'article'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='p', attrs={'id':'articleTags'}),
|
||||
dict(name='div', attrs={'id':'articleEpaper'}),
|
||||
dict(name='div', attrs={'id':'articleConnections'}),
|
||||
dict(name='div', attrs={'class':'articleFacts'}),
|
||||
dict(name='div', attrs={'id':'articleExternalLink'}),
|
||||
dict(name='div', attrs={'id':'articleMultimedia'}),
|
||||
dict(name='div', attrs={'id':'articleGalleries'}),
|
||||
dict(name='div', attrs={'id':'articleAlarm'}),
|
||||
dict(name='div', attrs={'id':'adholder_srodek1'}),
|
||||
dict(name='div', attrs={'id':'articleVideo'}),
|
||||
dict(name='a', attrs={'name':'fb_share'})]
|
||||
|
||||
extra_css = '''h1 { font-size: 1.4em; }
|
||||
h2 { font-size: 1.0em; }'''
|
||||
|
||||
|
@ -1,18 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2008-2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
harpers.org
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag
|
||||
|
||||
class Harpers(BasicNewsRecipe):
|
||||
title = u"Harper's Magazine"
|
||||
__author__ = u'Darko Miletic'
|
||||
language = 'en'
|
||||
|
||||
description = u"Harper's Magazine: Founded June 1850."
|
||||
publisher = "Harper's Magazine "
|
||||
category = 'news, politics, USA'
|
||||
@ -21,13 +17,12 @@ class Harpers(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment', description
|
||||
, '--category', category
|
||||
, '--publisher', publisher
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\noverride_css=" p {text-indent: 0em; margin-top: 0em; margin-bottom: 0.5em} img {margin-top: 0em; margin-bottom: 0.4em}"'
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
extra_css = '''
|
||||
h1{ font-family:georgia ; color:#111111; font-size:large;}
|
||||
@ -39,8 +34,9 @@ class Harpers(BasicNewsRecipe):
|
||||
keep_only_tags = [ dict(name='div', attrs={'id':'cached'}) ]
|
||||
remove_tags = [
|
||||
dict(name='table', attrs={'class':['rcnt','rcnt topline']})
|
||||
,dict(name=['link','object','embed'])
|
||||
,dict(name=['link','object','embed','meta','base'])
|
||||
]
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [(u"Harper's Magazine", u'http://www.harpers.org/rss/frontpage-rss20.xml')]
|
||||
|
||||
@ -49,20 +45,13 @@ class Harpers(BasicNewsRecipe):
|
||||
index = 'http://harpers.org/'
|
||||
soup = self.index_to_soup(index)
|
||||
link_item = soup.find(name = 'img',attrs= {'class':"cover"})
|
||||
print link_item
|
||||
if link_item:
|
||||
cover_url = 'http://harpers.org' + link_item['src']
|
||||
print cover_url
|
||||
return cover_url
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mcharset = Tag(soup,'meta',[("http-equiv","Content-Type"),("content","text/html; charset=utf-8")])
|
||||
soup.head.insert(1,mcharset)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll(xmlns=True):
|
||||
del item['xmlns']
|
||||
return soup
|
||||
|
||||
|
||||
|
||||
|
@ -33,9 +33,9 @@ class HBR(BasicNewsRecipe):
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser(self)
|
||||
br.open(self.LOGIN_URL)
|
||||
br.select_form(name='signInForm')
|
||||
br['signInForm:username'] = self.username
|
||||
br['signInForm:password'] = self.password
|
||||
br.select_form(name='signin-form')
|
||||
br['signin-form:username'] = self.username
|
||||
br['signin-form:password'] = self.password
|
||||
raw = br.submit().read()
|
||||
if 'My Account' not in raw:
|
||||
raise Exception('Failed to login, are you sure your username and password are correct?')
|
||||
|
40
resources/recipes/howtogeek.recipe
Normal file
@ -0,0 +1,40 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
title = 'How To Geek'
|
||||
language = 'en'
|
||||
__author__ = 'TonytheBookworm'
|
||||
description = 'Daily Computer Tips and Tricks'
|
||||
publisher = 'Howtogeek'
|
||||
category = 'PC,tips,tricks'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
linearize_tables = True
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
masthead_url = 'http://blog.stackoverflow.com/wp-content/uploads/how-to-geek-logo.png'
|
||||
|
||||
|
||||
|
||||
remove_tags =[dict(name='a', attrs={'target':['_blank']}),
|
||||
dict(name='table', attrs={'id':['articleTable']}),
|
||||
dict(name='div', attrs={'class':['feedflare']}),
|
||||
]
|
||||
|
||||
feeds = [
|
||||
('Tips', 'http://feeds.howtogeek.com/howtogeek')
|
||||
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
57
resources/recipes/jpost_fr.recipe
Normal file
@ -0,0 +1,57 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class JerusalemPost(BasicNewsRecipe):
|
||||
title = 'Jerusalem post'
|
||||
language = 'fr'
|
||||
__author__ = 'TonytheBookworm'
|
||||
description = 'The Jerusalem Post (in French)'
|
||||
publisher = 'jpost'
|
||||
category = 'news'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
linearize_tables = True
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
|
||||
masthead_url = 'http://static.jpost.com/JPSITES/images/JFrench/2008/site/jplogo.JFrench.gif'
|
||||
|
||||
remove_tags = [
|
||||
dict(name='a', attrs={'href':['javascript:window.print()']}),
|
||||
dict(name='div', attrs={'class':['bot']}),
|
||||
|
||||
]
|
||||
|
||||
feeds = [
|
||||
('NEWS', 'http://fr.jpost.com/servlet/Satellite?collId=1216805762036&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench En route vers la paix', 'http://fr.jpost.com/servlet/Satellite?collId=1216805762201&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Politique', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737334&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Securite', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737338&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Moyen Orient', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737342&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Diplomatie / Monde', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737346&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Economie / Sciences', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737358&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Societe', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737354&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Opinions', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737350&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Monde juif', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737366&pagename=JFrench%2FPage%2FRSS'),
|
||||
('JFrench Culture / Sport', 'http://fr.jpost.com/servlet/Satellite?collId=1215356737362&pagename=JFrench%2FPage%2FRSS')
|
||||
]
|
||||
def print_version(self, url):
|
||||
split1 = url.split("cid=")
|
||||
#for testing only -------
|
||||
#print 'SPLIT IS: ', split1
|
||||
#print 'ORG URL IS: ', url
|
||||
#---------------------------
|
||||
idnum = split1[1] # get the actual value of the id article
|
||||
#for testing only --------------------
|
||||
#print 'the idnum is: ', idnum
|
||||
#--------------------------------------
|
||||
print_url = 'http://fr.jpost.com/servlet/Satellite?cid=' + idnum + '&pagename=JFrench%2FJPArticle%2FPrinter'
|
||||
#for testing only -------------------------
|
||||
#print 'PRINT URL IS: ', print_url
|
||||
#------------------------------------------
|
||||
return print_url
|
||||
|
||||
#example of how links should be formated
|
||||
#--------------------------------------------------------------------------------------------------------------
|
||||
#org version = http://fr.jpost.com/servlet/Satellite?pagename=JFrench/JPArticle/ShowFull&cid=1282804806075
|
||||
#print version = http://fr.jpost.com/servlet/Satellite?cid=1282804806075&pagename=JFrench%2FJPArticle%2FPrinter
|
||||
#------------------------------------------------------------------------------------------------------------------
|
43
resources/recipes/le_journal.recipe
Normal file
@ -0,0 +1,43 @@
|
||||
__author__ = ' (lrfurtado@yahoo.com.br)'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class LeJournalDeMontrealRecipe(BasicNewsRecipe):
|
||||
|
||||
title = u'Le Journal de Montreal'
|
||||
description = u'Le Journal de Montreal'
|
||||
__author__ = 'Luciano Furtado'
|
||||
language = 'fr'
|
||||
|
||||
oldest_article = 7
|
||||
use_embedded_content=0
|
||||
max_articles_per_feed = 15
|
||||
|
||||
remove_tags = [
|
||||
dict(name='ul',attrs={'id':'mainNav'}),
|
||||
dict(name='div',attrs={'id':'boxPolitique'}),
|
||||
dict(name='div',attrs={'id':'boxScoop'}),
|
||||
dict(name='div',attrs={'id':'DossierSpec'}),
|
||||
dict(name='div',attrs={'id':'channelBoxes'}),
|
||||
dict(name='div',attrs={'id':'sectionBoxes'}),
|
||||
dict(name='div',attrs={'id':'header'}),
|
||||
dict(name='div',attrs={'id':'footer'}),
|
||||
dict(name='div',attrs={'id':'navbarCanoe_container'}),
|
||||
dict(name='div',attrs={'id':'popularCanoe'}),
|
||||
dict(name='div',attrs={'id':'textAds'}),
|
||||
dict(name='div',attrs={'id':'24heures'}),
|
||||
dict(name='div',attrs={'class':'bottomBox clear'}),
|
||||
dict(name='div',attrs={'class':'articleControls thin'}),
|
||||
]
|
||||
|
||||
|
||||
feeds = [
|
||||
(u'Actualites',
|
||||
u'http://www.canoe.com/rss/feed/nouvelles/ljm_actualites.xml'),
|
||||
(u'Arts et spectacle',
|
||||
u'http://www.canoe.com/rss/feed/nouvelles/ljm_arts.xml'),
|
||||
(u'Sports',
|
||||
u'http://www.canoe.com/rss/feed/nouvelles/ljm_sports.xml'),
|
||||
(u'Chroniques',
|
||||
u'http://www.canoe.com/rss/feed/nouvelles/ljm_chroniques.xml'),
|
||||
]
|
@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Tomasz Dlugosz <tomek3d@gmail.com>'
|
||||
'''
|
||||
nczas.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
#
|
||||
|
||||
class NCzas(BasicNewsRecipe):
|
||||
title = u'Najwy\u017cszy Czas!'
|
||||
description = u'Najwy\u017cszy Czas!\nwydanie internetowe'
|
||||
__author__ = u'Tomasz D\u0142ugosz'
|
||||
language = 'pl'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
cover_url = 'http://nczas.com/wp-content/themes/default/grafika/logo.png'
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'trescartykulu'})]
|
||||
|
||||
feeds = [(u'Najwy\u017cszy Czas!', u'http://nczas.com/feed/')]
|
||||
|
||||
def postprocess_html(self, soup, first):
|
||||
|
||||
for tag in soup.findAll(name= 'img', alt=""):
|
||||
tag.extract()
|
||||
|
||||
for item in soup.findAll(align = "right"):
|
||||
del item['align']
|
||||
|
||||
return soup
|
@ -37,6 +37,18 @@ class Novosti(BasicNewsRecipe):
|
||||
preprocess_regexps = [(re.compile(u'\u0110'), lambda match: u'\u00D0')]
|
||||
|
||||
keep_only_tags = [dict(attrs={'class':['articleTitle','author','articleLead','articleBody']})]
|
||||
remove_tags = [dict(name=['embed','object','iframe','base'])]
|
||||
|
||||
remove_tags = [dict(name=['embed','object','iframe','base','link','meta'])]
|
||||
feeds = [(u'Vesti', u'http://www.novosti.rs/rss/rss-vesti')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.findAll('span', attrs={'class':'author'}):
|
||||
item.name='p'
|
||||
for item in soup.findAll('img'):
|
||||
if not item.has_key('alt'):
|
||||
item['alt'] = 'image'
|
||||
return soup
|
||||
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@ nspm.rs
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag, NavigableString
|
||||
from calibre.ebooks.BeautifulSoup import NavigableString
|
||||
|
||||
class Nspm(BasicNewsRecipe):
|
||||
title = 'Nova srpska politicka misao'
|
||||
|
57
resources/recipes/popscience.recipe
Normal file
@ -0,0 +1,57 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, re
|
||||
|
||||
class AdvancedUserRecipe1282101454(BasicNewsRecipe):
|
||||
title = 'Popular Science'
|
||||
language = 'en'
|
||||
__author__ = 'TonytheBookworm'
|
||||
description = 'Popular Science'
|
||||
publisher = 'Popular Science'
|
||||
category = 'gadgets,science'
|
||||
oldest_article = 7 # change this if you want more current articles. I like to go a week in
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
use_embedded_content = True
|
||||
|
||||
masthead_url = 'http://www.raytheon.com/newsroom/rtnwcm/groups/Public/documents/masthead/rtn08_popscidec_masthead.jpg'
|
||||
|
||||
|
||||
feeds = [
|
||||
|
||||
('Gadgets', 'http://www.popsci.com/full-feed/gadgets'),
|
||||
('Cars', 'http://www.popsci.com/full-feed/cars'),
|
||||
('Science', 'http://www.popsci.com/full-feed/science'),
|
||||
('Technology', 'http://www.popsci.com/full-feed/technology'),
|
||||
('DIY', 'http://www.popsci.com/full-feed/diy'),
|
||||
|
||||
]
|
||||
|
||||
|
||||
#The following will get read of the Gallery: links when found
|
||||
|
||||
def preprocess_html(self, soup) :
|
||||
print 'SOUP IS: ', soup
|
||||
weblinks = soup.findAll(['head','h2'])
|
||||
if weblinks is not None:
|
||||
for link in weblinks:
|
||||
if re.search('(Gallery)(:)',str(link)):
|
||||
|
||||
link.parent.extract()
|
||||
return soup
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
46
resources/recipes/rmf24_ESKN.recipe
Normal file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
|
||||
'''
|
||||
rmf24.pl
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class RMF24_ESKN(BasicNewsRecipe):
|
||||
title = u'Rmf24.pl - Ekonomia Sport Kultura Nauka'
|
||||
description = u'Ekonomia, sport, kultura i nauka ze strony rmf24.pl'
|
||||
language = 'pl'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
__author__ = u'Tomasz D\u0142ugosz'
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
|
||||
feeds = [(u'Ekonomia', u'http://www.rmf24.pl/ekonomia/feed'),
|
||||
(u'Sport', u'http://www.rmf24.pl/sport/feed'),
|
||||
(u'Kultura', u'http://www.rmf24.pl/kultura/feed'),
|
||||
(u'Nauka', u'http://www.rmf24.pl/nauka/feed')]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'box articleSingle print'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':'toTop'}),
|
||||
dict(name='div', attrs={'class':'category'}),
|
||||
dict(name='div', attrs={'class':'REMOVE'}),
|
||||
dict(name='div', attrs={'class':'embed embedAd'})]
|
||||
|
||||
extra_css = '''
|
||||
h1 { font-size: 1.2em; }
|
||||
'''
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
[
|
||||
(r'<h2>Zdj.cie</h2>', lambda match: ''),
|
||||
(r'embed embed(Left|Right|Center) articleEmbed(Audio|Wideo articleEmbedVideo|ArticleFull|ArticleTitle|ArticleListTitle|AlbumHorizontal)">', lambda match: 'REMOVE">'),
|
||||
(r'<a href="http://www.facebook.com/pages/RMF24pl/.*?>RMF24.pl</a> on Facebook</div>', lambda match: '</div>')
|
||||
]
|
||||
]
|
44
resources/recipes/rmf24_fakty.recipe
Normal file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = u'2010, Tomasz Dlugosz <tomek3d@gmail.com>'
|
||||
'''
|
||||
rmf24.pl
|
||||
'''
|
||||
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class RMF24(BasicNewsRecipe):
|
||||
title = u'Rmf24.pl - Fakty'
|
||||
description = u'Fakty ze strony rmf24.pl'
|
||||
language = 'pl'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
__author__ = u'Tomasz D\u0142ugosz'
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
|
||||
feeds = [(u'Kraj', u'http://www.rmf24.pl/fakty/polska/feed'),
|
||||
(u'\u015awiat', u'http://www.rmf24.pl/fakty/swiat/feed')]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':'box articleSingle print'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':'adBox625'}),
|
||||
dict(name='div', attrs={'class':'toTop'}),
|
||||
dict(name='div', attrs={'class':'category'}),
|
||||
dict(name='div', attrs={'class':'REMOVE'}),
|
||||
dict(name='div', attrs={'class':'embed embedAd'})]
|
||||
|
||||
extra_css = '''
|
||||
h1 { font-size: 1.2em; }
|
||||
'''
|
||||
preprocess_regexps = [
|
||||
(re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in
|
||||
[
|
||||
(r'<h2>Zdj.cie</h2>', lambda match: ''),
|
||||
(r'embed embed(Left|Right|Center) articleEmbed(Audio|Wideo articleEmbedVideo|ArticleFull|ArticleTitle|ArticleListTitle|AlbumHorizontal)">', lambda match: 'REMOVE">'),
|
||||
(r'<a href="http://www.facebook.com/pages/RMF24pl/.*?>RMF24.pl</a> on Facebook</div>', lambda match: '</div>')
|
||||
]
|
||||
]
|
@ -1,68 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
sciam.com
|
||||
'''
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class ScientificAmerican(BasicNewsRecipe):
|
||||
title = u'Scientific American'
|
||||
description = u'Popular science. Monthly magazine.'
|
||||
__author__ = 'Kovid Goyal and Sujata Raman'
|
||||
description = u'Popular Science. Monthly magazine.'
|
||||
category = 'science'
|
||||
__author__ = 'Starson17'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
language = 'en'
|
||||
publisher = 'Nature Publishing Group'
|
||||
remove_empty_feeds = True
|
||||
remove_javascript = True
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
extra_css = '''
|
||||
p{font-weight: normal; font-size:small}
|
||||
li{font-weight: normal; font-size:small}
|
||||
.headline p{font-size:x-small; font-family:Arial,Helvetica,sans-serif;}
|
||||
h2{font-size:x-small;}
|
||||
h3{font-size:x-small;font-family:Arial,Helvetica,sans-serif;}
|
||||
'''
|
||||
remove_tags_before = dict(name='div', attrs={'class':'headline'})
|
||||
|
||||
remove_tags_after = dict(id=['article'])
|
||||
remove_tags = [
|
||||
dict(id=['sharetools', 'reddit']),
|
||||
#dict(name='script'),
|
||||
{'class':['float_left', 'atools']},
|
||||
{"class": re.compile(r'also-in-this')},
|
||||
dict(name='a',title = ["Get the Rest of the Article","Subscribe","Buy this Issue"]),
|
||||
dict(name = 'img',alt = ["Graphic - Get the Rest of the Article"]),
|
||||
dict(name='div', attrs={'class':['commentbox']}),
|
||||
dict(name='h2', attrs={'class':['discuss_h2']}),
|
||||
conversion_options = {'linearize_tables' : True
|
||||
, 'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='h2', attrs={'class':'articleTitle'})
|
||||
,dict(name='p', attrs={'id':'articleDek'})
|
||||
,dict(name='p', attrs={'class':'articleInfo'})
|
||||
,dict(name='div', attrs={'id':['articleContent']})
|
||||
,dict(name='img', attrs={'src':re.compile(r'/media/inline/blog/Image/', re.DOTALL|re.IGNORECASE)})
|
||||
]
|
||||
|
||||
html2lrf_options = ['--base-font-size', '8']
|
||||
recursions = 1
|
||||
match_regexps = [r'article.cfm.id=\S+page=(2|3|4|5|6|7|8|9|10|11|12|13|14|15)']
|
||||
remove_tags = [dict(name='a', attrs={'class':'tinyCommentCount'})]
|
||||
|
||||
def parse_index(self):
|
||||
soup = self.index_to_soup('http://www.scientificamerican.com/sciammag/')
|
||||
monthtag = soup.find('div',attrs={'id':'magazine-main_col2'})
|
||||
month = self.tag_to_string(monthtag.contents[1])
|
||||
|
||||
|
||||
self.timefmt = ' [%s]'%(self.tag_to_string(month))
|
||||
issuetag = soup.find('p',attrs={'id':'articleDek'})
|
||||
self.timefmt = ' [%s]'%(self.tag_to_string(issuetag))
|
||||
img = soup.find('img', alt='Scientific American Magazine', src=True)
|
||||
if img is not None:
|
||||
self.cover_url = img['src']
|
||||
features, feeds = [], []
|
||||
for p in soup.find(id='magazine-main_col2').findAll('p') :
|
||||
a = p.find('a', href=True)
|
||||
|
||||
for a in soup.find(attrs={'class':'primaryCol'}).findAll('a',attrs={'title':'Feature'}):
|
||||
if a is None: continue
|
||||
desc = ''
|
||||
s = p.find('span', attrs={'class':"sub"})
|
||||
s = a.parent.parent.find(attrs={'class':'dek'})
|
||||
desc = self.tag_to_string(s)
|
||||
|
||||
article = {
|
||||
'url' : a['href'],
|
||||
'title' : self.tag_to_string(a),
|
||||
@ -71,51 +56,36 @@ class ScientificAmerican(BasicNewsRecipe):
|
||||
}
|
||||
features.append(article)
|
||||
feeds.append(('Features', features))
|
||||
|
||||
section = []
|
||||
department = []
|
||||
title = None
|
||||
|
||||
for x in soup.find(id='magazine-main_col1').findAll(['div', 'a']):
|
||||
|
||||
if x.name == 'div':
|
||||
|
||||
if section:
|
||||
feeds.append((title, section))
|
||||
|
||||
title = self.tag_to_string(x)
|
||||
section = []
|
||||
else:
|
||||
|
||||
if 'article.cfm' in x['href']:
|
||||
for li in soup.find(attrs={'class':'secondaryCol'}).findAll('li'):
|
||||
if 'department.cfm' in li.a['href']:
|
||||
if department:
|
||||
feeds.append((title, department))
|
||||
title = self.tag_to_string(li.a)
|
||||
department = []
|
||||
if 'article.cfm' in li.h3.a['href']:
|
||||
article = {
|
||||
'url' : x['href'],
|
||||
'title' : self.tag_to_string(x),
|
||||
'url' : li.h3.a['href'],
|
||||
'title' : self.tag_to_string(li.h3.a),
|
||||
'date': '',
|
||||
'description': '',
|
||||
'description': self.tag_to_string(li.p),
|
||||
}
|
||||
|
||||
section.append(article)
|
||||
|
||||
if section:
|
||||
feeds.append((title, section))
|
||||
|
||||
department.append(article)
|
||||
if department:
|
||||
feeds.append((title, department))
|
||||
return feeds
|
||||
|
||||
|
||||
def postprocess_html(self, soup, first_fetch):
|
||||
if soup is not None:
|
||||
for span in soup.findAll('span', attrs={'class':'pagination'}):
|
||||
span.extract()
|
||||
if not first_fetch:
|
||||
div = soup.find('div', attrs={'class':'headline'})
|
||||
if div:
|
||||
div.extract()
|
||||
|
||||
for item in soup.findAll('a'):
|
||||
if 'topic.cfm' in item['href']:
|
||||
item.replaceWith(item.string)
|
||||
return soup
|
||||
|
||||
preprocess_regexps = [
|
||||
(re.compile(r'Already a Digital subscriber.*Now</a>', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||
(re.compile(r'If your institution has site license access, enter.*here</a>.', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||
(re.compile(r'to subscribe to our.*;.*\}', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||
(re.compile(r'\)\(jQuery\);.*-->', re.DOTALL|re.IGNORECASE), lambda match: ''),
|
||||
]
|
||||
extra_css = '''
|
||||
p{font-weight: normal; font-size:small}
|
||||
li{font-weight: normal; font-size:small}
|
||||
.headline p{font-size:x-small; font-family:Arial,Helvetica,sans-serif;}
|
||||
h2{font-size:large; font-family:Arial,Helvetica,sans-serif;}
|
||||
h3{font-size:x-small;font-family:Arial,Helvetica,sans-serif;}
|
||||
'''
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''
|
||||
calibre recipe for slate.com
|
||||
'''
|
||||
@ -10,13 +11,12 @@ import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString, CData, Comment, Tag
|
||||
|
||||
class PeriodicalNameHere(BasicNewsRecipe):
|
||||
class Slate(BasicNewsRecipe):
|
||||
# Method variables for customizing downloads
|
||||
title = 'Slate'
|
||||
description = 'A general-interest publication offering analysis and commentary about politics, news and culture.'
|
||||
__author__ = 'GRiker and Sujata Raman'
|
||||
max_articles_per_feed = 20
|
||||
oldest_article = 7.0
|
||||
__author__ = 'GRiker, Sujata Raman and Nick Redding'
|
||||
max_articles_per_feed = 100
|
||||
oldest_article = 14
|
||||
recursions = 0
|
||||
delay = 0
|
||||
simultaneous_downloads = 5
|
||||
@ -27,8 +27,11 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
encoding = None
|
||||
language = 'en'
|
||||
|
||||
|
||||
|
||||
slate_complete = True
|
||||
if slate_complete:
|
||||
title = 'Slate (complete)'
|
||||
else:
|
||||
title = 'Slate (weekly)'
|
||||
|
||||
# Method variables for customizing feed parsing
|
||||
summary_length = 250
|
||||
@ -51,7 +54,9 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
# The second entry is for 'Big Money', which comes from a different site, uses different markup
|
||||
remove_tags = [dict(attrs={ 'id':['toolbox','recommend_tab','insider_ad_wrapper',
|
||||
'article_bottom_tools_cntr','fray_article_discussion','fray_article_links','bottom_sponsored_links','author_bio',
|
||||
'bizbox_links_bottom','ris_links_wrapper','BOXXLE']}),
|
||||
'bizbox_links_bottom','ris_links_wrapper','BOXXLE',
|
||||
'comments_button','add_comments_button','comments-to-fray','marriott_ad',
|
||||
'article_bottom_tools','recommend_tab2','fbog_article_bottom_cntr']}),
|
||||
dict(attrs={ 'id':['content-top','service-links-bottom','hed']}) ]
|
||||
|
||||
excludedDescriptionKeywords = ['Slate V','Twitter feed','podcast']
|
||||
@ -62,16 +67,15 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
extra_css = '''
|
||||
.h1_subhead{font-family:Arial; font-size:small; }
|
||||
h1{font-family:Verdana; font-size:large; }
|
||||
.byline {font-family:Georgia; margin-bottom: 0px; color: #660033;}
|
||||
.dateline {font-family:Arial; font-size: smaller; height: 0pt; color:#666666;}
|
||||
.byline {font-family:Georgia; margin-bottom: 0px; }
|
||||
.dateline {font-family:Arial; font-size: smaller; height: 0pt;}
|
||||
.imagewrapper {font-family:Verdana;font-size:x-small; }
|
||||
.source {font-family:Verdana; font-size:x-small;}
|
||||
.credit {font-family:Verdana; font-size: smaller;}
|
||||
#article_body {font-family:Verdana; }
|
||||
#content {font-family:Arial; }
|
||||
.caption{font-family:Verdana;font-style:italic; font-size:x-small;}
|
||||
h3{font-family:Arial; color:#666666; font-size:small}
|
||||
a{color:#0066CC;}
|
||||
h3{font-family:Arial; font-size:small}
|
||||
'''
|
||||
|
||||
# Local variables to extend class
|
||||
@ -89,32 +93,59 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
if isinstance(item, (NavigableString, CData)):
|
||||
strings.append(item.string)
|
||||
elif isinstance(item, Tag):
|
||||
res = self.tag_to_string(item)
|
||||
res = self.tag_to_string(item,use_alt=False)
|
||||
if res:
|
||||
strings.append(res)
|
||||
return strings
|
||||
|
||||
|
||||
def extract_sections(self):
|
||||
def extract_named_sections(self):
|
||||
soup = self.index_to_soup( self.baseURL )
|
||||
soup_top_stories = soup.find(True, attrs={'class':'tap2_topic entry-content'})
|
||||
soup_nav_bar = soup.find(True, attrs={'id':'nav'})
|
||||
briefing_nav = soup.find('li')
|
||||
briefing_url = briefing_nav.a['href']
|
||||
for section_nav in soup_nav_bar.findAll('li'):
|
||||
section_name = self.tag_to_string(section_nav,use_alt=False)
|
||||
self.section_dates.append(section_name)
|
||||
|
||||
soup = self.index_to_soup(briefing_url)
|
||||
|
||||
self.log("Briefing url = %s " % briefing_url)
|
||||
section_lists = soup.findAll('ul','view_links_list')
|
||||
|
||||
sections = []
|
||||
for section in section_lists :
|
||||
sections.append(section)
|
||||
return sections
|
||||
|
||||
|
||||
def extract_dated_sections(self):
|
||||
soup = self.index_to_soup( self.baseURL )
|
||||
soup_top_stories = soup.find(True, attrs={'id':'tap3_cntr'})
|
||||
if soup_top_stories:
|
||||
self.section_dates.append("Top Stories")
|
||||
self.log("SELECTION TOP STORIES %s" % "Top Stories")
|
||||
|
||||
soup = soup.find(True, attrs={'id':'toc_links_container'})
|
||||
|
||||
todays_section = soup.find(True, attrs={'class':'todaydateline'})
|
||||
self.section_dates.append(self.tag_to_string(todays_section,use_alt=False))
|
||||
self.log("SELECTION DATE %s" % self.tag_to_string(todays_section,use_alt=False))
|
||||
|
||||
older_section_dates = soup.findAll(True, attrs={'class':'maindateline'})
|
||||
for older_section in older_section_dates :
|
||||
self.section_dates.append(self.tag_to_string(older_section,use_alt=False))
|
||||
self.log("SELECTION DATE %s" % self.tag_to_string(older_section,use_alt=False))
|
||||
|
||||
if soup_top_stories:
|
||||
headline_stories = soup_top_stories.find('ul')
|
||||
headline_stories = soup_top_stories
|
||||
self.log("HAVE top_stories")
|
||||
else:
|
||||
headline_stories = None
|
||||
self.log("NO top_stories")
|
||||
section_lists = soup.findAll('ul')
|
||||
# Prepend the headlines to the first section
|
||||
if headline_stories:
|
||||
section_lists[0].insert(0,headline_stories)
|
||||
section_lists.insert(0,headline_stories)
|
||||
|
||||
sections = []
|
||||
for section in section_lists :
|
||||
@ -124,8 +155,7 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
|
||||
def extract_section_articles(self, sections_html) :
|
||||
# Find the containers with section content
|
||||
soup = self.index_to_soup(str(sections_html))
|
||||
sections = soup.findAll('ul')
|
||||
sections = sections_html
|
||||
|
||||
articles = {}
|
||||
key = None
|
||||
@ -135,10 +165,25 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
|
||||
# Get the section name
|
||||
if section.has_key('id') :
|
||||
self.log("PROCESSING SECTION id = %s" % section['id'])
|
||||
key = self.section_dates[i]
|
||||
if key.startswith("Pod"):
|
||||
continue
|
||||
if key.startswith("Blog"):
|
||||
continue
|
||||
articles[key] = []
|
||||
ans.append(key)
|
||||
elif self.slate_complete:
|
||||
key = self.section_dates[i]
|
||||
if key.startswith("Pod"):
|
||||
continue
|
||||
if key.startswith("Blog"):
|
||||
continue
|
||||
self.log("PROCESSING SECTION name = %s" % key)
|
||||
articles[key] = []
|
||||
ans.append(key)
|
||||
else :
|
||||
self.log("SECTION %d HAS NO id" % i);
|
||||
continue
|
||||
|
||||
# Get the section article_list
|
||||
@ -149,8 +194,10 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
bylines = self.tag_to_strings(article)
|
||||
url = article.a['href']
|
||||
title = bylines[0]
|
||||
full_title = self.tag_to_string(article)
|
||||
|
||||
full_title = self.tag_to_string(article,use_alt=False)
|
||||
#self.log("ARTICLE TITLE%s" % title)
|
||||
#self.log("ARTICLE FULL_TITLE%s" % full_title)
|
||||
#self.log("URL %s" % url)
|
||||
author = None
|
||||
description = None
|
||||
pubdate = None
|
||||
@ -181,7 +228,7 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
excluded = re.compile('|'.join(self.excludedDescriptionKeywords))
|
||||
found_excluded = excluded.search(description)
|
||||
if found_excluded :
|
||||
if self.verbose : self.log(" >>> skipping %s (description keyword exclusion: %s) <<<\n" % (title, found_excluded.group(0)))
|
||||
self.log(" >>> skipping %s (description keyword exclusion: %s) <<<\n" % (title, found_excluded.group(0)))
|
||||
continue
|
||||
|
||||
# Skip articles whose title contain excluded keywords
|
||||
@ -190,7 +237,7 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
#self.log("evaluating full_title: %s" % full_title)
|
||||
found_excluded = excluded.search(full_title)
|
||||
if found_excluded :
|
||||
if self.verbose : self.log(" >>> skipping %s (title keyword exclusion: %s) <<<\n" % (title, found_excluded.group(0)))
|
||||
self.log(" >>> skipping %s (title keyword exclusion: %s) <<<\n" % (title, found_excluded.group(0)))
|
||||
continue
|
||||
|
||||
# Skip articles whose author contain excluded keywords
|
||||
@ -198,7 +245,7 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
excluded = re.compile('|'.join(self.excludedAuthorKeywords))
|
||||
found_excluded = excluded.search(author)
|
||||
if found_excluded :
|
||||
if self.verbose : self.log(" >>> skipping %s (author keyword exclusion: %s) <<<\n" % (title, found_excluded.group(0)))
|
||||
self.log(" >>> skipping %s (author keyword exclusion: %s) <<<\n" % (title, found_excluded.group(0)))
|
||||
continue
|
||||
|
||||
skip_this_article = False
|
||||
@ -206,6 +253,7 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
for article in articles[key] :
|
||||
if article['url'] == url :
|
||||
skip_this_article = True
|
||||
self.log("SKIPPING DUP %s" % url)
|
||||
break
|
||||
|
||||
if skip_this_article :
|
||||
@ -217,6 +265,8 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
articles[feed] = []
|
||||
articles[feed].append(dict(title=title, url=url, date=pubdate, description=description,
|
||||
author=author, content=''))
|
||||
#self.log("KEY %s" % feed)
|
||||
#self.log("APPENDED %s" % url)
|
||||
# Promote 'newspapers' to top
|
||||
for (i,article) in enumerate(articles[feed]) :
|
||||
if article['description'] is not None :
|
||||
@ -225,32 +275,6 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
|
||||
|
||||
ans = [(key, articles[key]) for key in ans if articles.has_key(key)]
|
||||
ans = self.remove_duplicates(ans)
|
||||
return ans
|
||||
|
||||
def flatten_document(self, ans):
|
||||
flat_articles = []
|
||||
for (i,section) in enumerate(ans) :
|
||||
#self.log("flattening section %s: " % section[0])
|
||||
for article in section[1] :
|
||||
#self.log("moving %s to flat_articles[]" % article['title'])
|
||||
flat_articles.append(article)
|
||||
flat_section = ['All Articles', flat_articles]
|
||||
flat_ans = [flat_section]
|
||||
return flat_ans
|
||||
|
||||
def remove_duplicates(self, ans):
|
||||
# Return a stripped ans
|
||||
for (i,section) in enumerate(ans) :
|
||||
#self.log("section %s: " % section[0])
|
||||
for article in section[1] :
|
||||
#self.log("\t%s" % article['title'])
|
||||
#self.log("\looking for %s" % article['url'])
|
||||
for (j,subsequent_section) in enumerate(ans[i+1:]) :
|
||||
for (k,subsequent_article) in enumerate(subsequent_section[1]) :
|
||||
if article['url'] == subsequent_article['url'] :
|
||||
#self.log( "removing %s (%s) from %s" % (subsequent_article['title'], subsequent_article['url'], subsequent_section[0]) )
|
||||
del subsequent_section[1][k]
|
||||
return ans
|
||||
|
||||
def print_version(self, url) :
|
||||
@ -258,13 +282,22 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
|
||||
# Class methods
|
||||
def parse_index(self) :
|
||||
sections = self.extract_sections()
|
||||
if self.slate_complete:
|
||||
sections = self.extract_named_sections()
|
||||
else:
|
||||
sections = self.extract_dated_sections()
|
||||
section_list = self.extract_section_articles(sections)
|
||||
section_list = self.flatten_document(section_list)
|
||||
return section_list
|
||||
|
||||
def get_browser(self) :
|
||||
return BasicNewsRecipe.get_browser()
|
||||
def get_masthead_url(self):
|
||||
masthead = 'http://img.slate.com/images/redesign2008/slate_logo.gif'
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
try:
|
||||
br.open(masthead)
|
||||
except:
|
||||
self.log("\nMasthead unavailable")
|
||||
masthead = None
|
||||
return masthead
|
||||
|
||||
def stripAnchors(self,soup):
|
||||
body = soup.find('div',attrs={'id':['article_body','content']})
|
||||
@ -294,8 +327,8 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
excluded = re.compile('|'.join(self.excludedContentKeywords))
|
||||
found_excluded = excluded.search(str(soup))
|
||||
if found_excluded :
|
||||
print "no allowed content found, removing article"
|
||||
raise Exception('String error')
|
||||
print "No allowed content found, removing article"
|
||||
raise Exception('Rejected article')
|
||||
|
||||
# Articles from www.thebigmoney.com use different tagging for byline, dateline and body
|
||||
head = soup.find('head')
|
||||
@ -328,7 +361,6 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
dept_kicker = soup.find('div', attrs={'class':'department_kicker'})
|
||||
if dept_kicker is not None :
|
||||
kicker_strings = self.tag_to_strings(dept_kicker)
|
||||
#kicker = kicker_strings[2] + kicker_strings[3]
|
||||
kicker = ''.join(kicker_strings[2:])
|
||||
kicker = re.sub('\.','',kicker)
|
||||
h3Tag = Tag(soup, "h3")
|
||||
@ -336,23 +368,9 @@ class PeriodicalNameHere(BasicNewsRecipe):
|
||||
emTag.insert(0,NavigableString(kicker))
|
||||
h3Tag.insert(0, emTag)
|
||||
dept_kicker.replaceWith(h3Tag)
|
||||
|
||||
# Change <h1> to <h2>
|
||||
headline = soup.find("h1")
|
||||
tag = headline.find("span")
|
||||
tag.name = 'div'
|
||||
|
||||
if headline is not None :
|
||||
h2tag = Tag(soup, "h2")
|
||||
h2tag['class'] = "headline"
|
||||
strs = self.tag_to_strings(headline)
|
||||
result = ''
|
||||
for (i,substr) in enumerate(strs) :
|
||||
result += substr
|
||||
if i < len(strs) -1 :
|
||||
result += '<br />'
|
||||
#h2tag.insert(0, result)
|
||||
#headline.replaceWith(h2tag)
|
||||
else:
|
||||
self.log("No kicker--return null")
|
||||
return None
|
||||
|
||||
# Fix up the concatenated byline and dateline
|
||||
byline = soup.find(True,attrs={'class':'byline'})
|
||||
|
79
resources/recipes/superesportes.recipe
Normal file
@ -0,0 +1,79 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Luciano Furtado <lrfurtado at yahoo.com.br>'
|
||||
'''
|
||||
www.superesportes.com.br
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class SuperEsportesRecipe(BasicNewsRecipe):
|
||||
|
||||
title = u'www.superesportes.com.br'
|
||||
description = u'Superesportes - Notícias do esporte no Brasil e no mundo'
|
||||
__author__ = 'Luciano Furtado'
|
||||
language = 'pt'
|
||||
category = 'esportes, Brasil'
|
||||
no_stylesheets = True
|
||||
oldest_article = 7
|
||||
|
||||
use_embedded_content=0
|
||||
max_articles_per_feed = 10
|
||||
cover_url = 'http://imgs.mg.superesportes.com.br/superesportes_logo.png'
|
||||
|
||||
extra_css = 'div.info_noticias h1 { font-size: 100% }'
|
||||
|
||||
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div',attrs={'class':'topo'}),
|
||||
dict(name='div',attrs={'class':'rodape'}),
|
||||
dict(name='div',attrs={'class':'navegacao'}),
|
||||
dict(name='div',attrs={'class':'lateral2'}),
|
||||
dict(name='div',attrs={'class':'leia_mais'}),
|
||||
dict(name='div',attrs={'id':'comentar'}),
|
||||
dict(name='div',attrs={'id':'vrumelc_noticia'}),
|
||||
dict(name='div',attrs={'class':'compartilhe'}),
|
||||
dict(name='div',attrs={'class':'linha_noticias'}),
|
||||
dict(name='div',attrs={'class':'botoes_noticias'}),
|
||||
dict(name='div',attrs={'class':'barra_time bg_time'}),
|
||||
]
|
||||
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
feeds = []
|
||||
sections = [
|
||||
(u'Atletico', 'http://www.df.superesportes.com.br/futebol/atletico-mg/capa_atletico_mg/index.shtml'),
|
||||
(u'Botafogo', 'http://www.df.superesportes.com.br/futebol/botafogo/capa_botafogo/index.shtml'),
|
||||
(u'Corinthinas', 'http://www.df.superesportes.com.br/futebol/corinthians/capa_corinthians/index.shtml'),
|
||||
(u'Cruzeiro', 'http://www.df.superesportes.com.br/futebol/cruzeiro/capa_cruzeiro/index.shtml'),
|
||||
(u'Flamengo', 'http://www.df.superesportes.com.br/futebol/flamengo/capa_flamengo/index.shtml'),
|
||||
(u'Fluminense', 'http://www.df.superesportes.com.br/futebol/fluminense/capa_fluminense/index.shtml'),
|
||||
(u'Palmeiras', 'http://www.df.superesportes.com.br/futebol/palmeiras/capa_palmeiras/index.shtml'),
|
||||
(u'Santos', 'http://www.df.superesportes.com.br/futebol/santos/capa_santos/index.shtml'),
|
||||
(u'S√£o Paulo', 'http://www.df.superesportes.com.br/futebol/sao-paulo/capa_sao_paulo/index.shtml'),
|
||||
(u'Vasco', 'http://www.df.superesportes.com.br/futebol/vasco/capa_vasco/index.shtml'),
|
||||
]
|
||||
|
||||
|
||||
for section, url in sections:
|
||||
current_articles = []
|
||||
|
||||
soup = self.index_to_soup(url)
|
||||
latestNews = soup.find(name='ul',attrs={'class': 'lista_ultimas_noticias'})
|
||||
|
||||
for li_tag in latestNews.findAll(name='li'):
|
||||
a_tag = li_tag.find('a', href= True)
|
||||
if a_tag is None:
|
||||
continue
|
||||
title = self.tag_to_string(a_tag)
|
||||
url = a_tag.get('href', False)
|
||||
self.log("\n\nFound title: " + title + "\nUrl: " + url + "\nSection: " + section)
|
||||
current_articles.append({'title': title, 'url': url, 'description': title, 'date':''})
|
||||
|
||||
if current_articles:
|
||||
feeds.append((section, current_articles))
|
||||
|
||||
|
||||
return feeds
|
||||
|
45
resources/recipes/tagesan.recipe
Normal file
@ -0,0 +1,45 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1284927619(BasicNewsRecipe):
|
||||
title = u'Tagesanzeiger'
|
||||
publisher = u'Tamedia AG'
|
||||
oldest_article = 2
|
||||
__author__ = 'noxxx'
|
||||
max_articles_per_feed = 100
|
||||
description = 'tagesanzeiger.ch: Nichts verpassen'
|
||||
category = 'News, Politik, Nachrichten, Schweiz, Zürich'
|
||||
language = 'de'
|
||||
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
}
|
||||
|
||||
remove_tags = [
|
||||
dict(name='img')
|
||||
,dict(name='div',attrs={'class':['swissquote ad','boxNews','centerAD','contentTabs2','sbsLabel']})
|
||||
,dict(name='div',attrs={'id':['colRightAd','singleRight','singleSmallRight','MailInfo','metaLine','sidebarSky','contentFooter','commentInfo','commentInfo2','commentInfo3','footerBottom','clear','boxExclusiv','singleLogo','navSearch','headerLogin','headerBottomRight','horizontalNavigation','subnavigation','googleAdSense','footerAd','contentbox','articleGalleryNav']})
|
||||
,dict(name='form',attrs={'id':['articleMailForm','commentform']})
|
||||
,dict(name='div',attrs={'style':['position:absolute']})
|
||||
,dict(name='script',attrs={'type':['text/javascript']})
|
||||
,dict(name='p',attrs={'class':['schreiben','smallPrint','charCounter','caption']})
|
||||
]
|
||||
feeds = [
|
||||
(u'Front', u'http://www.tagesanzeiger.ch/rss.html')
|
||||
,(u'Zürich', u'http://www.tagesanzeiger.ch/zuerich/rss.html')
|
||||
,(u'Schweiz', u'http://www.tagesanzeiger.ch/schweiz/rss.html')
|
||||
,(u'Ausland', u'http://www.tagesanzeiger.ch/ausland/rss.html')
|
||||
,(u'Digital', u'http://www.tagesanzeiger.ch/digital/rss.html')
|
||||
,(u'Wissen', u'http://www.tagesanzeiger.ch/wissen/rss.html')
|
||||
,(u'Panorama', u'http://www.tagesanzeiger.ch/panorama/rss.html')
|
||||
,(u'Wirtschaft', u'http://www.tagesanzeiger.ch/wirtschaft/rss.html')
|
||||
,(u'Sport', u'http://www.tagesanzeiger.ch/sport/rss.html')
|
||||
,(u'Kultur', u'http://www.tagesanzeiger.ch/kultur/rss.html')
|
||||
,(u'Leben', u'http://www.tagesanzeiger.ch/leben/rss.html')
|
||||
,(u'Auto', u'http://www.tagesanzeiger.ch/auto/rss.html')]
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '/print.html'
|
||||
|
@ -8,8 +8,9 @@ __docformat__ = 'restructuredtext de'
|
||||
'''
|
||||
www.taz.de/digiabo
|
||||
'''
|
||||
import os, urllib2, zipfile, tempfile
|
||||
import os, urllib2, zipfile
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
|
||||
class TazDigiabo(BasicNewsRecipe):
|
||||
|
||||
@ -26,7 +27,6 @@ class TazDigiabo(BasicNewsRecipe):
|
||||
}
|
||||
|
||||
def build_index(self):
|
||||
if self.username is not None and self.password is not None:
|
||||
domain = "http://www.taz.de"
|
||||
|
||||
url = domain + "/epub/"
|
||||
@ -46,11 +46,12 @@ class TazDigiabo(BasicNewsRecipe):
|
||||
raise ValueError('Failed to login, check your username and'
|
||||
' password')
|
||||
|
||||
tmp = tempfile.TemporaryFile()
|
||||
tmp = PersistentTemporaryFile(suffix='.epub')
|
||||
self.report_progress(0,_('downloading epub'))
|
||||
tmp.write(f.read())
|
||||
tmp.close()
|
||||
|
||||
zfile = zipfile.ZipFile(tmp, 'r')
|
||||
zfile = zipfile.ZipFile(tmp.name, 'r')
|
||||
self.report_progress(0,_('extracting epub'))
|
||||
|
||||
zfile.extractall(self.output_dir)
|
||||
@ -61,3 +62,4 @@ class TazDigiabo(BasicNewsRecipe):
|
||||
self.report_progress(1,_('epub downloaded and extracted'))
|
||||
|
||||
return index
|
||||
|
||||
|
24
resources/recipes/taz_rss.recipe
Normal file
@ -0,0 +1,24 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Alexander Schremmer <alex@alexanderweb.de>'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class TazRSSRecipe(BasicNewsRecipe):
|
||||
title = u'Taz.de (die tageszeitung) RSS Feed - German'
|
||||
__author__ = 'Alexander Schremmer'
|
||||
language = 'de'
|
||||
lang = 'de-DE'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
publisher = 'taz Entwicklungs GmbH & Co. Medien KG'
|
||||
|
||||
conversion_options = {'publisher': publisher,
|
||||
'language': lang,
|
||||
}
|
||||
|
||||
feeds = [(u'TAZ main feed', u'http://www.taz.de/rss.xml')]
|
||||
keep_only_tags = [dict(name='div', attrs={'class': 'sect sect_article'})]
|
||||
remove_tags_after = dict(name='div', attrs={'class': 'rack'})
|
||||
remove_tags = [dict(name=['div'], attrs={'class': 'rack'}),
|
||||
dict(name=['div'], attrs={'class': 'artikelwerbung'}),
|
||||
dict(name=['ul'], attrs={'class': 'toolbar'}),]
|
52
resources/recipes/the_marker.recipe
Normal file
@ -0,0 +1,52 @@
|
||||
import re
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1283848012(BasicNewsRecipe):
|
||||
description = 'TheMarker Financial News in Hebrew'
|
||||
__author__ = 'TonyTheBookworm, Marbs'
|
||||
cover_url = 'http://static.ispot.co.il/wp-content/upload/2009/09/themarker.jpg'
|
||||
title = u'TheMarker'
|
||||
language = 'he'
|
||||
simultaneous_downloads = 5
|
||||
remove_javascript = True
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
oldest_article = 1
|
||||
remove_tags = [dict(name='tr', attrs={'bgcolor':['#738A94']}) ]
|
||||
max_articles_per_feed = 10
|
||||
extra_css='body{direction: rtl;} .article_description{direction: rtl; } a.article{direction: rtl; } .calibre_feed_description{direction: rtl; }'
|
||||
feeds = [(u'Head Lines', u'http://www.themarker.com/tmc/content/xml/rss/hpfeed.xml'),
|
||||
(u'TA Market', u'http://www.themarker.com/tmc/content/xml/rss/sections/marketfeed.xml'),
|
||||
(u'Real Estate', u'http://www.themarker.com/tmc/content/xml/rss/sections/realEstaterfeed.xml'),
|
||||
(u'Wall Street & Global', u'http://www.themarker.com/tmc/content/xml/rss/sections/wallsfeed.xml'),
|
||||
(u'Law', u'http://www.themarker.com/tmc/content/xml/rss/sections/lawfeed.xml'),
|
||||
(u'Media', u'http://www.themarker.com/tmc/content/xml/rss/sections/mediafeed.xml'),
|
||||
(u'Consumer', u'http://www.themarker.com/tmc/content/xml/rss/sections/consumerfeed.xml'),
|
||||
(u'Career', u'http://www.themarker.com/tmc/content/xml/rss/sections/careerfeed.xml'),
|
||||
(u'Car', u'http://www.themarker.com/tmc/content/xml/rss/sections/carfeed.xml'),
|
||||
(u'High Tech', u'http://www.themarker.com/tmc/content/xml/rss/sections/hightechfeed.xml'),
|
||||
(u'Investor Guide', u'http://www.themarker.com/tmc/content/xml/rss/sections/investorGuidefeed.xml')]
|
||||
|
||||
def print_version(self, url):
|
||||
split1 = url.split("=")
|
||||
weblinks = url
|
||||
|
||||
if weblinks is not None:
|
||||
for link in weblinks:
|
||||
#---------------------------------------------------------
|
||||
#here we need some help with some regexpressions
|
||||
#we are trying to find it.themarker.com in a url
|
||||
#-----------------------------------------------------------
|
||||
re1='.*?' # Non-greedy match on filler
|
||||
re2='(it\\.themarker\\.com)' # Fully Qualified Domain Name 1
|
||||
rg = re.compile(re1+re2,re.IGNORECASE|re.DOTALL)
|
||||
m = rg.search(url)
|
||||
|
||||
|
||||
if m:
|
||||
split2 = url.split("article/")
|
||||
print_url = 'http://it.themarker.com/tmit/PrintArticle/' + split2[1]
|
||||
|
||||
else:
|
||||
print_url = 'http://www.themarker.com/ibo/misc/printFriendly.jhtml?ElementId=%2Fibo%2Frepositories%2Fstories%2Fm1_2000%2F' + split1[1]+'.xml'
|
||||
|
||||
return print_url
|
@ -70,10 +70,13 @@ class WallStreetJournal(BasicNewsRecipe):
|
||||
|
||||
def wsj_add_feed(self,feeds,title,url):
|
||||
self.log('Found section:', title)
|
||||
try:
|
||||
if url.endswith('whatsnews'):
|
||||
articles = self.wsj_find_wn_articles(url)
|
||||
else:
|
||||
articles = self.wsj_find_articles(url)
|
||||
except:
|
||||
articles = []
|
||||
if articles:
|
||||
feeds.append((title, articles))
|
||||
return feeds
|
||||
|
@ -54,10 +54,13 @@ class WallStreetJournal(BasicNewsRecipe):
|
||||
|
||||
def wsj_add_feed(self,feeds,title,url):
|
||||
self.log('Found section:', title)
|
||||
try:
|
||||
if url.endswith('whatsnews'):
|
||||
articles = self.wsj_find_wn_articles(url)
|
||||
else:
|
||||
articles = self.wsj_find_articles(url)
|
||||
except:
|
||||
articles = []
|
||||
if articles:
|
||||
feeds.append((title, articles))
|
||||
return feeds
|
||||
|
@ -35,7 +35,7 @@ class XkcdCom(BasicNewsRecipe):
|
||||
'date': item['title'],
|
||||
'timestamp': time.mktime(time.strptime(item['title'], '%Y-%m-%d'))+1,
|
||||
'url': 'http://xkcd.com' + item['href'],
|
||||
'title': self.tag_to_string(item).encode('UTF-8'),
|
||||
'title': self.tag_to_string(item),
|
||||
'description': '',
|
||||
'content': '',
|
||||
})
|
||||
|
@ -197,7 +197,7 @@ class GetTranslations(Translations):
|
||||
class ISO639(Command):
|
||||
|
||||
description = 'Compile translations for ISO 639 codes'
|
||||
XML = '/usr/lib/python2.6/site-packages/pycountry/databases/iso639.xml'
|
||||
XML = '/usr/lib/python2.7/site-packages/pycountry/databases/iso639.xml'
|
||||
|
||||
def run(self, opts):
|
||||
src = self.XML
|
||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.7.18'
|
||||
__version__ = '0.7.20'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
import re
|
||||
|
@ -459,7 +459,7 @@ from calibre.devices.iriver.driver import IRIVER_STORY
|
||||
from calibre.devices.binatone.driver import README
|
||||
from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
|
||||
from calibre.devices.edge.driver import EDGE
|
||||
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS
|
||||
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, SOVOS
|
||||
from calibre.devices.sne.driver import SNE
|
||||
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, GEMEI
|
||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
|
||||
@ -557,6 +557,7 @@ plugins += [
|
||||
TECLAST_K3,
|
||||
NEWSMY,
|
||||
IPAPYRUS,
|
||||
SOVOS,
|
||||
EDGE,
|
||||
SNE,
|
||||
ALEX,
|
||||
@ -665,13 +666,17 @@ class ActionCopyToLibrary(InterfaceActionBase):
|
||||
name = 'Copy To Library'
|
||||
actual_plugin = 'calibre.gui2.actions.copy_to_library:CopyToLibraryAction'
|
||||
|
||||
class ActionTweakEpub(InterfaceActionBase):
|
||||
name = 'Tweak ePub'
|
||||
actual_plugin = 'calibre.gui2.actions.tweak_epub:TweakEpubAction'
|
||||
|
||||
plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
||||
ActionConvert, ActionDelete, ActionEditMetadata, ActionView,
|
||||
ActionFetchNews, ActionSaveToDisk, ActionShowBookDetails,
|
||||
ActionRestart, ActionOpenFolder, ActionConnectShare,
|
||||
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
||||
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary,
|
||||
ActionCopyToLibrary]
|
||||
ActionCopyToLibrary, ActionTweakEpub]
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -155,7 +155,7 @@ class InputFormatPlugin(Plugin):
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def preprocess_html(self, html):
|
||||
def preprocess_html(self, opts, html):
|
||||
'''
|
||||
This method is called by the conversion pipeline on all HTML before it
|
||||
is parsed. It is meant to be used to do any required preprocessing on
|
||||
|
@ -248,6 +248,9 @@ class OutputProfile(Plugin):
|
||||
#: If True, the date is appended to the title of downloaded news
|
||||
periodical_date_in_title = True
|
||||
|
||||
#: The character used to represent a star in ratings
|
||||
ratings_char = u'*'
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
return escape(', '.join(tags))
|
||||
@ -273,6 +276,7 @@ class iPadOutput(OutputProfile):
|
||||
'macros': {'border-width': '{length}|medium|thick|thin'}
|
||||
}
|
||||
]
|
||||
ratings_char = u'\u2605'
|
||||
touchscreen = True
|
||||
# touchscreen_news_css {{{
|
||||
touchscreen_news_css = u'''
|
||||
@ -553,6 +557,7 @@ class KindleOutput(OutputProfile):
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
supports_mobi_indexing = True
|
||||
periodical_date_in_title = False
|
||||
ratings_char = u'\u2605'
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
|
@ -67,10 +67,17 @@ def load_plugin(path_to_zip_file): # {{{
|
||||
if name.lower().endswith('plugin.py'):
|
||||
locals = {}
|
||||
raw = zf.read(name)
|
||||
match = re.search(r'coding[:=]\s*([-\w.]+)', raw[:300])
|
||||
encoding = 'utf-8'
|
||||
lines, encoding = raw.splitlines(), 'utf-8'
|
||||
cr = re.compile(r'coding[:=]\s*([-\w.]+)')
|
||||
raw = []
|
||||
for l in lines[:2]:
|
||||
match = cr.search(l)
|
||||
if match is not None:
|
||||
encoding = match.group(1)
|
||||
else:
|
||||
raw.append(l)
|
||||
raw += lines[2:]
|
||||
raw = '\n'.join(raw)
|
||||
raw = raw.decode(encoding)
|
||||
raw = re.sub('\r\n', '\n', raw)
|
||||
exec raw in locals
|
||||
|
@ -29,10 +29,12 @@ class ANDROID(USBMS):
|
||||
# Sony Ericsson
|
||||
0xfce : { 0xd12e : [0x0100]},
|
||||
|
||||
0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]},
|
||||
# Google
|
||||
0x18d1 : { 0x4e11 : [0x0100, 0x226, 0x227], 0x4e12: [0x0100, 0x226,
|
||||
0x227]},
|
||||
|
||||
# Samsung
|
||||
0x04e8 : { 0x681d : [0x0222, 0x0400],
|
||||
0x04e8 : { 0x681d : [0x0222, 0x0224, 0x0400],
|
||||
0x681c : [0x0222, 0x0224, 0x0400],
|
||||
0x6640 : [0x0100],
|
||||
},
|
||||
|
@ -207,8 +207,8 @@ class ITUNES(DriverBase):
|
||||
for (j,p_book) in enumerate(self.update_list):
|
||||
if False:
|
||||
if isosx:
|
||||
self.log.info(" looking for %s" %
|
||||
str(p_book['lib_book'])[-9:])
|
||||
self.log.info(" looking for '%s' by %s uuid:%s" %
|
||||
(p_book['title'],p_book['author'], p_book['uuid']))
|
||||
elif iswindows:
|
||||
self.log.info(" looking for '%s' by %s (%s)" %
|
||||
(p_book['title'],p_book['author'], p_book['uuid']))
|
||||
@ -303,7 +303,7 @@ class ITUNES(DriverBase):
|
||||
this_book.device_collections = []
|
||||
this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
||||
this_book.size = book.size()
|
||||
this_book.uuid = book.album()
|
||||
this_book.uuid = book.composer()
|
||||
# Hack to discover if we're running in GUI environment
|
||||
if self.report_progress is not None:
|
||||
this_book.thumbnail = self._generate_thumbnail(this_book.path, book)
|
||||
@ -732,7 +732,7 @@ class ITUNES(DriverBase):
|
||||
for path in paths:
|
||||
if DEBUG:
|
||||
self._dump_cached_book(self.cached_books[path], indent=2)
|
||||
self.log.info(" looking for '%s' by '%s' (%s)" %
|
||||
self.log.info(" looking for '%s' by '%s' uuid:%s" %
|
||||
(self.cached_books[path]['title'],
|
||||
self.cached_books[path]['author'],
|
||||
self.cached_books[path]['uuid']))
|
||||
@ -740,7 +740,7 @@ class ITUNES(DriverBase):
|
||||
# Purge the booklist, self.cached_books, thumb cache
|
||||
for i,bl_book in enumerate(booklists[0]):
|
||||
if False:
|
||||
self.log.info(" evaluating '%s' by '%s' (%s)" %
|
||||
self.log.info(" evaluating '%s' by '%s' uuid:%s" %
|
||||
(bl_book.title, bl_book.author,bl_book.uuid))
|
||||
|
||||
found = False
|
||||
@ -781,10 +781,10 @@ class ITUNES(DriverBase):
|
||||
zf.close()
|
||||
|
||||
break
|
||||
# else:
|
||||
# if DEBUG:
|
||||
# self.log.error(" unable to find '%s' by '%s' (%s)" %
|
||||
# (bl_book.title, bl_book.author,bl_book.uuid))
|
||||
else:
|
||||
if DEBUG:
|
||||
self.log.error(" unable to find '%s' by '%s' (%s)" %
|
||||
(bl_book.title, bl_book.author,bl_book.uuid))
|
||||
|
||||
if False:
|
||||
self._dump_booklist(booklists[0], indent = 2)
|
||||
@ -905,7 +905,8 @@ class ITUNES(DriverBase):
|
||||
|
||||
# Add new_book to self.cached_books
|
||||
if DEBUG:
|
||||
self.log.info(" adding '%s' by '%s' ['%s'] to self.cached_books" %
|
||||
self.log.info("ITUNES.upload_books()")
|
||||
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" %
|
||||
( metadata[i].title, metadata[i].author, metadata[i].uuid))
|
||||
self.cached_books[this_book.path] = {
|
||||
'author': metadata[i].author,
|
||||
@ -943,7 +944,11 @@ class ITUNES(DriverBase):
|
||||
new_booklist.append(this_book)
|
||||
self._update_iTunes_metadata(metadata[i], db_added, lb_added, this_book)
|
||||
|
||||
# Add new_book to self.cached_paths
|
||||
# Add new_book to self.cached_books
|
||||
if DEBUG:
|
||||
self.log.info("ITUNES.upload_books()")
|
||||
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" %
|
||||
( metadata[i].title, metadata[i].author, metadata[i].uuid))
|
||||
self.cached_books[this_book.path] = {
|
||||
'author': metadata[i].author[0],
|
||||
'dev_book': db_added,
|
||||
@ -1406,8 +1411,8 @@ class ITUNES(DriverBase):
|
||||
|
||||
for book in booklist:
|
||||
if isosx:
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s" %
|
||||
(' '*indent,book.title, book.author, str(book.library_id)[-9:]))
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s %s" %
|
||||
(' '*indent,book.title, book.author, str(book.library_id)[-9:], book.uuid))
|
||||
elif iswindows:
|
||||
self.log.info("%s%-40.40s %-30.30s" %
|
||||
(' '*indent,book.title, book.author))
|
||||
@ -1547,11 +1552,12 @@ class ITUNES(DriverBase):
|
||||
|
||||
if isosx:
|
||||
for ub in self.update_list:
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s" %
|
||||
self.log.info("%s%-40.40s %-30.30s %-10.10s %s" %
|
||||
(' '*indent,
|
||||
ub['title'],
|
||||
ub['author'],
|
||||
str(ub['lib_book'])[-9:]))
|
||||
str(ub['lib_book'])[-9:],
|
||||
ub['uuid']))
|
||||
elif iswindows:
|
||||
for ub in self.update_list:
|
||||
self.log.info("%s%-40.40s %-30.30s" %
|
||||
@ -2342,8 +2348,10 @@ class ITUNES(DriverBase):
|
||||
if isosx:
|
||||
if DEBUG:
|
||||
self.log.info(" deleting '%s' from iDevice" % cached_book['title'])
|
||||
try:
|
||||
cached_book['dev_book'].delete()
|
||||
|
||||
except:
|
||||
self.log.error(" error deleting '%s'" % cached_book['title'])
|
||||
elif iswindows:
|
||||
hit = self._find_device_book(cached_book)
|
||||
if hit:
|
||||
@ -2802,7 +2810,7 @@ class ITUNES_ASYNC(ITUNES):
|
||||
#this_book.library_id = library_books[this_book.path] if this_book.path in library_books else None
|
||||
this_book.library_id = library_books[book]
|
||||
this_book.size = library_books[book].size()
|
||||
this_book.uuid = library_books[book].album()
|
||||
this_book.uuid = library_books[book].composer()
|
||||
# Hack to discover if we're running in GUI environment
|
||||
if self.report_progress is not None:
|
||||
this_book.thumbnail = self._generate_thumbnail(this_book.path, library_books[book])
|
||||
@ -2842,6 +2850,7 @@ class ITUNES_ASYNC(ITUNES):
|
||||
this_book.device_collections = []
|
||||
this_book.library_id = library_books[book]
|
||||
this_book.size = library_books[book].Size
|
||||
this_book.uuid = library_books[book].Composer
|
||||
# Hack to discover if we're running in GUI environment
|
||||
if self.report_progress is not None:
|
||||
this_book.thumbnail = self._generate_thumbnail(this_book.path, library_books[book])
|
||||
|
@ -11,6 +11,10 @@ import re
|
||||
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
|
||||
def is_alex(device_info):
|
||||
return device_info[3] == u'Linux 2.6.28 with pxa3xx_u2d' and \
|
||||
device_info[4] == u'Seleucia Disk'
|
||||
|
||||
class N516(USBMS):
|
||||
|
||||
name = 'N516 driver'
|
||||
@ -34,6 +38,9 @@ class N516(USBMS):
|
||||
EBOOK_DIR_MAIN = 'e_book'
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def can_handle(self, device_info, debug=False):
|
||||
return not is_alex(device_info)
|
||||
|
||||
class THEBOOK(N516):
|
||||
name = 'The Book driver'
|
||||
gui_name = 'The Book'
|
||||
@ -61,6 +68,9 @@ class ALEX(N516):
|
||||
EBOOK_DIR_MAIN = 'eBooks'
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
def can_handle(self, device_info, debug=False):
|
||||
return is_alex(device_info)
|
||||
|
||||
class AZBOOKA(ALEX):
|
||||
|
||||
name = 'Azbooka driver'
|
||||
@ -74,6 +84,9 @@ class AZBOOKA(ALEX):
|
||||
|
||||
EBOOK_DIR_MAIN = ''
|
||||
|
||||
def can_handle(self, device_info, debug=False):
|
||||
return not is_alex(device_info)
|
||||
|
||||
|
||||
class EB511(USBMS):
|
||||
name = 'Elonex EB 511 driver'
|
||||
|
@ -41,6 +41,10 @@ class Book(MetaInformation):
|
||||
self.authors = ['']
|
||||
else:
|
||||
self.authors = [authors]
|
||||
|
||||
if not title:
|
||||
self.title = _('Unknown')
|
||||
|
||||
self.mime = mime
|
||||
|
||||
self.size = size # will be set later if None
|
||||
|
@ -5,15 +5,16 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Timothy Legge <timlegge at gmail.com> and Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
import os, time
|
||||
import sqlite3 as sqlite
|
||||
|
||||
from calibre.devices.usbms.books import BookList
|
||||
from calibre.devices.kobo.books import Book
|
||||
from calibre.devices.kobo.books import ImageWrapper
|
||||
from calibre.devices.mime import mime_type_ext
|
||||
from calibre.devices.usbms.driver import USBMS
|
||||
from calibre.devices.usbms.driver import USBMS, debug_print
|
||||
from calibre import prints
|
||||
from calibre.devices.usbms.books import CollectionsBookList
|
||||
|
||||
class KOBO(USBMS):
|
||||
|
||||
@ -21,12 +22,15 @@ class KOBO(USBMS):
|
||||
gui_name = 'Kobo Reader'
|
||||
description = _('Communicate with the Kobo Reader')
|
||||
author = 'Timothy Legge and Kovid Goyal'
|
||||
version = (1, 0, 4)
|
||||
version = (1, 0, 6)
|
||||
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
|
||||
booklist_class = CollectionsBookList
|
||||
|
||||
# Ordered list of supported formats
|
||||
FORMATS = ['epub', 'pdf']
|
||||
CAN_SET_METADATA = True
|
||||
|
||||
VENDOR_ID = [0x2237]
|
||||
PRODUCT_ID = [0x4161]
|
||||
@ -40,6 +44,12 @@ class KOBO(USBMS):
|
||||
|
||||
VIRTUAL_BOOK_EXTENSIONS = frozenset(['kobo'])
|
||||
|
||||
EXTRA_CUSTOMIZATION_MESSAGE = _('The Kobo supports only one collection '
|
||||
'currently: the \"Im_Reading\" list. Create a tag called \"Im_Reading\" ')+\
|
||||
'for automatic management'
|
||||
|
||||
EXTRA_CUSTOMIZATION_DEFAULT = ', '.join(['tags'])
|
||||
|
||||
def initialize(self):
|
||||
USBMS.initialize(self)
|
||||
self.book_class = Book
|
||||
@ -63,6 +73,8 @@ class KOBO(USBMS):
|
||||
self._card_b_prefix if oncard == 'cardb' \
|
||||
else self._main_prefix
|
||||
|
||||
self.booklist_class.rebuild_collections = self.rebuild_collections
|
||||
|
||||
# get the metadata cache
|
||||
bl = self.booklist_class(oncard, prefix, self.settings)
|
||||
need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE)
|
||||
@ -85,9 +97,9 @@ class KOBO(USBMS):
|
||||
playlist_map = {}
|
||||
|
||||
if readstatus == 1:
|
||||
if lpath not in playlist_map:
|
||||
playlist_map[lpath] = []
|
||||
playlist_map[lpath].append("I\'m Reading")
|
||||
playlist_map[lpath]= "Im_Reading"
|
||||
elif readstatus == 2:
|
||||
playlist_map[lpath]= "Read"
|
||||
|
||||
path = self.normalize_path(path)
|
||||
# print "Normalized FileName: " + path
|
||||
@ -104,14 +116,17 @@ class KOBO(USBMS):
|
||||
if self.update_metadata_item(bl[idx]):
|
||||
# print 'update_metadata_item returned true'
|
||||
changed = True
|
||||
bl[idx].device_collections = playlist_map.get(lpath, [])
|
||||
if lpath in playlist_map and \
|
||||
playlist_map[lpath] not in bl[idx].device_collections:
|
||||
bl[idx].device_collections.append(playlist_map[lpath])
|
||||
else:
|
||||
if ContentType == '6':
|
||||
book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size=1048576)
|
||||
else:
|
||||
book = self.book_from_path(prefix, lpath, title, authors, mime, date, ContentType, ImageID)
|
||||
# print 'Update booklist'
|
||||
book.device_collections = playlist_map.get(book.lpath, [])
|
||||
book.device_collections = [playlist_map[lpath]] if lpath in playlist_map else []
|
||||
|
||||
if bl.add_book(book, replace_metadata=False):
|
||||
changed = True
|
||||
except: # Probably a path encoding error
|
||||
@ -398,3 +413,163 @@ class KOBO(USBMS):
|
||||
size = os.stat(cls.normalize_path(os.path.join(prefix, lpath))).st_size
|
||||
book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size=size, other=mi)
|
||||
return book
|
||||
|
||||
def get_device_paths(self):
|
||||
paths, prefixes = {}, {}
|
||||
for prefix, path, source_id in [
|
||||
('main', 'metadata.calibre', 0),
|
||||
('card_a', 'metadata.calibre', 1),
|
||||
('card_b', 'metadata.calibre', 2)
|
||||
]:
|
||||
prefix = getattr(self, '_%s_prefix'%prefix)
|
||||
if prefix is not None and os.path.exists(prefix):
|
||||
paths[source_id] = os.path.join(prefix, *(path.split('/')))
|
||||
return paths
|
||||
|
||||
def update_device_database_collections(self, booklists, collections_attributes, oncard):
|
||||
# debug_print('Starting update_device_database_collections', collections_attributes)
|
||||
|
||||
# Force collections_attributes to be 'tags' as no other is currently supported
|
||||
# debug_print('KOBO: overriding the provided collections_attributes:', collections_attributes)
|
||||
collections_attributes = ['tags']
|
||||
|
||||
collections = booklists.get_collections(collections_attributes)
|
||||
# debug_print('Collections', collections)
|
||||
|
||||
# Create a connection to the sqlite database
|
||||
# Needs to be outside books collection as in the case of removing
|
||||
# the last book from the collection the list of books is empty
|
||||
# and the removal of the last book would not occur
|
||||
connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite')
|
||||
cursor = connection.cursor()
|
||||
|
||||
|
||||
if collections:
|
||||
# Process any collections that exist
|
||||
for category, books in collections.items():
|
||||
if category == 'Im_Reading':
|
||||
# Reset Im_Reading list in the database
|
||||
if oncard == 'carda':
|
||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID like \'file:///mnt/sd/%\''
|
||||
elif oncard != 'carda' and oncard != 'cardb':
|
||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 1 and ContentID not like \'file:///mnt/sd/%\''
|
||||
|
||||
try:
|
||||
cursor.execute (query)
|
||||
except:
|
||||
debug_print('Database Exception: Unable to reset Im_Reading list')
|
||||
raise
|
||||
else:
|
||||
# debug_print('Commit: Reset Im_Reading list')
|
||||
connection.commit()
|
||||
|
||||
for book in books:
|
||||
# debug_print('Title:', book.title, 'lpath:', book.path)
|
||||
book.device_collections = ['Im_Reading']
|
||||
|
||||
extension = os.path.splitext(book.path)[1]
|
||||
ContentType = self.get_content_type_from_extension(extension)
|
||||
|
||||
ContentID = self.contentid_from_path(book.path, ContentType)
|
||||
datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
|
||||
|
||||
t = (datelastread,ContentID,)
|
||||
|
||||
try:
|
||||
cursor.execute('update content set ReadStatus=1,FirstTimeReading=\'false\',DateLastRead=? where BookID is Null and ContentID = ?', t)
|
||||
except:
|
||||
debug_print('Database Exception: Unable create Im_Reading list')
|
||||
raise
|
||||
else:
|
||||
connection.commit()
|
||||
# debug_print('Database: Commit create Im_Reading list')
|
||||
if category == 'Read':
|
||||
# Reset Im_Reading list in the database
|
||||
if oncard == 'carda':
|
||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID like \'file:///mnt/sd/%\''
|
||||
elif oncard != 'carda' and oncard != 'cardb':
|
||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ReadStatus = 2 and ContentID not like \'file:///mnt/sd/%\''
|
||||
|
||||
try:
|
||||
cursor.execute (query)
|
||||
except:
|
||||
debug_print('Database Exception: Unable to reset Im_Reading list')
|
||||
raise
|
||||
else:
|
||||
# debug_print('Commit: Reset Im_Reading list')
|
||||
connection.commit()
|
||||
|
||||
for book in books:
|
||||
# debug_print('Title:', book.title, 'lpath:', book.path)
|
||||
book.device_collections = ['Read']
|
||||
|
||||
extension = os.path.splitext(book.path)[1]
|
||||
ContentType = self.get_content_type_from_extension(extension)
|
||||
|
||||
ContentID = self.contentid_from_path(book.path, ContentType)
|
||||
# datelastread = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
|
||||
|
||||
t = (ContentID,)
|
||||
|
||||
try:
|
||||
cursor.execute('update content set ReadStatus=2,FirstTimeReading=\'true\' where BookID is Null and ContentID = ?', t)
|
||||
except:
|
||||
debug_print('Database Exception: Unable set book as Rinished')
|
||||
raise
|
||||
else:
|
||||
connection.commit()
|
||||
# debug_print('Database: Commit set ReadStatus as Finished')
|
||||
else: # No collections
|
||||
# Since no collections exist the ReadStatus needs to be reset to 0 (Unread)
|
||||
print "Reseting ReadStatus to 0"
|
||||
# Reset Im_Reading list in the database
|
||||
if oncard == 'carda':
|
||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID like \'file:///mnt/sd/%\''
|
||||
elif oncard != 'carda' and oncard != 'cardb':
|
||||
query= 'update content set ReadStatus=0, FirstTimeReading = \'true\' where BookID is Null and ContentID not like \'file:///mnt/sd/%\''
|
||||
|
||||
try:
|
||||
cursor.execute (query)
|
||||
except:
|
||||
debug_print('Database Exception: Unable to reset Im_Reading list')
|
||||
raise
|
||||
else:
|
||||
# debug_print('Commit: Reset Im_Reading list')
|
||||
connection.commit()
|
||||
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
# debug_print('Finished update_device_database_collections', collections_attributes)
|
||||
|
||||
def sync_booklists(self, booklists, end_session=True):
|
||||
# debug_print('KOBO: started sync_booklists')
|
||||
paths = self.get_device_paths()
|
||||
|
||||
blists = {}
|
||||
for i in paths:
|
||||
if booklists[i] is not None:
|
||||
#debug_print('Booklist: ', i)
|
||||
blists[i] = booklists[i]
|
||||
opts = self.settings()
|
||||
if opts.extra_customization:
|
||||
collections = [x.lower().strip() for x in
|
||||
opts.extra_customization.split(',')]
|
||||
else:
|
||||
collections = []
|
||||
|
||||
#debug_print('KOBO: collection fields:', collections)
|
||||
for i, blist in blists.items():
|
||||
if i == 0:
|
||||
oncard = 'main'
|
||||
else:
|
||||
oncard = 'carda'
|
||||
self.update_device_database_collections(blist, collections, oncard)
|
||||
|
||||
USBMS.sync_booklists(self, booklists, end_session=end_session)
|
||||
#debug_print('KOBO: finished sync_booklists')
|
||||
|
||||
def rebuild_collections(self, booklist, oncard):
|
||||
collections_attributes = []
|
||||
self.update_device_database_collections(booklist, collections_attributes, oncard)
|
||||
|
||||
|
@ -35,16 +35,16 @@ class PRS505(USBMS):
|
||||
|
||||
VENDOR_NAME = 'SONY'
|
||||
WINDOWS_MAIN_MEM = re.compile(
|
||||
r'(PRS-(505|300|500))|'
|
||||
r'(PRS-((700[#/])|((6|9)00&)))'
|
||||
r'(PRS-(505|500|300))|'
|
||||
r'(PRS-((700[#/])|((6|9|3)(0|5)0&)))'
|
||||
)
|
||||
WINDOWS_CARD_A_MEM = re.compile(
|
||||
r'(PRS-(505|500)[#/]\S+:MS)|'
|
||||
r'(PRS-((700[/#]\S+:)|((6|9)00[#_]))MS)'
|
||||
r'(PRS-((700[/#]\S+:)|((6|9)(0|5)0[#_]))MS)'
|
||||
)
|
||||
WINDOWS_CARD_B_MEM = re.compile(
|
||||
r'(PRS-(505|500)[#/]\S+:SD)|'
|
||||
r'(PRS-((700[/#]\S+:)|((6|9)00[#_]))SD)'
|
||||
r'(PRS-((700[/#]\S+:)|((6|9)(0|5)0[#_]))SD)'
|
||||
)
|
||||
|
||||
|
||||
|
@ -52,3 +52,14 @@ class IPAPYRUS(TECLAST_K3):
|
||||
VENDOR_NAME = 'E_READER'
|
||||
WINDOWS_MAIN_MEM = ''
|
||||
|
||||
class SOVOS(TECLAST_K3):
|
||||
|
||||
name = 'Sovos device interface'
|
||||
gui_name = 'Sovos'
|
||||
description = _('Communicate with the Sovos reader.')
|
||||
|
||||
FORMATS = ['epub', 'fb2', 'pdf', 'txt']
|
||||
|
||||
VENDOR_NAME = 'RK28XX'
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'USB-MSC'
|
||||
|
||||
|
@ -132,7 +132,11 @@ class CHMReader(CHMFile):
|
||||
for path in self.Contents():
|
||||
lpath = os.path.join(output_dir, path)
|
||||
self._ensure_dir(lpath)
|
||||
try:
|
||||
data = self.GetFile(path)
|
||||
except:
|
||||
self.log.exception('Failed to extract %s from CHM, ignoring'%path)
|
||||
continue
|
||||
if lpath.find(';') != -1:
|
||||
# fix file names with ";<junk>" at the end, see _reformat()
|
||||
lpath = lpath.split(';')[0]
|
||||
|
@ -122,7 +122,7 @@ def add_pipeline_options(parser, plumber):
|
||||
'font_size_mapping',
|
||||
'line_height',
|
||||
'linearize_tables',
|
||||
'extra_css',
|
||||
'extra_css', 'smarten_punctuation',
|
||||
'margin_top', 'margin_left', 'margin_right',
|
||||
'margin_bottom', 'change_justification',
|
||||
'insert_blank_line', 'remove_paragraph_spacing','remove_paragraph_spacing_indent_size',
|
||||
@ -137,7 +137,7 @@ def add_pipeline_options(parser, plumber):
|
||||
'chapter', 'chapter_mark',
|
||||
'prefer_metadata_cover', 'remove_first_image',
|
||||
'insert_metadata', 'page_breaks_before',
|
||||
'preprocess_html',
|
||||
'preprocess_html', 'html_unwrap_factor',
|
||||
]
|
||||
),
|
||||
|
||||
|
@ -241,7 +241,7 @@ OptionRecommendation(name='toc_filter',
|
||||
|
||||
OptionRecommendation(name='chapter',
|
||||
recommended_value="//*[((name()='h1' or name()='h2') and "
|
||||
r"re:test(., 'chapter|book|section|part\s+', 'i')) or @class "
|
||||
r"re:test(., 'chapter|book|section|part|prologue|epilogue\s+', 'i')) or @class "
|
||||
"= 'chapter']", level=OptionRecommendation.LOW,
|
||||
help=_('An XPath expression to detect chapter titles. The default '
|
||||
'is to consider <h1> or <h2> tags that contain the words '
|
||||
@ -362,6 +362,23 @@ OptionRecommendation(name='preprocess_html',
|
||||
)
|
||||
),
|
||||
|
||||
OptionRecommendation(name='html_unwrap_factor',
|
||||
recommended_value=0.40, level=OptionRecommendation.LOW,
|
||||
help=_('Scale used to determine the length at which a line should '
|
||||
'be unwrapped if preprocess is enabled. Valid values are a decimal between 0 and 1. The '
|
||||
'default is 0.40, just below the median line length. This will unwrap typical books '
|
||||
' with hard line breaks, but should be reduced if the line length is variable.'
|
||||
)
|
||||
),
|
||||
|
||||
OptionRecommendation(name='smarten_punctuation',
|
||||
recommended_value=False, level=OptionRecommendation.LOW,
|
||||
help=_('Convert plain quotes, dashes and ellipsis to their '
|
||||
'typographically correct equivalents. For details, see '
|
||||
'http://daringfireball.net/projects/smartypants'
|
||||
)
|
||||
),
|
||||
|
||||
OptionRecommendation(name='remove_header',
|
||||
recommended_value=False, level=OptionRecommendation.LOW,
|
||||
help=_('Use a regular expression to try and remove the header.'
|
||||
|
@ -75,6 +75,8 @@ def line_length(format, raw, percent):
|
||||
linere = re.compile('(?<=<p).*?(?=</p>)', re.DOTALL)
|
||||
elif format == 'pdf':
|
||||
linere = re.compile('(?<=<br>).*?(?=<br>)', re.DOTALL)
|
||||
elif format == 'spanned_html':
|
||||
linere = re.compile('(?<=<span).*?(?=</span>)', re.DOTALL)
|
||||
lines = linere.findall(raw)
|
||||
|
||||
lengths = []
|
||||
@ -104,6 +106,52 @@ def line_length(format, raw, percent):
|
||||
|
||||
return lengths[index]
|
||||
|
||||
class Dehyphenator(object):
|
||||
'''
|
||||
Analyzes words to determine whether hyphens should be retained/removed. Uses the document
|
||||
itself is as a dictionary. This method handles all languages along with uncommon, made-up, and
|
||||
scientific words. The primary disadvantage is that words appearing only once in the document
|
||||
retain hyphens.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
# Add common suffixes to the regex below to increase the likelihood of a match -
|
||||
# don't add suffixes which are also complete words, such as 'able' or 'sex'
|
||||
self.removesuffixes = re.compile(r"((ed)?ly|('e)?s|a?(t|s)ion(s|al(ly)?)?|ings?|(i)?ous|(i|a)ty|(it)?ies|ive|gence|istic|(e|a)nce|ment(s)?|ism|ated|(e|u)ct(ed)?|ed|(i|ed)?ness|(e|a)ncy|ble|ier|al|ex)$", re.IGNORECASE)
|
||||
# remove prefixes if the prefix was not already the point of hyphenation
|
||||
self.prefixes = re.compile(r'^(un|in|ex)$', re.IGNORECASE)
|
||||
self.removeprefix = re.compile(r'^(un|in|ex)', re.IGNORECASE)
|
||||
|
||||
def dehyphenate(self, match):
|
||||
firsthalf = match.group('firstpart')
|
||||
secondhalf = match.group('secondpart')
|
||||
hyphenated = str(firsthalf) + "-" + str(secondhalf)
|
||||
dehyphenated = str(firsthalf) + str(secondhalf)
|
||||
lookupword = self.removesuffixes.sub('', dehyphenated)
|
||||
if self.prefixes.match(firsthalf) is None:
|
||||
lookupword = self.removeprefix.sub('', lookupword)
|
||||
booklookup = re.compile(u'%s' % lookupword, re.IGNORECASE)
|
||||
#print "lookup word is: "+str(lookupword)+", orig is: " + str(hyphenated)
|
||||
match = booklookup.search(self.html)
|
||||
if match:
|
||||
#print "returned dehyphenated word: " + str(dehyphenated)
|
||||
return dehyphenated
|
||||
else:
|
||||
#print "returned hyphenated word: " + str(hyphenated)
|
||||
return hyphenated
|
||||
|
||||
def __call__(self, html, format, length=1):
|
||||
self.html = html
|
||||
if format == 'html':
|
||||
intextmatch = re.compile(u'(?<=.{%i})(?P<firstpart>[^“"\s>]+)-\s*(?=<)(</span>\s*(</[iubp]>\s*<[iubp][^>]*>\s*)?<span[^>]*>|</[iubp]>\s*<[iubp][^>]*>)?\s*(?P<secondpart>[\w\d]+)' % length)
|
||||
elif format == 'pdf':
|
||||
intextmatch = re.compile(u'(?<=.{%i})(?P<firstpart>[^“"\s>]+)-\s*(<p>|</[iub]>\s*<p>\s*<[iub]>)\s*(?P<secondpart>[\w\d]+)'% length)
|
||||
elif format == 'individual_words':
|
||||
intextmatch = re.compile('>[^<]*\b(?P<firstpart>[^"\s>]+)-(?P<secondpart)\w+)\b[^<]*<') # for later, not called anywhere yet
|
||||
|
||||
html = intextmatch.sub(self.dehyphenate, html)
|
||||
return html
|
||||
|
||||
|
||||
class CSSPreProcessor(object):
|
||||
|
||||
@ -142,7 +190,6 @@ class HTMLPreProcessor(object):
|
||||
# Fix pdftohtml markup
|
||||
PDFTOHTML = [
|
||||
# Fix umlauts
|
||||
# ¨
|
||||
(re.compile(u'¨\s*(<br.*?>)*\s*a', re.UNICODE), lambda match: u'ä'),
|
||||
(re.compile(u'¨\s*(<br.*?>)*\s*A', re.UNICODE), lambda match: u'Ä'),
|
||||
(re.compile(u'¨\s*(<br.*?>)*\s*e', re.UNICODE), lambda match: u'ë'),
|
||||
@ -215,8 +262,8 @@ class HTMLPreProcessor(object):
|
||||
(re.compile(u'¸\s*(<br.*?>)*\s*C', re.UNICODE), lambda match: u'Ç'),
|
||||
|
||||
# ˛
|
||||
(re.compile(u'˛\s*(<br.*?>)*\s*a', re.UNICODE), lambda match: u'ą'),
|
||||
(re.compile(u'˛\s*(<br.*?>)*\s*A', re.UNICODE), lambda match: u'Ą'),
|
||||
(re.compile(u'\s*˛\s*(<br.*?>)*\s*a', re.UNICODE), lambda match: u'ą'),
|
||||
(re.compile(u'\s*˛\s*(<br.*?>)*\s*A', re.UNICODE), lambda match: u'Ą'),
|
||||
(re.compile(u'˛\s*(<br.*?>)*\s*e', re.UNICODE), lambda match: u'ę'),
|
||||
(re.compile(u'˛\s*(<br.*?>)*\s*E', re.UNICODE), lambda match: u'Ę'),
|
||||
|
||||
@ -224,30 +271,29 @@ class HTMLPreProcessor(object):
|
||||
(re.compile(u'˙\s*(<br.*?>)*\s*z', re.UNICODE), lambda match: u'ż'),
|
||||
(re.compile(u'˙\s*(<br.*?>)*\s*Z', re.UNICODE), lambda match: u'Ż'),
|
||||
|
||||
# If pdf printed from a browser then the header/footer has a reliable pattern
|
||||
(re.compile(r'((?<=</a>)\s*file:////?[A-Z].*<br>|file:////?[A-Z].*<br>(?=\s*<hr>))', re.IGNORECASE), lambda match: ''),
|
||||
|
||||
# Center separator lines
|
||||
(re.compile(u'<br>\s*(?P<break>([*#•]+\s*)+)\s*<br>'), lambda match: '<p>\n<p style="text-align:center">' + match.group(1) + '</p>'),
|
||||
|
||||
# Remove page links
|
||||
(re.compile(r'<a name=\d+></a>', re.IGNORECASE), lambda match: ''),
|
||||
# Remove <hr> tags
|
||||
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<br />'),
|
||||
# Replace <br><br> with <p>
|
||||
(re.compile(r'<br.*?>\s*<br.*?>', re.IGNORECASE), lambda match: '<p>'),
|
||||
|
||||
# Remove hyphenation
|
||||
(re.compile(r'-<br.*?>\n\r?'), lambda match: ''),
|
||||
(re.compile(r'<hr.*?>', re.IGNORECASE), lambda match: '<br>'),
|
||||
|
||||
# Remove gray background
|
||||
(re.compile(r'<BODY[^<>]+>'), lambda match : '<BODY>'),
|
||||
|
||||
# Detect Chapters to match default XPATH in GUI
|
||||
(re.compile(r'(?=<(/?br|p))(<(/?br|p)[^>]*)?>\s*(?P<chap>(<(i|b)>(<(i|b)>)?)?(.?Chapter|Epilogue|Prologue|Book|Part)\s*([\d\w-]+(\s\w+)?)?(</(i|b)>(</(i|b)>)?)?)</?(br|p)[^>]*>\s*(?P<title>(<(i|b)>)?\s*\w+(\s*\w+)?\s*(</(i|b)>)?\s*(</?(br|p)[^>]*>))?', re.IGNORECASE), chap_head),
|
||||
(re.compile(r'(?=<(/?br|p))(<(/?br|p)[^>]*)?>\s*(?P<chap>([A-Z \'"!]{5,})\s*(\d+|\w+)?)(</?p[^>]*>|<br[^>]*>)\n?((?=(<i>)?\s*\w+(\s+\w+)?(</i>)?(<br[^>]*>|</?p[^>]*>))((?P<title>.*)(<br[^>]*>|</?p[^>]*>)))?'), chap_head),
|
||||
(re.compile(r'<br>\s*(?P<chap>(<[ibu]>){0,2}\s*.?(Introduction|Chapter|Epilogue|Prologue|Book|Part|Dedication|Volume|Preface|Acknowledgments)\s*([\d\w-]+\s*){0,3}\s*(</[ibu]>){0,2})\s*(<br>\s*){1,3}\s*(?P<title>(<[ibu]>){0,2}(\s*\w+){1,4}\s*(</[ibu]>){0,2}\s*<br>)?', re.IGNORECASE), chap_head),
|
||||
# Cover the case where every letter in a chapter title is separated by a space
|
||||
(re.compile(r'<br>\s*(?P<chap>([A-Z]\s+){4,}\s*([\d\w-]+\s*){0,3}\s*)\s*(<br>\s*){1,3}\s*(?P<title>(<[ibu]>){0,2}(\s*\w+){1,4}\s*(</[ibu]>){0,2}\s*(<br>))?'), chap_head),
|
||||
|
||||
# Have paragraphs show better
|
||||
(re.compile(r'<br.*?>'), lambda match : '<p>'),
|
||||
# Clean up spaces
|
||||
(re.compile(u'(?<=[\.,;\?!”"\'])[\s^ ]*(?=<)'), lambda match: ' '),
|
||||
# Connect paragraphs split by -
|
||||
(re.compile(u'(?<=[^\s][-–])[\s]*(</p>)*[\s]*(<p>)*\s*(?=[^\s])'), lambda match: ''),
|
||||
# Add space before and after italics
|
||||
(re.compile(u'(?<!“)<i>'), lambda match: ' <i>'),
|
||||
(re.compile(r'</i>(?=\w)'), lambda match: '</i> '),
|
||||
@ -328,12 +374,29 @@ class HTMLPreProcessor(object):
|
||||
print 'Failed to parse remove_footer regexp'
|
||||
traceback.print_exc()
|
||||
|
||||
# unwrap em/en dashes, delete soft hyphens - moved here so it's executed after header/footer removal
|
||||
if is_pdftohtml:
|
||||
# unwrap em/en dashes
|
||||
end_rules.append((re.compile(u'(?<=[–—])\s*<p>\s*(?=[[a-z\d])'), lambda match: ''))
|
||||
# unwrap/delete soft hyphens
|
||||
end_rules.append((re.compile(u'[](\s*<p>)+\s*(?=[[a-z\d])'), lambda match: ''))
|
||||
# unwrap/delete soft hyphens with formatting
|
||||
end_rules.append((re.compile(u'[]\s*(</(i|u|b)>)+(\s*<p>)+\s*(<(i|u|b)>)+\s*(?=[[a-z\d])'), lambda match: ''))
|
||||
|
||||
# Make the more aggressive chapter marking regex optional with the preprocess option to
|
||||
# reduce false positives and move after header/footer removal
|
||||
if getattr(self.extra_opts, 'preprocess_html', None):
|
||||
if is_pdftohtml:
|
||||
end_rules.append((re.compile(r'<p>\s*(?P<chap>(<[ibu]>){0,2}\s*([A-Z \'"!]{3,})\s*([\dA-Z:]+\s){0,4}\s*(</[ibu]>){0,2})\s*<p>\s*(?P<title>(<[ibu]>){0,2}(\s*\w+){1,4}\s*(</[ibu]>){0,2}\s*<p>)?'), chap_head),)
|
||||
|
||||
length = -1
|
||||
if getattr(self.extra_opts, 'unwrap_factor', 0.0) > 0.01:
|
||||
length = line_length('pdf', html, getattr(self.extra_opts, 'unwrap_factor'))
|
||||
if length:
|
||||
# print "The pdf line length returned is " + str(length)
|
||||
end_rules.append(
|
||||
# Un wrap using punctuation
|
||||
(re.compile(r'(?<=.{%i}[a-z\.,;:)\-IA])\s*(?P<ital></(i|b|u)>)?\s*(<p.*?>)\s*(?=(<(i|b|u)>)?\s*[\w\d(])' % length, re.UNICODE), wrap_lines),
|
||||
(re.compile(r'(?<=.{%i}([a-z,:)\IA]|(?<!\&\w{4});))\s*(?P<ital></(i|b|u)>)?\s*(<p.*?>\s*)+\s*(?=(<(i|b|u)>)?\s*[\w\d$(])' % length, re.UNICODE), wrap_lines),
|
||||
)
|
||||
|
||||
for rule in self.PREPROCESS + start_rules:
|
||||
@ -363,6 +426,11 @@ class HTMLPreProcessor(object):
|
||||
for rule in rules + end_rules:
|
||||
html = rule[0].sub(rule[1], html)
|
||||
|
||||
if is_pdftohtml and length > -1:
|
||||
# Dehyphenate
|
||||
dehyphenator = Dehyphenator()
|
||||
html = dehyphenator(html,'pdf', length)
|
||||
|
||||
#dump(html, 'post-preprocess')
|
||||
|
||||
# Handle broken XHTML w/ SVG (ugh)
|
||||
@ -381,7 +449,16 @@ class HTMLPreProcessor(object):
|
||||
html = unidecoder.decode(html)
|
||||
|
||||
if self.plugin_preprocess:
|
||||
html = self.input_plugin_preprocess(html)
|
||||
html = self.input_plugin_preprocess(self.extra_opts, html)
|
||||
|
||||
if getattr(self.extra_opts, 'smarten_punctuation', False):
|
||||
html = self.smarten_punctuation(html)
|
||||
|
||||
return html
|
||||
|
||||
def smarten_punctuation(self, html):
|
||||
from calibre.utils.smartypants import smartyPants
|
||||
from calibre.ebooks.chardet import substitute_entites
|
||||
html = smartyPants(html)
|
||||
return substitute_entites(html)
|
||||
|
||||
|
205
src/calibre/ebooks/conversion/utils.py
Normal file
@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from calibre.ebooks.conversion.preprocess import line_length, Dehyphenator
|
||||
from calibre.utils.logging import default_log
|
||||
|
||||
class PreProcessor(object):
|
||||
|
||||
def __init__(self, extra_opts=None, log=None):
|
||||
self.log = default_log if log is None else log
|
||||
self.html_preprocess_sections = 0
|
||||
self.found_indents = 0
|
||||
self.extra_opts = extra_opts
|
||||
|
||||
def chapter_head(self, match):
|
||||
chap = match.group('chap')
|
||||
title = match.group('title')
|
||||
if not title:
|
||||
self.html_preprocess_sections = self.html_preprocess_sections + 1
|
||||
self.log("found " + str(self.html_preprocess_sections) + " chapters. - " + str(chap))
|
||||
return '<h2>'+chap+'</h2>\n'
|
||||
else:
|
||||
self.html_preprocess_sections = self.html_preprocess_sections + 1
|
||||
self.log("found " + str(self.html_preprocess_sections) + " chapters & titles. - " + str(chap) + ", " + str(title))
|
||||
return '<h2>'+chap+'</h2>\n<h3>'+title+'</h3>\n'
|
||||
|
||||
def chapter_break(self, match):
|
||||
chap = match.group('section')
|
||||
styles = match.group('styles')
|
||||
self.html_preprocess_sections = self.html_preprocess_sections + 1
|
||||
self.log("marked " + str(self.html_preprocess_sections) + " section markers based on punctuation. - " + str(chap))
|
||||
return '<'+styles+' style="page-break-before:always">'+chap
|
||||
|
||||
def insert_indent(self, match):
|
||||
pstyle = match.group('formatting')
|
||||
span = match.group('span')
|
||||
self.found_indents = self.found_indents + 1
|
||||
if pstyle:
|
||||
if not span:
|
||||
return '<p '+pstyle+' style="text-indent:3%">'
|
||||
else:
|
||||
return '<p '+pstyle+' style="text-indent:3%">'+span
|
||||
else:
|
||||
if not span:
|
||||
return '<p style="text-indent:3%">'
|
||||
else:
|
||||
return '<p style="text-indent:3%">'+span
|
||||
|
||||
def no_markup(self, raw, percent):
|
||||
'''
|
||||
Detects total marked up line endings in the file. raw is the text to
|
||||
inspect. Percent is the minimum percent of line endings which should
|
||||
be marked up to return true.
|
||||
'''
|
||||
htm_end_ere = re.compile('</p>', re.DOTALL)
|
||||
line_end_ere = re.compile('(\n|\r|\r\n)', re.DOTALL)
|
||||
htm_end = htm_end_ere.findall(raw)
|
||||
line_end = line_end_ere.findall(raw)
|
||||
tot_htm_ends = len(htm_end)
|
||||
tot_ln_fds = len(line_end)
|
||||
self.log("There are " + str(tot_ln_fds) + " total Line feeds, and " + str(tot_htm_ends) + " marked up endings")
|
||||
|
||||
if percent > 1:
|
||||
percent = 1
|
||||
if percent < 0:
|
||||
percent = 0
|
||||
|
||||
min_lns = tot_ln_fds * percent
|
||||
self.log("There must be fewer than " + str(min_lns) + " unmarked lines to add markup")
|
||||
if min_lns > tot_htm_ends:
|
||||
return True
|
||||
|
||||
def __call__(self, html):
|
||||
self.log("********* Preprocessing HTML *********")
|
||||
###### Check Markup ######
|
||||
#
|
||||
# some lit files don't have any <p> tags or equivalent (generally just plain text between
|
||||
# <pre> tags), check and mark up line endings if required before proceeding
|
||||
if self.no_markup(html, 0.1):
|
||||
self.log("not enough paragraph markers, adding now")
|
||||
# check if content is in pre tags, use txt procesor to mark up if so
|
||||
pre = re.compile(r'<pre>', re.IGNORECASE)
|
||||
if len(pre.findall(html)) == 1:
|
||||
self.log("Running Text Processing")
|
||||
from calibre.ebooks.txt.processor import convert_basic, preserve_spaces, \
|
||||
separate_paragraphs_single_line
|
||||
outerhtml = re.compile(r'.*?(?<=<pre>)(?P<text>.*)(?=</pre>).*', re.IGNORECASE|re.DOTALL)
|
||||
html = outerhtml.sub('\g<text>', html)
|
||||
html = separate_paragraphs_single_line(html)
|
||||
html = preserve_spaces(html)
|
||||
html = convert_basic(html, epub_split_size_kb=0)
|
||||
else:
|
||||
# Add markup naively
|
||||
# TODO - find out if there are cases where there are more than one <pre> tag or
|
||||
# other types of unmarked html and handle them in some better fashion
|
||||
add_markup = re.compile('(?<!>)(\n)')
|
||||
html = add_markup.sub('</p>\n<p>', html)
|
||||
|
||||
###### Mark Indents/Cleanup ######
|
||||
#
|
||||
# Replace series of non-breaking spaces with text-indent
|
||||
txtindent = re.compile(ur'<p(?P<formatting>[^>]*)>\s*(?P<span>(<span[^>]*>\s*)+)?\s*(\u00a0){2,}', re.IGNORECASE)
|
||||
html = txtindent.sub(self.insert_indent, html)
|
||||
if self.found_indents > 1:
|
||||
self.log("replaced "+str(self.found_indents)+ " nbsp indents with inline styles")
|
||||
# remove remaining non-breaking spaces
|
||||
html = re.sub(ur'\u00a0', ' ', html)
|
||||
# Get rid of empty <o:p> tags to simplify other processing
|
||||
html = re.sub(ur'\s*<o:p>\s*</o:p>', ' ', html)
|
||||
# Get rid of empty span, bold, & italics tags
|
||||
html = re.sub(r"\s*<span[^>]*>\s*(<span[^>]>\s*</span>){0,2}\s*</span>\s*", " ", html)
|
||||
html = re.sub(r"\s*<[ibu][^>]*>\s*(<[ibu][^>]*>\s*</[ibu]>\s*){0,2}\s*</[ibu]>", " ", html)
|
||||
html = re.sub(r"\s*<span[^>]*>\s*(<span[^>]>\s*</span>){0,2}\s*</span>\s*", " ", html)
|
||||
|
||||
# If more than 40% of the lines are empty paragraphs then delete them to clean up spacing
|
||||
linereg = re.compile('(?<=<p).*?(?=</p>)', re.IGNORECASE|re.DOTALL)
|
||||
blankreg = re.compile(r'\s*(?P<openline><p[^>]*>)\s*(?P<closeline></p>)', re.IGNORECASE)
|
||||
#multi_blank = re.compile(r'(\s*<p[^>]*>\s*(<(b|i|u)>)?\s*(</(b|i|u)>)?\s*</p>){2,}', re.IGNORECASE)
|
||||
blanklines = blankreg.findall(html)
|
||||
lines = linereg.findall(html)
|
||||
if len(lines) > 1:
|
||||
self.log("There are " + str(len(blanklines)) + " blank lines. " + str(float(len(blanklines)) / float(len(lines))) + " percent blank")
|
||||
if float(len(blanklines)) / float(len(lines)) > 0.40 and getattr(self.extra_opts,
|
||||
'remove_paragraph_spacing', False):
|
||||
self.log("deleting blank lines")
|
||||
html = blankreg.sub('', html)
|
||||
# Arrange line feeds and </p> tags so the line_length and no_markup functions work correctly
|
||||
html = re.sub(r"\s*</p>", "</p>\n", html)
|
||||
html = re.sub(r"\s*<p>\s*", "\n<p>", html)
|
||||
# detect chapters/sections to match xpath or splitting logic
|
||||
heading = re.compile('<h[1-3][^>]*>', re.IGNORECASE)
|
||||
self.html_preprocess_sections = len(heading.findall(html))
|
||||
self.log("found " + str(self.html_preprocess_sections) + " pre-existing headings")
|
||||
#
|
||||
# Start with most typical chapter headings, get more aggressive until one works
|
||||
if self.html_preprocess_sections < 10:
|
||||
chapdetect = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu][^>]*>){0,2}\s*.?(Introduction|Synopsis|Acknowledgements|Chapter|Epilogue|Volume|Prologue|Book\s|Part\s|Dedication)\s*([\d\w-]+\:?\s*){0,8}\s*(</[ibu]>){0,2})\s*(</span>)?s*(</[ibu]>){0,2}\s*(</span>)?\s*(</(p|/?br)>)\s*\s*(\s*<p[^>]*>\s*</p>){0,2}\s*(<(/?br|p)[^>]*>\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu][^>]*>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.IGNORECASE|re.VERBOSE)
|
||||
html = chapdetect.sub(self.chapter_head, html)
|
||||
if self.html_preprocess_sections < 10:
|
||||
self.log("not enough chapters, only " + str(self.html_preprocess_sections) + ", trying numeric chapters")
|
||||
chapdetect2 = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu][^>]*>){0,2}\s*.?(\d+\.?|(CHAPTER\s*([\dA-Z\-\'\"\?\.!#,]+\s*){1,10}))\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(p|/?br)>)\s*(<(/?br|p)[^>]*>\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu][^>]*>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.UNICODE)
|
||||
html = chapdetect2.sub(self.chapter_head, html)
|
||||
|
||||
if self.html_preprocess_sections < 10:
|
||||
self.log("not enough chapters, only " + str(self.html_preprocess_sections) + ", trying with uppercase words")
|
||||
chapdetect2 = re.compile(r'(?=</?(br|p))(<(/?br|p)[^>]*>)\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<chap>(<[ibu][^>]*>){0,2}\s*.?([A-Z#\-\s]+)\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(p|/?br)>)\s*(<(/?br|p)[^>]*>\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(?P<title>(<[ibu][^>]*>){0,2}(\s*[\w\'\"-]+){1,5}\s*(</[ibu]>){0,2})\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</(br|p)>))?', re.UNICODE)
|
||||
html = chapdetect2.sub(self.chapter_head, html)
|
||||
|
||||
###### Unwrap lines ######
|
||||
#
|
||||
self.log("Unwrapping Lines")
|
||||
# Some OCR sourced files have line breaks in the html using a combination of span & p tags
|
||||
# span are used for hard line breaks, p for new paragraphs. Determine which is used so
|
||||
# that lines can be un-wrapped across page boundaries
|
||||
paras_reg = re.compile('<p[^>]*>', re.IGNORECASE)
|
||||
spans_reg = re.compile('<span[^>]*>', re.IGNORECASE)
|
||||
paras = len(paras_reg.findall(html))
|
||||
spans = len(spans_reg.findall(html))
|
||||
if spans > 1:
|
||||
if float(paras) / float(spans) < 0.75:
|
||||
format = 'spanned_html'
|
||||
else:
|
||||
format = 'html'
|
||||
else:
|
||||
format = 'html'
|
||||
|
||||
# Calculate Length
|
||||
length = line_length(format, html, getattr(self.extra_opts,
|
||||
'html_unwrap_factor', 0.4))
|
||||
self.log("*** Median line length is " + str(length) + ", calculated with " + format + " format ***")
|
||||
max_length = length * 1.4
|
||||
min_max = str("(?<=.{"+str(length)+"})(?<!.{"+str(max_length)+"})")
|
||||
#
|
||||
# Unwrap em/en dashes, delete soft-hyphens
|
||||
#self.log("\n\n\n\n\n\n\n\n\n\n\n"+html+"\n\n\n\n\n\n\n\n\n\n\n\n\n")
|
||||
html = re.sub(u'\xad\s*(</span>\s*(</[iubp]>\s*<[iubp][^>]*>\s*)?<span[^>]*>|</[iubp]>\s*<[iubp][^>]*>)?\s*', '', html)
|
||||
html = re.sub(u'%s(?<=[\u2013\u2014])\s*(?=<)(</span>\s*(</[iubp]>\s*<[iubp][^>]*>\s*)?<span[^>]*>|</[iubp]>\s*<[iubp][^>]*>)?\s*(?=[[a-z\d])' % min_max, '', html)
|
||||
# Dehyphenate
|
||||
dehyphenator = Dehyphenator()
|
||||
html = dehyphenator(html,'html', length)
|
||||
|
||||
# Unwrap lines using punctation and line length
|
||||
unwrap = re.compile(r"(?<=.{%i}([a-z,;):\IA]|(?<!\&\w{4});))\s*</(span|p|div)>\s*(</(p|span|div)>)?\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" % length, re.UNICODE)
|
||||
html = unwrap.sub(' ', html)
|
||||
|
||||
# If still no sections after unwrapping mark split points on lines with no punctuation
|
||||
if self.html_preprocess_sections < 10:
|
||||
self.log("Looking for more split points based on punctuation, currently have " + str(self.html_preprocess_sections))
|
||||
chapdetect3 = re.compile(r'<(?P<styles>(p|div)[^>]*)>\s*(?P<section>(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*(<[ibu][^>]*>){0,2}\s*(<span[^>]*>)?\s*.?(?=[a-z#\-*\s]+<)([a-z#-*]+\s*){1,5}\s*\s*(</span>)?(</[ibu]>){0,2}\s*(</span>)?\s*(</[ibu]>){0,2}\s*(</span>)?\s*</(p|div)>)', re.IGNORECASE)
|
||||
html = chapdetect3.sub(self.chapter_break, html)
|
||||
# search for places where a first or second level heading is immediately followed by another
|
||||
# top level heading. demote the second heading to h3 to prevent splitting between chapter
|
||||
# headings and titles, images, etc
|
||||
doubleheading = re.compile(r'(?P<firsthead><h(1|2)[^>]*>.+?</h(1|2)>\s*(<(?!h\d)[^>]*>\s*)*)<h(1|2)(?P<secondhead>[^>]*>.+?)</h(1|2)>', re.IGNORECASE)
|
||||
html = doubleheading.sub('\g<firsthead>'+'\n<h3'+'\g<secondhead>'+'</h3>', html)
|
||||
|
||||
# put back non-breaking spaces in empty paragraphs to preserve original formatting
|
||||
html = blankreg.sub('\n'+r'\g<openline>'+u'\u00a0'+r'\g<closeline>', html)
|
||||
|
||||
return html
|
@ -28,6 +28,9 @@ class FB2Output(OutputFormatPlugin):
|
||||
])
|
||||
|
||||
def convert(self, oeb_book, output_path, input_plugin, opts, log):
|
||||
from calibre.ebooks.oeb.transforms.jacket import linearize_jacket
|
||||
linearize_jacket(oeb_book)
|
||||
|
||||
fb2mlizer = FB2MLizer(log)
|
||||
fb2_content = fb2mlizer.extract_content(oeb_book, opts)
|
||||
|
||||
|
@ -24,7 +24,7 @@ from calibre.constants import islinux, isfreebsd, iswindows
|
||||
from calibre import unicode_path
|
||||
from calibre.utils.localization import get_lang
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
from calibre.ebooks.conversion.preprocess import line_length
|
||||
from calibre.ebooks.conversion.utils import PreProcessor
|
||||
|
||||
class Link(object):
|
||||
'''
|
||||
@ -490,21 +490,8 @@ class HTMLInput(InputFormatPlugin):
|
||||
return (None, None)
|
||||
return (None, raw)
|
||||
|
||||
def preprocess_html(self, html):
|
||||
if not hasattr(self, 'log'):
|
||||
from calibre.utils.logging import default_log
|
||||
self.log = default_log
|
||||
self.log("********* Preprocessing HTML *********")
|
||||
# Detect Chapters to match the xpath in the GUI
|
||||
chapdetect = re.compile(r'(?=</?(br|p|span))(</?(br|p|span)[^>]*>)?\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)(</?(p|br|span)[^>]*>)', re.IGNORECASE)
|
||||
html = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', html)
|
||||
# Unwrap lines using punctation if the median length of all lines is less than 150
|
||||
#
|
||||
# Insert extra line feeds so the line length regex functions properly
|
||||
html = re.sub(r"</p>", "</p>\n", html)
|
||||
length = line_length('html', html, 0.4)
|
||||
self.log.debug("*** Median length is " + str(length) + " ***")
|
||||
unwrap = re.compile(r"(?<=.{%i}[a-z,;:\IA])\s*</(span|p|div)>\s*(</(p|span|div)>)?\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" % length, re.UNICODE)
|
||||
if length < 150:
|
||||
html = unwrap.sub(' ', html)
|
||||
return html
|
||||
def preprocess_html(self, options, html):
|
||||
self.options = options
|
||||
preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None))
|
||||
return preprocessor(html)
|
||||
|
||||
|
@ -6,10 +6,9 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
|
||||
from calibre.customize.conversion import InputFormatPlugin
|
||||
from calibre.ebooks.conversion.preprocess import line_length
|
||||
from calibre.ebooks.conversion.utils import PreProcessor
|
||||
|
||||
|
||||
class LITInput(InputFormatPlugin):
|
||||
|
||||
@ -54,19 +53,8 @@ class LITInput(InputFormatPlugin):
|
||||
pre.append(ne)
|
||||
|
||||
|
||||
def preprocess_html(self, html):
|
||||
self.log("********* Preprocessing HTML *********")
|
||||
# Detect Chapters to match the xpath in the GUI
|
||||
chapdetect = re.compile(r'(?=</?(br|p|span))(</?(br|p|span)[^>]*>)?\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)(</?(p|br|span)[^>]*>)', re.IGNORECASE)
|
||||
html = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', html)
|
||||
# Unwrap lines using punctation if the median length of all lines is less than 150
|
||||
#
|
||||
# Insert extra line feeds so the line length regex functions properly
|
||||
html = re.sub(r"</p>", "</p>\n", html)
|
||||
length = line_length('html', html, 0.4)
|
||||
self.log("*** Median length is " + str(length) + " ***")
|
||||
unwrap = re.compile(r"(?<=.{%i}[a-z,;:\IA])\s*</(span|p|div)>\s*(</(p|span|div)>)?\s*(?P<up2threeblanks><(p|span|div)[^>]*>\s*(<(p|span|div)[^>]*>\s*</(span|p|div)>\s*)</(span|p|div)>\s*){0,3}\s*<(span|div|p)[^>]*>\s*(<(span|div|p)[^>]*>)?\s*" % length, re.UNICODE)
|
||||
if length < 150:
|
||||
html = unwrap.sub(' ', html)
|
||||
return html
|
||||
def preprocess_html(self, options, html):
|
||||
self.options = options
|
||||
preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None))
|
||||
return preprocessor(html)
|
||||
|
||||
|
@ -12,6 +12,7 @@ from copy import deepcopy
|
||||
from lxml import etree
|
||||
|
||||
from calibre.customize.conversion import InputFormatPlugin
|
||||
from calibre.ebooks.conversion.utils import PreProcessor
|
||||
from calibre import guess_type
|
||||
|
||||
class Canvas(etree.XSLTExtension):
|
||||
@ -419,4 +420,10 @@ class LRFInput(InputFormatPlugin):
|
||||
styles.write()
|
||||
return os.path.abspath('content.opf')
|
||||
|
||||
def preprocess_html(self, options, html):
|
||||
self.options = options
|
||||
preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None))
|
||||
return preprocessor(html)
|
||||
|
||||
|
||||
|
||||
|
@ -176,6 +176,7 @@ def get_metadata(stream, extract_cover=True):
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
mi.timestamp = None
|
||||
return mi
|
||||
|
||||
def get_quick_metadata(stream):
|
||||
|
@ -3,6 +3,7 @@ __license__ = 'GPL 3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from calibre.customize.conversion import InputFormatPlugin
|
||||
|
||||
class MOBIInput(InputFormatPlugin):
|
||||
@ -37,3 +38,12 @@ class MOBIInput(InputFormatPlugin):
|
||||
include_meta_content_type=False))
|
||||
accelerators['pagebreaks'] = '//h:div[@class="mbp_pagebreak"]'
|
||||
return mr.created_opf_path
|
||||
|
||||
def preprocess_html(self, options, html):
|
||||
# search for places where a first or second level heading is immediately followed by another
|
||||
# top level heading. demote the second heading to h3 to prevent splitting between chapter
|
||||
# headings and titles, images, etc
|
||||
doubleheading = re.compile(r'(?P<firsthead><h(1|2)[^>]*>.+?</h(1|2)>\s*(<(?!h\d)[^>]*>\s*)*)<h(1|2)(?P<secondhead>[^>]*>.+?)</h(1|2)>', re.IGNORECASE)
|
||||
html = doubleheading.sub('\g<firsthead>'+'\n<h3'+'\g<secondhead>'+'</h3>', html)
|
||||
return html
|
||||
|
||||
|
@ -1574,6 +1574,7 @@ class MobiWriter(object):
|
||||
id = unicode(oeb.metadata.cover[0])
|
||||
item = oeb.manifest.ids[id]
|
||||
href = item.href
|
||||
if href in self._images:
|
||||
index = self._images[href] - 1
|
||||
exth.write(pack('>III', 0xc9, 0x0c, index))
|
||||
exth.write(pack('>III', 0xcb, 0x0c, 0))
|
||||
@ -1695,11 +1696,12 @@ class MobiWriter(object):
|
||||
header.write(pack('>I', 1))
|
||||
|
||||
# 0x1c - 0x1f : Text encoding ?
|
||||
# GR: Language encoding for NCX entries (latin_1)
|
||||
header.write(pack('>I', 0x4e4))
|
||||
# header.write(pack('>I', 650001))
|
||||
# GR: This needs to be either 0xFDE9 or 0x4E4
|
||||
header.write(pack('>I', 0xFDE9))
|
||||
|
||||
# 0x20 - 0x23 : Mimicking kindleGen
|
||||
header.write(pack('>I', 0xFFFFFFFF))
|
||||
# 0x20 - 0x23 : Language code?
|
||||
header.write(iana2mobi(str(self._oeb.metadata.language[0])))
|
||||
|
||||
# 0x24 - 0x27 : Number of TOC entries in INDX1
|
||||
header.write(pack('>I', indxt_count + 1))
|
||||
@ -1799,7 +1801,7 @@ class MobiWriter(object):
|
||||
text = text.strip()
|
||||
if not isinstance(text, unicode):
|
||||
text = text.decode('utf-8', 'replace')
|
||||
text = text.encode('cp1252','replace')
|
||||
text = text.encode('ascii','replace')
|
||||
return text
|
||||
|
||||
def _add_to_ctoc(self, ctoc_str, record_offset):
|
||||
@ -2149,26 +2151,6 @@ class MobiWriter(object):
|
||||
indxt.write(decint(self._ctoc_map[index]['titleOffset'], DECINT_FORWARD)) # vwi title offset in CNCX
|
||||
indxt.write(decint(0, DECINT_FORWARD)) # unknown byte
|
||||
|
||||
def _write_subchapter_node(self, indxt, indices, index, offset, length, count):
|
||||
# This style works without a parent chapter, mimicking what KindleGen does,
|
||||
# using a value of 0x0B for parentIndex
|
||||
# Writes an INDX1 NCXEntry of entryType 0x1F - subchapter
|
||||
if self.opts.verbose > 2:
|
||||
# *** GR: Turn this off while I'm developing my code
|
||||
#self._oeb.log.debug('Writing TOC node to IDXT:', node.title, 'href:', node.href)
|
||||
pass
|
||||
|
||||
pos = 0xc0 + indxt.tell()
|
||||
indices.write(pack('>H', pos)) # Save the offset for IDXTIndices
|
||||
name = "%04X"%count
|
||||
indxt.write(chr(len(name)) + name) # Write the name
|
||||
indxt.write(INDXT['subchapter']) # entryType [0x0F | 0xDF | 0xFF | 0x3F]
|
||||
indxt.write(decint(offset, DECINT_FORWARD)) # offset
|
||||
indxt.write(decint(length, DECINT_FORWARD)) # length
|
||||
indxt.write(decint(self._ctoc_map[index]['titleOffset'], DECINT_FORWARD)) # vwi title offset in CNCX
|
||||
indxt.write(decint(0, DECINT_FORWARD)) # unknown byte
|
||||
indxt.write(decint(0xb, DECINT_FORWARD)) # parentIndex - null
|
||||
|
||||
def _compute_offset_length(self, i, node, entries) :
|
||||
h = node.href
|
||||
if h not in self._id_offsets:
|
||||
|
@ -15,7 +15,7 @@ from calibre.customize.ui import available_input_formats
|
||||
from calibre.ebooks.metadata.opf2 import OPF
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.utils.zipfile import safe_replace, ZipFile
|
||||
from calibre.utils.zipfile import safe_replace
|
||||
from calibre.utils.config import DynamicConfig
|
||||
from calibre.utils.logging import Log
|
||||
from calibre import guess_type, prints
|
||||
@ -294,12 +294,8 @@ class EbookIterator(object):
|
||||
zf = open(self.pathtoebook, 'r+b')
|
||||
except IOError:
|
||||
return
|
||||
zipf = ZipFile(zf, mode='a')
|
||||
for name in zipf.namelist():
|
||||
if name == 'META-INF/calibre_bookmarks.txt':
|
||||
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat))
|
||||
return
|
||||
zipf.writestr('META-INF/calibre_bookmarks.txt', dat)
|
||||
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat),
|
||||
add_missing=True)
|
||||
else:
|
||||
self.config['bookmarks_'+self.pathtoebook] = dat
|
||||
|
||||
|
@ -99,7 +99,8 @@ class CoverManager(object):
|
||||
series_string = None
|
||||
if m.series and m.series_index:
|
||||
series_string = _('Book %s of %s')%(
|
||||
fmt_sidx(m.series_index[0], use_roman=True), m.series[0])
|
||||
fmt_sidx(m.series_index[0], use_roman=True),
|
||||
unicode(m.series[0]))
|
||||
|
||||
try:
|
||||
from calibre.ebooks import calibre_cover
|
||||
|
@ -138,6 +138,7 @@ class CSSFlattener(object):
|
||||
float(self.context.margin_left))
|
||||
bs.append('margin-right : %fpt'%\
|
||||
float(self.context.margin_right))
|
||||
bs.extend(['padding-left: 0pt', 'padding-right: 0pt'])
|
||||
if self.context.change_justification != 'original':
|
||||
bs.append('text-align: '+ self.context.change_justification)
|
||||
body.set('style', '; '.join(bs))
|
||||
@ -146,7 +147,6 @@ class CSSFlattener(object):
|
||||
extra_css=css)
|
||||
self.stylizers[item] = stylizer
|
||||
|
||||
|
||||
def baseline_node(self, node, stylizer, sizes, csize):
|
||||
csize = stylizer.style(node)['font-size']
|
||||
if node.text:
|
||||
@ -194,7 +194,7 @@ class CSSFlattener(object):
|
||||
value = 0.0
|
||||
cssdict[property] = "%0.5fem" % (value / fsize)
|
||||
|
||||
def flatten_node(self, node, stylizer, names, styles, psize, left=0):
|
||||
def flatten_node(self, node, stylizer, names, styles, psize, item_id, left=0):
|
||||
if not isinstance(node.tag, basestring) \
|
||||
or namespace(node.tag) != XHTML_NS:
|
||||
return
|
||||
@ -219,7 +219,10 @@ class CSSFlattener(object):
|
||||
fnums = self.context.source.fnums
|
||||
if size[0] in ('+', '-'):
|
||||
# Oh, the warcrimes
|
||||
try:
|
||||
esize = 3 + force_int(size)
|
||||
except:
|
||||
esize = 3
|
||||
if esize < 1:
|
||||
esize = 1
|
||||
if esize > 7:
|
||||
@ -286,8 +289,10 @@ class CSSFlattener(object):
|
||||
if self.lineh and 'line-height' not in cssdict:
|
||||
lineh = self.lineh / psize
|
||||
cssdict['line-height'] = "%0.5fem" % lineh
|
||||
|
||||
if (self.context.remove_paragraph_spacing or
|
||||
self.context.insert_blank_line) and tag in ('p', 'div'):
|
||||
if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle':
|
||||
for prop in ('margin', 'padding', 'border'):
|
||||
for edge in ('top', 'bottom'):
|
||||
cssdict['%s-%s'%(prop, edge)] = '0pt'
|
||||
@ -295,6 +300,7 @@ class CSSFlattener(object):
|
||||
cssdict['margin-top'] = cssdict['margin-bottom'] = '0.5em'
|
||||
if self.context.remove_paragraph_spacing:
|
||||
cssdict['text-indent'] = "%1.1fem" % self.context.remove_paragraph_spacing_indent_size
|
||||
|
||||
if cssdict:
|
||||
items = cssdict.items()
|
||||
items.sort()
|
||||
@ -313,7 +319,7 @@ class CSSFlattener(object):
|
||||
if 'style' in node.attrib:
|
||||
del node.attrib['style']
|
||||
for child in node:
|
||||
self.flatten_node(child, stylizer, names, styles, psize, left)
|
||||
self.flatten_node(child, stylizer, names, styles, psize, item_id, left)
|
||||
|
||||
def flatten_head(self, item, stylizer, href):
|
||||
html = item.data
|
||||
@ -360,7 +366,7 @@ class CSSFlattener(object):
|
||||
stylizer = self.stylizers[item]
|
||||
body = html.find(XHTML('body'))
|
||||
fsize = self.context.dest.fbase
|
||||
self.flatten_node(body, stylizer, names, styles, fsize)
|
||||
self.flatten_node(body, stylizer, names, styles, fsize, item.id)
|
||||
items = [(key, val) for (val, key) in styles.items()]
|
||||
items.sort()
|
||||
css = ''.join(".%s {\n%s;\n}\n\n" % (key, val) for key, val in items)
|
||||
|
@ -6,61 +6,95 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import textwrap
|
||||
import sys
|
||||
from xml.sax.saxutils import escape
|
||||
from itertools import repeat
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from calibre.ebooks.oeb.base import XPath, XPNSMAP
|
||||
from calibre import guess_type
|
||||
from calibre import guess_type, strftime
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
from calibre.ebooks.oeb.base import XPath, XHTML_NS, XHTML
|
||||
from calibre.library.comments import comments_to_html
|
||||
|
||||
JACKET_XPATH = '//h:meta[@name="calibre-content" and @content="jacket"]'
|
||||
|
||||
class Jacket(object):
|
||||
'''
|
||||
Book jacket manipulation. Remove first image and insert comments at start of
|
||||
book.
|
||||
'''
|
||||
|
||||
JACKET_TEMPLATE = textwrap.dedent(u'''\
|
||||
<html xmlns="%(xmlns)s">
|
||||
<head>
|
||||
<title>%(title)s</title>
|
||||
<meta name="calibre-content" content="jacket"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="calibre_rescale_100">
|
||||
<div style="text-align:center">
|
||||
<h1 class="calibre_rescale_180">%(title)s</h1>
|
||||
<h2 class="calibre_rescale_140">%(jacket)s</h2>
|
||||
<div class="calibre_rescale_100">%(series)s</div>
|
||||
<div class="calibre_rescale_100">%(rating)s</div>
|
||||
<div class="calibre_rescale_100">%(tags)s</div>
|
||||
</div>
|
||||
<div style="margin-top:2em" class="calibre_rescale_100">
|
||||
%(comments)s
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
''')
|
||||
|
||||
def remove_first_image(self):
|
||||
def remove_images(self, item, limit=1):
|
||||
path = XPath('//h:img[@src]')
|
||||
for i, item in enumerate(self.oeb.spine):
|
||||
if i > 2: break
|
||||
removed = 0
|
||||
for img in path(item.data):
|
||||
if removed >= limit:
|
||||
break
|
||||
href = item.abshref(img.get('src'))
|
||||
image = self.oeb.manifest.hrefs.get(href, None)
|
||||
if image is not None:
|
||||
self.log('Removing first image', img.get('src'))
|
||||
self.oeb.manifest.remove(image)
|
||||
img.getparent().remove(img)
|
||||
return
|
||||
removed += 1
|
||||
return removed
|
||||
|
||||
def get_rating(self, rating):
|
||||
def remove_first_image(self):
|
||||
for item in self.oeb.spine:
|
||||
removed = self.remove_images(item)
|
||||
if removed > 0:
|
||||
self.log('Removed first image')
|
||||
break
|
||||
|
||||
def insert_metadata(self, mi):
|
||||
self.log('Inserting metadata into book...')
|
||||
|
||||
try:
|
||||
tags = map(unicode, self.oeb.metadata.subject)
|
||||
except:
|
||||
tags = []
|
||||
|
||||
try:
|
||||
comments = unicode(self.oeb.metadata.description[0])
|
||||
except:
|
||||
comments = ''
|
||||
|
||||
try:
|
||||
title = unicode(self.oeb.metadata.title[0])
|
||||
except:
|
||||
title = _('Unknown')
|
||||
|
||||
root = render_jacket(mi, self.opts.output_profile,
|
||||
alt_title=title, alt_tags=tags,
|
||||
alt_comments=comments)
|
||||
id, href = self.oeb.manifest.generate('calibre_jacket', 'jacket.xhtml')
|
||||
|
||||
item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root)
|
||||
self.oeb.spine.insert(0, item, True)
|
||||
|
||||
def remove_existing_jacket(self):
|
||||
for x in self.oeb.spine[:4]:
|
||||
if XPath(JACKET_XPATH)(x.data):
|
||||
self.remove_images(x, limit=sys.maxint)
|
||||
self.oeb.manifest.remove(x)
|
||||
self.log('Removed existing jacket')
|
||||
break
|
||||
|
||||
def __call__(self, oeb, opts, metadata):
|
||||
'''
|
||||
Add metadata in jacket.xhtml if specified in opts
|
||||
If not specified, remove previous jacket instance
|
||||
'''
|
||||
self.oeb, self.opts, self.log = oeb, opts, oeb.log
|
||||
self.remove_existing_jacket()
|
||||
if opts.remove_first_image:
|
||||
self.remove_first_image()
|
||||
if opts.insert_metadata:
|
||||
self.insert_metadata(metadata)
|
||||
|
||||
# Render Jacket {{{
|
||||
|
||||
def get_rating(rating, rchar):
|
||||
ans = ''
|
||||
if rating is None:
|
||||
return
|
||||
try:
|
||||
num = float(rating)/2
|
||||
except:
|
||||
@ -69,76 +103,103 @@ class Jacket(object):
|
||||
num = min(num, 5)
|
||||
if num < 1:
|
||||
return ans
|
||||
id, href = self.oeb.manifest.generate('star', 'star.png')
|
||||
self.oeb.manifest.add(id, href, 'image/png', data=I('star.png', data=True))
|
||||
ans = 'Rating: ' + ''.join(repeat('<img style="vertical-align:text-top" alt="star" src="%s" />'%href, num))
|
||||
|
||||
ans = rchar * int(num)
|
||||
return ans
|
||||
|
||||
def insert_metadata(self, mi):
|
||||
self.log('Inserting metadata into book...')
|
||||
comments = mi.comments
|
||||
if not comments:
|
||||
|
||||
def render_jacket(mi, output_profile,
|
||||
alt_title=_('Unknown'), alt_tags=[], alt_comments=''):
|
||||
css = P('jacket/stylesheet.css', data=True).decode('utf-8')
|
||||
|
||||
try:
|
||||
comments = unicode(self.oeb.metadata.description[0])
|
||||
title_str = mi.title if mi.title else alt_title
|
||||
except:
|
||||
comments = ''
|
||||
if not comments.strip():
|
||||
comments = ''
|
||||
orig_comments = comments
|
||||
if comments:
|
||||
comments = comments_to_html(comments)
|
||||
series = '<b>Series: </b>' + escape(mi.series if mi.series else '')
|
||||
title_str = _('Unknown')
|
||||
title = '<span class="title">%s</span>' % (escape(title_str))
|
||||
|
||||
series = escape(mi.series if mi.series else '')
|
||||
if mi.series and mi.series_index is not None:
|
||||
series += escape(' [%s]'%mi.format_series_index())
|
||||
if not mi.series:
|
||||
series = ''
|
||||
tags = mi.tags
|
||||
if not tags:
|
||||
|
||||
try:
|
||||
tags = map(unicode, self.oeb.metadata.subject)
|
||||
pubdate = strftime(u'%Y', mi.pubdate.timetuple())
|
||||
except:
|
||||
tags = []
|
||||
pubdate = ''
|
||||
|
||||
rating = get_rating(mi.rating, output_profile.ratings_char)
|
||||
|
||||
tags = mi.tags if mi.tags else alt_tags
|
||||
if tags:
|
||||
tags = '<b>Tags: </b>' + self.opts.dest.tags_to_string(tags)
|
||||
tags = output_profile.tags_to_string(tags)
|
||||
else:
|
||||
tags = ''
|
||||
try:
|
||||
title = mi.title if mi.title else unicode(self.oeb.metadata.title[0])
|
||||
except:
|
||||
title = _('Unknown')
|
||||
|
||||
comments = mi.comments if mi.comments else alt_comments
|
||||
comments = comments.strip()
|
||||
orig_comments = comments
|
||||
if comments:
|
||||
comments = comments_to_html(comments)
|
||||
|
||||
def generate_html(comments):
|
||||
return self.JACKET_TEMPLATE%dict(xmlns=XPNSMAP['h'],
|
||||
title=escape(title), comments=comments,
|
||||
jacket=escape(_('Book Jacket')), series=series,
|
||||
tags=tags, rating=self.get_rating(mi.rating))
|
||||
id, href = self.oeb.manifest.generate('jacket', 'jacket.xhtml')
|
||||
from calibre.ebooks.oeb.base import RECOVER_PARSER, XPath
|
||||
args = dict(xmlns=XHTML_NS,
|
||||
title_str=title_str,
|
||||
css=css,
|
||||
title=title,
|
||||
pubdate_label=_('Published'), pubdate=pubdate,
|
||||
series_label=_('Series'), series=series,
|
||||
rating_label=_('Rating'), rating=rating,
|
||||
tags_label=_('Tags'), tags=tags,
|
||||
comments=comments,
|
||||
footer=''
|
||||
)
|
||||
|
||||
generated_html = P('jacket/template.xhtml',
|
||||
data=True).decode('utf-8').format(**args)
|
||||
|
||||
# Post-process the generated html to strip out empty header items
|
||||
soup = BeautifulSoup(generated_html)
|
||||
if not series:
|
||||
series_tag = soup.find('tr', attrs={'class':'cbj_series'})
|
||||
series_tag.extract()
|
||||
if not rating:
|
||||
rating_tag = soup.find('tr', attrs={'class':'cbj_rating'})
|
||||
rating_tag.extract()
|
||||
if not tags:
|
||||
tags_tag = soup.find('tr', attrs={'class':'cbj_tags'})
|
||||
tags_tag.extract()
|
||||
if not pubdate:
|
||||
pubdate_tag = soup.find('tr', attrs={'class':'cbj_pubdate'})
|
||||
pubdate_tag.extract()
|
||||
if output_profile.short_name != 'kindle':
|
||||
hr_tag = soup.find('hr', attrs={'class':'cbj_kindle_banner_hr'})
|
||||
hr_tag.extract()
|
||||
|
||||
return soup.renderContents(None)
|
||||
|
||||
from calibre.ebooks.oeb.base import RECOVER_PARSER
|
||||
|
||||
try:
|
||||
root = etree.fromstring(generate_html(comments), parser=RECOVER_PARSER)
|
||||
except:
|
||||
try:
|
||||
root = etree.fromstring(generate_html(escape(orig_comments)),
|
||||
parser=RECOVER_PARSER)
|
||||
jacket = XPath('//h:meta[@name="calibre-content" and @content="jacket"]')
|
||||
found = None
|
||||
for item in list(self.oeb.spine)[:4]:
|
||||
try:
|
||||
if jacket(item.data):
|
||||
found = item
|
||||
break
|
||||
except:
|
||||
continue
|
||||
if found is None:
|
||||
item = self.oeb.manifest.add(id, href, guess_type(href)[0], data=root)
|
||||
self.oeb.spine.insert(0, item, True)
|
||||
else:
|
||||
self.log('Found existing book jacket, replacing...')
|
||||
found.data = root
|
||||
root = etree.fromstring(generate_html(''),
|
||||
parser=RECOVER_PARSER)
|
||||
return root
|
||||
|
||||
# }}}
|
||||
|
||||
def linearize_jacket(oeb):
|
||||
for x in oeb.spine[:4]:
|
||||
if XPath(JACKET_XPATH)(x.data):
|
||||
for e in XPath('//h:table|//h:tr|//h:th')(x.data):
|
||||
e.tag = XHTML('div')
|
||||
for e in XPath('//h:td')(x.data):
|
||||
e.tag = XHTML('span')
|
||||
break
|
||||
|
||||
def __call__(self, oeb, opts, metadata):
|
||||
self.oeb, self.opts, self.log = oeb, opts, oeb.log
|
||||
if opts.remove_first_image:
|
||||
self.remove_first_image()
|
||||
if opts.insert_metadata:
|
||||
self.insert_metadata(metadata)
|
||||
|
@ -72,10 +72,13 @@ class RescaleImages(object):
|
||||
Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||
data = pixmap_to_data(img, format=ext)
|
||||
else:
|
||||
try:
|
||||
im = im.resize((int(new_width), int(new_height)), PILImage.ANTIALIAS)
|
||||
of = cStringIO.StringIO()
|
||||
im.convert('RGB').save(of, ext)
|
||||
data = of.getvalue()
|
||||
except:
|
||||
self.log.exception('Failed to rescale image')
|
||||
if data is not None:
|
||||
item.data = data
|
||||
item.unload_data_from_memory()
|
||||
|
@ -21,7 +21,7 @@ class Reader(FormatReader):
|
||||
self.options = options
|
||||
setattr(self.options, 'new_pdf_engine', False)
|
||||
setattr(self.options, 'no_images', False)
|
||||
setattr(self.options, 'unwrap_factor', 0.5)
|
||||
setattr(self.options, 'unwrap_factor', 0.45)
|
||||
|
||||
def extract_content(self, output_dir):
|
||||
self.log.info('Extracting PDF...')
|
||||
|
@ -22,10 +22,10 @@ class PDFInput(InputFormatPlugin):
|
||||
options = set([
|
||||
OptionRecommendation(name='no_images', recommended_value=False,
|
||||
help=_('Do not extract images from the document')),
|
||||
OptionRecommendation(name='unwrap_factor', recommended_value=0.5,
|
||||
OptionRecommendation(name='unwrap_factor', recommended_value=0.45,
|
||||
help=_('Scale used to determine the length at which a line should '
|
||||
'be unwrapped. Valid values are a decimal between 0 and 1. The '
|
||||
'default is 0.5, this is the median line length.')),
|
||||
'default is 0.45, just below the median line length.')),
|
||||
OptionRecommendation(name='new_pdf_engine', recommended_value=False,
|
||||
help=_('Use the new PDF conversion engine.'))
|
||||
])
|
||||
|
@ -7,7 +7,7 @@ import os, glob, re, textwrap
|
||||
from lxml import etree
|
||||
|
||||
from calibre.customize.conversion import InputFormatPlugin
|
||||
from calibre.ebooks.conversion.preprocess import line_length
|
||||
from calibre.ebooks.conversion.utils import PreProcessor
|
||||
|
||||
class InlineClass(etree.XSLTExtension):
|
||||
|
||||
@ -232,16 +232,8 @@ class RTFInput(InputFormatPlugin):
|
||||
res = transform.tostring(result)
|
||||
res = res[:100].replace('xmlns:html', 'xmlns') + res[100:]
|
||||
if self.options.preprocess_html:
|
||||
self.log("********* Preprocessing HTML *********")
|
||||
# Detect Chapters to match the xpath in the GUI
|
||||
chapdetect = re.compile(r'<p[^>]*>\s*<span[^>]*>\s*(?P<chap>(<(i|b)><(i|b)>|<(i|b)>)?(.?Chapter|Epilogue|Prologue|Book|Part|Dedication)\s*([\d\w-]+(\s\w+)?)?(</(i|b)></(i|b)>|</(i|b)>)?)\s*</span>\s*</p>', re.IGNORECASE)
|
||||
res = chapdetect.sub('<h2>'+'\g<chap>'+'</h2>\n', res)
|
||||
# Unwrap lines using punctation if the median length of all lines is less than 150
|
||||
length = line_length('html', res, 0.4)
|
||||
self.log("*** Median length is " + str(length) + " ***")
|
||||
unwrap = re.compile(r"(?<=.{%i}[a-z,;:\IA])\s*</span>\s*(</p>)?\s*(?P<up2threeblanks><p[^>]*>\s*(<span[^>]*>\s*</span>\s*)</p>\s*){0,3}\s*<p[^>]*>\s*(<span[^>]*>)?\s*" % length, re.UNICODE)
|
||||
if length < 150:
|
||||
res = unwrap.sub(' ', res)
|
||||
preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None))
|
||||
res = preprocessor(res)
|
||||
f.write(res)
|
||||
self.write_inline_css(inline_class)
|
||||
stream.seek(0)
|
||||
|
@ -1,7 +1,7 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
""" The GUI """
|
||||
import os, sys
|
||||
import os, sys, Queue
|
||||
from threading import RLock
|
||||
|
||||
from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
|
||||
@ -39,7 +39,7 @@ gprefs.defaults['action-layout-context-menu'] = (
|
||||
'Edit Metadata', 'Send To Device', 'Save To Disk',
|
||||
'Connect Share', 'Copy To Library', None,
|
||||
'Convert Books', 'View', 'Open Folder', 'Show Book Details',
|
||||
'Similar Books', None, 'Remove Books',
|
||||
'Similar Books', 'Tweak ePub', None, 'Remove Books',
|
||||
)
|
||||
|
||||
gprefs.defaults['action-layout-context-menu-device'] = (
|
||||
@ -50,6 +50,7 @@ gprefs.defaults['action-layout-context-menu-device'] = (
|
||||
gprefs.defaults['show_splash_screen'] = True
|
||||
gprefs.defaults['toolbar_icon_size'] = 'medium'
|
||||
gprefs.defaults['toolbar_text'] = 'auto'
|
||||
gprefs.defaults['show_child_bar'] = False
|
||||
|
||||
# }}}
|
||||
|
||||
@ -295,6 +296,34 @@ class Dispatcher(QObject):
|
||||
def dispatch(self, args, kwargs):
|
||||
self.func(*args, **kwargs)
|
||||
|
||||
class FunctionDispatcher(QObject):
|
||||
'''
|
||||
Convenience class to use Qt signals with arbitrary python functions.
|
||||
By default, ensures that a function call always happens in the
|
||||
thread this Dispatcher was created in.
|
||||
'''
|
||||
dispatch_signal = pyqtSignal(object, object, object)
|
||||
|
||||
def __init__(self, func, queued=True, parent=None):
|
||||
QObject.__init__(self, parent)
|
||||
self.func = func
|
||||
typ = Qt.QueuedConnection
|
||||
if not queued:
|
||||
typ = Qt.AutoConnection if queued is None else Qt.DirectConnection
|
||||
self.dispatch_signal.connect(self.dispatch, type=typ)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
q = Queue.Queue()
|
||||
self.dispatch_signal.emit(q, args, kwargs)
|
||||
return q.get()
|
||||
|
||||
def dispatch(self, q, args, kwargs):
|
||||
try:
|
||||
res = self.func(*args, **kwargs)
|
||||
except:
|
||||
res = None
|
||||
q.put(res)
|
||||
|
||||
class GetMetadata(QObject):
|
||||
'''
|
||||
Convenience class to ensure that metadata readers are used only in the
|
||||
@ -574,18 +603,6 @@ class Application(QApplication):
|
||||
self._file_open_paths = []
|
||||
self._file_open_lock = RLock()
|
||||
|
||||
if islinux:
|
||||
self.setStyleSheet('''
|
||||
QToolTip {
|
||||
border: 2px solid black;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
opacity: 200;
|
||||
background-color: #e1e1ff;
|
||||
color: black;
|
||||
}
|
||||
''')
|
||||
|
||||
def _send_file_open_events(self):
|
||||
with self._file_open_lock:
|
||||
if self._file_open_paths:
|
||||
|
@ -71,6 +71,12 @@ class InterfaceAction(QObject):
|
||||
all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu',
|
||||
'context-menu-device'])
|
||||
|
||||
#: Type of action
|
||||
#: 'current' means acts on the current view
|
||||
#: 'global' means an action that does not act on the current view, but rather
|
||||
#: on calibre as a whole
|
||||
action_type = 'global'
|
||||
|
||||
def __init__(self, parent, site_customization):
|
||||
QObject.__init__(self, parent)
|
||||
self.setObjectName(self.name)
|
||||
|
@ -25,6 +25,7 @@ class AddAction(InterfaceAction):
|
||||
action_spec = (_('Add books'), 'add_book.png',
|
||||
_('Add books to the calibre library/device from files on your computer')
|
||||
, _('A'))
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book)
|
||||
|
@ -13,6 +13,7 @@ class AddToLibraryAction(InterfaceAction):
|
||||
action_spec = (_('Add books to library'), 'add_book.png',
|
||||
_('Add books to your calibre library from the connected device'), None)
|
||||
dont_add_to = frozenset(['toolbar', 'context-menu'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.add_books_to_library)
|
||||
|
@ -18,6 +18,7 @@ class FetchAnnotationsAction(InterfaceAction):
|
||||
|
||||
name = 'Fetch Annotations'
|
||||
action_spec = (_('Fetch annotations (experimental)'), None, None, None)
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
pass
|
||||
|
@ -21,6 +21,7 @@ class ConvertAction(InterfaceAction):
|
||||
name = 'Convert Books'
|
||||
action_spec = (_('Convert books'), 'convert.png', None, _('C'))
|
||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
cm = QMenu()
|
||||
|
@ -80,6 +80,7 @@ class CopyToLibraryAction(InterfaceAction):
|
||||
_('Copy selected books to the specified library'), None)
|
||||
popup_type = QToolButton.InstantPopup
|
||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.menu = QMenu(self.gui)
|
||||
|
@ -16,6 +16,7 @@ class DeleteAction(InterfaceAction):
|
||||
|
||||
name = 'Remove Books'
|
||||
action_spec = (_('Remove books'), 'trash.png', None, _('Del'))
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.delete_books)
|
||||
|
@ -13,6 +13,7 @@ class EditCollectionsAction(InterfaceAction):
|
||||
action_spec = (_('Manage collections'), None,
|
||||
_('Manage the collections on this device'), None)
|
||||
dont_add_to = frozenset(['toolbar', 'context-menu'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.edit_collections)
|
||||
|
@ -22,6 +22,7 @@ class EditMetadataAction(InterfaceAction):
|
||||
|
||||
name = 'Edit Metadata'
|
||||
action_spec = (_('Edit metadata'), 'edit_input.png', None, _('E'))
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.create_action(spec=(_('Merge book records'), 'merge_books.png',
|
||||
@ -172,6 +173,8 @@ class EditMetadataAction(InterfaceAction):
|
||||
'''
|
||||
rows = [r.row() for r in \
|
||||
self.gui.library_view.selectionModel().selectedRows()]
|
||||
m = self.gui.library_view.model()
|
||||
ids = [m.id(r) for r in rows]
|
||||
if not rows or len(rows) == 0:
|
||||
d = error_dialog(self.gui, _('Cannot edit metadata'),
|
||||
_('No books selected'))
|
||||
@ -190,6 +193,7 @@ class EditMetadataAction(InterfaceAction):
|
||||
self.gui.tags_view.recount()
|
||||
if self.gui.cover_flow:
|
||||
self.gui.cover_flow.dataChanged()
|
||||
self.gui.library_view.select_rows(ids)
|
||||
|
||||
# Merge books {{{
|
||||
def merge_books(self, safe_merge=False):
|
||||
|
@ -14,6 +14,7 @@ class OpenFolderAction(InterfaceAction):
|
||||
action_spec = (_('Open containing folder'), 'document_open.png', None,
|
||||
_('O'))
|
||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.gui.iactions['View'].view_folder)
|
||||
|
@ -38,6 +38,7 @@ class SaveToDiskAction(InterfaceAction):
|
||||
|
||||
name = "Save To Disk"
|
||||
action_spec = (_('Save to disk'), 'save.png', None, _('S'))
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.save_to_disk)
|
||||
|
@ -16,6 +16,7 @@ class ShowBookDetailsAction(InterfaceAction):
|
||||
action_spec = (_('Show book details'), 'dialog_information.png', None,
|
||||
_('I'))
|
||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.show_book_info)
|
||||
|
@ -16,6 +16,7 @@ class SimilarBooksAction(InterfaceAction):
|
||||
name = 'Similar Books'
|
||||
action_spec = (_('Similar books...'), None, None, None)
|
||||
popup_type = QToolButton.InstantPopup
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
m = QMenu(self.gui)
|
||||
|
55
src/calibre/gui2/actions/tweak_epub.py
Executable file
@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2.dialogs.tweak_epub import TweakEpub
|
||||
|
||||
class TweakEpubAction(InterfaceAction):
|
||||
|
||||
name = 'Tweak ePub'
|
||||
action_spec = (_('Tweak ePub'), 'trim.png',
|
||||
_('Make small changes to ePub format books'),
|
||||
_('T'))
|
||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.edit_epub_in_situ)
|
||||
|
||||
def edit_epub_in_situ(self, *args):
|
||||
row = self.gui.library_view.currentIndex()
|
||||
if not row.isValid():
|
||||
return error_dialog(self.gui, _('Cannot tweak ePub'),
|
||||
_('No book selected'), show=True)
|
||||
|
||||
# Confirm 'EPUB' in formats
|
||||
book_id = self.gui.library_view.model().id(row)
|
||||
try:
|
||||
path_to_epub = self.gui.library_view.model().db.format_abspath(
|
||||
book_id, 'EPUB', index_is_id=True)
|
||||
except:
|
||||
path_to_epub = None
|
||||
|
||||
if not path_to_epub:
|
||||
return error_dialog(self.gui, _('Cannot tweak ePub'),
|
||||
_('No ePub available. First convert the book to ePub.'),
|
||||
show=True)
|
||||
|
||||
# Launch modal dialog waiting for user to tweak or cancel
|
||||
dlg = TweakEpub(self.gui, path_to_epub)
|
||||
if dlg.exec_() == dlg.Accepted:
|
||||
self.update_db(book_id, dlg._output)
|
||||
dlg.cleanup()
|
||||
|
||||
def update_db(self, book_id, rebuilt):
|
||||
'''
|
||||
Update the calibre db with the tweaked epub
|
||||
'''
|
||||
self.gui.library_view.model().db.add_format(book_id, 'EPUB',
|
||||
open(rebuilt, 'rb'), index_is_id=True)
|
||||
|
@ -22,6 +22,7 @@ class ViewAction(InterfaceAction):
|
||||
|
||||
name = 'View'
|
||||
action_spec = (_('View'), 'view.png', None, _('V'))
|
||||
action_type = 'current'
|
||||
|
||||
def genesis(self):
|
||||
self.persistent_files = []
|
||||
|
@ -22,7 +22,7 @@ class LookAndFeelWidget(Widget, Ui_Form):
|
||||
Widget.__init__(self, parent,
|
||||
['change_justification', 'extra_css', 'base_font_size',
|
||||
'font_size_mapping', 'line_height',
|
||||
'linearize_tables',
|
||||
'linearize_tables', 'smarten_punctuation',
|
||||
'disable_font_rescaling', 'insert_blank_line',
|
||||
'remove_paragraph_spacing', 'remove_paragraph_spacing_indent_size','input_encoding',
|
||||
'asciiize', 'keep_ligatures']
|
||||
|
@ -178,7 +178,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="4">
|
||||
<item row="10" column="0" colspan="4">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Extra &CSS</string>
|
||||
@ -214,6 +214,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="opt_smarten_punctuation">
|
||||
<property name="text">
|
||||
<string>Smarten &punctuation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
|
@ -46,7 +46,7 @@
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.500000000000000</double>
|
||||
<double>0.450000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|