merge with John's branch
104
Changelog.yaml
@ -19,6 +19,106 @@
|
||||
# new recipes:
|
||||
# - title:
|
||||
|
||||
- version: 0.8.14
|
||||
date: 2011-08-12
|
||||
|
||||
new features:
|
||||
- title: "Make the keyboard shortcuts used by the main calibre interface user customizable, via Preferences->Advanced->Keyboard"
|
||||
type: major
|
||||
|
||||
- title: "When switching libraries, if the library no longer exists, give the user a chance to specify a new location for the library, in case it was moved, before forgetting it."
|
||||
tickets: [822018]
|
||||
|
||||
- title: "Template language: Add strcat and strlen builtin functions."
|
||||
tickets: [821935]
|
||||
|
||||
bug fixes:
|
||||
- title: "The various options to control how automerging works when adding books now also apply when copying a book from one library to another."
|
||||
tickets: [822033]
|
||||
|
||||
- title: "Ebook viewer: Respond to key presses even when the book display area does not have keyboard focus"
|
||||
|
||||
- title: "Allow integer and float column values to go to -999999. -1000000 is the value of 'undefined'."
|
||||
tickets: [821941]
|
||||
|
||||
- title: "Fix in calibre browser not working for the Open books store in Get Books."
|
||||
tickets: [822359]
|
||||
|
||||
- title: "Fix regression in 0.8.13 that caused incorrect title/author for downloaded news if you turned off reading metadata from file contents in Preferences->Adding books"
|
||||
|
||||
- title: "Save to disk: When saving to a single directory, handle the case of the save to disk template containing path separators inside template expression correctly."
|
||||
tickets: [821912]
|
||||
|
||||
- title: "Get Books: Always read metadata from the file contents, ignoring the setting in Preferences->Adding books"
|
||||
|
||||
- title: "Fix merge_metadata to not overwrite non-text fields ('bool', 'int', 'float', 'rating', 'datetime') that have a value of zero/false instead of None."
|
||||
tickets: [821665]
|
||||
|
||||
improved recipes:
|
||||
- The Independent
|
||||
|
||||
new recipes:
|
||||
- title: "Novinite"
|
||||
author: Martin Tsanchev
|
||||
|
||||
- title: "Blog Escrevinhador"
|
||||
author: Diniz Bortolotto
|
||||
|
||||
|
||||
|
||||
- version: 0.8.13
|
||||
date: 2011-08-05
|
||||
|
||||
new features:
|
||||
- title: "Add a new action 'Pick Random Book' that can be added to the toolbar via Preferences->Toolbars."
|
||||
tickets: [818315]
|
||||
|
||||
- title: "Driver for Droid X2"
|
||||
tickets: [821053]
|
||||
|
||||
- title: "PDF metadata: Support reading/writing of tags from the Keywords field in PDF files."
|
||||
|
||||
- title: "MOBI Input: Speedup reading of HUFF/CDIC compressed files"
|
||||
|
||||
- title: "MOBI Output: Add a command line option --extract-to that uses the inspect MOBI tool to extract the created MOBI file to the specified directory"
|
||||
|
||||
- title: "Template language: Add a few new functions to manipulate lists (list_difference, list_intersection, list_sort)"
|
||||
|
||||
- title: "Make the Manage Tags/Publishers/etc. dialog show a column with counts for each item, to easily sort by number of items"
|
||||
|
||||
- title: "MOBI Output: Generate navpoints for items at every level in the TOC, not just the deepest level"
|
||||
|
||||
bug fixes:
|
||||
- title: "MOBI Output: Remove option to choose masthead font as the font selection control causes crashes on some windows systems"
|
||||
|
||||
- title: "MOBI Output: Fix bug that caused paragraphs that had only a non breaking space as text before the first child element to be removed."
|
||||
tickets: [819058]
|
||||
|
||||
- title: "Display undefined dates properly in the Book details panel."
|
||||
tickets: [819222]
|
||||
|
||||
- title: "Fix regression that broke deleting of books from first generation Kobos with un-upgraded firmware"
|
||||
tickets: [818704]
|
||||
|
||||
- title: "Get books: Fix Gutenberg store and improvements to chitanka.info and e-knigni.net"
|
||||
|
||||
- title: "News download: Support https proxies"
|
||||
|
||||
- title: "Check library did not know about original_* files"
|
||||
|
||||
- title: "Fix crash caused by having very large numbers of authors > 100 for a book"
|
||||
|
||||
improved recipes:
|
||||
- Nikkei News
|
||||
|
||||
new recipes:
|
||||
- title: Carta Capital
|
||||
author: Pablo Aldama
|
||||
|
||||
- title: El Tiempo, El Colombiano and Portafolio Colombia
|
||||
author: Cavalencia
|
||||
|
||||
|
||||
- version: 0.8.12
|
||||
date: 2011-07-29
|
||||
|
||||
@ -198,8 +298,8 @@
|
||||
- title: Techcrunch and Pecat
|
||||
author: Darko Miletic
|
||||
|
||||
- title: Vio Mundo, IDG Now and Tojolaco
|
||||
author: Diniz Bortoletto
|
||||
- title: "Vio Mundo, IDG Now! and Tojolaco"
|
||||
author: Diniz Bortolotto
|
||||
|
||||
- title: Geek and Poke, Automatiseringgids IT
|
||||
author: DrMerry
|
||||
|
912
imgsrc/keyboard-prefs.svg
Normal file
@ -0,0 +1,912 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
|
||||
<svg
|
||||
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"
|
||||
version="1.0"
|
||||
id="Livello_1"
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 144 94"
|
||||
overflow="visible"
|
||||
enable-background="new 0 0 144 94"
|
||||
xml:space="preserve"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.45+devel"
|
||||
sodipodi:docname="preferences-desktop-keyboard.svgz"
|
||||
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
|
||||
style="overflow:visible"><metadata
|
||||
id="metadata224"><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><defs
|
||||
id="defs222"><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_35_"
|
||||
id="linearGradient2719"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
x1="72.000504"
|
||||
y1="83.799797"
|
||||
x2="72.000504"
|
||||
y2="5.8003001" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#rect3941_1_"
|
||||
id="linearGradient2721"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.474754,0,0,-0.465075,-255.92554,-542.49842)"
|
||||
x1="780.77576"
|
||||
y1="-1248.1824"
|
||||
x2="780.81049"
|
||||
y2="-1195.5962" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#rect3941_1_"
|
||||
id="linearGradient2723"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.474754,0,0,-0.465075,-263.1733,-542.49842)"
|
||||
x1="708.36438"
|
||||
y1="-1248.1824"
|
||||
x2="708.39648"
|
||||
y2="-1195.5962" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#rect3941_1_"
|
||||
id="linearGradient2725"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.474754,0,0,-0.465075,-270.42218,-542.49842)"
|
||||
x1="635.95538"
|
||||
y1="-1248.1824"
|
||||
x2="635.9834"
|
||||
y2="-1195.5962" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#rect3941_1_"
|
||||
id="linearGradient2727"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.474754,0,0,-0.465075,-253.92268,-535.12325)"
|
||||
x1="790.77502"
|
||||
y1="-1324.245"
|
||||
x2="790.81049"
|
||||
y2="-1271.6509" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#rect3941_1_"
|
||||
id="linearGradient2729"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.474754,0,0,-0.465075,-261.17157,-535.12325)"
|
||||
x1="718.36609"
|
||||
y1="-1324.245"
|
||||
x2="718.39838"
|
||||
y2="-1271.6509" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#rect3941_1_"
|
||||
id="linearGradient2731"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.474754,0,0,-0.465075,-268.41933,-535.12325)"
|
||||
x1="645.95471"
|
||||
y1="-1324.245"
|
||||
x2="645.9834"
|
||||
y2="-1271.6509" /><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.36659993"
|
||||
width="1.7331999"
|
||||
y="-0.17839379"
|
||||
height="1.3567876"
|
||||
id="filter3416"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.51430916"
|
||||
id="feGaussianBlur3418" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.36972603"
|
||||
width="1.7394521"
|
||||
y="-0.17766281"
|
||||
height="1.3553256"
|
||||
id="filter3424"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.51984026"
|
||||
id="feGaussianBlur3426" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.22179123"
|
||||
width="1.4435825"
|
||||
y="-0.10660794"
|
||||
height="1.2132159"
|
||||
id="filter3444"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.31193415"
|
||||
id="feGaussianBlur3446" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.21995996"
|
||||
width="1.4399199"
|
||||
y="-0.10703628"
|
||||
height="1.2140726"
|
||||
id="filter3448"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.30858549"
|
||||
id="feGaussianBlur3450" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.22183562"
|
||||
width="1.4436712"
|
||||
y="-0.10659768"
|
||||
height="1.2131954"
|
||||
id="filter3452"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.31190415"
|
||||
id="feGaussianBlur3454" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.21995996"
|
||||
width="1.4399199"
|
||||
y="-0.10703628"
|
||||
height="1.2140726"
|
||||
id="filter3456"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.30858549"
|
||||
id="feGaussianBlur3458" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.22179123"
|
||||
width="1.4435825"
|
||||
y="-0.10660794"
|
||||
height="1.2132159"
|
||||
id="filter3460"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.31193415"
|
||||
id="feGaussianBlur3462" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.21991603"
|
||||
width="1.4398321"
|
||||
y="-0.10704668"
|
||||
height="1.2140934"
|
||||
id="filter3464"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.30861549"
|
||||
id="feGaussianBlur3466" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.22179123"
|
||||
width="1.4435825"
|
||||
y="-0.10660794"
|
||||
height="1.2132159"
|
||||
id="filter3468"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.31193415"
|
||||
id="feGaussianBlur3470" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.21995996"
|
||||
width="1.4399199"
|
||||
y="-0.10703628"
|
||||
height="1.2140726"
|
||||
id="filter3472"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.30858549"
|
||||
id="feGaussianBlur3474" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.22183562"
|
||||
width="1.4436712"
|
||||
y="-0.10659768"
|
||||
height="1.2131954"
|
||||
id="filter3476"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.31190415"
|
||||
id="feGaussianBlur3478" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.21995996"
|
||||
width="1.4399199"
|
||||
y="-0.10703628"
|
||||
height="1.2140726"
|
||||
id="filter3484"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.30858549"
|
||||
id="feGaussianBlur3486" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.02891983"
|
||||
width="1.0578397"
|
||||
y="-0.14107949"
|
||||
height="1.282159"
|
||||
id="filter3492"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.33591005"
|
||||
id="feGaussianBlur3494" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.02891983"
|
||||
width="1.0578397"
|
||||
y="-0.14107949"
|
||||
height="1.282159"
|
||||
id="filter3496"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.33591005"
|
||||
id="feGaussianBlur3498" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.028919654"
|
||||
width="1.0578393"
|
||||
y="-0.14108369"
|
||||
height="1.2821674"
|
||||
id="filter3500"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.33592005"
|
||||
id="feGaussianBlur3502" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.02891983"
|
||||
width="1.0578397"
|
||||
y="-0.14107949"
|
||||
height="1.282159"
|
||||
id="filter3504"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.33591005"
|
||||
id="feGaussianBlur3506" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.02891983"
|
||||
width="1.0578397"
|
||||
y="-0.14107949"
|
||||
height="1.282159"
|
||||
id="filter3508"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.33591005"
|
||||
id="feGaussianBlur3510" /></filter><filter
|
||||
inkscape:collect="always"
|
||||
x="-0.02891983"
|
||||
width="1.0578397"
|
||||
y="-0.14107949"
|
||||
height="1.282159"
|
||||
id="filter3512"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.33591005"
|
||||
id="feGaussianBlur3514" /></filter><linearGradient
|
||||
id="XMLID_31_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="69.333504"
|
||||
y1="17.6504"
|
||||
x2="69.333504"
|
||||
y2="9.7958002"
|
||||
xlink:href="#XMLID_32_">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop169" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#DDDDDD"
|
||||
id="stop171" />
|
||||
</linearGradient><linearGradient
|
||||
id="XMLID_32_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="106.334"
|
||||
y1="17.6504"
|
||||
x2="106.334"
|
||||
y2="9.7958002">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop186" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#DDDDDD"
|
||||
id="stop188" />
|
||||
</linearGradient><linearGradient
|
||||
id="XMLID_30_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="31.742201"
|
||||
y1="17.6504"
|
||||
x2="31.742201"
|
||||
y2="9.7958002">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop152" /><stop
|
||||
id="stop3366"
|
||||
style="stop-color:#eaeaea;stop-opacity:1;"
|
||||
offset="0.68235296" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#c8c8c8;stop-opacity:1;"
|
||||
id="stop154" />
|
||||
</linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient2945"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="69.333504"
|
||||
y1="17.6504"
|
||||
x2="69.333504"
|
||||
y2="9.7958002" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient2947"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="106.334"
|
||||
y1="17.6504"
|
||||
x2="106.334"
|
||||
y2="9.7958002" /><linearGradient
|
||||
xlink:href="#XMLID_30_"
|
||||
id="XMLID_28_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="38.033699"
|
||||
y1="55.649399"
|
||||
x2="38.033699"
|
||||
y2="47.795502"
|
||||
spreadMethod="pad">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop118" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#DDDDDD"
|
||||
id="stop120" />
|
||||
</linearGradient><linearGradient
|
||||
id="XMLID_29_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="75.333"
|
||||
y1="55.649399"
|
||||
x2="75.333"
|
||||
y2="47.795502">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop135" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#DDDDDD"
|
||||
id="stop137" />
|
||||
</linearGradient><linearGradient
|
||||
id="XMLID_33_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="112.334"
|
||||
y1="55.649399"
|
||||
x2="112.334"
|
||||
y2="47.795502">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#FFFFFF"
|
||||
id="stop203" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#DDDDDD"
|
||||
id="stop205" />
|
||||
</linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient2979"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
spreadMethod="pad"
|
||||
x1="38.033699"
|
||||
y1="55.649399"
|
||||
x2="38.033699"
|
||||
y2="47.795502" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient2981"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="75.333"
|
||||
y1="55.649399"
|
||||
x2="75.333"
|
||||
y2="47.795502" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient2983"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="112.334"
|
||||
y1="55.649399"
|
||||
x2="112.334"
|
||||
y2="47.795502" /><filter
|
||||
inkscape:collect="always"
|
||||
id="filter3372"><feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.3179705"
|
||||
id="feGaussianBlur3374" /></filter><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient3378"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="106.334"
|
||||
y1="17.6504"
|
||||
x2="106.334"
|
||||
y2="9.7958002" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#XMLID_30_"
|
||||
id="linearGradient3380"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="31.742201"
|
||||
y1="17.6504"
|
||||
x2="31.742201"
|
||||
y2="9.7958002" /></defs><sodipodi:namedview
|
||||
inkscape:window-height="696"
|
||||
inkscape:window-width="998"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
guidetolerance="10.0"
|
||||
gridtolerance="10.0"
|
||||
objecttolerance="10.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base"
|
||||
height="128px"
|
||||
width="128px"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="65.761733"
|
||||
inkscape:cy="68.182683"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="0"
|
||||
inkscape:current-layer="g2620" />
|
||||
<filter
|
||||
id="AI_Sfocatura_2">
|
||||
<feGaussianBlur
|
||||
stdDeviation="2"
|
||||
id="feGaussianBlur4" />
|
||||
</filter>
|
||||
<path
|
||||
display="none"
|
||||
d="M 89.758,1.8 C 88.983,1.8 88.229,1.945 87.501,2.17 C 86.774,1.945 86.022,1.8 85.248,1.8 L 52.752,1.8 C 51.978,1.8 51.226,1.945 50.499,2.17 C 49.772,1.944 49.02,1.8 48.245,1.8 L 15.755,1.8 C 11.479,1.8 8,5.275 8,9.546 L 8,42.054 C 8,45.72 10.57,48.783 14,49.582 C 14,53.16 14,80.057 14,80.057 C 14,84.327 17.478,87.8 21.752,87.8 L 54.248,87.8 C 55.022,87.8 55.774,87.655 56.5,87.431 C 57.227,87.656 57.979,87.8 58.754,87.8 L 91.241,87.8 C 92.017,87.8 92.77,87.655 93.498,87.431 C 94.225,87.655 94.977,87.8 95.751,87.8 L 128.241,87.8 C 132.518,87.8 135.999,84.326 135.999,80.057 L 135.999,47.546 C 135.999,43.881 133.43,40.818 129.999,40.019 C 129.999,36.442 129.999,9.546 129.999,9.546 C 129.999,5.275 126.521,1.8 122.247,1.8 L 89.758,1.8 L 89.758,1.8 z"
|
||||
id="path6"
|
||||
style="fill:#ff00bf;display:none" />
|
||||
|
||||
<linearGradient
|
||||
id="XMLID_35_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="72.000504"
|
||||
y1="83.799797"
|
||||
x2="72.000504"
|
||||
y2="5.8003001">
|
||||
<stop
|
||||
offset="0.0059"
|
||||
style="stop-color:#888888"
|
||||
id="stop13" />
|
||||
<stop
|
||||
offset="0.5"
|
||||
style="stop-color:#555555"
|
||||
id="stop15" />
|
||||
<stop
|
||||
offset="0.54"
|
||||
style="stop-color:#888888"
|
||||
id="stop17" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#555555"
|
||||
id="stop19" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient
|
||||
id="rect3785_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="780.81049"
|
||||
y1="-1240.9404"
|
||||
x2="780.81049"
|
||||
y2="-1195.5962"
|
||||
gradientTransform="matrix(0.422,0,0,-0.4134,-223.4874,-472.1986)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#BEBEBE"
|
||||
id="stop24" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#EDEDED"
|
||||
id="stop26" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
<linearGradient
|
||||
id="rect3791_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="708.39648"
|
||||
y1="-1240.9404"
|
||||
x2="708.39648"
|
||||
y2="-1195.5962"
|
||||
gradientTransform="matrix(0.422,0,0,-0.4134,-229.9298,-472.1986)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#BEBEBE"
|
||||
id="stop38" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#EDEDED"
|
||||
id="stop40" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
<linearGradient
|
||||
id="rect3797_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="635.9834"
|
||||
y1="-1240.9404"
|
||||
x2="635.9834"
|
||||
y2="-1195.5962"
|
||||
gradientTransform="matrix(0.422,0,0,-0.4134,-236.3732,-472.1986)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#BEBEBE"
|
||||
id="stop52" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#EDEDED"
|
||||
id="stop54" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
<linearGradient
|
||||
id="rect3929_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="790.81049"
|
||||
y1="-1316.9951"
|
||||
x2="790.81049"
|
||||
y2="-1271.6509"
|
||||
gradientTransform="matrix(0.422,0,0,-0.4134,-221.7071,-465.6429)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#BEBEBE"
|
||||
id="stop66" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#EDEDED"
|
||||
id="stop68" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
<linearGradient
|
||||
id="rect3935_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="718.39838"
|
||||
y1="-1316.9951"
|
||||
x2="718.39838"
|
||||
y2="-1271.6509"
|
||||
gradientTransform="matrix(0.422,0,0,-0.4134,-228.1505,-465.6429)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#BEBEBE"
|
||||
id="stop80" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#EDEDED"
|
||||
id="stop82" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
<linearGradient
|
||||
id="rect3941_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="645.9834"
|
||||
y1="-1316.9951"
|
||||
x2="645.9834"
|
||||
y2="-1271.6509"
|
||||
gradientTransform="matrix(0.422,0,0,-0.4134,-234.5929,-465.6429)">
|
||||
<stop
|
||||
offset="0"
|
||||
style="stop-color:#e4e4e4;stop-opacity:1;"
|
||||
id="stop94" /><stop
|
||||
id="stop3516"
|
||||
style="stop-color:#bebebe;stop-opacity:1;"
|
||||
offset="0.18012393" /><stop
|
||||
offset="0.61417598"
|
||||
style="stop-color:#cdcdcd;stop-opacity:1;"
|
||||
id="stop3376" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#b3b3b3;stop-opacity:1;"
|
||||
id="stop96" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<g
|
||||
id="g8"
|
||||
style="opacity:0.8;filter:url(#AI_Sfocatura_2)"
|
||||
transform="matrix(1.1250094,0,0,1.125,-9.0001128,-12.4)">
|
||||
<path
|
||||
d="M 128.242,45.8 L 125.547,45.8 C 125.826,45.276 126,44.688 126,44.054 L 126,11.546 C 126,9.481 124.316,7.8 122.248,7.8 L 89.758,7.8 C 88.907,7.8 88.131,8.093 87.501,8.57 C 86.872,8.093 86.097,7.8 85.248,7.8 L 52.752,7.8 C 51.903,7.8 51.128,8.093 50.499,8.57 C 49.869,8.094 49.094,7.8 48.245,7.8 L 15.755,7.8 C 13.685,7.8 12,9.48 12,11.546 L 12,44.054 C 12,46.119 13.685,47.8 15.755,47.8 L 18.453,47.8 C 18.173,48.323 18,48.912 18,49.546 L 18,82.057 C 18,84.121 19.683,85.8 21.752,85.8 L 54.248,85.8 C 55.097,85.8 55.872,85.507 56.5,85.031 C 57.13,85.507 57.905,85.8 58.754,85.8 L 91.241,85.8 C 92.092,85.8 92.868,85.507 93.498,85.03 C 94.127,85.507 94.902,85.8 95.751,85.8 L 128.241,85.8 C 130.313,85.8 131.999,84.121 131.999,82.057 L 131.999,49.546 C 132,47.48 130.314,45.8 128.242,45.8 z"
|
||||
id="path10"
|
||||
style="opacity:0.8" />
|
||||
</g><g
|
||||
id="g2620"
|
||||
transform="translate(-4.5,0)"><path
|
||||
style="fill:url(#linearGradient2721);fill-opacity:1"
|
||||
d="M 96.478481,-2.5 L 133.03004,-2.5 C 134.11905,-2.5 135.00105,-1.621375 135.00105,-0.53575 L 135.00105,36.03575 C 135.00105,37.121374 134.11905,38 133.03004,38 L 96.478481,38 C 95.382722,38 94.500715,37.121374 94.500715,36.03575 L 94.500715,-0.53575 C 94.500715,-1.621375 95.382722,-2.5 96.478481,-2.5 z"
|
||||
id="rect3785" /><g
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
nodetypes="cccsssssscccc"
|
||||
id="path3787">
|
||||
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-240.4975,-567.9111)"
|
||||
r="40.036301"
|
||||
cy="-971.75677"
|
||||
cx="527.62299"
|
||||
id="XMLID_17_">
|
||||
<stop
|
||||
id="stop31"
|
||||
style="stop-color:#E8E8E8"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop33"
|
||||
style="stop-color:#FFFFFF"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<path
|
||||
style="fill:url(#XMLID_17_)"
|
||||
id="path35"
|
||||
d="M 91.145,9.856 L 89.924,35.848 L 89.852,37.041 C 91.107,37.752 92.344,38.38 93.661,38.908 C 93.683,38.914 93.717,38.9 93.738,38.908 C 97.663,40.465 101.819,41.179 105.966,41.145 C 106.662,41.14 107.359,41.12 108.055,41.072 C 109.436,40.973 110.852,40.816 112.223,40.55 C 113.593,40.285 114.914,39.932 116.252,39.504 C 118.259,38.861 120.26,38.043 122.15,37.042 L 122.077,35.849 L 120.928,9.857 L 91.145,9.857 L 91.145,9.856 z" />
|
||||
</g><path
|
||||
style="fill:url(#linearGradient2723);fill-opacity:1"
|
||||
d="M 54.846383,-2.5 L 91.404689,-2.5 C 92.492573,-2.5 93.375705,-1.621375 93.375705,-0.53575 L 93.375705,36.03575 C 93.375705,37.121374 92.492573,38 91.404689,38 L 54.846383,38 C 53.758499,38 52.875367,37.121374 52.875367,36.03575 L 52.875367,-0.53575 C 52.875367,-1.621375 53.758499,-2.5 54.846383,-2.5 z"
|
||||
id="rect3791" /><g
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
nodetypes="cccsssssscccc"
|
||||
id="path3793">
|
||||
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-247.2058,-567.9111)"
|
||||
r="40.036098"
|
||||
cy="-971.12653"
|
||||
cx="480.29791"
|
||||
id="XMLID_19_">
|
||||
<stop
|
||||
id="stop45"
|
||||
style="stop-color:#E8E8E8"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop47"
|
||||
style="stop-color:#FFFFFF"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<path
|
||||
style="fill:url(#XMLID_19_)"
|
||||
id="path49"
|
||||
d="M 54.145,9.856 L 52.921,35.848 L 52.849,37.041 C 54.109,37.752 55.348,38.38 56.663,38.908 C 56.682,38.914 56.715,38.9 56.734,38.908 C 60.662,40.465 64.821,41.179 68.962,41.145 C 69.658,41.14 70.355,41.12 71.048,41.072 C 72.433,40.973 73.848,40.816 75.218,40.55 C 76.589,40.285 77.909,39.932 79.253,39.504 C 81.254,38.861 83.255,38.043 85.147,37.042 L 85.08,35.849 L 83.927,9.856 L 54.145,9.856 z" />
|
||||
</g><path
|
||||
style="fill:url(#linearGradient2725);fill-opacity:1"
|
||||
d="M 13.224411,-2.5 L 49.775966,-2.5 C 50.867225,-2.5 51.750357,-1.621375 51.750357,-0.53575 L 51.750357,36.03575 C 51.750357,37.121374 50.867225,38 49.775966,38 L 13.224411,38 C 12.133151,38 11.250019,37.121374 11.250019,36.03575 L 11.250019,-0.53575 C 11.250019,-1.621375 12.133151,-2.5 13.224411,-2.5 z"
|
||||
id="rect3797" /><g
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
nodetypes="cccsssssscccc"
|
||||
id="path3799">
|
||||
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6403,-8.5e-3,-7.7e-3,-0.6279,-253.8748,-567.9111)"
|
||||
r="40.033199"
|
||||
cy="-970.48578"
|
||||
cx="432.98141"
|
||||
id="XMLID_24_">
|
||||
<stop
|
||||
id="stop59"
|
||||
style="stop-color:#E8E8E8"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop61"
|
||||
style="stop-color:#FFFFFF"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<path
|
||||
style="fill:url(#XMLID_24_)"
|
||||
id="path63"
|
||||
d="M 17.146,9.856 L 15.924,35.848 L 15.852,37.041 C 17.109,37.752 18.348,38.38 19.663,38.908 C 19.682,38.914 19.716,38.9 19.735,38.908 C 23.662,40.465 27.821,41.179 31.964,41.145 C 32.657,41.14 33.357,41.12 34.05,41.072 C 35.435,40.973 36.85,40.816 38.22,40.55 C 39.591,40.285 40.909,39.932 42.249,39.504 C 44.258,38.861 46.254,38.043 48.146,37.042 L 48.075,35.849 L 46.925,9.857 L 17.146,9.857 L 17.146,9.856 z" />
|
||||
</g><path
|
||||
style="fill:url(#linearGradient2727);fill-opacity:1"
|
||||
d="M 103.22179,40.25 L 139.77334,40.25 C 140.8691,40.25 141.75111,41.128625 141.75111,42.21425 L 141.75111,78.789125 C 141.75111,79.87475 140.8691,80.75 139.77334,80.75 L 103.22179,80.75 C 102.13278,80.75 101.25077,79.873625 101.25077,78.789125 L 101.25077,42.21425 C 101.25077,41.128625 102.13278,40.25 103.22179,40.25 z"
|
||||
id="rect3929" /><g
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
nodetypes="cccsssssscccc"
|
||||
id="path3931">
|
||||
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-238.6489,-561.7972)"
|
||||
r="40.036499"
|
||||
cy="-1022.5366"
|
||||
cx="533.49512"
|
||||
id="XMLID_25_">
|
||||
<stop
|
||||
id="stop73"
|
||||
style="stop-color:#E8E8E8"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop75"
|
||||
style="stop-color:#FFFFFF"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<path
|
||||
style="fill:url(#XMLID_25_)"
|
||||
id="path77"
|
||||
d="M 97.145,47.856 L 95.918,73.85 L 95.851,75.04 C 97.113,75.751 98.35,76.383 99.665,76.908 C 99.682,76.914 99.716,76.902 99.731,76.908 C 103.661,78.47 107.824,79.181 111.963,79.148 C 112.654,79.142 113.357,79.125 114.047,79.075 C 115.434,78.979 116.849,78.821 118.221,78.55 C 119.591,78.29 120.912,77.936 122.25,77.506 C 124.262,76.863 126.252,76.045 128.147,75.04 L 128.075,73.85 L 126.926,47.857 L 97.145,47.857 L 97.145,47.856 z" />
|
||||
</g><path
|
||||
style="fill:url(#linearGradient2729);fill-opacity:1"
|
||||
d="M 61.599815,40.25 L 98.148,40.25 C 99.24263,40.25 100.12576,41.128625 100.12576,42.21425 L 100.12576,78.789125 C 100.12576,79.87475 99.24263,80.75 98.148,80.75 L 61.599815,80.75 C 60.511931,80.75 59.625423,79.873625 59.625423,78.789125 L 59.625423,42.21425 C 59.625423,41.128625 60.511931,40.25 61.599815,40.25 z"
|
||||
id="rect3935" /><g
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
nodetypes="cccsssssscccc"
|
||||
id="path3937">
|
||||
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6403,-8.5e-3,-7.7e-3,-0.6279,-245.3333,-561.7972)"
|
||||
r="40.038898"
|
||||
cy="-1021.9087"
|
||||
cx="486.17969"
|
||||
id="XMLID_26_">
|
||||
<stop
|
||||
id="stop87"
|
||||
style="stop-color:#E8E8E8"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop89"
|
||||
style="stop-color:#FFFFFF"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<path
|
||||
style="fill:url(#XMLID_26_)"
|
||||
id="path91"
|
||||
d="M 60.145,47.856 L 58.923,73.85 L 58.852,75.04 C 60.112,75.751 61.347,76.383 62.663,76.908 C 62.684,76.914 62.715,76.902 62.735,76.908 C 66.662,78.47 70.824,79.181 74.964,79.148 C 75.654,79.142 76.357,79.125 77.048,79.075 C 78.441,78.979 79.856,78.821 81.227,78.55 C 82.597,78.29 83.913,77.936 85.25,77.506 C 87.263,76.863 89.258,76.045 91.153,75.04 L 91.076,73.85 L 89.926,47.857 L 60.145,47.857 L 60.145,47.856 z" />
|
||||
</g><path
|
||||
style="fill:url(#linearGradient2731)"
|
||||
d="M 19.971092,40.25 L 56.529397,40.25 C 57.617282,40.25 58.500414,41.128625 58.500414,42.21425 L 58.500414,78.789125 C 58.500414,79.87475 57.617282,80.75 56.529397,80.75 L 19.971092,80.75 C 18.883208,80.75 18.000075,79.873625 18.000075,78.789125 L 18.000075,42.21425 C 18.000075,41.128625 18.883208,40.25 19.971092,40.25 z"
|
||||
id="rect3941" /><g
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)"
|
||||
nodetypes="cccsssssscccc"
|
||||
id="path3943"
|
||||
style="filter:url(#filter3372)">
|
||||
|
||||
<radialGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.6404,-8.5e-3,-7.7e-3,-0.6279,-252.0475,-561.7972)"
|
||||
r="40.039001"
|
||||
cy="-1021.27"
|
||||
cx="438.85059"
|
||||
id="XMLID_27_">
|
||||
<stop
|
||||
id="stop101"
|
||||
style="stop-color:#E8E8E8"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop103"
|
||||
style="stop-color:#FFFFFF"
|
||||
offset="0.5858" />
|
||||
</radialGradient>
|
||||
<path
|
||||
style="fill:url(#XMLID_27_)"
|
||||
id="path105"
|
||||
d="M 23.146,47.856 L 21.921,73.85 L 21.849,75.04 C 23.11,75.751 24.348,76.383 25.663,76.908 C 25.682,76.914 25.715,76.902 25.734,76.908 C 29.662,78.47 33.821,79.181 37.965,79.148 C 38.658,79.142 39.358,79.125 40.049,79.075 C 41.436,78.979 42.851,78.821 44.222,78.55 C 45.593,78.29 46.911,77.936 48.252,77.506 C 50.262,76.863 52.257,76.045 54.15,75.04 L 54.078,73.85 L 52.926,47.857 L 23.146,47.857 L 23.146,47.856 z" />
|
||||
</g><g
|
||||
style="filter:url(#filter3460)"
|
||||
id="g109"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 22.213,81.018 L 23.666,76.231 C 23.666,76.231 23.743,74.036 21.849,75.041 L 21.921,76.231 L 20.291,81.018 C 20.605,81.49 21.142,81.8 21.752,81.8 L 23.515,81.8 C 22.971,81.8 22.493,81.489 22.213,81.018 z"
|
||||
id="path111"
|
||||
style="fill:#ffffff" />
|
||||
</g><g
|
||||
style="filter:url(#filter3464)"
|
||||
id="g113"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 54.079,76.23 L 54.151,75.04 C 52.89,74.329 52.333,76.23 52.333,76.23 L 53.779,81.033 C 53.498,81.496 53.023,81.8 52.485,81.8 L 54.249,81.8 C 54.853,81.8 55.386,81.496 55.701,81.033 L 54.079,76.23 z"
|
||||
id="path115"
|
||||
style="fill:#ffffff" />
|
||||
</g><path
|
||||
d="M 24.1,48.856 C 23.693,57.45 52.262,55.434 51.971,48.856 C 42.68,48.856 33.39,48.856 24.1,48.856"
|
||||
id="path122"
|
||||
style="fill:url(#linearGradient2979);fill-opacity:1;filter:url(#filter3500)"
|
||||
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-41.815049)" /><g
|
||||
style="filter:url(#filter3468)"
|
||||
id="g126"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 59.513,81.018 L 60.966,76.231 C 60.966,76.231 61.043,74.036 59.149,75.041 L 59.221,76.231 L 57.591,81.018 C 57.905,81.49 58.442,81.8 59.052,81.8 L 60.815,81.8 C 60.271,81.8 59.792,81.489 59.513,81.018 z"
|
||||
id="path128"
|
||||
style="fill:#ffffff" />
|
||||
</g><g
|
||||
style="filter:url(#filter3472)"
|
||||
id="g130"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 91.379,76.23 L 91.451,75.04 C 90.189,74.329 89.633,76.23 89.633,76.23 L 91.078,81.033 C 90.797,81.496 90.322,81.8 89.784,81.8 L 91.548,81.8 C 92.152,81.8 92.685,81.496 93,81.033 L 91.379,76.23 z"
|
||||
id="path132"
|
||||
style="fill:#ffffff" />
|
||||
</g><path
|
||||
d="M 61.399,48.856 C 60.992,57.45 89.56,55.434 89.269,48.856 C 79.98,48.856 70.689,48.856 61.399,48.856"
|
||||
id="path139"
|
||||
style="fill:url(#linearGradient2981);filter:url(#filter3496)"
|
||||
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-41.815049)" /><g
|
||||
id="g143"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 15.921,43.018 L 17.374,38.231 C 17.374,38.231 17.451,36.036 15.557,37.041 L 15.629,38.231 L 14,43.018 C 14.314,43.49 14.851,43.8 15.461,43.8 L 17.224,43.8 C 16.68,43.8 16.201,43.489 15.921,43.018 z"
|
||||
id="path145"
|
||||
style="fill:#ffffff;filter:url(#filter3424)" />
|
||||
</g><path
|
||||
id="path149"
|
||||
d="M 47.788,38.23 L 47.86,37.04 C 46.598,36.329 46.042,38.23 46.042,38.23 L 47.487,43.033 C 47.206,43.496 46.731,43.8 46.193,43.8 L 47.957,43.8 C 48.561,43.8 49.094,43.496 49.409,43.033 L 47.788,38.23 z"
|
||||
style="fill:#ffffff;filter:url(#filter3416)"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)" /><path
|
||||
d="M 17.808,10.856 C 17.401,19.45 45.969,17.434 45.678,10.856 C 36.389,10.856 27.098,10.856 17.808,10.856"
|
||||
id="path156"
|
||||
style="fill:url(#linearGradient3380);fill-opacity:1;filter:url(#filter3504)"
|
||||
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-17.235103)" /><g
|
||||
style="filter:url(#filter3444)"
|
||||
id="g160"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 53.513,43.018 L 54.966,38.231 C 54.966,38.231 55.043,36.036 53.149,37.041 L 53.221,38.231 L 51.591,43.018 C 51.905,43.49 52.442,43.8 53.052,43.8 L 54.815,43.8 C 54.271,43.8 53.792,43.489 53.513,43.018 z"
|
||||
id="path162"
|
||||
style="fill:#ffffff" />
|
||||
</g><g
|
||||
style="filter:url(#filter3448)"
|
||||
id="g164"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 85.379,38.23 L 85.451,37.04 C 84.189,36.329 83.633,38.23 83.633,38.23 L 85.078,43.033 C 84.797,43.496 84.322,43.8 83.784,43.8 L 85.548,43.8 C 86.152,43.8 86.685,43.496 87,43.033 L 85.379,38.23 z"
|
||||
id="path166"
|
||||
style="fill:#ffffff" />
|
||||
</g><path
|
||||
d="M 55.399,10.856 C 54.992,19.45 83.56,17.434 83.269,10.856 C 73.98,10.856 64.689,10.856 55.399,10.856"
|
||||
id="path173"
|
||||
style="fill:url(#linearGradient2945);fill-opacity:1;filter:url(#filter3508)"
|
||||
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-17.235103)" /><g
|
||||
style="filter:url(#filter3452)"
|
||||
id="g177"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 90.513,43.018 L 91.966,38.231 C 91.966,38.231 92.043,36.036 90.149,37.041 L 90.221,38.231 L 88.592,43.018 C 88.905,43.49 89.442,43.8 90.053,43.8 L 91.815,43.8 C 91.271,43.8 90.793,43.489 90.513,43.018 z"
|
||||
id="path179"
|
||||
style="fill:#ffffff" />
|
||||
</g><g
|
||||
style="filter:url(#filter3456)"
|
||||
id="g181"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 122.379,38.23 L 122.451,37.04 C 121.189,36.329 120.633,38.23 120.633,38.23 L 122.078,43.033 C 121.797,43.496 121.322,43.8 120.784,43.8 L 122.548,43.8 C 123.152,43.8 123.685,43.496 124,43.033 L 122.379,38.23 z"
|
||||
id="path183"
|
||||
style="fill:#ffffff" />
|
||||
</g><path
|
||||
d="M 92.399,10.856 C 91.992,19.45 120.56,17.434 120.269,10.856 C 110.98,10.856 101.689,10.856 92.399,10.856"
|
||||
id="path190"
|
||||
style="fill:url(#linearGradient3378);fill-opacity:1;filter:url(#filter3512)"
|
||||
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-17.235103)" /><g
|
||||
style="filter:url(#filter3476)"
|
||||
id="g194"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 96.513,81.018 L 97.966,76.231 C 97.966,76.231 98.043,74.036 96.149,75.041 L 96.221,76.231 L 94.592,81.018 C 94.905,81.49 95.442,81.8 96.053,81.8 L 97.815,81.8 C 97.271,81.8 96.793,81.489 96.513,81.018 z"
|
||||
id="path196"
|
||||
style="fill:#ffffff" />
|
||||
</g><g
|
||||
style="filter:url(#filter3484)"
|
||||
id="g198"
|
||||
transform="matrix(1.1250094,0,0,1.125,-4.5001124,-11.275)">
|
||||
<path
|
||||
d="M 128.379,76.23 L 128.451,75.04 C 127.189,74.329 126.633,76.23 126.633,76.23 L 128.078,81.033 C 127.797,81.496 127.322,81.8 126.784,81.8 L 128.548,81.8 C 129.152,81.8 129.685,81.496 130,81.033 L 128.379,76.23 z"
|
||||
id="path200"
|
||||
style="fill:#ffffff" />
|
||||
</g><path
|
||||
d="M 98.399,48.856 C 97.992,57.45 126.56,55.434 126.269,48.856 C 116.98,48.856 107.689,48.856 98.399,48.856"
|
||||
id="path207"
|
||||
style="fill:url(#linearGradient2983);filter:url(#filter3492)"
|
||||
transform="matrix(1.1250094,0,0,1.7718407,-4.5001124,-41.815049)" /><path
|
||||
style="fill:#323232"
|
||||
id="path209"
|
||||
d="M 21.99503,12.125 L 19.778139,23.842759 C 19.61869,24.635599 19.540614,25.268992 19.540614,25.848502 C 19.540614,27.088895 20.172912,27.749779 21.150501,27.749779 C 22.470079,27.749779 23.261827,26.799689 23.788558,23.974716 L 26.058233,12.125 L 28.723782,12.125 L 26.533281,23.632728 C 25.715142,27.960908 24.079966,30.125 20.832702,30.125 C 18.27382,30.125 16.875066,28.645985 16.875066,26.112415 C 16.875066,25.399849 16.981732,24.502542 17.166473,23.47328 L 19.330582,12.125 L 21.99503,12.125 L 21.99503,12.125 z" /><path
|
||||
style="fill:#323232"
|
||||
id="path211"
|
||||
d="M 64.643632,12.125 L 61.198712,30.125 L 58.500414,30.125 L 61.918629,12.125 L 64.643632,12.125 L 64.643632,12.125 z" /><path
|
||||
style="fill:#323232"
|
||||
id="path213"
|
||||
d="M 111.34148,17.759442 C 111.34148,21.280968 110.11546,26.238278 107.82102,28.532705 C 106.7515,29.602206 105.57766,30.125 104.19402,30.125 C 100.56813,30.125 100.12576,26.368704 100.12576,24.464472 C 100.12576,20.995117 101.35178,16.037807 103.77666,13.664038 C 104.79508,12.672793 105.99394,12.125 107.35038,12.125 C 110.95129,12.125 111.34148,15.62044 111.34148,17.759442 z M 105.60374,15.124819 C 103.82883,16.899712 102.83867,21.985274 102.83867,24.593812 C 102.83867,25.793739 102.94301,27.802314 104.58532,27.802314 C 105.05486,27.802314 105.4983,27.567546 105.91676,27.15018 C 107.76775,25.270946 108.60358,19.585419 108.60358,17.629015 C 108.60358,16.089978 108.47315,14.446599 106.98627,14.446599 C 106.48956,14.447686 106.0211,14.681367 105.60374,15.124819 z" /><path
|
||||
style="fill:#323232"
|
||||
id="path215"
|
||||
d="M 30.011882,54.875 L 32.678531,54.875 L 30.223015,67.727587 C 29.431267,71.819347 27.794991,72.875 25.182225,72.875 C 24.522435,72.875 23.91543,72.768335 23.625122,72.663868 L 24.073779,70.288648 C 24.443261,70.394213 24.785251,70.420605 25.287791,70.420605 C 26.450121,70.420605 27.161593,69.681647 27.531075,67.728687 L 30.011882,54.875 z" /><path
|
||||
style="fill:#323232"
|
||||
id="path217"
|
||||
d="M 68.668474,54.876125 L 71.338789,54.876125 L 69.683194,63.422176 L 69.763303,63.422176 C 70.350773,62.407464 70.804726,61.659781 71.285383,60.885397 L 75.156227,54.877238 L 78.146981,54.877238 L 72.512615,62.72901 L 74.782383,72.876125 L 71.978553,72.876125 L 70.428657,64.732844 L 69.148018,66.387312 L 67.895195,72.875012 L 65.25047,72.875012 L 68.668474,54.876125 z" /><path
|
||||
style="fill:#323232"
|
||||
id="path219"
|
||||
d="M 110.29425,54.873875 L 113.0183,54.873875 L 110.02717,70.523727 L 114.67522,70.523727 L 114.22121,72.873875 L 106.87582,72.873875 L 110.29425,54.873875 z" /></g>
|
||||
</svg>
|
After Width: | Height: | Size: 34 KiB |
98
imgsrc/languages.svg
Normal file
@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="128" width="128" version="1.0" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256">
|
||||
<defs>
|
||||
<linearGradient id="b" y2="158.07" gradientUnits="userSpaceOnUse" x2="141.27" gradientTransform="matrix(1.68, 0, 0, 1.68, -86.7, -86.7)" y1="70.428" x1="141.27">
|
||||
<stop stop-color="#FFF" offset="0"/>
|
||||
<stop stop-color="#00a200" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="a" y2="158.07" gradientUnits="userSpaceOnUse" y1="70.428" gradientTransform="matrix(1.68, 0, 0, 1.68, -86.7, -86.7)" x2="141.27" x1="141.27">
|
||||
<stop stop-color="#FFF" offset="0"/>
|
||||
<stop stop-color="#00a100" offset="0.5"/>
|
||||
<stop stop-color="#000" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" y2="397.34" gradientUnits="userSpaceOnUse" x2="12.991" gradientTransform="matrix(2.573, 0, 0, -2.573, 207.924, 1307.73)" y1="397.34" x1="-117">
|
||||
<stop stop-color="#0053BD" offset="0"/>
|
||||
<stop stop-color="#0032A4" offset="1"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="d" gradientUnits="userSpaceOnUse" cy="439.63" cx="-57.022" gradientTransform="matrix(2.573, 0, 0, -2.573, 207.924, 1307.73)" r="98">
|
||||
<stop stop-color="#FFF" offset="0"/>
|
||||
<stop stop-color="#57ADFF" offset="0.6"/>
|
||||
<stop stop-color="#C9E6FF" offset="1"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="e" y2="183.37" gradientUnits="userSpaceOnUse" x2="127.66" gradientTransform="matrix(2.573, 0, 0, 2.573, -251.365, -39.26)" y1="63.215" x1="127.66">
|
||||
<stop stop-color="#006a00" offset="0"/>
|
||||
<stop stop-color="#004000" offset="0.2"/>
|
||||
<stop stop-color="#00d000" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="f" y2="361.42" gradientUnits="userSpaceOnUse" x2="-52.251" gradientTransform="matrix(2.573, 0, 0, -2.573, 207.924, 1307.73)" y1="457.03" x1="-52.251">
|
||||
<stop stop-color="#FFF" offset="0"/>
|
||||
<stop stop-color="#94CAFF" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="g" y2="158.07" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="141.27" gradientTransform="matrix(2.573, 0, 0, 2.573, -251.365, -39.26)" y1="70.428" x1="141.27"/>
|
||||
<linearGradient id="h" y2="130.03" xlink:href="#a" gradientUnits="userSpaceOnUse" x2="100.51" gradientTransform="matrix(2.573, 0, 0, 2.573, -251.365, -39.26)" y1="70.033" x1="100.51"/>
|
||||
<linearGradient id="i" y2="85.32" xlink:href="#b" gradientUnits="userSpaceOnUse" x2="120.48" gradientTransform="matrix(2.573, 0, 0, 2.573, -251.365, -39.26)" y1="68.117" x1="120.48"/>
|
||||
<linearGradient id="j" y2="79.161" xlink:href="#b" gradientUnits="userSpaceOnUse" x2="124.57" y1="73.444" x1="124.57"/>
|
||||
<linearGradient id="k" y2="73.865" xlink:href="#b" gradientUnits="userSpaceOnUse" x2="132.78" y1="67.756" x1="132.78"/>
|
||||
<linearGradient id="l" y2="323.36" gradientUnits="userSpaceOnUse" x2="258.77" gradientTransform="translate(5.58, -12.8322)" y1="408.7" x1="258.77">
|
||||
<stop stop-color="#3434ff" offset="0"/>
|
||||
<stop stop-color="#b9b9b9" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="m" y2="85.792" gradientUnits="userSpaceOnUse" x2="-60.735" gradientTransform="translate(2.16, -1.33)" y1="171.13" x1="-60.735">
|
||||
<stop stop-color="#ffff01" offset="0"/>
|
||||
<stop stop-color="#b9b9b9" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="n" y2="298.71" gradientUnits="userSpaceOnUse" x2="-105.42" y1="384.04" x1="-105.42">
|
||||
<stop stop-color="red" offset="0"/>
|
||||
<stop stop-color="#b9b9b9" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="o" y2="408.7" gradientUnits="userSpaceOnUse" x2="32.595" gradientTransform="translate(-3.45, -0.43)" y1="494.61" x1="32.595">
|
||||
<stop stop-color="lime" offset="0"/>
|
||||
<stop stop-color="#b9b9b9" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="p" y2="99.849" gradientUnits="userSpaceOnUse" x2="230.67" gradientTransform="translate(1.59, 1.61)" y1="171.13" x1="230.67">
|
||||
<stop stop-color="#F0F" offset="0"/>
|
||||
<stop stop-color="#b9b9b9" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="translate(-3.417, 1.068)">
|
||||
<g transform="matrix(0.6, 0, 0, 0.6, 83.43, -47.62)">
|
||||
<path fill-opacity="0.3" d="M-39.634,171.47c-31.743,31.66-49.227,73.53-49.227,117.89,0,92.35,75.02,167.48,167.23,167.48,92.218,0,167.25-75.13,167.25-167.48,0-92.06-75.03-166.96-167.25-166.96-44.38,0.01-86.288,17.43-118.01,49.07z" fill="#000"/>
|
||||
<path d="M-43.9,167.2c-31.744,31.66-49.228,73.53-49.228,117.89,0,92.35,75.02,167.48,167.23,167.48,92.225,0,167.24-75.13,167.24-167.48,0-92.06-75.02-166.96-167.24-166.96-44.38,0.01-86.287,17.43-118,49.07z" fill="url(#c)"/>
|
||||
<path d="M-39.03,172.09c-30.439,30.35-47.207,70.49-47.207,113,0,88.55,71.929,160.59,160.34,160.59,88.42,0,160.35-72.04,160.35-160.59,0-88.25-71.93-160.06-160.35-160.06-42.533,0.01-82.714,16.72-113.13,47.06z" fill="#b0d9ff"/>
|
||||
<path d="M74.105,440.51c85.675,0,155.18-69.8,155.18-155.42,0-85.08-69.51-154.88-155.18-154.88-85.068,0-155.16,69.8-155.16,154.88,0,85.62,70.098,155.42,155.16,155.42z" fill="#FFF"/>
|
||||
<path d="M74.105,440.51c85.675,0,155.18-69.8,155.18-155.42,0-85.08-69.51-154.88-155.18-154.88-85.068,0-155.16,69.8-155.16,154.88,0,85.62,70.098,155.42,155.16,155.42z" fill="url(#d)"/>
|
||||
<path d="M22.564,147.28c-0.767,0-1.608,0.31-2.467,0.8,2.019-0.59,2.969-0.8,2.467-0.8m49.961,9.74l0.502-7.31-7.712,0.49,1.019,6.82h6.191m-87.044,126.31c-1.554-1.46-0.512-7.81-0.512-7.81s-23.183-12.18-48.417-19.5c-3.111-0.9-1.541-7.31,1.539-9.74l-1.022-6.84c-0.512-3.41,5.157-19.99,10.82-21.44,5.669-1.47-0.517,9.74-0.517,9.74l-5.661,3.41s6.693,7.8,8.244,7.8c1.542,0,4.117-3.9,4.117-3.9l-7.207-4.87,6.695-2.93,0.422-2.57,1.125-0.36,11.087-16.79c7.662-3.14,17.08-7.02,18.276-7.59,2.064-0.97,16.478-9.26,19.056-11.2,2.5833-1.97,8.2368-1.47,10.292-1.47,2.072,0,5.157-0.98,5.669-6.35,0.514-5.36,2.578-6.34,4.127-4.87,1.549,1.45-1.549,3.9,2.061,4.87,3.602,0.98,6.69,3.42,9.273,0.98,1.881-1.78-0.638-3.82-2.336-5.35h28.599l3.096-8.8-6.695-0.97-24.735-2.43v-2.93l-1.971,0.33c2.679-14.71,18.58-12.29,6.608-20.8-0.729-0.53-11.203,16.5-13.869,16.17-4.83-0.64-11.067-0.69-12.395,0.88-1.7595,2.08,3.95-7.13,8.862-9.92-7.845,2.31-32.626,10.79-58.82,38.7-25.046,26.67-35.032,62.62-35.032,63.88,0,2.43,5.156,3.41,5.668,6.34,0.518,2.91-9.785,12.68-9.785,17.55,0,2.25-2.426,32.3,6.17,57.06,10.051,28.89,31.505,53.03,34.001,54.62l5.146-2.44s-11.847-20.97-12.359-22.93c-0.509-1.94,13.391-30.23,20.093-29.24,6.693,0.95,5.152,2.92,9.271,0.48,4.122-2.43,6.6951-22.44,11.332-24.38,4.6392-1.96,9.7853-4.39,9.2733-9.27-0.5378-4.89-18.047-13.66-19.589-15.12m111.77-143.37l-13.905-4.88,2.578,7.81,11.327-2.93m-52.022,18.04c1.552,0,32.449-20.47,29.359-20.96-3.08-0.49-3.598,0-11.854-0.98-8.229-0.98-16.993,11.7-19.049,13.66-2.061,1.95-1.366,8.28,1.544,8.28m141.8,147.04l3.99-5.18-3.99-1.4-2.99,3.76-3.48,5.16,2.99,1.41,3.48-3.75m17.45,16.93l-1-7.53h-6.48l-0.5,5.64-5.99-0.93-1.48-6.12-2.99-1.88-3.5,4.23-3.48-0.94-1,3.29,3.99,0.95v30.54l14.11,3.4c-0.33,0.56-0.57,1.03-0.65,1.29-1.01,3.29,3.98,4.72,7.46,3.29,1.3-0.51,5.4-4.66,7.98-10.8,3.59-8.54,6.73-19.82,7.36-23.8l1.61-3.45-9.97,3.75-5.49-0.93h0.02m20.94-56.88l-2.61-5.04c-3.08-17.39-10.15-39.64-25.3-62.64-22.8-34.57-86.26-54.51-86.26-54.51l-3.48,4.22-2-3.28-4.99-1.89v4.24l4.5,3.76-2.99,1.41-11.476,0.93-25.918,14.11,2.488,11.28-2.997,0.94-1.483,2.35,8.474,12.68,0.496,4.24-6.978,1.41v8.45l-3.986,0.94,0.5,6.58-33.906,23.52,0.999,13.14c2.488,3.29,21.935,23.04,21.935,23.04s22.429,0.92,27.413-1.88c4.987-2.82,1.493,2.82,2.995,4.23,1.488,1.42,1.989,11.28,3.482,12.22,1.495,0.93,0,6.57,1.994,8.46,1.991,1.87,1.991,24.44,1.991,24.44s11.967,20.2,11.967,25.37c0,5.18-0.504,4.7,8.97,4.24,9.48-0.47,11.47-4.24,13.45-5.64,2.01-1.41,2.01-4.7,4-7.52,2-2.83,5.48-13.63,9.98-17.39,4.48-3.77,16.45-6.59,17.43-13.16,1-6.58,5.49-11.75,5.49-11.75l21.55-22.8-0.6,3.06-0.5,11.74,6.48-2.34-0.49-12.7-2.33-2.46,0.33-0.36s-1.49-2.82-3.48-2.82-13.97,2.82-15.95,2.35c-2-0.47-10.48-23.03-11.97-23.96-1.5-0.94-10.97-16.46-10.97-16.46s21.93,26.32,25.43,36.66c2.02,6.02,9.63,0.41,15.82-5.87l1.64,4,3.98-0.95-0.5-4.7h4.48v7.05l-1.49,3.76-0.51,6.1,3.99,3.77,2-3.28,6.47-6.12,7.48-3.76,2,3.76,1,5.18-2,5.63-3.99,3.29-1.99,8.46v4.23l-4.48-2.82-0.49-8.93-6.49,0.48-2.99,7.98,4.49,6.59,10.46,1.41,8.48-8,1-15.49,3.77-4.98c2.45,6.31,4.21,12.92,4.21,19.08,0,6.74,3.08-4.68,0.46-25.26l1.52-2.02m-125.63-47.94l-26.917-0.95,11.46-9.39h5.983l9.474,6.57v3.77m32.913-3.29v4.23h-11.46l0.99,2.83-6.98,0.95-0.49,2.34-4.99-0.93-8.98-1.89,1.5-2.34,1.5-2.84,4.98-5.16,2,3.76,7.48-0.48,3.98-4.23,15.46,2.82-4.99,0.94m0.98-6.11l-5.97,0.94-1-4.24,7.47-0.93,1-4.23,5.5,5.65-7,2.8v0.01m28.93,146.16l-3.5,2.82,0.5,7.06h4.49v-6.1l3.99-5.18v-10.81l-2.5-0.48-2.98,12.69m-33.4-14.08s-3.49,0.91,0.49,2.33,19.94-23.01,19.94-23.01l-13.46,8.45-6.98,12.23h0.01m-27.14,90.07l-2.978-2.84-5.985-0.95-0.991,2.84-7.976-0.94-0.499-3.78h-5.98l-6.476,3.78h-11.458l-0.996-2.84-18.433-1.9-2.995,2.84-7.462-1.88-1.001-6.63-3.487-0.49-3.988,7.12-13.452-0.47c2.4088,1.13,22.491,13.12,53.301,15.61,40.856,3.31,60.296-6.62,60.296-6.62l-1.5-1.43-17.94-1.41v-0.01z" fill="url(#e)"/>
|
||||
<path d="M73.583,254c53.147,0,99.387-18.31,123.18-45.31-23.96-45.72-70.24-76.92-123.41-76.92-52.699,0-99.045,31.1-123.15,76.69,23.726,27.12,70.075,45.54,123.38,45.54z" fill="url(#f)"/>
|
||||
<path d="M141.67,229.84s3.61,4.33,8.13,10.17c19.53-7.86,35.75-18.61,46.96-31.32-7.94-15.15-18.35-28.7-30.64-40.06-25.84-16.6-54.87-25.73-54.87-25.73l-3.48,4.22-2-3.28-4.99-1.89v4.24l4.5,3.76-2.99,1.41-11.476,0.93-25.918,14.11,2.488,11.28-2.997,0.94-1.483,2.35,8.474,12.68,0.496,4.24-6.978,1.41v8.45l-3.986,0.94,0.5,6.58-33.906,23.52,0.803,10.55c14.209,3,29.423,4.66,45.276,4.66,27.537,0,53.187-4.94,74.847-13.44-3.12-4.76-6.76-10.72-6.76-10.72zm-41.883-12.69l-26.917-0.95,11.46-9.39h5.983l9.474,6.57v3.77zm32.913-3.29v4.23h-11.46l0.99,2.83-6.98,0.95-0.49,2.34-4.99-0.93-8.98-1.89,1.5-2.34,1.5-2.84,4.98-5.16,2,3.76,7.48-0.48,3.98-4.23,15.46,2.82-4.99,0.94zm0.98-6.12v0.01l-5.97,0.94-1-4.24,7.47-0.93,1-4.23,5.5,5.65-7,2.8z" fill="url(#g)"/>
|
||||
<path d="M-15.54,199.94c2.063-0.97,16.478-9.26,19.056-11.2,2.5833-1.97,8.2361-1.47,10.292-1.47,2.071,0,5.156-0.98,5.669-6.35,0.514-5.36,2.578-6.34,4.127-4.87,1.549,1.45-1.549,3.9,2.061,4.87,3.602,0.98,6.69,3.42,9.273,0.98,1.881-1.78-0.638-3.82-2.336-5.35h28.599l3.095-8.8-6.695-0.97-24.734-2.43v-2.93l-1.971,0.33c2.678-14.71,18.58-12.29,6.607-20.8-0.728-0.53-11.203,16.5-13.868,16.17-4.83-0.64-11.067-0.69-12.395,0.88-1.7598,2.08,3.95-7.13,8.862-9.92-6.106,1.8-22.478,7.38-41.781,22.9-11.17,10.8-20.701,23.45-28.121,37.48,2.789,3.19,5.918,6.25,9.309,9.18l6.674-10.11c7.663-3.13,17.083-7.02,18.277-7.59z" fill="url(#h)"/>
|
||||
<path d="M43.679,149.72c-2.059,1.95-1.364,8.28,1.546,8.28,1.552,0,32.449-20.47,29.359-20.96-3.08-0.49-3.598,0-11.854-0.98-8.232-0.98-16.996,11.7-19.051,13.66z" fill="url(#i)"/>
|
||||
<polygon points="126.07,73.444,123.08,73.631,123.47,76.284,125.88,76.284,125.88,76.285,126.07,73.444" transform="matrix(2.573, 0, 0, 2.573, -251.365, -39.26)" fill="url(#j)"/>
|
||||
<polygon points="135.49,69.653,130.08,67.756,131.08,70.792,135.49,69.653" transform="matrix(2.573, 0, 0, 2.573, -251.365, -39.26)" fill="url(#k)"/>
|
||||
</g>
|
||||
<g transform="matrix(0.6, 0, 0, 0.6, 83.43, -47.62)">
|
||||
<path d="M247.4,375.48l15.77,2.64c-2.02,5.79-5.23,10.19-9.6,13.21-4.38,3.02-9.85,4.54-16.42,4.54-10.4,0-18.1-3.4-23.09-10.2-3.95-5.44-5.92-12.32-5.92-20.61,0-9.92,2.59-17.68,7.78-23.3,5.18-5.61,11.73-8.42,19.65-8.42,8.9,0,15.92,2.94,21.07,8.82,5.14,5.88,7.6,14.88,7.38,27.01h-39.66c0.12,4.69,1.39,8.34,3.83,10.95,2.45,2.61,5.49,3.92,9.13,3.92,2.48,0,4.56-0.68,6.25-2.03s2.97-3.53,3.83-6.53zm0.9-16c-0.11-4.58-1.29-8.07-3.55-10.45-2.25-2.38-4.99-3.58-8.22-3.58-3.45,0-6.31,1.26-8.56,3.78-2.25,2.51-3.36,5.93-3.32,10.25h23.65zm-20.22-32.11l7.78-16.84h17.74l-15.49,16.84h-10.03z" stroke="#000064" stroke-width="10" fill="none"/>
|
||||
<path fill-opacity="0.3" d="M236.03,308.41l-1.72,3.71-7.78,16.85-3.06,6.62c-2.91,1.64-5.67,3.57-8,6.1-6.46,6.99-9.47,16.59-9.47,27.62,0,9.31,2.28,17.68,7.12,24.38l0.04,0.03c6.25,8.5,16.45,12.81,28.25,12.81,7.55,0,14.45-1.78,20.06-5.65,5.56-3.85,9.61-9.57,12-16.38l2.53-7.16-1.38-0.25,0.07-3.53c0.24-13.05-2.36-23.79-8.97-31.34-4.14-4.73-9.57-7.82-15.66-9.5l12.5-13.6,9.88-10.71h-36.41zm-2.75,36.59c-2.2,1.01-4.2,2.42-5.81,4.22-3.48,3.88-5.02,9.15-4.97,14.59l0.03,3.22h-0.47l0.16,6.56c0.14,5.78,1.85,11.19,5.56,15.16,1.5,1.6,3.33,2.86,5.28,3.84-4.13-1.16-7.22-3.21-9.59-6.43-3.04-4.2-4.66-9.56-4.66-16.85,0-8.8,2.16-14.7,6.07-18.93,2.5-2.72,5.24-4.41,8.4-5.38zm15.94,0.59c0.6,0.25,1.1,0.64,1.66,0.94-0.53-0.36-1.1-0.63-1.66-0.94zm1.72,0.97c1.9,1.05,3.63,2.35,5.15,4.1,2.93,3.34,4.95,8.71,5.57,16.37h-2.6l-0.09-3.44c-0.14-5.6-1.66-10.85-5.28-14.68-0.83-0.88-1.77-1.66-2.75-2.35zm-10.13,9.56c1.43,0.01,2.18,0.34,3.13,1.22h-6.44c0.91-0.86,1.7-1.21,3.31-1.22zm-3.78,23.72h7.75c-0.38,0.77-0.75,1.3-0.94,1.44-0.49,0.4-0.93,0.63-2.25,0.63-2.11,0-3.11-0.43-4.47-1.88-0.04-0.04-0.05-0.14-0.09-0.19zm18.53,7.07l1.97,0.31c-1,1.18-2.09,2.26-3.34,3.12-1.14,0.79-2.44,1.38-3.85,1.91,0.51-0.31,1.04-0.6,1.5-0.97,1.54-1.23,2.75-2.72,3.72-4.37z" fill="#000"/>
|
||||
<path d="M247.4,375.48l15.77,2.64c-2.02,5.79-5.23,10.19-9.6,13.21-4.38,3.02-9.85,4.54-16.42,4.54-10.4,0-18.1-3.4-23.09-10.2-3.95-5.44-5.92-12.32-5.92-20.61,0-9.92,2.59-17.68,7.78-23.3,5.18-5.61,11.73-8.42,19.65-8.42,8.9,0,15.92,2.94,21.07,8.82,5.14,5.88,7.6,14.88,7.38,27.01h-39.66c0.12,4.69,1.39,8.34,3.83,10.95,2.45,2.61,5.49,3.92,9.13,3.92,2.48,0,4.56-0.68,6.25-2.03s2.97-3.53,3.83-6.53zm0.9-16c-0.11-4.58-1.29-8.07-3.55-10.45-2.25-2.38-4.99-3.58-8.22-3.58-3.45,0-6.31,1.26-8.56,3.78-2.25,2.51-3.36,5.93-3.32,10.25h23.65zm-20.22-32.11l7.78-16.84h17.74l-15.49,16.84h-10.03z" fill="url(#l)"/>
|
||||
</g>
|
||||
<g transform="matrix(0.6, 0, 0, 0.6, 83.43, -47.62)">
|
||||
<path fill-opacity="0.3" d="M25.844,80.688c-3.427,0.201-6.099,1.831-7.969,3.968-0.97,1.099-1.8,2.437-2.344,4-2.276,0.558-4.235,1.611-5.656,3.032l-0.0312-0.032c-0.1083,0.101-0.2094,0.206-0.3126,0.313-0.0412,0.042-0.0845,0.081-0.125,0.125l-0.1562,0.156-0.125,0.156c-1.8144,2.203-3.1928,5.222-2.5625,9.034,0.4099,2.47,1.6306,4.23,2.9063,5.62-0.4704-0.02-0.9399-0.09-1.4063-0.09h-14.75c-0.0153-1.66-0.0326-3.43-0.0937-6.13v-2.871c0.0081-0.22,0.0081-0.218,0-0.438v-0.219c-0.2142-2.996-1.4385-6.385-4.5628-8.781-2.742-2.103-5.854-2.492-8.312-2.406v-0.063h-0.313c-2.23,0.081-5.351,0.632-8,2.782-3.122,2.534-4.187,5.874-4.187,9.125-0.004,0.146-0.004,0.135,0,0.281v0.188c0.148,2.732,0.27,5.532,0.344,8.342,0.001,0.07-0.002,0.12,0,0.19h-17.563c-2.952,0-6.189,0.93-8.719,3.78-2.285,2.57-2.885,5.66-2.968,7.91v0.03c-0.077,2.15,0.238,5.34,2.343,8.12,2.478,3.27,5.856,4.32,9,4.31h15.875c-1.561,6.33-4.261,11.82-8.281,16.69-3.494,4.25-8.049,8.02-13.844,11.28-2.61,1.47-5.285,3.91-6.437,7.66-0.904,2.94-0.651,5.87,0.594,8.59,0.075,0.17,0.135,0.34,0.218,0.5,0.133,0.32,0.25,0.63,0.25,0.63l0.063,0.09c0.007,0.01,0.024,0.02,0.031,0.03l0.031,0.07c-0.541-0.91-0.521-0.79,0.157,0.28l3.093,4.87,1.563-0.78c0.986,0.54,1.933,1.15,3.062,1.41,3.446,0.79,6.763,0.03,9.406-1.38l0.219-0.12,0.125-0.1c0.021-0.01,0.042-0.02,0.063-0.03,0.111-0.07,0.235-0.14,0.343-0.22v-0.03c10.4-6.22,18.697-14.33,24.75-24.03h0.376l1.812-3.34c3.595-6.66,6.063-14.07,7.6875-22.07h9.5625c-0.44,5.74-0.9,11.66-1.1562,14.1v0.09l-0.03,0.22c-0.5064,5.19-1.1158,9.15-1.5625,10.94-0.2162,0.78-0.4454,1.14-0.625,1.47h-6.4688c-2.9742,0-6.5122,0.82-9.3122,3.62-2.488,2.49-3.5,5.52-3.5,8.63-0.011,0.25-0.011,0.24,0,0.5v0.25c0.061,0.73,0.386,1.33,0.562,2l-1.344,1.34,4.532,4.53c1.718,1.72,1.831,1.74,0.187,0.16-0.202-0.2-0.188-0.18,0.063,0.06,2.71,2.56,6.053,3.5,9.156,3.5l7.2812,0.13h0.1876c6.6615,0,12.942-2.73,17.25-7.57h0.719l1.812-3.15,0.031-0.03c2.821-4.91,4.094-11.02,5.313-19.07l0.156-0.31,0.125-1.06c0.479-4.07,0.943-9.08,1.406-15.09,0.467-5.21,0.696-8.98,0.781-11.72,0.104-1.87-0.192-3.7-0.687-5.5,2.84,0.2,5.131-0.73,7.031-2l0.032,0.06c0.073-0.04,0.146-0.06,0.218-0.1l0.688-0.37,0.625-0.53c1.368-1.22,2.599-2.87,3.343-4.94,1.367-0.35,2.474-0.98,3.532-1.69l0.031,0.07c0.073-0.04,0.146-0.06,0.219-0.1l0.687-0.37,0.625-0.53c2.042-1.82,3.909-4.57,4.032-8.32,0.116-3.585-1.544-6.502-3.188-8.34l-0.062-0.062-0.094-0.094c-0.072-0.075-0.144-0.147-0.219-0.219-1.61-1.691-2.862-2.91-4.094-4.063l-0.093-0.093-0.094-0.094c-1.897-1.724-3.728-3.203-5.625-4.469-1.827-1.279-4.511-2.402-7.625-2.218zm-17.75,37.752c0.1539,0.02,0.9063,0.12,0.9062,0.12-0.0002,0,0.3432,0.11,0.625,0.19-0.3653-0.07-1.4337-0.29-1.5312-0.31zm-4.2188,1.34h0.1875c-0.3096,0.23-0.3931,0.27-0.625,0.44,0.0494-0.07,0.25-0.35,0.25-0.34,0,0,0.1622-0.09,0.1875-0.1zm8.063,0.78c0.02,0.01,0.042,0.02,0.062,0.03l-0.625,0.53,0.563-0.56zm0.843,0.53c0.027,0.03,0.037,0.07,0.063,0.1l-0.938,0.65,0.875-0.75zm0.5,0.69c0.093,0.16,0.184,0.31,0.25,0.5-0.048-0.08-0.113-0.26-0.25-0.5zm-11.594,1.03c-0.0233,0.11-0.0516,0.24-0.0937,0.44,0.0065-0.07,0.0312-0.34,0.0312-0.34s0.0587-0.09,0.0625-0.1zm11.906,4.28c-0.003,0.06-0.028,0.16-0.032,0.22-0.137,0.23-1,1.72-1,1.72,0.001,0-0.369,0.28-0.593,0.44,0.331-0.49,1.323-1.94,1.625-2.38zm-10.938,1.6c0.1258,0.16,0.316,0.33,0.4688,0.5l-0.0625,0.06c-0.0913-0.1-0.2038-0.23-0.25-0.28-0.1353-0.16-0.1402-0.2-0.1563-0.22-0.004,0-0.0291-0.03-0.0312-0.03l0.0312-0.03zm0.6876,0.75c0.1283,0.12,0.1857,0.25,0.3437,0.37-0.1548-0.12-0.3158-0.25-0.4063-0.34l0.0626-0.03zm0.5312,0.53c0.2412,0.19,0.5718,0.42,1.25,0.72-0.1829-0.08-0.3407-0.14-0.5938-0.28-0.0098-0.01-0.0212-0.03-0.0312-0.03,0.0002,0-0.4694-0.29-0.625-0.41zm7.031,0.25c-0.319,0.23-0.75,0.53-0.75,0.53s-0.3648,0.06-0.531,0.09c0.399-0.19,0.73-0.35,1.281-0.62z" fill="#000"/>
|
||||
<path d="M21.968,82.795c-1.392,0.082-2.666,0.824-3.531,1.812-0.889,1.008-1.555,2.502-1.312,4,0.242,1.499,1.174,2.603,2.218,3.438,1.879,1.503,3.31,2.692,4.219,3.531,1.214,1.143,2.159,2.174,2.906,3.125,0.021,0.032,0.041,0.063,0.063,0.094,0.933,1.088,2.154,1.985,3.687,2.185,1.534,0.21,3.014-0.43,4.094-1.341,0.021-0.01,0.042-0.021,0.063-0.032,1.018-0.905,1.857-2.235,1.906-3.75,0.049-1.514-0.646-2.818-1.563-3.843-0.02-0.021-0.041-0.042-0.062-0.063-1.559-1.636-2.918-2.965-4.094-4.062-0.01-0.011-0.021-0.021-0.031-0.032-1.741-1.582-3.356-2.893-4.875-3.906-1.063-0.744-2.266-1.24-3.688-1.156zm-45.968,5.406c-1.526,0.055-2.976,0.36-4.188,1.344-1.228,0.997-1.844,2.662-1.844,4.156-0.001,0.042-0.001,0.084,0,0.125,0.151,2.794,0.269,5.661,0.344,8.534,0.055,2.48,0.026,4.61,0,6.75h-23.969c-1.377,0-2.914,0.51-3.906,1.62-0.992,1.12-1.323,2.52-1.375,3.91-0.05,1.4,0.145,2.82,1.063,4.03,0.917,1.21,2.503,1.78,3.875,1.78h23.5c-1.118,10.52-4.697,19.55-10.969,27.16-4.045,4.91-9.235,9.18-15.625,12.78-1.585,0.89-2.932,2.22-3.469,3.97-0.472,1.53-0.261,3.32,0.563,4.72,0.003,0-0.004,0.02,0,0.03,0.023,0.04,0.037,0.08,0.062,0.12l0.063-0.03c0.814,1.38,2.219,2.37,3.718,2.72,1.664,0.38,3.407,0.04,4.938-0.78,0.032-0.02,0.063-0.04,0.094-0.06,10.928-6.45,19.303-14.87,24.937-25.19h0.031c3.976-7.36,6.576-15.91,8-25.44h20.688c0.9846,0,0.952,0.2,0.875,0.09,0.0205,0.03,0.0413,0.05,0.0625,0.07-0.1075-0.15,0.1515,0.24,0.0937,1.28-0.6844,9.66-1.2515,16.24-1.625,19.75v0.03c-0.5275,5.5-1.1278,9.6-1.75,12.09-0.76,2.76-1.7441,4.35-2.5937,5.07-0.021,0.01-0.0418,0.02-0.0625,0.03-0.8088,0.72-2.0336,1.22-4.125,1.22h-6.5309c-1.748,0-3.534,0.5-4.782,1.75-1.062,1.06-1.625,2.63-1.625,4.09-0.003,0.07-0.003,0.15,0,0.22,0.123,1.47,0.862,2.86,1.907,3.84l-0.032,0.03c0.027,0.03,0.067,0.04,0.094,0.07,0.011,0.01,0.021,0.02,0.031,0.03,1.262,1.19,3.032,1.75,4.75,1.75l7.4066,0.12h0.0313c6.2648,0,11.418-2.61,14.281-7.53h0.0313c2.1206-3.69,3.5126-9.5,4.7496-17.94,0.011-0.02,0.022-0.04,0.032-0.06,0.462-3.93,0.916-8.88,1.375-14.84,0.459-5.13,0.702-8.88,0.781-11.41,0.184-3.32-0.967-6.4-3.406-8.44v-0.03c-2.262-1.85-5.2882-2.62-8.7191-2.62h-21.188c0.036-1.59,0.094-3.12,0.094-4.88,0-1.65-0.049-4.14-0.125-7.498v-3.031c0.002-0.062,0.002-0.125,0-0.187-0.11-1.546-0.755-3.154-2.062-4.157-1.29-0.989-2.871-1.245-4.438-1.156h-0.062zm37.406,2.094c-1.368,0.121-2.581,0.835-3.4689,1.781-0.0431,0.04-0.0848,0.082-0.125,0.125-0.8348,1.014-1.4285,2.481-1.1875,3.938,0.2411,1.457,1.1663,2.537,2.1874,3.343,1.792,1.498,3.121,2.698,4.156,3.658,0.011,0.01,0.021,0.02,0.032,0.03,1.298,1.15,2.297,2.17,3,3.06,0.02,0.03,0.041,0.07,0.062,0.1,0.934,1.09,2.159,2,3.688,2.21,1.529,0.22,3.029-0.42,4.125-1.34,0.021-0.01,0.041-0.02,0.062-0.03,1.026-0.91,1.875-2.27,1.906-3.78,0.032-1.52-0.683-2.8-1.593-3.814-0.021-0.021-0.042-0.042-0.063-0.062-1.547-1.625-2.9-2.98-4.094-4.094-0.01-0.011-0.02-0.021-0.031-0.031-1.727-1.57-3.347-2.912-4.937-4-1.059-0.725-2.349-1.215-3.719-1.094z" stroke="#3c3c00" stroke-width="10" fill="none"/>
|
||||
<path d="M21.968,82.795c-1.392,0.082-2.666,0.824-3.531,1.812-0.889,1.008-1.555,2.502-1.312,4,0.242,1.499,1.174,2.603,2.218,3.438,1.879,1.503,3.31,2.692,4.219,3.531,1.214,1.143,2.159,2.174,2.906,3.125,0.021,0.032,0.041,0.063,0.063,0.094,0.933,1.088,2.154,1.985,3.687,2.185,1.534,0.21,3.014-0.43,4.094-1.341,0.021-0.01,0.042-0.021,0.063-0.032,1.018-0.905,1.857-2.235,1.906-3.75,0.049-1.514-0.646-2.818-1.563-3.843-0.02-0.021-0.041-0.042-0.062-0.063-1.559-1.636-2.918-2.965-4.094-4.062-0.01-0.011-0.021-0.021-0.031-0.032-1.741-1.582-3.356-2.893-4.875-3.906-1.063-0.744-2.266-1.24-3.688-1.156zm-45.968,5.406c-1.526,0.055-2.976,0.36-4.188,1.344-1.228,0.997-1.844,2.662-1.844,4.156-0.001,0.042-0.001,0.084,0,0.125,0.151,2.794,0.269,5.661,0.344,8.534,0.055,2.48,0.026,4.61,0,6.75h-23.969c-1.377,0-2.914,0.51-3.906,1.62-0.992,1.12-1.323,2.52-1.375,3.91-0.05,1.4,0.145,2.82,1.063,4.03,0.917,1.21,2.503,1.78,3.875,1.78h23.5c-1.118,10.52-4.697,19.55-10.969,27.16-4.045,4.91-9.235,9.18-15.625,12.78-1.585,0.89-2.932,2.22-3.469,3.97-0.472,1.53-0.261,3.32,0.563,4.72,0.003,0-0.004,0.02,0,0.03,0.023,0.04,0.037,0.08,0.062,0.12l0.063-0.03c0.814,1.38,2.219,2.37,3.718,2.72,1.664,0.38,3.407,0.04,4.938-0.78,0.032-0.02,0.063-0.04,0.094-0.06,10.928-6.45,19.303-14.87,24.937-25.19h0.031c3.976-7.36,6.576-15.91,8-25.44h20.688c0.9846,0,0.952,0.2,0.875,0.09,0.0205,0.03,0.0413,0.05,0.0625,0.07-0.1075-0.15,0.1515,0.24,0.0937,1.28-0.6844,9.66-1.2515,16.24-1.625,19.75v0.03c-0.5275,5.5-1.1278,9.6-1.75,12.09-0.76,2.76-1.7441,4.35-2.5937,5.07-0.021,0.01-0.0418,0.02-0.0625,0.03-0.8088,0.72-2.0336,1.22-4.125,1.22h-6.5309c-1.748,0-3.534,0.5-4.782,1.75-1.062,1.06-1.625,2.63-1.625,4.09-0.003,0.07-0.003,0.15,0,0.22,0.123,1.47,0.862,2.86,1.907,3.84l-0.032,0.03c0.027,0.03,0.067,0.04,0.094,0.07,0.011,0.01,0.021,0.02,0.031,0.03,1.262,1.19,3.032,1.75,4.75,1.75l7.4066,0.12h0.0313c6.2648,0,11.418-2.61,14.281-7.53h0.0313c2.1206-3.69,3.5126-9.5,4.7496-17.94,0.011-0.02,0.022-0.04,0.032-0.06,0.462-3.93,0.916-8.88,1.375-14.84,0.459-5.13,0.702-8.88,0.781-11.41,0.184-3.32-0.967-6.4-3.406-8.44v-0.03c-2.262-1.85-5.2882-2.62-8.7191-2.62h-21.188c0.036-1.59,0.094-3.12,0.094-4.88,0-1.65-0.049-4.14-0.125-7.498v-3.031c0.002-0.062,0.002-0.125,0-0.187-0.11-1.546-0.755-3.154-2.062-4.157-1.29-0.989-2.871-1.245-4.438-1.156h-0.062zm37.406,2.094c-1.368,0.121-2.581,0.835-3.4689,1.781-0.0431,0.04-0.0848,0.082-0.125,0.125-0.8348,1.014-1.4285,2.481-1.1875,3.938,0.2411,1.457,1.1663,2.537,2.1874,3.343,1.792,1.498,3.121,2.698,4.156,3.658,0.011,0.01,0.021,0.02,0.032,0.03,1.298,1.15,2.297,2.17,3,3.06,0.02,0.03,0.041,0.07,0.062,0.1,0.934,1.09,2.159,2,3.688,2.21,1.529,0.22,3.029-0.42,4.125-1.34,0.021-0.01,0.041-0.02,0.062-0.03,1.026-0.91,1.875-2.27,1.906-3.78,0.032-1.52-0.683-2.8-1.593-3.814-0.021-0.021-0.042-0.042-0.063-0.062-1.547-1.625-2.9-2.98-4.094-4.094-0.01-0.011-0.02-0.021-0.031-0.031-1.727-1.57-3.347-2.912-4.937-4-1.059-0.725-2.349-1.215-3.719-1.094z" fill="url(#m)"/>
|
||||
</g>
|
||||
<g transform="matrix(0.6, 0, 0, 0.6, 83.43, -47.62)">
|
||||
<path fill-opacity="0.3" d="M-61.025,286.89c-3.782,0-7.29,2.17-8.968,5.56-2.725,5.45-4,11.45-4,17.66,0.002,2.9,1.311,5.59,3.437,7.47l1,6.03c1.254,7.88,2.268,14.86,3.125,21.22,0.957,7.16,1.294,11.61,1.406,14.46-0.503,0.33-0.753,0.54-2.125,1.1-3.979,1.6-8.262,2.44-13.218,2.44-4.428,0-6.878-0.95-7.75-1.57-1.089-0.76-0.594,0.48-0.594-1.93,0-1.31,0.182-2.78,0.656-4.47,0.403-1.41,1.087-3.24,2.094-5.41,0.778-1.65,1.762-3.55,3.031-5.84,2.602-4.68,0.949-10.72-3.656-13.44l-1.688-1.03c-2.324-1.4-5.099-1.77-7.718-1.06-2.619,0.7-4.847,2.43-6.157,4.81-1.99,3.6-3.62,6.73-4.91,9.56-1.75,3.83-3.1,7.47-4.03,10.97-1.11,4.12-1.68,8.22-1.68,12.28,0,7.05,2.85,13.72,7.9,18.47v0.62l2.81,1.88c6.143,4.11,13.616,5.53,21.785,5.53,6.2,0,11.614-0.55,16.532-1.75h0.437l0.656-0.19,0.125-0.03,0.063-0.03c6.188-1.71,11.792-4.84,15.781-9.78l0.031-0.03c4.435-5.56,6.282-12.64,6.282-20.03,0-4.72-0.518-10.68-1.407-18.38-0.561-4.86-1.368-11.12-2.5-18.75v-0.03c-0.239-1.59-0.264-1.84-0.375-2.59,0.733-1.01,1.337-2.11,1.657-3.35,0.59-2.28,1.016-4.12,1.281-5.81,0.335-2.15,0.406-4.35,0.406-7.09-0.003-3.79-2.172-7.29-5.562-8.97-0.744-0.37-1.617-0.81-2.657-1.31-0.48-0.24-0.886-0.39-1.343-0.6-1.431-3.8-4.989-6.58-9.188-6.59h-0.969zm2.5,11.25h0.032c-0.007,0.01-0.025,0.02-0.032,0.03v-0.03zm1,6.37c0.79,0.35,1.549,0.68,2.407,1.1h0.031v0.03c0.334,0.16,0.597,0.28,0.906,0.44-0.038,1.29-0.061,2.76-0.156,3.37-0.103,0.66-0.436,1.97-0.719,3.16l-3.187,1.09-0.157-0.97-0.531-3.09-2.094-1.13c0.071-1.36,0.191-2.7,0.438-3.97l3.062-0.03zm4.938,2.35h0.031v0.03c-0.013-0.01-0.018-0.03-0.031-0.03zm-47.344,59.28c1.065,2.28,2.593,4.26,4.438,5.56,3.942,2.78,8.992,3.94,15.125,3.94,6.437,0,12.488-1.14,18.031-3.38,3.08-1.24,5.327-2.45,7.312-4.37,0.407-0.4,0.767-1.03,1.157-1.53-0.585,2.41-1.505,4.43-2.782,6.03-1.939,2.4-4.919,4.22-9.062,5.4l-0.188,0.07c-3.645,0.99-8.417,1.53-14.375,1.53-6.53,0-11.23-1.15-14.437-3.22l-0.219-0.16c-3.183-2.14-4.887-5.42-5-9.87z" fill="#000"/>
|
||||
<path d="M-63.656,295.12c-1.366,0.01-2.613,0.78-3.219,2-2.252,4.51-3.344,9.49-3.344,14.82,0.001,1.34,0.748,2.57,1.938,3.18l0.937,0.5,1.469,8.76c1.262,7.93,2.292,15.03,3.156,21.43,1.113,8.33,1.656,14.29,1.657,17.41-0.001,0.55-0.107,1.01-1.032,1.9-0.924,0.9-2.69,1.99-5.281,3.04-4.761,1.91-9.928,2.9-15.625,2.9-5.28,0-9.03-1.05-11.438-2.75-2.331-1.64-3.312-3.57-3.312-7.15,0-1.96,0.297-4.01,0.906-6.19,0.512-1.79,1.306-3.94,2.438-6.38,0.868-1.84,1.945-3.88,3.25-6.25,0.948-1.7,0.365-3.85-1.313-4.84l-1.719-1.03c-0.836-0.5-1.842-0.65-2.785-0.39-0.943,0.25-1.743,0.88-2.215,1.73-1.942,3.52-3.502,6.53-4.692,9.13-1.62,3.55-2.83,6.87-3.65,9.97-0.98,3.63-1.47,7.17-1.47,10.62,0,6.5,2.76,12.18,7.88,15.63-0.01,0.01-0.01,0.02,0,0.03,4.669,3.13,10.796,4.44,18.214,4.43,6.324,0,11.633-0.54,16.062-1.74h0.032c5.224-1.45,9.549-3.97,12.531-7.66,3.326-4.17,4.875-9.67,4.875-16.03,0-4.25-0.468-10.07-1.344-17.66-0.55-4.76-1.375-10.95-2.5-18.53-0.547-3.63-0.617-4.16-0.875-6,1.142-0.39,2.01-1.33,2.313-2.5,0.563-2.18,0.948-3.89,1.156-5.22,0.239-1.53,0.312-3.47,0.312-6.09-0.001-1.37-0.776-2.62-2-3.22-0.754-0.38-1.612-0.81-2.594-1.28-1.378-0.67-2.668-1.25-3.874-1.75h-0.032c0.001-0.03-0.201-0.54-0.312-1.6-0.193-1.82-1.728-3.21-3.563-3.22h-0.937z" transform="translate(-1.63, -6.1)" stroke="#510000" stroke-width="10" fill="none"/>
|
||||
<path transform="translate(-1.63, -6.1)" d="M-63.656,295.12c-1.366,0.01-2.613,0.78-3.219,2-2.252,4.51-3.344,9.49-3.344,14.82,0.001,1.34,0.748,2.57,1.938,3.18l0.937,0.5,1.469,8.76c1.262,7.93,2.292,15.03,3.156,21.43,1.113,8.33,1.656,14.29,1.657,17.41-0.001,0.55-0.107,1.01-1.032,1.9-0.924,0.9-2.69,1.99-5.281,3.04-4.761,1.91-9.928,2.9-15.625,2.9-5.28,0-9.03-1.05-11.438-2.75-2.331-1.64-3.312-3.57-3.312-7.15,0-1.96,0.297-4.01,0.906-6.19,0.512-1.79,1.306-3.94,2.438-6.38,0.868-1.84,1.945-3.88,3.25-6.25,0.948-1.7,0.365-3.85-1.313-4.84l-1.719-1.03c-0.836-0.5-1.842-0.65-2.785-0.39-0.943,0.25-1.743,0.88-2.215,1.73-1.942,3.52-3.502,6.53-4.692,9.13-1.62,3.55-2.83,6.87-3.65,9.97-0.98,3.63-1.47,7.17-1.47,10.62,0,6.5,2.76,12.18,7.88,15.63-0.01,0.01-0.01,0.02,0,0.03,4.669,3.13,10.796,4.44,18.214,4.43,6.324,0,11.633-0.54,16.062-1.74h0.032c5.224-1.45,9.549-3.97,12.531-7.66,3.326-4.17,4.875-9.67,4.875-16.03,0-4.25-0.468-10.07-1.344-17.66-0.55-4.76-1.375-10.95-2.5-18.53-0.547-3.63-0.617-4.16-0.875-6,1.142-0.39,2.01-1.33,2.313-2.5,0.563-2.18,0.948-3.89,1.156-5.22,0.239-1.53,0.312-3.47,0.312-6.09-0.001-1.37-0.776-2.62-2-3.22-0.754-0.38-1.612-0.81-2.594-1.28-1.378-0.67-2.668-1.25-3.874-1.75h-0.032c0.001-0.03-0.201-0.54-0.312-1.6-0.193-1.82-1.728-3.21-3.563-3.22h-0.937z" fill="url(#n)"/>
|
||||
</g>
|
||||
<g transform="matrix(0.6, 0, 0, 0.6, 83.43, -47.62)">
|
||||
<path fill-opacity="0.3" d="M84.844,406.06c-1.134,0.12-2.236,0.56-3.25,1.25-2.029,1.38-3.196,3.74-3.063,6.19,0.174,5.03,0.324,11.84,0.5,18.62h-3.937l-1.406-1.24c0.945-0.89,1.658-2.03,2-3.32,0.671-2.53-0.141-5.28-2.094-7.03l-0.094-0.09-0.125-0.1-6.156-5.06c-1.462-1.3-3.212-1.76-5.157-1.56-0.608-0.95-1.353-1.81-2.374-2.38l-7.032-4.25c-1.169-0.73-2.433-1.03-3.812-1h-0.032c-3.379,0.1-6.28,2.69-6.75,6.03,0.078-0.54-0.176,0.75-0.812,2.35-0.636,1.59-1.628,3.76-2.906,6.44-2.398,5.01-6.437,11.81-12.094,20.06-1.892,2.72-1.551,6.46,0.75,8.84l0.031,0.03,0.031,0.04,1.094,1.09c1.083,1.09,2.488,1.66,3.938,1.87-0.042,8.6-0.07,18.19-0.219,22.94v0.03c-0.029,1.03,0.291,1.99,0.687,2.91-0.999,3.22-2.13,5.28-2.093,5.25-1.466,1.29-3.047,2.89-3.907,5.47-1.038,3.11-0.225,6.71,2.126,9.06,2.968,2.97,8.227,3.76,11.937,1.91,1.539-0.77,2.807-1.85,3.875-3.07,2.082,1.47,4.625,1.82,6.625,1.54,1.928-0.28,3.638-1.36,5.094-2.69,0.346,0.2,0.676,0.45,1.031,0.59-0.108,1.44,0.166,2.93,0.969,4.25l0.062,0.09,0.063,0.1,0.718,1.09c2.045,3.15,6.379,4.08,9.563,2.1,7.964-4.74,14.225-11.13,18.937-18.88v2.44c0,3.22,0.735,6.66,3.094,9.47,2.405,2.86,5.991,4.2,9.282,4.4h0.252c0.12,0.01,0.23,0.01,0.34,0h11.06c5.8,0,11.08-4.17,13.29-9.31,0.95-2.06,0.78-4.28-0.35-6.25-0.64-1.12-1.61-1.95-2.72-2.56-0.19-2.55-0.31-5.72-0.31-9.84,0.02-1.89-0.73-3.64-2.06-4.97-1.34-1.34-3.18-2.09-5.06-2.07h-1.72c-2.79-0.03-5.22,1.65-6.35,4.07v-22.94h12.13c2.86,0.05,5.41-1.69,6.5-4.35,1.09-2.65,0.44-5.73-1.63-7.71l-6.03-6.07c-0.43-0.43-0.98-0.69-1.5-1-0.14-2.99-1.36-5.84-3.06-7.96-3-3.75-7.01-5.8-12.69-8.38-0.143-0.07-0.294-0.04-0.436-0.09-0.658-0.82-1.432-1.58-2.406-2.03l-0.126-0.07-0.124-0.03-8.688-3.62c-1.113-0.52-2.272-0.72-3.406-0.6zm-5.813,42.6c-0.196,5.17-0.706,10.05-1.75,14.46-0.072-4-0.164-8.36-0.187-14.12,0.509-0.04,0.999-0.09,1.5-0.25l0.062-0.03h0.063c0.085-0.03,0.225-0.04,0.312-0.06z" fill="#000"/>
|
||||
<path d="M44.734,408.22c-0.301,0.01-0.552,0.23-0.593,0.53-0.21,1.47-1.637,5.13-4.282,10.66-2.628,5.5-6.819,12.48-12.593,20.91-0.169,0.24-0.143,0.56,0.062,0.78l1.094,1.09c0.229,0.23,0.598,0.25,0.844,0.03,1.939-1.74,3.586-3.5,5.062-5.25-0.009,15.45-0.083,27.42-0.312,34.75-0.007,0.22,0.104,0.43,0.291,0.54,0.187,0.12,0.421,0.13,0.615,0.02l4.687-2.53c0.204-0.1,0.336-0.3,0.344-0.53v-2.62h20.438v3.97c-0.06-0.03-0.097-0.07-0.157-0.1-0.286-0.15-0.641-0.05-0.812,0.22l-0.719,1.09c-0.152,0.24-0.126,0.55,0.063,0.75,1.673,1.92,2.84,3.45,3.531,4.6,0.687,1.14,1.345,2.7,1.937,4.59,0.325,1.04,0.759,1.82,1.469,2.22s1.604,0.24,2.375-0.25c0.324-0.21,0.57-0.47,0.813-0.75-2.695,3.53-5.749,6.78-9.313,9.69-0.253,0.19-0.32,0.54-0.156,0.81l0.719,1.09c0.183,0.29,0.558,0.37,0.843,0.19,17.903-10.64,26.682-29.9,26.344-57.4h2.375v46.37c0,2.26,0.507,4.08,1.594,5.38,1.086,1.29,2.737,1.99,4.781,2.12h11.252c3.35,0,5.9-1.92,7.4-5.44,0.09-0.18,0.08-0.39-0.02-0.56-0.1-0.18-0.28-0.29-0.48-0.31-0.95-0.11-1.59-0.47-2.09-1.13s-0.82-1.66-0.94-3.03c-0.24-2.87-0.37-6.56-0.37-11.12,0-0.17-0.07-0.33-0.18-0.45-0.12-0.12-0.28-0.18-0.45-0.18h-1.81c-0.33,0-0.61,0.26-0.62,0.6-0.25,5.29-0.55,9.38-0.91,12.25-0.17,1.37-0.53,2.34-1,2.93s-1,0.85-1.81,0.85h-4.03c-1.827,0.22-2.995-0.07-3.661-0.78-0.666-0.72-0.949-2.02-0.718-3.97,0.001-0.02,0.001-0.05,0-0.07v-43.46h18.529c0.26,0,0.49-0.15,0.58-0.39,0.1-0.23,0.04-0.5-0.14-0.68l-6.13-6.15c-0.11-0.12-0.27-0.19-0.43-0.19-0.17,0-0.32,0.07-0.44,0.19l-4.16,4.15h-16.122v-19.31l2.969-1.97c0.179-0.13,0.278-0.34,0.258-0.56-0.019-0.22-0.153-0.41-0.352-0.5l-8.687-3.63c-0.197-0.09-0.427-0.07-0.606,0.05s-0.281,0.33-0.269,0.55c0.235,6.82,0.482,15.34,0.718,25.37h-12c-0.238,0-0.457,0.13-0.562,0.35l-5.188-4.53c-0.128-0.11-0.292-0.16-0.457-0.14-0.164,0.02-0.315,0.1-0.418,0.23l-2.718,3.37h-7.094c2.095-2.77,3.962-5.22,5.312-6.84,1.548-1.86,2.694-2.98,3.094-3.22,1.15-0.69,2.306-1.15,3.438-1.37,0.226-0.05,0.406-0.22,0.465-0.45,0.06-0.22-0.012-0.46-0.184-0.62l-6.156-5.06c-0.13-0.11-0.302-0.17-0.474-0.15-0.173,0.02-0.33,0.11-0.433,0.25l-2.343,3h-8.688c0.965-1.64,1.886-3,2.688-3.85,0.961-1.02,1.763-1.4,2.343-1.4,0.284,0,0.534-0.19,0.608-0.46s-0.048-0.56-0.295-0.7l-7.219-4.34c-0.103-0.07-0.222-0.1-0.344-0.1zm48.094,5.78c-0.154,0.01-0.299,0.08-0.406,0.19l-1.094,1.09c-0.119,0.13-0.179,0.3-0.168,0.47,0.012,0.17,0.096,0.33,0.231,0.44,3.795,3.08,6.048,6.14,6.843,9.09,0.432,1.61,0.907,2.79,1.5,3.6,0.296,0.4,0.626,0.72,1.036,0.9,0.4,0.19,0.88,0.23,1.31,0.1,1.44-0.44,2.42-1.73,2.94-3.53,0.54-1.9-0.04-3.85-1.57-5.75-1.59-1.99-5.003-4.11-10.341-6.53-0.087-0.05-0.184-0.07-0.281-0.07zm-47.75,8.13h9.531l-5.562,11.4h-8.094l-2.562-1.71c2.227-2.93,4.462-6.15,6.687-9.69zm24.438,13.81l2.281,2.28c0.162,0.17,0.404,0.23,0.625,0.16,2.098-0.7,4.468-1.06,7.062-1.06h1.907c0.622,18.59-3.482,33.69-12.282,45.37,0.239-0.33,0.435-0.72,0.563-1.12,0.278-0.89,0.285-1.89,0.094-3.04-0.221-1.32-1.188-2.59-2.782-3.9-1.344-1.11-3.247-2.29-5.531-3.5l4.875-2.06c0.236-0.11,0.386-0.34,0.375-0.6-0.238-6.18-0.342-16.38-0.344-30.37l2.969-2c0.072-0.04,0.136-0.1,0.188-0.16zm-29.563,0.66h7.438v11.75h-7.438v-11.75zm13.031,0h7.407v11.75h-7.407v-11.75zm-13.031,14.81h7.438v12.12h-7.438v-12.12zm13.031,0h7.407v12.12h-7.407v-12.12zm-1.812,20.09c-0.089,0.02-0.175,0.05-0.25,0.1l-1.094,0.72c-0.282,0.18-0.365,0.55-0.187,0.84,0.953,1.67,1.786,3.33,2.5,5,0.7,1.63,1.238,3.49,1.593,5.62,0.193,1.16,0.497,2.05,1.157,2.57,0.659,0.52,1.595,0.48,2.437,0.06,1.68-0.84,2.471-2.67,2.344-5.09-0.072-1.37-0.845-2.79-2.188-4.38s-3.29-3.37-5.843-5.31c-0.133-0.11-0.302-0.15-0.469-0.13zm-8.656,1.44c-0.144,0.02-0.276,0.08-0.375,0.19l-0.719,0.72c-0.167,0.17-0.216,0.43-0.125,0.65,0.466,1.17,0.894,2.59,1.25,4.25,0.345,1.62,0.431,3.56,0.312,5.82-0.064,1.22,0.113,2.2,0.657,2.9,0.543,0.7,1.46,0.95,2.437,0.81,0.993-0.14,1.811-0.67,2.344-1.5,0.532-0.82,0.811-1.91,0.875-3.25,0.137-2.88-2.012-6.26-6.156-10.4-0.13-0.14-0.314-0.21-0.5-0.19zm-6.875,0.72c-0.256,0.04-0.455,0.24-0.5,0.5-1.192,5.24-2.85,8.69-4.719,10.34-1.067,0.94-1.765,1.8-2.063,2.69s-0.075,1.86,0.594,2.53c1.23,1.23,2.914,1.5,4.531,0.69,1.597-0.8,2.767-2.25,3.532-4.16,0.816-2.04,1.023-5.81,0.781-11.62-0.016-0.27-0.206-0.5-0.469-0.56l-1.437-0.38c-0.081-0.03-0.166-0.04-0.25-0.03z" stroke="#003c00" stroke-width="10" fill="none"/>
|
||||
<path d="M44.734,408.22c-0.301,0.01-0.552,0.23-0.593,0.53-0.21,1.47-1.637,5.13-4.282,10.66-2.628,5.5-6.819,12.48-12.593,20.91-0.169,0.24-0.143,0.56,0.062,0.78l1.094,1.09c0.229,0.23,0.598,0.25,0.844,0.03,1.939-1.74,3.586-3.5,5.062-5.25-0.009,15.45-0.083,27.42-0.312,34.75-0.007,0.22,0.104,0.43,0.291,0.54,0.187,0.12,0.421,0.13,0.615,0.02l4.687-2.53c0.204-0.1,0.336-0.3,0.344-0.53v-2.62h20.438v3.97c-0.06-0.03-0.097-0.07-0.157-0.1-0.286-0.15-0.641-0.05-0.812,0.22l-0.719,1.09c-0.152,0.24-0.126,0.55,0.063,0.75,1.673,1.92,2.84,3.45,3.531,4.6,0.687,1.14,1.345,2.7,1.937,4.59,0.325,1.04,0.759,1.82,1.469,2.22s1.604,0.24,2.375-0.25c0.324-0.21,0.57-0.47,0.813-0.75-2.695,3.53-5.749,6.78-9.313,9.69-0.253,0.19-0.32,0.54-0.156,0.81l0.719,1.09c0.183,0.29,0.558,0.37,0.843,0.19,17.903-10.64,26.682-29.9,26.344-57.4h2.375v46.37c0,2.26,0.507,4.08,1.594,5.38,1.086,1.29,2.737,1.99,4.781,2.12h11.252c3.35,0,5.9-1.92,7.4-5.44,0.09-0.18,0.08-0.39-0.02-0.56-0.1-0.18-0.28-0.29-0.48-0.31-0.95-0.11-1.59-0.47-2.09-1.13s-0.82-1.66-0.94-3.03c-0.24-2.87-0.37-6.56-0.37-11.12,0-0.17-0.07-0.33-0.18-0.45-0.12-0.12-0.28-0.18-0.45-0.18h-1.81c-0.33,0-0.61,0.26-0.62,0.6-0.25,5.29-0.55,9.38-0.91,12.25-0.17,1.37-0.53,2.34-1,2.93s-1,0.85-1.81,0.85h-4.03c-1.827,0.22-2.995-0.07-3.661-0.78-0.666-0.72-0.949-2.02-0.718-3.97,0.001-0.02,0.001-0.05,0-0.07v-43.46h18.529c0.26,0,0.49-0.15,0.58-0.39,0.1-0.23,0.04-0.5-0.14-0.68l-6.13-6.15c-0.11-0.12-0.27-0.19-0.43-0.19-0.17,0-0.32,0.07-0.44,0.19l-4.16,4.15h-16.122v-19.31l2.969-1.97c0.179-0.13,0.278-0.34,0.258-0.56-0.019-0.22-0.153-0.41-0.352-0.5l-8.687-3.63c-0.197-0.09-0.427-0.07-0.606,0.05s-0.281,0.33-0.269,0.55c0.235,6.82,0.482,15.34,0.718,25.37h-12c-0.238,0-0.457,0.13-0.562,0.35l-5.188-4.53c-0.128-0.11-0.292-0.16-0.457-0.14-0.164,0.02-0.315,0.1-0.418,0.23l-2.718,3.37h-7.094c2.095-2.77,3.962-5.22,5.312-6.84,1.548-1.86,2.694-2.98,3.094-3.22,1.15-0.69,2.306-1.15,3.438-1.37,0.226-0.05,0.406-0.22,0.465-0.45,0.06-0.22-0.012-0.46-0.184-0.62l-6.156-5.06c-0.13-0.11-0.302-0.17-0.474-0.15-0.173,0.02-0.33,0.11-0.433,0.25l-2.343,3h-8.688c0.965-1.64,1.886-3,2.688-3.85,0.961-1.02,1.763-1.4,2.343-1.4,0.284,0,0.534-0.19,0.608-0.46s-0.048-0.56-0.295-0.7l-7.219-4.34c-0.103-0.07-0.222-0.1-0.344-0.1zm48.094,5.78c-0.154,0.01-0.299,0.08-0.406,0.19l-1.094,1.09c-0.119,0.13-0.179,0.3-0.168,0.47,0.012,0.17,0.096,0.33,0.231,0.44,3.795,3.08,6.048,6.14,6.843,9.09,0.432,1.61,0.907,2.79,1.5,3.6,0.296,0.4,0.626,0.72,1.036,0.9,0.4,0.19,0.88,0.23,1.31,0.1,1.44-0.44,2.42-1.73,2.94-3.53,0.54-1.9-0.04-3.85-1.57-5.75-1.59-1.99-5.003-4.11-10.341-6.53-0.087-0.05-0.184-0.07-0.281-0.07zm-47.75,8.13h9.531l-5.562,11.4h-8.094l-2.562-1.71c2.227-2.93,4.462-6.15,6.687-9.69zm24.438,13.81l2.281,2.28c0.162,0.17,0.404,0.23,0.625,0.16,2.098-0.7,4.468-1.06,7.062-1.06h1.907c0.622,18.59-3.482,33.69-12.282,45.37,0.239-0.33,0.435-0.72,0.563-1.12,0.278-0.89,0.285-1.89,0.094-3.04-0.221-1.32-1.188-2.59-2.782-3.9-1.344-1.11-3.247-2.29-5.531-3.5l4.875-2.06c0.236-0.11,0.386-0.34,0.375-0.6-0.238-6.18-0.342-16.38-0.344-30.37l2.969-2c0.072-0.04,0.136-0.1,0.188-0.16zm-29.563,0.66h7.438v11.75h-7.438v-11.75zm13.031,0h7.407v11.75h-7.407v-11.75zm-13.031,14.81h7.438v12.12h-7.438v-12.12zm13.031,0h7.407v12.12h-7.407v-12.12zm-1.812,20.09c-0.089,0.02-0.175,0.05-0.25,0.1l-1.094,0.72c-0.282,0.18-0.365,0.55-0.187,0.84,0.953,1.67,1.786,3.33,2.5,5,0.7,1.63,1.238,3.49,1.593,5.62,0.193,1.16,0.497,2.05,1.157,2.57,0.659,0.52,1.595,0.48,2.437,0.06,1.68-0.84,2.471-2.67,2.344-5.09-0.072-1.37-0.845-2.79-2.188-4.38s-3.29-3.37-5.843-5.31c-0.133-0.11-0.302-0.15-0.469-0.13zm-8.656,1.44c-0.144,0.02-0.276,0.08-0.375,0.19l-0.719,0.72c-0.167,0.17-0.216,0.43-0.125,0.65,0.466,1.17,0.894,2.59,1.25,4.25,0.345,1.62,0.431,3.56,0.312,5.82-0.064,1.22,0.113,2.2,0.657,2.9,0.543,0.7,1.46,0.95,2.437,0.81,0.993-0.14,1.811-0.67,2.344-1.5,0.532-0.82,0.811-1.91,0.875-3.25,0.137-2.88-2.012-6.26-6.156-10.4-0.13-0.14-0.314-0.21-0.5-0.19zm-6.875,0.72c-0.256,0.04-0.455,0.24-0.5,0.5-1.192,5.24-2.85,8.69-4.719,10.34-1.067,0.94-1.765,1.8-2.063,2.69s-0.075,1.86,0.594,2.53c1.23,1.23,2.914,1.5,4.531,0.69,1.597-0.8,2.767-2.25,3.532-4.16,0.816-2.04,1.023-5.81,0.781-11.62-0.016-0.27-0.206-0.5-0.469-0.56l-1.437-0.38c-0.081-0.03-0.166-0.04-0.25-0.03z" fill="url(#o)"/>
|
||||
</g>
|
||||
<g transform="matrix(0.6, 0, 0, 0.6, 83.43, -47.62)">
|
||||
<path fill-opacity="0.3" d="M208.28,99.312c-9.25,0.001-16.95,1.548-22.87,5.718-5.68,4-9.54,10.17-11.6,17.53l-1.87,6.78,6.9,1.25,7.72,1.41c-0.33,0.15-0.77,0.25-1.09,0.41-4.18,2.02-7.7,5.14-10.09,9.06-2.4,3.92-3.57,8.51-3.57,13.22,0,7.06,2.76,13.73,7.81,18.5,5.22,4.91,12.4,7.19,20.29,7.19,4.61,0,9.11-0.88,13.28-2.66,1.26-0.54,2.42-1.31,3.62-2l1.25,3.19h31.69l-4.47-9.19c-1.33-2.73-2.17-5.18-2.59-7.25-0.38-1.87-0.69-5.87-0.69-11.41l0.16-20.12v-0.06c0-7.95-0.41-13.62-3.1-18.54-2.31-4.23-6.15-7.42-10.78-9.65-5.31-2.57-11.79-3.377-20-3.378zm0,12.808c7.2,0.01,12.2,1.02,14.44,2.1,2.92,1.41,4.36,2.86,5.12,4.25,0.39,0.7,1.53,5.34,1.54,12.41l-0.19,20.12v0.06c0,5.95,0.24,10.39,0.97,13.97,0.07,0.36,0.22,0.73,0.31,1.09h-3.41c-0.06-0.22-0.09-0.26-0.15-0.5-0.31-1.08-0.44-1.65-0.75-2.5l-2.6-6.9c0.2-0.49,0.54-0.9,0.69-1.41,0.94-3.07,0.94-5.84,0.94-10.22v-16.43c0-4.38-1.17-8.91-4.69-11.94-3.86-3.33-8.22-3.69-13.44-3.69-3.91,0-7.68,0.76-10.87,3-1.72,1.21-3.04,2.73-4.13,4.44l-3.03-0.56c1.08-1.63,2.34-2.91,3.75-3.91,2.69-1.89,7.78-3.37,15.5-3.38zm-1.22,13.22c1.61,0,2.63,0.2,3.5,0.38-2.14,0.54-5.01,1.15-8.03,1.75,0.47-0.85,0.89-1.39,1-1.47,0.26-0.18,1.3-0.66,3.53-0.66zm-13.47,17.66c-2.66,2.57-4.5,6.2-4.5,9.91,0,4.01,1.78,8.05,4.63,10.84,1.89,1.84,4.29,3.08,6.84,3.78-0.22,0.01-0.42,0.06-0.65,0.06-5.57,0-8.97-1.36-11.5-3.75-2.69-2.53-3.82-5.06-3.82-9.15,0-2.67,0.59-4.71,1.72-6.57,1.14-1.86,2.57-3.13,4.75-4.18,0.61-0.3,1.67-0.63,2.53-0.94zm18.5,6.44c-0.03,0.33-0.1,1.75-0.09,1.72v0.03l-0.03,0.03c-0.26,0.88-0.62,1.51-1.78,2.37-1.93,1.4-3.38,1.82-5.1,1.82-1.44,0-1.81-0.21-2.43-0.82-0.76-0.74-0.78-0.86-0.78-1.68,0-0.51-0.31-0.18,0.68-0.88-0.33,0.22,2.55-1,7.06-1.97,1.09-0.23,1.51-0.41,2.47-0.62z" fill="#000"/>
|
||||
<path d="M191.4,122.86l-15.68-2.83c1.76-6.31,4.8-10.99,9.1-14.02,4.31-3.04,10.7-4.55,19.19-4.55,7.71,0,13.45,0.91,17.22,2.73,3.77,1.83,6.43,4.14,7.97,6.95,1.53,2.81,2.3,7.97,2.3,15.47l-0.18,20.17c0,5.74,0.28,9.98,0.83,12.7,0.55,2.73,1.59,5.65,3.1,8.77h-17.09c-0.45-1.15-1.01-2.85-1.66-5.11-0.29-1.02-0.5-1.7-0.62-2.03-2.95,2.87-6.11,5.03-9.47,6.46-3.36,1.44-6.95,2.15-10.76,2.15-6.73,0-12.03-1.82-15.9-5.47-3.88-3.65-5.81-8.26-5.81-13.84,0-3.69,0.88-6.98,2.64-9.87,1.77-2.89,4.24-5.1,7.41-6.64,3.18-1.54,7.76-2.88,13.75-4.03,8.08-1.52,13.67-2.93,16.79-4.24v-1.73c0-3.32-0.82-5.68-2.46-7.1-1.64-1.41-4.74-2.12-9.29-2.12-3.07,0-5.47,0.6-7.19,1.81-1.73,1.21-3.12,3.33-4.19,6.37zm23.13,14.02c-2.22,0.74-5.72,1.62-10.52,2.65-4.8,1.02-7.93,2.03-9.41,3.01-2.25,1.6-3.38,3.63-3.38,6.09,0,2.42,0.9,4.51,2.71,6.27,1.8,1.76,4.1,2.65,6.88,2.65,3.12,0,6.09-1.03,8.92-3.08,2.09-1.56,3.47-3.46,4.12-5.72,0.45-1.48,0.68-4.28,0.68-8.42v-3.45z" stroke="#500050" stroke-width="10" fill="none"/>
|
||||
<path d="M191.4,122.86l-15.68-2.83c1.76-6.31,4.8-10.99,9.1-14.02,4.31-3.04,10.7-4.55,19.19-4.55,7.71,0,13.45,0.91,17.22,2.73,3.77,1.83,6.43,4.14,7.97,6.95,1.53,2.81,2.3,7.97,2.3,15.47l-0.18,20.17c0,5.74,0.28,9.98,0.83,12.7,0.55,2.73,1.59,5.65,3.1,8.77h-17.09c-0.45-1.15-1.01-2.85-1.66-5.11-0.29-1.02-0.5-1.7-0.62-2.03-2.95,2.87-6.11,5.03-9.47,6.46-3.36,1.44-6.95,2.15-10.76,2.15-6.73,0-12.03-1.82-15.9-5.47-3.88-3.65-5.81-8.26-5.81-13.84,0-3.69,0.88-6.98,2.64-9.87,1.77-2.89,4.24-5.1,7.41-6.64,3.18-1.54,7.76-2.88,13.75-4.03,8.08-1.52,13.67-2.93,16.79-4.24v-1.73c0-3.32-0.82-5.68-2.46-7.1-1.64-1.41-4.74-2.12-9.29-2.12-3.07,0-5.47,0.6-7.19,1.81-1.73,1.21-3.12,3.33-4.19,6.37zm23.13,14.02c-2.22,0.74-5.72,1.62-10.52,2.65-4.8,1.02-7.93,2.03-9.41,3.01-2.25,1.6-3.38,3.63-3.38,6.09,0,2.42,0.9,4.51,2.71,6.27,1.8,1.76,4.1,2.65,6.88,2.65,3.12,0,6.09-1.03,8.92-3.08,2.09-1.56,3.47-3.46,4.12-5.72,0.45-1.48,0.68-4.28,0.68-8.42v-3.45z" fill="url(#p)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 41 KiB |
438
imgsrc/mimetypes/djvu.svg
Normal file
@ -0,0 +1,438 @@
|
||||
<?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"
|
||||
version="1.0"
|
||||
width="128"
|
||||
height="128"
|
||||
id="svg2606"
|
||||
inkscape:version="0.48.1 "
|
||||
sodipodi:docname="C:\cygwin\home\mperry\calibre\imgsrc\mimetypes\djvu.svg">
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="975"
|
||||
inkscape:window-height="735"
|
||||
id="namedview3077"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0390625"
|
||||
inkscape:cx="64"
|
||||
inkscape:cy="64"
|
||||
inkscape:window-x="298"
|
||||
inkscape:window-y="122"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2606" />
|
||||
<defs
|
||||
id="defs2608">
|
||||
<linearGradient
|
||||
id="linearGradient10207">
|
||||
<stop
|
||||
id="stop10209"
|
||||
style="stop-color:#a2a2a2;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop10211"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="96"
|
||||
y1="104"
|
||||
x2="88.000198"
|
||||
y2="96.000198"
|
||||
id="XMLID_12_"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
id="stop83"
|
||||
style="stop-color:#888a85;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop85"
|
||||
style="stop-color:#8c8e89;stop-opacity:1"
|
||||
offset="0.0072" />
|
||||
<stop
|
||||
id="stop87"
|
||||
style="stop-color:#abaca9;stop-opacity:1"
|
||||
offset="0.0673" />
|
||||
<stop
|
||||
id="stop89"
|
||||
style="stop-color:#c5c6c4;stop-opacity:1"
|
||||
offset="0.1347" />
|
||||
<stop
|
||||
id="stop91"
|
||||
style="stop-color:#dbdbda;stop-opacity:1"
|
||||
offset="0.2652576" />
|
||||
<stop
|
||||
id="stop93"
|
||||
style="stop-color:#ebebeb;stop-opacity:1"
|
||||
offset="0.37646064" />
|
||||
<stop
|
||||
id="stop95"
|
||||
style="stop-color:#f7f7f6;stop-opacity:1"
|
||||
offset="0.48740286" />
|
||||
<stop
|
||||
id="stop97"
|
||||
style="stop-color:#fdfdfd;stop-opacity:1"
|
||||
offset="0.6324091" />
|
||||
<stop
|
||||
id="stop99"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
cx="102"
|
||||
cy="112.3047"
|
||||
r="139.55859"
|
||||
id="XMLID_8_"
|
||||
gradientUnits="userSpaceOnUse">
|
||||
<stop
|
||||
id="stop41"
|
||||
style="stop-color:#b7b8b9;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop47"
|
||||
style="stop-color:#ececec;stop-opacity:1"
|
||||
offset="0.18851049" />
|
||||
<stop
|
||||
id="stop49"
|
||||
style="stop-color:#fafafa;stop-opacity:1"
|
||||
offset="0.25718147" />
|
||||
<stop
|
||||
id="stop51"
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="0.30111277" />
|
||||
<stop
|
||||
id="stop53"
|
||||
style="stop-color:#fafafa;stop-opacity:1"
|
||||
offset="0.53130001" />
|
||||
<stop
|
||||
id="stop55"
|
||||
style="stop-color:#ebecec;stop-opacity:1"
|
||||
offset="0.84490001" />
|
||||
<stop
|
||||
id="stop57"
|
||||
style="stop-color:#e1e2e3;stop-opacity:1"
|
||||
offset="1" />
|
||||
</radialGradient>
|
||||
<filter
|
||||
x="-0.19200002"
|
||||
y="-0.19199999"
|
||||
width="1.3839999"
|
||||
height="1.3839999"
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter6697">
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur6699"
|
||||
stdDeviation="1.9447689" />
|
||||
</filter>
|
||||
<clipPath
|
||||
id="clipPath7084">
|
||||
<path
|
||||
d="m 72,88 -32,32 -8,0 0,-40 40,0 0,8 z"
|
||||
id="path7086"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
</clipPath>
|
||||
<radialGradient
|
||||
cx="102"
|
||||
cy="112.3047"
|
||||
r="139.55859"
|
||||
id="radialGradient9437"
|
||||
xlink:href="#XMLID_8_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.9996653,2e-6,0.00301608)" />
|
||||
<linearGradient
|
||||
x1="98.617439"
|
||||
y1="106.41443"
|
||||
x2="91.228737"
|
||||
y2="99.254974"
|
||||
id="linearGradient10213"
|
||||
xlink:href="#linearGradient10207"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
color-interpolation-filters="sRGB"
|
||||
id="filter2770">
|
||||
<feGaussianBlur
|
||||
id="feGaussianBlur2772"
|
||||
stdDeviation="2.0786429" />
|
||||
</filter>
|
||||
<linearGradient
|
||||
x1="45.033901"
|
||||
y1="44.966038"
|
||||
x2="11.675456"
|
||||
y2="1.4610662"
|
||||
id="linearGradient2774"
|
||||
xlink:href="#linearGradient2545"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0115079,0,0,1.0161106,5.5234761,9.1336546)" />
|
||||
<linearGradient
|
||||
x1="25.553648"
|
||||
y1="34.006008"
|
||||
x2="0"
|
||||
y2="34.153435"
|
||||
id="linearGradient2756"
|
||||
xlink:href="#linearGradient11545"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0237894,0,0,1.0414051,4.9857129,9.0513362)" />
|
||||
<linearGradient
|
||||
x1="40.864098"
|
||||
y1="40.518246"
|
||||
x2="33.136433"
|
||||
y2="32.651588"
|
||||
id="linearGradient2749"
|
||||
xlink:href="#linearGradient11663"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0237894,0,0,1.0414051,4.9857129,9.0513362)" />
|
||||
<linearGradient
|
||||
x1="45.033901"
|
||||
y1="44.966038"
|
||||
x2="11.675456"
|
||||
y2="1.4610662"
|
||||
id="linearGradient2543"
|
||||
xlink:href="#linearGradient2545"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9880039,0,0,0.9757112,0.5252674,0.07904551)" />
|
||||
<linearGradient
|
||||
id="linearGradient2545">
|
||||
<stop
|
||||
id="stop2547"
|
||||
style="stop-color:#342679;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop2553"
|
||||
style="stop-color:#7b51ae;stop-opacity:0.96862745"
|
||||
offset="0.72235626" />
|
||||
<stop
|
||||
id="stop2549"
|
||||
style="stop-color:#d9cce8;stop-opacity:0.96862745"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
x1="25.553648"
|
||||
y1="34.006008"
|
||||
x2="0"
|
||||
y2="34.153435"
|
||||
id="linearGradient11653"
|
||||
xlink:href="#linearGradient11545"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.4601868,0,0,0.9728475,2.2307835,28.622031)" />
|
||||
<linearGradient
|
||||
id="linearGradient11545">
|
||||
<stop
|
||||
id="stop11547"
|
||||
style="stop-color:#f89c11;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop11549"
|
||||
style="stop-color:#fabf60;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<clipPath
|
||||
id="clip7">
|
||||
<path
|
||||
d="m 10.84375,39.414062 -10.84375,0 0,-10.953124 10.84375,0 0,-28.45703175 35.542969,0 0,32.73437475 -13.058594,13.058594 -22.484375,0 0,-6.382813"
|
||||
id="path25" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clip32">
|
||||
<path
|
||||
d="m 10.84375,39.414062 -10.84375,0 0,-10.953124 10.84375,0 0,-28.45703175 35.542969,0 0,32.73437475 -13.058594,13.058594 -22.484375,0 0,-6.382813"
|
||||
id="path100" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
id="clip48">
|
||||
<path
|
||||
d="m 10.84375,39.414062 -10.84375,0 0,-10.953124 10.84375,0 0,-28.45703175 35.542969,0 0,32.73437475 -13.058594,13.058594 -22.484375,0 0,-6.382813"
|
||||
id="path148" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
x1="25.553648"
|
||||
y1="34.006008"
|
||||
x2="0"
|
||||
y2="34.153435"
|
||||
id="linearGradient2525"
|
||||
xlink:href="#linearGradient11545"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
x1="40.864098"
|
||||
y1="40.518246"
|
||||
x2="33.136433"
|
||||
y2="32.651588"
|
||||
id="linearGradient2518"
|
||||
xlink:href="#linearGradient11663"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient11663">
|
||||
<stop
|
||||
id="stop11665"
|
||||
style="stop-color:#342679;stop-opacity:1"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop11667"
|
||||
style="stop-color:#dacfe4;stop-opacity:1"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<metadata
|
||||
id="metadata2611">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
d="m 16,8 0,112 c 0,0 63.15625,0 63.15625,0 l 0.03125,0 c 3e-6,0 11.90625,-9.90625 17.40625,-15.40625 C 102.09375,99.09375 112,87.1875 112,87.1875 L 112,87.15625 112,8 16,8 z"
|
||||
transform="matrix(1.0416667,0,0,1.0267857,-2.6666667,-1.2142891)"
|
||||
id="path7865"
|
||||
style="opacity:0.5;fill:#000000;fill-opacity:1;filter:url(#filter2770)" />
|
||||
<path
|
||||
d="M 16.000001,8 16,120 c 0,0 63.146418,0 63.146418,0 L 112,87.14642 112,8 16.000001,8 z"
|
||||
id="path34"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<path
|
||||
d="m 18.000002,9.0000034 c -0.551,0 -1,0.44885 -1,0.999665 l 0,107.9638516 c 0,0.55181 0.449,0.99966 1,0.99966 l 59.171997,0 c 0.263,0 2.76268,0.11813 2.948681,-0.0688 L 110.707,88.094202 C 110.894,87.907264 111,85.40942 111,85.146508 l 0,-75.1468396 c 0,-0.550815 -0.448,-0.999665 -1,-0.999665 l -91.999998,0 z"
|
||||
id="path59"
|
||||
style="fill:url(#radialGradient9437);fill-opacity:1" />
|
||||
<path
|
||||
d="m 41.879531,115.98249 c 0,0 24.309609,-24.309614 24.309609,-24.309614 0,0 -9.35314,2.913124 -19.60314,2.913124 0,10.25 -4.706469,21.39649 -4.706469,21.39649 z"
|
||||
transform="translate(40,0)"
|
||||
clip-path="url(#clipPath7084)"
|
||||
id="path5540"
|
||||
style="opacity:0.4;fill:#000000;fill-opacity:1;filter:url(#filter6697)" />
|
||||
<path
|
||||
d="m 79.172,120 c 0,0 11.914,-9.914 17.414,-15.414 5.5,-5.5 15.414,-17.414 15.414,-17.414 0,0 -13.75,8.828 -24,8.828 0,10.25 -8.828,24 -8.828,24 z"
|
||||
id="path14523"
|
||||
style="fill:url(#linearGradient10213);fill-opacity:1" />
|
||||
<g
|
||||
transform="matrix(2.6666667,0,0,2.6666667,-26.364309,-16.219923)"
|
||||
id="layer1-2">
|
||||
<g
|
||||
transform="matrix(1.000026,0,0,0.9968473,-1.2968723e-4,0.405534)"
|
||||
id="g2454">
|
||||
<g
|
||||
transform="matrix(1.0237894,0,0,1.0414051,9.6816161,-27.57005)"
|
||||
id="g11649">
|
||||
<rect
|
||||
width="11.895136"
|
||||
height="11.569371"
|
||||
ry="2.7001941e-017"
|
||||
x="3.1296141"
|
||||
y="57.056187"
|
||||
transform="matrix(0.9396926,-0.3420201,0.3420201,0.9396926,0,0)"
|
||||
id="rect11645"
|
||||
style="fill:#000000;fill-opacity:1" />
|
||||
<rect
|
||||
width="11.895135"
|
||||
height="11.56937"
|
||||
ry="2.7001941e-017"
|
||||
x="2.2307839"
|
||||
y="56.063431"
|
||||
transform="matrix(0.9396926,-0.3420201,0.3420201,0.9396926,0,0)"
|
||||
id="rect11641"
|
||||
style="fill:url(#linearGradient11653);fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.0237894,0,0,1.0414051,4.9857129,9.0513362)"
|
||||
clip-path="url(#clip7)"
|
||||
id="g1139" />
|
||||
<g
|
||||
transform="matrix(1.0237894,0,0,1.0414051,0.779179,8.770206)"
|
||||
id="g11677"
|
||||
style="fill:#816392;fill-opacity:0.86179516">
|
||||
<g
|
||||
transform="translate(4.2261802,1.3274155)"
|
||||
clip-path="url(#clip32)"
|
||||
id="g11679"
|
||||
style="fill:#816392;fill-opacity:0.86179516">
|
||||
<path
|
||||
d="m 26.128906,23.296875 c 2.015625,1.242187 4.480469,1.78125 6.523438,0.195313 1.574218,-1.210938 1.84375,-3.335938 1.324218,-5.414063 -0.917968,-3.585937 -4.5625,-7.527344 -4.5625,-7.527344 L 18.972656,-2.15625 c 0,0 -0.136718,-0.171875 -0.433594,-0.261719 -0.414062,-0.117187 -1.035156,0.261719 -0.683593,1.027344 l 10.910156,13.445313 c 0,0 3.265625,3.304687 3.886719,6.472656 0.296875,1.484375 0.214844,2.960937 -0.917969,3.871094 C 30.214844,23.621094 28.289062,22.9375 26.605469,21.863281 24.34375,20.417969 22.429688,18.09375 22.429688,18.09375 L 7.777344,0.679688 7.613281,2.613281 21.472656,19.136719 c 0,0 2.082032,2.566406 4.65625,4.160156"
|
||||
id="path11681"
|
||||
style="fill:#816392;fill-opacity:0.86179516;fill-rule:nonzero;stroke:none" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(4.2261807,1.4535628)"
|
||||
clip-path="url(#clip48)"
|
||||
id="g11683"
|
||||
style="fill:#816392;fill-opacity:0.86179516">
|
||||
<path
|
||||
d="m 28.304688,15.984375 c 2.003906,2.550781 0.160156,3.707031 -1.125,3.027344 -1.875,-1 -4.425782,-4.386719 -4.425782,-4.386719 L 15.613281,6.003906 C 15.070312,5.257812 14.429688,5.644531 14.296875,6.09375 c -0.09766,0.347656 0.238281,0.683594 0.238281,0.683594 l 7.191406,8.703125 c 0,0 2.207032,2.601562 3.398438,3.738281 3.5,3.367188 7.761719,0.152344 4.050781,-4.558594 C 27.074219,12 23.867188,8.175781 23.867188,8.175781 L 13.371094,-4.707031 11.859375,-4.839844 11.789062,-4.050781 22.511719,8.71875 c 0,0 3.636719,4.523438 5.792969,7.265625"
|
||||
id="path11685"
|
||||
style="fill:#816392;fill-opacity:0.86179516;fill-rule:nonzero;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1.0237894,0,0,1.0414051,0.659598,7.725718)"
|
||||
id="g11671">
|
||||
<g
|
||||
transform="translate(4.2261802,1.3274155)"
|
||||
clip-path="url(#clip32)"
|
||||
id="g3875">
|
||||
<path
|
||||
d="m 26.128906,23.296875 c 2.015625,1.242187 4.480469,1.78125 6.523438,0.195313 1.574218,-1.210938 1.84375,-3.335938 1.324218,-5.414063 -0.917968,-3.585937 -4.5625,-7.527344 -4.5625,-7.527344 L 18.972656,-2.15625 c 0,0 -0.136718,-0.171875 -0.433594,-0.261719 -0.414062,-0.117187 -1.035156,0.261719 -0.683593,1.027344 l 10.910156,13.445313 c 0,0 3.265625,3.304687 3.886719,6.472656 0.296875,1.484375 0.214844,2.960937 -0.917969,3.871094 C 30.214844,23.621094 28.289062,22.9375 26.605469,21.863281 24.34375,20.417969 22.429688,18.09375 22.429688,18.09375 L 7.777344,0.679688 7.613281,2.613281 21.472656,19.136719 c 0,0 2.082032,2.566406 4.65625,4.160156"
|
||||
id="path3877"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(4.2261807,1.4535628)"
|
||||
clip-path="url(#clip48)"
|
||||
id="g6171">
|
||||
<path
|
||||
d="m 28.304688,15.984375 c 2.003906,2.550781 0.160156,3.707031 -1.125,3.027344 -1.875,-1 -4.425782,-4.386719 -4.425782,-4.386719 L 15.613281,6.003906 C 15.070312,5.257812 14.429688,5.644531 14.296875,6.09375 c -0.09766,0.347656 0.238281,0.683594 0.238281,0.683594 l 7.191406,8.703125 c 0,0 2.207032,2.601562 3.398438,3.738281 3.5,3.367188 7.761719,0.152344 4.050781,-4.558594 C 27.074219,12 23.867188,8.175781 23.867188,8.175781 L 13.371094,-4.707031 11.859375,-4.839844 11.789062,-4.050781 22.511719,8.71875 c 0,0 3.636719,4.523438 5.792969,7.265625"
|
||||
id="path6173"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
<text
|
||||
x="6.855123"
|
||||
y="40.434292"
|
||||
transform="scale(0.9915063,1.0085665)"
|
||||
id="text11553"
|
||||
xml:space="preserve"
|
||||
style="font-size:41.30238724px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"><tspan
|
||||
x="6.855123"
|
||||
y="40.434292"
|
||||
id="tspan11555" /></text>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(0.10181328,0,0,0.10181328,23.503861,90.420112)"
|
||||
id="g8370">
|
||||
<path
|
||||
d="m 20.795,23.442 c 41.244,-1.555 81.688,-2.171 104.79,17.582 10.619,27.832 15.009,83.404 4.99,117.209 -18.421,31.11 -62.996,31.504 -109.78,29.303 0,-54.699 0,-109.396 0,-164.094 z m 34.93,134.79 c 11.643,0 23.287,0 34.93,0 16.604,-30.859 22.254,-125.032 -34.93,-105.488 0,35.163 0,70.326 0,105.488 z"
|
||||
id="path8372"
|
||||
style="fill-rule:evenodd" />
|
||||
<path
|
||||
d="m 165.505,23.442 c 33.267,0 66.533,0 99.8,0 7.251,46.182 16.995,89.436 34.931,123.07 14.479,-35.739 17.123,-85.377 29.939,-123.07 11.644,0 23.287,0 34.93,0 -12.062,58.113 -35.35,103.042 -44.909,164.093 -14.971,0 -29.94,0 -44.91,0 -9.471,-49.436 -28.165,-88.039 -39.92,-134.791 -23.287,0 -46.573,0 -69.86,0 -0.001,-9.767 -0.001,-19.535 -0.001,-29.302 z"
|
||||
id="path8374"
|
||||
style="fill-rule:evenodd" />
|
||||
<path
|
||||
d="m 385.065,76.186 c 12.301,-0.773 24.042,-0.888 29.939,5.86 -2.146,26.978 -3.585,49.781 4.99,76.186 45.408,12.307 33.898,-42.234 34.93,-82.046 9.98,0 19.96,0 29.94,0 0,37.116 0,74.232 0,111.349 -22.437,6.814 -26.368,-8.103 -29.94,-23.442 -14.933,22.293 -52.771,32.37 -69.859,5.86 0,-31.255 0,-62.511 0,-93.767 z"
|
||||
id="path8376"
|
||||
style="fill-rule:evenodd" />
|
||||
<path
|
||||
d="m 165.505,87.907 c 9.98,0 19.96,0 29.94,0 -2.328,48.057 6.913,109.699 -9.98,140.651 -23.857,2.642 -21.052,2.642 -44.91,0 24.686,-27.659 29.399,-78.774 24.95,-140.651 z"
|
||||
id="path8378"
|
||||
style="fill-rule:evenodd" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 17 KiB |
758
imgsrc/random.svg
Normal file
@ -0,0 +1,758 @@
|
||||
<?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="svg10643"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.46+devel"
|
||||
sodipodi:docname="pointer.svgz"
|
||||
inkscape:output_extension="org.inkscape.output.svgz.inkscape"
|
||||
inkscape:export-filename="/home/pinheiro/pics/oxygen-icons/scalable/actions/small/32x32/pointer.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
version="1.0">
|
||||
<defs
|
||||
id="defs10645">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 12 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="24 : 12 : 1"
|
||||
inkscape:persp3d-origin="12 : 8 : 1"
|
||||
id="perspective108" />
|
||||
<linearGradient
|
||||
id="linearGradient3233">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3235" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3237" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3866">
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="0"
|
||||
style="stop-color:#fff299;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3870"
|
||||
offset="1"
|
||||
style="stop-color:#dcd8bd;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient11059">
|
||||
<stop
|
||||
style="stop-color:#727272;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop11061" />
|
||||
<stop
|
||||
id="stop11067"
|
||||
offset="0.5"
|
||||
style="stop-color:#a6a6a6;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#cdcdcd;stop-opacity:1;"
|
||||
offset="0.75"
|
||||
id="stop11069" />
|
||||
<stop
|
||||
style="stop-color:#acacac;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop11063" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient10925">
|
||||
<stop
|
||||
style="stop-color:#bf0303;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop10927" />
|
||||
<stop
|
||||
id="stop10978"
|
||||
offset="0.39309064"
|
||||
style="stop-color:#bf0303;stop-opacity:0;" />
|
||||
<stop
|
||||
id="stop10935"
|
||||
offset="0.46538317"
|
||||
style="stop-color:#bf0303;stop-opacity:0.49803922;" />
|
||||
<stop
|
||||
style="stop-color:#bf0303;stop-opacity:1;"
|
||||
offset="0.5"
|
||||
id="stop10976" />
|
||||
<stop
|
||||
id="stop10933"
|
||||
offset="0.5"
|
||||
style="stop-color:#bf0303;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#bf0303;stop-opacity:0.49803922;"
|
||||
offset="0.55339807"
|
||||
id="stop10937" />
|
||||
<stop
|
||||
id="stop10980"
|
||||
offset="0.60542935"
|
||||
style="stop-color:#bf0303;stop-opacity:0;" />
|
||||
<stop
|
||||
style="stop-color:#bf0303;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop10929" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient10901">
|
||||
<stop
|
||||
id="stop10903"
|
||||
offset="0"
|
||||
style="stop-color:#fff299;stop-opacity:0;" />
|
||||
<stop
|
||||
style="stop-color:#fff299;stop-opacity:1;"
|
||||
offset="0.5"
|
||||
id="stop10909" />
|
||||
<stop
|
||||
id="stop10905"
|
||||
offset="1"
|
||||
style="stop-color:#fff299;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient10854">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10856" />
|
||||
<stop
|
||||
id="stop10862"
|
||||
offset="0.5"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop10858" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient10711">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop10713" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop10715" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient10875"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8967678,0.05935673,-0.05873468,0.8873664,-5.4012494,0.1392525)"
|
||||
spreadMethod="reflect"
|
||||
cx="18.708233"
|
||||
cy="24.759357"
|
||||
fx="18.708233"
|
||||
fy="24.759357"
|
||||
r="13.169441" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10925"
|
||||
id="radialGradient10931"
|
||||
cx="9.996233"
|
||||
cy="23.364098"
|
||||
fx="7.6629176"
|
||||
fy="18.295921"
|
||||
r="8.7188435"
|
||||
gradientTransform="matrix(3.0577456,1.8802807,-0.9054531,1.4724637,3.4545267,-24.480143)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient10968"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8967678,0.05935673,-0.05873468,0.8873664,-5.4012494,0.1392525)"
|
||||
spreadMethod="reflect"
|
||||
cx="18.708233"
|
||||
cy="24.759357"
|
||||
fx="18.708233"
|
||||
fy="24.759357"
|
||||
r="13.169441" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10925"
|
||||
id="radialGradient10971"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7002217,0.5715519,-0.4374946,2.0668853,-4.8632848,-26.818351)"
|
||||
cx="9.1802711"
|
||||
cy="24.942194"
|
||||
fx="6.0336409"
|
||||
fy="17.669048"
|
||||
r="8.7188435" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath10999">
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
id="path11001"
|
||||
d="M 3.6413483,1.9681703 3.779696,17.490509 14.887308,19.785771 21.079035,17.498126 3.6413483,1.9681703 z"
|
||||
style="fill:#ff80ff;fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
</clipPath>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10925"
|
||||
id="radialGradient11003"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7002217,0.5715519,-0.4374946,2.0668853,-4.8632848,-26.818351)"
|
||||
cx="8.2921495"
|
||||
cy="23.935163"
|
||||
fx="8.2488832"
|
||||
fy="19.781427"
|
||||
r="8.7188435" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10925"
|
||||
id="radialGradient11030"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7002217,0.5715519,-0.4374946,2.0668853,-4.8632848,-26.818351)"
|
||||
cx="8.2921495"
|
||||
cy="23.935163"
|
||||
fx="8.2488832"
|
||||
fy="19.781427"
|
||||
r="8.7188435" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient11032"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8967678,0.05935673,-0.05873468,0.8873664,-5.4012494,0.1392525)"
|
||||
spreadMethod="reflect"
|
||||
cx="18.708233"
|
||||
cy="24.759357"
|
||||
fx="18.708233"
|
||||
fy="24.759357"
|
||||
r="13.169441" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10925"
|
||||
id="radialGradient11034"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(2.7002217,0.5715519,-0.4374946,2.0668853,-4.8632848,-26.818351)"
|
||||
cx="8.2921495"
|
||||
cy="23.935163"
|
||||
fx="8.2488832"
|
||||
fy="19.781427"
|
||||
r="8.7188435" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3294"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.7030075,0.6357809,-0.8060735,0.8913044,14.84311,-8.1934483)"
|
||||
spreadMethod="reflect"
|
||||
cx="16.993044"
|
||||
cy="20.648924"
|
||||
fx="16.993044"
|
||||
fy="20.648924"
|
||||
r="13.169441" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="linearGradient3297"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9823337,0,0,0.9823337,0.03300739,0.6182451)"
|
||||
spreadMethod="pad"
|
||||
x1="19.879225"
|
||||
y1="12.061514"
|
||||
x2="16.034332"
|
||||
y2="15.552854" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="linearGradient3353"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9823337,0,0,0.9823337,0.03300739,0.6182451)"
|
||||
spreadMethod="pad"
|
||||
x1="19.879225"
|
||||
y1="12.061514"
|
||||
x2="16.034332"
|
||||
y2="15.552854" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3355"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.7030075,0.6357809,-0.8060735,0.8913044,14.84311,-8.1934483)"
|
||||
spreadMethod="reflect"
|
||||
cx="16.993044"
|
||||
cy="20.648924"
|
||||
fx="16.993044"
|
||||
fy="20.648924"
|
||||
r="13.169441" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="linearGradient3362"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9823337,0,0,0.9823337,0.03300739,0.6182451)"
|
||||
spreadMethod="pad"
|
||||
x1="19.879225"
|
||||
y1="12.061514"
|
||||
x2="16.034332"
|
||||
y2="15.552854" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3364"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8341244,0.2489558,-0.2435026,0.8158514,0.7851109,-0.01382395)"
|
||||
spreadMethod="reflect"
|
||||
cx="17.54755"
|
||||
cy="21.708042"
|
||||
fx="17.54755"
|
||||
fy="21.708042"
|
||||
r="13.169441" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3367"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8151394,0.2358626,-0.2306962,0.7972824,0.7011221,-1.0582457)"
|
||||
spreadMethod="reflect"
|
||||
cx="17.54755"
|
||||
cy="21.708042"
|
||||
fx="17.54755"
|
||||
fy="21.708042"
|
||||
r="13.169441" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="linearGradient3370"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9575785,-0.00803118,0.00803118,0.9575785,-0.0268605,-0.4359562)"
|
||||
spreadMethod="pad"
|
||||
x1="19.879225"
|
||||
y1="12.061514"
|
||||
x2="16.034332"
|
||||
y2="15.552854" />
|
||||
<linearGradient
|
||||
y2="19.626715"
|
||||
x2="10.711697"
|
||||
y1="18.63658"
|
||||
x1="9.7192469"
|
||||
gradientTransform="matrix(3.547255,-0.03993894,0.03993894,3.547255,-27.397339,-48.790495)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3488"
|
||||
xlink:href="#linearGradient10711"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="1.15625"
|
||||
fy="20.478674"
|
||||
fx="11.413477"
|
||||
cy="20.478674"
|
||||
cx="11.413477"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(1.7083003,-0.01851949,0.01798426,1.6589328,-8.4797796,-13.189665)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3486"
|
||||
xlink:href="#linearGradient3330"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="19.626715"
|
||||
x2="10.711697"
|
||||
y1="18.63658"
|
||||
x1="9.7192469"
|
||||
gradientTransform="matrix(3.5474799,0,0,3.5474799,-26.927898,-62.356391)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3475"
|
||||
xlink:href="#linearGradient10711"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="1.15625"
|
||||
fy="20.478674"
|
||||
fx="11.413477"
|
||||
cy="20.478674"
|
||||
cx="11.413477"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(1.7083003,-0.01851949,0.01798426,1.6589328,-8.4797796,-13.189665)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3473"
|
||||
xlink:href="#linearGradient3330"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
spreadMethod="reflect"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.7809876,0.01449707,-0.0055455,0.2987498,-0.2924169,2.0957246)"
|
||||
r="11.765625"
|
||||
fy="10.911069"
|
||||
fx="1.1416299"
|
||||
cy="10.911069"
|
||||
cx="1.1416299"
|
||||
id="radialGradient3317"
|
||||
xlink:href="#linearGradient3206"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="26.641653"
|
||||
x2="16.836901"
|
||||
y1="6.8943019"
|
||||
x1="5.6869311"
|
||||
gradientTransform="translate(0,-7.2094174)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3265"
|
||||
xlink:href="#linearGradient3267"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="17.133453"
|
||||
x2="16.836901"
|
||||
y1="-2.6138983"
|
||||
x1="5.6869311"
|
||||
gradientTransform="translate(0,2.298783)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3261"
|
||||
xlink:href="#linearGradient3267"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(0,-4.8361309)"
|
||||
y2="24.268368"
|
||||
x2="16.836901"
|
||||
y1="4.5210156"
|
||||
x1="5.6869311"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3257"
|
||||
xlink:href="#linearGradient3267"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(0,-2.4628444)"
|
||||
y2="21.895081"
|
||||
x2="16.836901"
|
||||
y1="2.1477292"
|
||||
x1="5.6869311"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3249"
|
||||
xlink:href="#linearGradient3267"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
y2="19.432236"
|
||||
x2="16.836901"
|
||||
y1="-0.31511527"
|
||||
x1="5.6869311"
|
||||
id="linearGradient3239"
|
||||
xlink:href="#linearGradient3267"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
y2="19.626715"
|
||||
x2="10.711697"
|
||||
y1="18.384007"
|
||||
x1="9.8687286"
|
||||
gradientTransform="matrix(3.6334443,0,0,3.6334443,-27.580699,-51.677773)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3220"
|
||||
xlink:href="#linearGradient10711"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
r="1.15625"
|
||||
fy="20.478674"
|
||||
fx="11.413477"
|
||||
cy="20.478674"
|
||||
cx="11.413477"
|
||||
spreadMethod="pad"
|
||||
gradientTransform="matrix(1.7083003,-0.01851949,0.01798426,1.6589328,-8.4797796,-13.189665)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3218"
|
||||
xlink:href="#linearGradient10711"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient2657">
|
||||
<stop
|
||||
id="stop2659"
|
||||
offset="0"
|
||||
style="stop-color:#ff80ff;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop2661"
|
||||
offset="1"
|
||||
style="stop-color:#ff80ff;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3206">
|
||||
<stop
|
||||
style="stop-color:#b1d28f;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3208" />
|
||||
<stop
|
||||
style="stop-color:#b1d28f;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3210" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3241">
|
||||
<stop
|
||||
id="stop3243"
|
||||
offset="0"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3245"
|
||||
offset="1"
|
||||
style="stop-color:#debc85;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3267">
|
||||
<stop
|
||||
style="stop-color:#debc85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3269" />
|
||||
<stop
|
||||
style="stop-color:#debc85;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3271" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3273">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3275" />
|
||||
<stop
|
||||
style="stop-color:#debc85;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3277" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3279">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3281" />
|
||||
<stop
|
||||
style="stop-color:#debc85;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3283" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3285">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3287" />
|
||||
<stop
|
||||
style="stop-color:#debc85;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3289" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3330">
|
||||
<stop
|
||||
style="stop-color:#ff80ff;stop-opacity:0;"
|
||||
offset="0"
|
||||
id="stop3332" />
|
||||
<stop
|
||||
style="stop-color:#666666;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3334" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient4021"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.9318803,-0.2210697,0.2308678,0.9731826,-3.9252239,2.7241703)"
|
||||
spreadMethod="pad"
|
||||
cx="11.074039"
|
||||
cy="20.428291"
|
||||
fx="11.074039"
|
||||
fy="20.428291"
|
||||
r="1.15625" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="linearGradient4023"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8514941,0.5243642,-0.5243642,0.8514941,24.154135,2.8247022)"
|
||||
x1="21.461079"
|
||||
y1="23.349636"
|
||||
x2="22.96941"
|
||||
y2="28.038134" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="linearGradient4030"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.8514941,0.5243642,-0.5243642,0.8514941,18.007546,-15.657615)"
|
||||
x1="21.461079"
|
||||
y1="23.349636"
|
||||
x2="22.96941"
|
||||
y2="28.038134" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
x="-0.20028582"
|
||||
width="1.4005716"
|
||||
y="-0.11837127"
|
||||
height="1.2367425"
|
||||
id="filter3484">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.97202214"
|
||||
id="feGaussianBlur3486" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3490"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1086176,-0.4093269,0.6608062,1.7897223,-9.2289678,-4.0397151)"
|
||||
spreadMethod="reflect"
|
||||
cx="8.8133469"
|
||||
cy="14.235861"
|
||||
fx="8.8133469"
|
||||
fy="14.235861"
|
||||
r="5.3238101" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath3496">
|
||||
<rect
|
||||
style="opacity:0.62633481;fill:none;stroke:#000000;stroke-width:0.19602102;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="rect3498"
|
||||
width="13.277639"
|
||||
height="22.63365"
|
||||
x="5.309958"
|
||||
y="1.2316679"
|
||||
ry="1.171887" />
|
||||
</clipPath>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3508"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.1106976,-0.4036489,0.6516398,1.7930801,-9.2127514,-4.7972628)"
|
||||
spreadMethod="reflect"
|
||||
cx="8.8133469"
|
||||
cy="14.235861"
|
||||
fx="8.8133469"
|
||||
fy="14.235861"
|
||||
r="5.3238101" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3233"
|
||||
id="linearGradient3240"
|
||||
x1="9.4485903"
|
||||
y1="2.761672"
|
||||
x2="7.6776314"
|
||||
y2="19.013866"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(4.1741381,0,0,4.1613891,14.977639,14.527008)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient10711"
|
||||
id="radialGradient3253"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(4.7157796,-1.4428762,2.6160831,8.4980426,-22.699134,-22.277012)"
|
||||
spreadMethod="reflect"
|
||||
cx="8.2230186"
|
||||
cy="14.316785"
|
||||
fx="8.2230186"
|
||||
fy="14.316785"
|
||||
r="5.3238101" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3757"
|
||||
x="-0.14567212"
|
||||
width="1.2913442"
|
||||
y="-0.098205952"
|
||||
height="1.1964119">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.79012916"
|
||||
id="feGaussianBlur3759" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2"
|
||||
inkscape:cx="8.5584572"
|
||||
inkscape:cy="52.628863"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="742"
|
||||
inkscape:window-x="296"
|
||||
inkscape:window-y="56"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
width="24px"
|
||||
height="24px"
|
||||
inkscape:object-paths="false"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-nodes="false"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3664"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
spacingx="2.6666px"
|
||||
spacingy="2.6666px" />
|
||||
<sodipodi:guide
|
||||
orientation="1,0"
|
||||
position="10.507812,7.328125"
|
||||
id="guide3666" />
|
||||
<sodipodi:guide
|
||||
orientation="0,1"
|
||||
position="10.292968,7.5546875"
|
||||
id="guide3668" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata10648">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;opacity:0.18099551;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter3757);enable-background:accumulate;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans"
|
||||
d="m 5,19 c 5.24e-5,0.523584 0.4764155,0.999948 1,1 l 3.59375,0 2.5,2.5625 c 0.272702,0.267764 0.706204,0.357015 1.0625,0.21875 l 1.25,-0.46875 c 0.353635,-0.127466 0.619754,-0.46962 0.65625,-0.84375 l 0.34375,-3.3125 2.40625,-3 c 0.296435,-0.374818 0.26821,-0.967546 -0.0625,-1.3125 L 5.2034921,1.0488435 5,19 z"
|
||||
id="path3670"
|
||||
sodipodi:nodetypes="cccccccccccc"
|
||||
transform="matrix(3.8351065,0,0,3.8305733,20.000787,24.35592)" />
|
||||
<path
|
||||
style="fill:#201020;fill-rule:evenodd;stroke:#595959;stroke-width:5.33333349;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 40.022468,18.688398 0,66.582224 16.696553,0 L 68.436432,97.122145 73.70512,95.153883 75.247683,80.05609 86.03678,66.636329 40.022468,18.688398 z"
|
||||
id="path3502"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
id="path3504"
|
||||
d="m 40.022468,18.688397 0,66.58222 16.696554,0 11.717412,11.851511 5.268688,-1.968253 1.54256,-15.09779 L 85.892267,66.67168 40.022468,18.688397 z"
|
||||
style="fill:#c4c4c4;fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
<path
|
||||
style="fill:url(#radialGradient3253);fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
d="m 40.022468,18.688397 0,66.58222 16.696554,0 11.717412,11.851511 5.268688,-1.968253 1.54256,-15.09779 10.56036,-13.22296 -45.785574,-48.144728 z"
|
||||
id="path3506"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:url(#linearGradient3240);stroke-width:2.667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="M 41.38037,83.915714 58.396539,84.11082 69.204513,94.929961 72.193882,93.656427 73.839587,79.399575 84.08764,66.742537 41.448646,22.246495 41.38037,83.915714 z"
|
||||
id="path2253"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 25 KiB |
23
recipes/carta_capital.recipe
Normal file
@ -0,0 +1,23 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1312361378(BasicNewsRecipe):
|
||||
title = u'Carta capital'
|
||||
__author__ = 'Pablo Aldama'
|
||||
language = 'pt_BR'
|
||||
oldest_article = 9
|
||||
max_articles_per_feed = 100
|
||||
|
||||
feeds = [(u'Politica', u'http://www.cartacapital.com.br/category/politica/feed')
|
||||
,(u'Economia', u'http://www.cartacapital.com.br/category/economia/feed')
|
||||
,(u'Cultura', u'http://www.cartacapital.com.br/category/cultura/feed')
|
||||
,(u'Internacional', u'http://www.cartacapital.com.br/category/internacional/feed')
|
||||
,(u'Saude', u'http://www.cartacapital.com.br/category/saude/feed')
|
||||
,(u'Sociedade', u'http://www.cartacapital.com.br/category/sociedade/feed')
|
||||
,(u'Tecnologia', u'http://www.cartacapital.com.br/category/tecnologia/feed')
|
||||
,(u'Carta na escola', u'http://www.cartacapital.com.br/category/carta-na-escola/feed')
|
||||
,(u'Carta fundamental', u'http://www.cartacapital.com.br/category/carta-fundamental/feed')
|
||||
,(u'Carta verde', u'http://www.cartacapital.com.br/category/carta-verde/feed')
|
||||
|
||||
]
|
||||
def print_version(self, url):
|
||||
return url + '/print'
|
@ -30,8 +30,14 @@ class CnetNews(BasicNewsRecipe):
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'id':'tweetmemeAndFacebook'})
|
||||
,dict(name='ul', attrs={'class':'contentTools'})
|
||||
,dict(name='aside', attrs={'id':'filed'})
|
||||
,dict(name='div', attrs={'class':'postLinks'})
|
||||
,dict(name='span', attrs={'class':'shareButton'})
|
||||
,dict(name='span', attrs={'class':'printButton'})
|
||||
,dict(name='span', attrs={'class':'emailButton'})
|
||||
,dict(name='div', attrs={'class':'editorBio'})
|
||||
]
|
||||
keep_only_tags = dict(name='div', attrs={'class':'txtWrap'})
|
||||
keep_only_tags = dict(name='div', attrs={'class':'post'})
|
||||
|
||||
feeds = [(u'News', u'http://news.cnet.com/2547-1_3-0-20.xml')]
|
||||
|
||||
|
@ -9,7 +9,7 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag, NavigableString
|
||||
from collections import OrderedDict
|
||||
|
||||
import time, re
|
||||
import re
|
||||
|
||||
class Economist(BasicNewsRecipe):
|
||||
|
||||
@ -31,45 +31,41 @@ class Economist(BasicNewsRecipe):
|
||||
{'class': lambda x: x and 'share-links-header' in x},
|
||||
]
|
||||
keep_only_tags = [dict(id='ec-article-body')]
|
||||
needs_subscription = False
|
||||
no_stylesheets = True
|
||||
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
|
||||
lambda x:'</html>')]
|
||||
|
||||
# economist.com has started throttling after about 60% of the total has
|
||||
# downloaded with connection reset by peer (104) errors.
|
||||
delay = 1
|
||||
|
||||
needs_subscription = False
|
||||
'''
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
br.open('http://www.economist.com')
|
||||
req = mechanize.Request(
|
||||
'http://www.economist.com/members/members.cfm?act=exec_login',
|
||||
headers = {
|
||||
'Referer':'http://www.economist.com/',
|
||||
},
|
||||
data=urllib.urlencode({
|
||||
'logging_in' : 'Y',
|
||||
'returnURL' : '/',
|
||||
'email_address': self.username,
|
||||
'fakepword' : 'Password',
|
||||
'pword' : self.password,
|
||||
'x' : '0',
|
||||
'y' : '0',
|
||||
}))
|
||||
br.open(req).read()
|
||||
if self.username and self.password:
|
||||
br.open('http://www.economist.com/user/login')
|
||||
br.select_form(nr=1)
|
||||
br['name'] = self.username
|
||||
br['pass'] = self.password
|
||||
res = br.submit()
|
||||
raw = res.read()
|
||||
if '>Log out<' not in raw:
|
||||
raise ValueError('Failed to login to economist.com. '
|
||||
'Check your username and password.')
|
||||
return br
|
||||
'''
|
||||
|
||||
def parse_index(self):
|
||||
try:
|
||||
return self.economist_parse_index()
|
||||
except:
|
||||
raise
|
||||
self.log.warn(
|
||||
'Initial attempt to parse index failed, retrying in 30 seconds')
|
||||
time.sleep(30)
|
||||
return self.economist_parse_index()
|
||||
return self.economist_parse_index()
|
||||
|
||||
def economist_parse_index(self):
|
||||
soup = self.index_to_soup(self.INDEX)
|
||||
div = soup.find('div', attrs={'class':'issue-image'})
|
||||
if div is not None:
|
||||
img = div.find('img', src=True)
|
||||
if img is not None:
|
||||
self.cover_url = img['src']
|
||||
feeds = OrderedDict()
|
||||
for section in soup.findAll(attrs={'class':lambda x: x and 'section' in
|
||||
x}):
|
||||
@ -109,7 +105,9 @@ class Economist(BasicNewsRecipe):
|
||||
'description':'', 'date':''})
|
||||
|
||||
if articles:
|
||||
feeds[section_title] = articles
|
||||
if section_title not in feeds:
|
||||
feeds[section_title] = []
|
||||
feeds[section_title] += articles
|
||||
|
||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||
if not ans:
|
||||
|
@ -1,3 +1,140 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
'''
|
||||
economist.com
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import Tag, NavigableString
|
||||
from collections import OrderedDict
|
||||
|
||||
import time, re
|
||||
|
||||
class Economist(BasicNewsRecipe):
|
||||
|
||||
title = 'The Economist'
|
||||
language = 'en'
|
||||
|
||||
__author__ = "Kovid Goyal"
|
||||
INDEX = 'http://www.economist.com/printedition'
|
||||
description = ('Global news and current affairs from a European'
|
||||
' perspective. Best downloaded on Friday mornings (GMT)')
|
||||
extra_css = '.headline {font-size: x-large;} \n h2 { font-size: small; } \n h1 { font-size: medium; }'
|
||||
oldest_article = 7.0
|
||||
cover_url = 'http://media.economist.com/sites/default/files/imagecache/print-cover-thumbnail/print-covers/currentcoverus_large.jpg'
|
||||
#cover_url = 'http://www.economist.com/images/covers/currentcoverus_large.jpg'
|
||||
remove_tags = [
|
||||
dict(name=['script', 'noscript', 'title', 'iframe', 'cf_floatingcontent']),
|
||||
dict(attrs={'class':['dblClkTrk', 'ec-article-info',
|
||||
'share_inline_header', 'related-items']}),
|
||||
{'class': lambda x: x and 'share-links-header' in x},
|
||||
]
|
||||
keep_only_tags = [dict(id='ec-article-body')]
|
||||
needs_subscription = False
|
||||
no_stylesheets = True
|
||||
preprocess_regexps = [(re.compile('</html>.*', re.DOTALL),
|
||||
lambda x:'</html>')]
|
||||
|
||||
# economist.com has started throttling after about 60% of the total has
|
||||
# downloaded with connection reset by peer (104) errors.
|
||||
delay = 1
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
try:
|
||||
return self.economist_parse_index()
|
||||
except:
|
||||
raise
|
||||
self.log.warn(
|
||||
'Initial attempt to parse index failed, retrying in 30 seconds')
|
||||
time.sleep(30)
|
||||
return self.economist_parse_index()
|
||||
|
||||
def economist_parse_index(self):
|
||||
soup = self.index_to_soup(self.INDEX)
|
||||
div = soup.find('div', attrs={'class':'issue-image'})
|
||||
if div is not None:
|
||||
img = div.find('img', src=True)
|
||||
if img is not None:
|
||||
self.cover_url = img['src']
|
||||
feeds = OrderedDict()
|
||||
for section in soup.findAll(attrs={'class':lambda x: x and 'section' in
|
||||
x}):
|
||||
h4 = section.find('h4')
|
||||
if h4 is None:
|
||||
continue
|
||||
section_title = self.tag_to_string(h4).strip()
|
||||
if not section_title:
|
||||
continue
|
||||
self.log('Found section: %s'%section_title)
|
||||
articles = []
|
||||
for h5 in section.findAll('h5'):
|
||||
article_title = self.tag_to_string(h5).strip()
|
||||
if not article_title:
|
||||
continue
|
||||
data = h5.findNextSibling(attrs={'class':'article'})
|
||||
if data is None: continue
|
||||
a = data.find('a', href=True)
|
||||
if a is None: continue
|
||||
url = a['href']
|
||||
if url.startswith('/'): url = 'http://www.economist.com'+url
|
||||
url += '/print'
|
||||
article_title += ': %s'%self.tag_to_string(a).strip()
|
||||
articles.append({'title':article_title, 'url':url,
|
||||
'description':'', 'date':''})
|
||||
if not articles:
|
||||
# We have last or first section
|
||||
for art in section.findAll(attrs={'class':'article'}):
|
||||
a = art.find('a', href=True)
|
||||
if a is not None:
|
||||
url = a['href']
|
||||
if url.startswith('/'): url = 'http://www.economist.com'+url
|
||||
url += '/print'
|
||||
title = self.tag_to_string(a)
|
||||
if title:
|
||||
articles.append({'title':title, 'url':url,
|
||||
'description':'', 'date':''})
|
||||
|
||||
if articles:
|
||||
if section_title not in feeds:
|
||||
feeds[section_title] = []
|
||||
feeds[section_title] += articles
|
||||
|
||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||
if not ans:
|
||||
raise Exception('Could not find any articles, either the '
|
||||
'economist.com server is having trouble and you should '
|
||||
'try later or the website format has changed and the '
|
||||
'recipe needs to be updated.')
|
||||
return ans
|
||||
|
||||
def eco_find_image_tables(self, soup):
|
||||
for x in soup.findAll('table', align=['right', 'center']):
|
||||
if len(x.findAll('font')) in (1,2) and len(x.findAll('img')) == 1:
|
||||
yield x
|
||||
|
||||
def postprocess_html(self, soup, first):
|
||||
body = soup.find('body')
|
||||
for name, val in body.attrs:
|
||||
del body[name]
|
||||
|
||||
for table in list(self.eco_find_image_tables(soup)):
|
||||
caption = table.find('font')
|
||||
img = table.find('img')
|
||||
div = Tag(soup, 'div')
|
||||
div['style'] = 'text-align:left;font-size:70%'
|
||||
ns = NavigableString(self.tag_to_string(caption))
|
||||
div.insert(0, ns)
|
||||
div.insert(1, Tag(soup, 'br'))
|
||||
del img['width']
|
||||
del img['height']
|
||||
img.extract()
|
||||
div.insert(2, img)
|
||||
table.replaceWith(div)
|
||||
return soup
|
||||
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.utils.threadpool import ThreadPool, makeRequests
|
||||
from calibre.ebooks.BeautifulSoup import Tag, NavigableString
|
||||
@ -145,3 +282,5 @@ class Economist(BasicNewsRecipe):
|
||||
div.insert(2, img)
|
||||
table.replaceWith(div)
|
||||
return soup
|
||||
'''
|
||||
|
||||
|
40
recipes/el_mostrador.recipe
Normal file
@ -0,0 +1,40 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1313609361(BasicNewsRecipe):
|
||||
news = True
|
||||
title = u'El Mostrador'
|
||||
__author__ = 'Alex Mitrani'
|
||||
description = u'Chilean online newspaper'
|
||||
publisher = u'La Plaza S.A.'
|
||||
category = 'news, rss'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
summary_length = 1000
|
||||
language = 'es_CL'
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
masthead_url = 'http://www.elmostrador.cl/assets/img/logo-elmostrador-m.jpg'
|
||||
remove_tags_before = dict(name='div', attrs={'class':'news-heading cf'})
|
||||
remove_tags_after = dict(name='div', attrs={'class':'footer-actions cf'})
|
||||
remove_tags = [dict(name='div', attrs={'class':'footer-actions cb cf'})
|
||||
,dict(name='div', attrs={'class':'news-aside fl'})
|
||||
,dict(name='div', attrs={'class':'footer-actions cf'})
|
||||
,dict(name='div', attrs={'class':'user-bar','id':'top'})
|
||||
,dict(name='div', attrs={'class':'indicators'})
|
||||
,dict(name='div', attrs={'id':'header'})
|
||||
]
|
||||
|
||||
|
||||
feeds = [(u'Temas Destacados'
|
||||
, u'http://www.elmostrador.cl/destacado/feed/')
|
||||
, (u'El D\xeda', u'http://www.elmostrador.cl/dia/feed/')
|
||||
, (u'Pa\xeds', u'http://www.elmostrador.cl/noticias/pais/feed/')
|
||||
, (u'Mundo', u'http://www.elmostrador.cl/noticias/mundo/feed/')
|
||||
, (u'Negocios', u'http://www.elmostrador.cl/noticias/negocios/feed/')
|
||||
, (u'Cultura', u'http://www.elmostrador.cl/noticias/cultura/feed/')
|
||||
, (u'Vida en L\xednea', u'http://www.elmostrador.cl/vida-en-linea/feed/')
|
||||
, (u'Opini\xf3n & Blogs', u'http://www.elmostrador.cl/opinion/feed/')
|
||||
]
|
||||
|
@ -18,7 +18,7 @@ class ElMundo(BasicNewsRecipe):
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'iso8859_15'
|
||||
language = 'es_ES'
|
||||
language = 'es'
|
||||
masthead_url = 'http://estaticos03.elmundo.es/elmundo/iconos/v4.x/v4.01/bg_h1.png'
|
||||
publication_type = 'newspaper'
|
||||
extra_css = """
|
||||
|
28
recipes/escrevinhador.recipe
Normal file
@ -0,0 +1,28 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Escrevinhador(BasicNewsRecipe):
|
||||
title = 'Blog Escrevinhador'
|
||||
__author__ = 'Diniz Bortolotto'
|
||||
description = 'Posts do Blog Escrevinhador'
|
||||
publisher = 'Rodrigo Viana'
|
||||
oldest_article = 5
|
||||
max_articles_per_feed = 20
|
||||
category = 'news, politics, Brazil'
|
||||
language = 'pt_BR'
|
||||
publication_type = 'news and politics portal'
|
||||
use_embedded_content = False
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
|
||||
feeds = [(u'Blog Escrevinhador', u'http://www.rodrigovianna.com.br/feed')]
|
||||
|
||||
reverse_article_order = True
|
||||
|
||||
remove_tags_after = [dict(name='div', attrs={'class':'text'})]
|
||||
|
||||
remove_tags = [
|
||||
dict(id='header'),
|
||||
dict(name='p', attrs={'class':'tags'}),
|
||||
dict(name='div', attrs={'class':'sociable'})
|
||||
]
|
||||
|
BIN
recipes/icons/independent.png
Normal file
After Width: | Height: | Size: 343 B |
@ -1,70 +1,86 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
www.independent.co.uk
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup
|
||||
|
||||
class TheIndependent(BasicNewsRecipe):
|
||||
title = u'The Independent'
|
||||
language = 'en_GB'
|
||||
__author__ = 'Krittika Goyal'
|
||||
oldest_article = 1 #days
|
||||
max_articles_per_feed = 30
|
||||
encoding = 'latin1'
|
||||
title = 'The Independent'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Independent News - Breaking news, comment and features from The Independent newspaper'
|
||||
publisher = 'The Independent'
|
||||
category = 'news, politics, UK'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 200
|
||||
no_stylesheets = True
|
||||
encoding = 'cp1252'
|
||||
use_embedded_content = False
|
||||
language = 'en_GB'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
masthead_url = 'http://www.independent.co.uk/independent.co.uk/images/logo-london.png'
|
||||
extra_css = """
|
||||
h1{font-family: Georgia,serif }
|
||||
body{font-family: Verdana,Arial,Helvetica,sans-serif}
|
||||
img{margin-bottom: 0.4em; display:block}
|
||||
.info,.caption,.credits{font-size: x-small}
|
||||
"""
|
||||
|
||||
no_stylesheets = True
|
||||
#remove_tags_before = dict(name='h1', attrs={'class':'heading'})
|
||||
#remove_tags_after = dict(name='td', attrs={'class':'newptool1'})
|
||||
remove_tags = [
|
||||
dict(name='iframe'),
|
||||
dict(name='div', attrs={'class':'related-articles'}),
|
||||
dict(name='div', attrs={'id':['qrformdiv', 'inSection', 'alpha-inner']}),
|
||||
dict(name='ul', attrs={'class':'article-tools'}),
|
||||
dict(name='ul', attrs={'class':'articleTools'}),
|
||||
]
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : category
|
||||
, 'publisher' : publisher
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
feeds = [
|
||||
('UK',
|
||||
'http://www.independent.co.uk/news/uk/rss'),
|
||||
('World',
|
||||
'http://www.independent.co.uk/news/world/rss'),
|
||||
('Business',
|
||||
'http://www.independent.co.uk/news/business/rss'),
|
||||
('People',
|
||||
'http://www.independent.co.uk/news/people/rss'),
|
||||
('Science',
|
||||
'http://www.independent.co.uk/news/science/rss'),
|
||||
('Media',
|
||||
'http://www.independent.co.uk/news/media/rss'),
|
||||
('Education',
|
||||
'http://www.independent.co.uk/news/education/rss'),
|
||||
('Obituaries',
|
||||
'http://www.independent.co.uk/news/obituaries/rss'),
|
||||
remove_tags =[
|
||||
dict(name=['meta','link','object','embed','iframe','base','style'])
|
||||
,dict(attrs={'class':['related-articles','share','googleCols','article-tools','paging','googleArt']})
|
||||
,dict(attrs={'id':['newsVideoPlayer','yahoobook','google-intext']})
|
||||
]
|
||||
keep_only_tags =[dict(attrs={'id':'article'})]
|
||||
remove_attributes=['lang','onclick','width','xmlns:fb']
|
||||
|
||||
('Opinion',
|
||||
'http://www.independent.co.uk/opinion/rss'),
|
||||
|
||||
('Environment',
|
||||
'http://www.independent.co.uk/environment/rss'),
|
||||
feeds = [
|
||||
(u'UK' , u'http://www.independent.co.uk/news/uk/rss' )
|
||||
,(u'World' , u'http://www.independent.co.uk/news/world/rss' )
|
||||
,(u'Business' , u'http://www.independent.co.uk/news/business/rss' )
|
||||
,(u'People' , u'http://www.independent.co.uk/news/people/rss' )
|
||||
,(u'Science' , u'http://www.independent.co.uk/news/science/rss' )
|
||||
,(u'Media' , u'http://www.independent.co.uk/news/media/rss' )
|
||||
,(u'Education' , u'http://www.independent.co.uk/news/education/rss' )
|
||||
,(u'Leading Articles' , u'http://www.independent.co.uk/opinion/leading-articles/rss')
|
||||
,(u'Comentators' , u'http://www.independent.co.uk/opinion/commentators/rss' )
|
||||
,(u'Columnists' , u'http://www.independent.co.uk/opinion/columnists/rss' )
|
||||
,(u'Letters' , u'http://www.independent.co.uk/opinion/letters/rss' )
|
||||
,(u'Big Question' , u'http://www.independent.co.uk/extras/big-question/rss' )
|
||||
,(u'Sport' , u'http://www.independent.co.uk/sport/rss' )
|
||||
,(u'Life&Style' , u'http://www.independent.co.uk/life-style/rss' )
|
||||
,(u'Arts&Entertainment' , u'http://www.independent.co.uk/arts-entertainment/rss' )
|
||||
,(u'Travel' , u'http://www.independent.co.uk/travel/rss' )
|
||||
,(u'Money' , u'http://www.independent.co.uk/money/rss' )
|
||||
]
|
||||
|
||||
('Sport',
|
||||
'http://www.independent.co.uk/sport/rss'),
|
||||
|
||||
('Life and Style',
|
||||
'http://www.independent.co.uk/life-style/rss'),
|
||||
|
||||
('Arts and Entertainment',
|
||||
'http://www.independent.co.uk/arts-entertainment/rss'),
|
||||
|
||||
('Travel',
|
||||
'http://www.independent.co.uk/travel/rss'),
|
||||
|
||||
('Money',
|
||||
'http://www.independent.co.uk/money/rss'),
|
||||
]
|
||||
def get_article_url(self, article):
|
||||
return article.get('guid', None)
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
story = soup.find(name='div', attrs={'id':'mainColumn'})
|
||||
#td = heading.findParent(name='td')
|
||||
#td.extract()
|
||||
soup = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||
body = soup.find(name='body')
|
||||
body.insert(0, story)
|
||||
return soup
|
||||
for item in soup.body.findAll(style=True):
|
||||
del item['style']
|
||||
for item in soup.body.findAll(['author','preform']):
|
||||
item.name='span'
|
||||
for item in soup.body.findAll('img'):
|
||||
if not item.has_key('alt'):
|
||||
item['alt'] = 'image'
|
||||
for item in soup.body.findAll('div', attrs={'class':['clear-o','body','photoCaption']}):
|
||||
item.name = 'p'
|
||||
for item in soup.body.findAll('div'):
|
||||
if not item.attrs and not item.contents:
|
||||
item.extract()
|
||||
soup2 = BeautifulSoup('<html><head><title>t</title></head><body></body></html>')
|
||||
soup2.body.replaceWith(soup.body)
|
||||
return soup2
|
||||
|
@ -1,55 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008-2009, Darko Miletic <darko.miletic at gmail.com>'
|
||||
__copyright__ = '2011, Oscar Megia Lopez'
|
||||
'''
|
||||
juventudrebelde.cu
|
||||
'''
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
class JuventudRebelde(BasicNewsRecipe):
|
||||
title = u'Juventud Rebelde'
|
||||
__author__ = 'Oscar Megia Lopez'
|
||||
description = 'Periodico cubano'
|
||||
oldest_article = 30
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
#delay = 1
|
||||
use_embedded_content = False
|
||||
encoding = 'utf8'
|
||||
publisher = 'Juventud Rebelde'
|
||||
category = 'Noticias'
|
||||
language = 'es'
|
||||
publication_type = 'Periodico'
|
||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .title{font-weight: bold} .read{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
,'linearize_tables': True
|
||||
}
|
||||
|
||||
class Juventudrebelde(BasicNewsRecipe):
|
||||
title = 'Juventud Rebelde'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Diario de la Juventud Cubana'
|
||||
publisher = 'Juventud rebelde'
|
||||
category = 'news, politics, Cuba'
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
encoding = 'cp1252'
|
||||
language = 'es_CU'
|
||||
|
||||
cover_url = strftime('http://www.juventudrebelde.cu/UserFiles/File/impreso/iportada-%Y-%m-%d.jpg')
|
||||
remove_javascript = True
|
||||
|
||||
html2lrf_options = [
|
||||
'--comment' , description
|
||||
, '--category' , category
|
||||
, '--publisher', publisher
|
||||
, '--ignore-tables'
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':['title']})
|
||||
,dict(attrs={'class':['read']})
|
||||
,dict(attrs={'class':['author']})
|
||||
]
|
||||
|
||||
html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"\nlinearize_tables=True'
|
||||
remove_tags = [
|
||||
dict(name='div', attrs={'class':['share']}),
|
||||
]
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'noticia'})]
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
(u'Generales', u'http://www.juventudrebelde.cu/rss/generales.php' )
|
||||
,(u'Cuba', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=cuba' )
|
||||
,(u'Internacionales', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=internacionales' )
|
||||
,(u'Opinion', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=opinion' )
|
||||
,(u'Cultura', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=cultura' )
|
||||
,(u'Deportes', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=deportes' )
|
||||
,(u'Lectura', u'http://www.juventudrebelde.cu/rss/generales.php?seccion=lectura' )
|
||||
]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
mtag = '<meta http-equiv="Content-Language" content="es-CU"/>'
|
||||
soup.head.insert(0,mtag)
|
||||
for item in soup.findAll(style=True):
|
||||
del item['style']
|
||||
return soup
|
||||
feeds = [(u'Generales', u'http://www.juventudrebelde.cu/get/rss/grupo/generales/'), (u'Internacionales', u'http://www.psychologytoday.com/blog/romance-redux/feed'), (u'Ciencia y Tecnica', u'http://www.juventudrebelde.cu/get/rss/noticias/ciencia-tecnica/'), (u'Opini\xf3n', u'http://www.juventudrebelde.cu/get/rss/noticias/opinion/'), (u'Cuba', u'http://www.juventudrebelde.cu/get/rss/noticias/cuba/'), (u'Cultura', u'http://www.juventudrebelde.cu/get/rss/noticias/cultura/'), (u'Deportes', u'http://www.juventudrebelde.cu/get/rss/noticias/deportes')]
|
||||
|
||||
|
@ -26,7 +26,7 @@ class AdvancedUserRecipe1294946868(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
|
||||
encoding = 'utf-8'
|
||||
language = 'es_ES'
|
||||
language = 'es'
|
||||
timefmt = '[%a, %d %b, %Y]'
|
||||
|
||||
keep_only_tags = [
|
||||
|
@ -17,18 +17,15 @@ class Lanacion(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
no_stylesheets = True
|
||||
language = 'es_AR'
|
||||
delay = 14
|
||||
publication_type = 'newspaper'
|
||||
remove_empty_feeds = True
|
||||
masthead_url = 'http://www.lanacion.com.ar/_ui/desktop/imgs/layout/logos/ln341x47.gif'
|
||||
masthead_url = 'http://www.lanacion.com.ar/_ui/desktop/imgs/layout/logos/ln-home.gif'
|
||||
extra_css = """
|
||||
h1{font-family: Georgia,serif}
|
||||
h2{color: #626262; font-weight: normal; font-size: 1.1em}
|
||||
h1{font-family: TheSans,Arial,sans-serif}
|
||||
body{font-family: Arial,sans-serif}
|
||||
img{margin-top: 0.5em; margin-bottom: 0.2em; display: block}
|
||||
.notaFecha{color: #808080; font-size: small}
|
||||
.notaEpigrafe{font-size: x-small}
|
||||
.topNota h1{font-family: Arial,sans-serif}
|
||||
img{display: block}
|
||||
.firma,.fecha{font-size: small}
|
||||
.epigrafe-columna{font-size: x-small}
|
||||
"""
|
||||
|
||||
|
||||
@ -39,21 +36,13 @@ class Lanacion(BasicNewsRecipe):
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div', attrs={'class':['topNota','itemHeader','nota','itemBody']})
|
||||
,dict(name='div', attrs={'id':'content'})
|
||||
]
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div' , attrs={'class':'notaComentario floatFix noprint' })
|
||||
,dict(name='ul' , attrs={'class':['cajaHerramientas cajaTop noprint','herramientas noprint']})
|
||||
,dict(name='div' , attrs={'class':['titulosMultimedia','herramientas noprint','cajaHerramientas noprint','cajaHerramientas floatFix'] })
|
||||
,dict(attrs={'class':['izquierda','espacio17','espacio10','espacio20','floatFix ultimasNoticias','relacionadas','titulosMultimedia','derecha','techo color','encuesta','izquierda compartir','floatFix','videoCentro']})
|
||||
,dict(name=['iframe','embed','object','form','base','hr','meta','link','input'])
|
||||
dict(name=['iframe','embed','object','meta','link'])
|
||||
,dict(attrs={'id':['herramientas','relacionadas','ampliar']})
|
||||
]
|
||||
|
||||
remove_tags_after = dict(attrs={'class':['tags','nota-destacado']})
|
||||
remove_attributes = ['height','width','visible','onclick','data-count','name']
|
||||
remove_tags_before = dict(attrs={'id':'encabezado'})
|
||||
remove_tags_after = dict(attrs={'id':'relacionadas'})
|
||||
|
||||
feeds = [
|
||||
(u'Politica' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=30' )
|
||||
@ -91,6 +80,15 @@ class Lanacion(BasicNewsRecipe):
|
||||
if link.rfind('galeria=') > 0:
|
||||
return None
|
||||
return link
|
||||
|
||||
def get_cover_url(self):
|
||||
soup = self.index_to_soup('http://www.lanacion.com.ar/edicion-impresa')
|
||||
atap = soup.find(attrs={'class':'tapa'})
|
||||
if atap:
|
||||
li = atap.find('img')
|
||||
if li:
|
||||
return li['src']
|
||||
return None
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
for item in soup.findAll(style=True):
|
||||
|
@ -23,7 +23,7 @@ class LaTribuna(BasicNewsRecipe):
|
||||
encoding = 'utf-8'
|
||||
language = 'es_HN'
|
||||
|
||||
lang = 'es-HN'
|
||||
lang = 'es_HN'
|
||||
direction = 'ltr'
|
||||
|
||||
html2lrf_options = [
|
||||
|
@ -19,7 +19,7 @@ class Marca(BasicNewsRecipe):
|
||||
use_embedded_content = False
|
||||
delay = 1
|
||||
encoding = 'iso-8859-15'
|
||||
language = 'es_ES'
|
||||
language = 'es'
|
||||
publication_type = 'newsportal'
|
||||
masthead_url = 'http://estaticos.marca.com/deporte/img/v3.0/img_marca-com.png'
|
||||
extra_css = """
|
||||
|
@ -2,6 +2,9 @@ from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
title = u'Metro Nieuws NL'
|
||||
description = u'Metro Nieuws - NL'
|
||||
# Version 1.2, updated cover image to match the changed website.
|
||||
# added info date on title
|
||||
oldest_article = 2
|
||||
max_articles_per_feed = 100
|
||||
__author__ = u'DrMerry'
|
||||
@ -10,11 +13,11 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
|
||||
simultaneous_downloads = 5
|
||||
delay = 1
|
||||
# timefmt = ' [%A, %d %B, %Y]'
|
||||
timefmt = ''
|
||||
timefmt = ' [%A, %d %b %Y]'
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
remove_empty_feeds = True
|
||||
cover_url = 'http://www.readmetro.com/img/en/metroholland/last/1/small.jpg'
|
||||
cover_url = 'http://www.oldreadmetro.com/img/en/metroholland/last/1/small.jpg'
|
||||
remove_empty_feeds = True
|
||||
publication_type = 'newspaper'
|
||||
remove_tags_before = dict(name='div', attrs={'id':'date'})
|
||||
|
@ -8,6 +8,9 @@ class Newsweek(BasicNewsRecipe):
|
||||
language = 'en'
|
||||
encoding = 'utf-8'
|
||||
no_stylesheets = True
|
||||
recipe_disabled = ('Newsweek was taken over by The Daily Beast,'
|
||||
' newsweek.com no longer exists, so this recipe '
|
||||
' has been disabled.')
|
||||
|
||||
BASE_URL = 'http://www.newsweek.com'
|
||||
|
||||
|
@ -1,91 +1,135 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, matek09, matek09@gmail.com'
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
import datetime
|
||||
|
||||
|
||||
class Newsweek(BasicNewsRecipe):
|
||||
FIND_LAST_FULL_ISSUE = True
|
||||
EDITION = '0'
|
||||
EXCLUDE_LOCKED = True
|
||||
LOCKED_ICO = 'http://www.newsweek.pl/bins/media/static/newsweek/img/ico_locked.gif'
|
||||
DATE = None
|
||||
YEAR = datetime.datetime.now().year
|
||||
|
||||
title = u'Newsweek Polska'
|
||||
__author__ = 'matek09'
|
||||
description = 'Weekly magazine'
|
||||
encoding = 'utf-8'
|
||||
no_stylesheets = True
|
||||
language = 'pl'
|
||||
remove_javascript = True
|
||||
|
||||
keep_only_tags =[]
|
||||
keep_only_tags.append(dict(name = 'div', attrs = {'class' : 'article'}))
|
||||
temp_files = []
|
||||
articles_are_obfuscated = True
|
||||
|
||||
remove_tags =[]
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'copy'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'url'}))
|
||||
|
||||
extra_css = '''
|
||||
.body {font-size: small}
|
||||
.author {font-size: x-small}
|
||||
.lead {font-size: x-small}
|
||||
.title{font-size: x-large; font-weight: bold}
|
||||
'''
|
||||
|
||||
def print_version(self, url):
|
||||
return url.replace("http://www.newsweek.pl/artykuly/wydanie/" + str(self.EDITION), "http://www.newsweek.pl/artykuly") + '/print'
|
||||
|
||||
def is_locked(self, a):
|
||||
if a.findNext('img')['src'] == 'http://www.newsweek.pl/bins/media/static/newsweek/img/ico_locked.gif':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
def get_obfuscated_article(self, url):
|
||||
br = self.get_browser()
|
||||
br.open(url)
|
||||
source = br.response().read()
|
||||
page = self.index_to_soup(source)
|
||||
|
||||
main_section = page.find(id='mainSection')
|
||||
|
||||
title = main_section.find('h1')
|
||||
info = main_section.find('ul', attrs={'class' : 'articleInfo'})
|
||||
authors = info.find('li').find('h4')
|
||||
article = main_section.find('div', attrs={'id' : 'article'})
|
||||
html = unicode(title) + unicode(authors) + unicode(article)
|
||||
next = main_section.find('li', attrs={'class' : 'next'})
|
||||
|
||||
while next:
|
||||
url = next.find('a')['href']
|
||||
br.open(url)
|
||||
source = br.response().read()
|
||||
page = self.index_to_soup(source)
|
||||
main_section = page.find(id='mainSection')
|
||||
article = main_section.find('div', attrs={'id' : 'article'})
|
||||
aside = article.find(id='articleAside')
|
||||
if aside is not None:
|
||||
aside.extract()
|
||||
html = html + unicode(article)
|
||||
next = main_section.find('li', attrs={'class' : 'next'})
|
||||
|
||||
|
||||
self.temp_files.append(PersistentTemporaryFile('_temparse.html'))
|
||||
self.temp_files[-1].write(html)
|
||||
self.temp_files[-1].close()
|
||||
return self.temp_files[-1].name
|
||||
|
||||
def is_full(self, issue_soup):
|
||||
if len(issue_soup.findAll('img', attrs={'src' : 'http://www.newsweek.pl/bins/media/static/newsweek/img/ico_locked.gif'})) > 1:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def find_last_full_issue(self):
|
||||
frame_url = 'http://www.newsweek.pl/Frames/IssueCover.aspx'
|
||||
while True:
|
||||
frame_soup = self.index_to_soup(frame_url)
|
||||
self.EDITION = frame_soup.find('a', attrs={'target' : '_parent'})['href'].replace('/wydania/','')
|
||||
main_section = issue_soup.find(id='mainSection')
|
||||
next = main_section.find('li', attrs={'class' : 'next'})
|
||||
if len(main_section.findAll(attrs={'class' : 'locked'})) > 1:
|
||||
return False
|
||||
elif next is None:
|
||||
return True
|
||||
else:
|
||||
issue_soup = self.index_to_soup(next.find('a')['href'])
|
||||
|
||||
def find_last_full_issue(self, archive_url):
|
||||
archive_soup = self.index_to_soup(archive_url)
|
||||
select = archive_soup.find('select', attrs={'id' : 'paper_issue_select'})
|
||||
for option in select.findAll(lambda tag: tag.name == 'option' and tag.has_key('value')):
|
||||
self.EDITION = option['value'].replace('http://www.newsweek.pl/wydania/','')
|
||||
issue_soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + self.EDITION)
|
||||
if self.is_full(issue_soup):
|
||||
break
|
||||
frame_url = 'http://www.newsweek.pl/Frames/' + frame_soup.find(lambda tag: tag.name == 'span' and not tag.attrs).a['href']
|
||||
|
||||
|
||||
|
||||
return
|
||||
|
||||
self.YEAR = self.YEAR - 1
|
||||
self.find_last_full_issue(archive_url + ',' + str(self.YEAR))
|
||||
|
||||
def parse_index(self):
|
||||
if self.FIND_LAST_FULL_ISSUE:
|
||||
self.find_last_full_issue()
|
||||
archive_url = 'http://www.newsweek.pl/wydania/archiwum'
|
||||
self.find_last_full_issue(archive_url)
|
||||
soup = self.index_to_soup('http://www.newsweek.pl/wydania/' + self.EDITION)
|
||||
img = soup.find('img', id="ctl00_C1_PaperIsssueView_IssueImage", src=True)
|
||||
self.DATE = self.tag_to_string(soup.find('span', attrs={'class' : 'data'}))
|
||||
main_section = soup.find(id='mainSection')
|
||||
img = main_section.find(lambda tag: tag.name == 'img' and tag.has_key('alt') and tag.has_key('title'))
|
||||
self.cover_url = img['src']
|
||||
feeds = []
|
||||
parent = soup.find(id='content-left-big')
|
||||
for txt in parent.findAll(attrs={'class':'txt_normal_red strong'}):
|
||||
articles = list(self.find_articles(txt))
|
||||
if len(articles) > 0:
|
||||
section = self.tag_to_string(txt).capitalize()
|
||||
feeds.append((section, articles))
|
||||
articles = {}
|
||||
sections = []
|
||||
while True:
|
||||
news_list = main_section.find('ul', attrs={'class' : 'newsList'})
|
||||
for h2 in news_list.findAll('h2'):
|
||||
|
||||
article = self.create_article(h2)
|
||||
category_div = h2.findNext('div', attrs={'class' : 'kategorie'})
|
||||
section = self.tag_to_string(category_div)
|
||||
if articles.has_key(section):
|
||||
articles[section].append(article)
|
||||
else:
|
||||
articles[section] = [article]
|
||||
sections.append(section)
|
||||
|
||||
next = main_section.find('li', attrs={'class' : 'next'})
|
||||
if next is None:
|
||||
break
|
||||
soup = self.index_to_soup(next.find('a')['href'])
|
||||
main_section = soup.find(id='mainSection')
|
||||
|
||||
for section in sections:
|
||||
feeds.append((section, articles[section]))
|
||||
return feeds
|
||||
|
||||
def find_articles(self, txt):
|
||||
for a in txt.findAllNext( attrs={'class':['strong','hr']}):
|
||||
if a.name in "div":
|
||||
break
|
||||
if (not self.FIND_LAST_FULL_ISSUE) & self.EXCLUDE_LOCKED & self.is_locked(a):
|
||||
continue
|
||||
yield {
|
||||
'title' : self.tag_to_string(a),
|
||||
'url' : 'http://www.newsweek.pl' + a['href'],
|
||||
'date' : '',
|
||||
'description' : ''
|
||||
}
|
||||
def create_article(self, h2):
|
||||
article = {}
|
||||
a = h2.find('a')
|
||||
article['title'] = self.tag_to_string(a)
|
||||
article['url'] = a['href']
|
||||
article['date'] = self.DATE
|
||||
desc = h2.findNext('p')
|
||||
|
||||
if desc is not None:
|
||||
article['description'] = self.tag_to_string(desc)
|
||||
else:
|
||||
article['description'] = ''
|
||||
return article
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -85,4 +85,5 @@ class NikkeiNet_paper_subscription(BasicNewsRecipe):
|
||||
description='', content=''))
|
||||
result.append([sect_title, sect_result])
|
||||
#pp.pprint(result)
|
||||
return result
|
||||
|
||||
|
35
recipes/novinite.recipe
Normal file
@ -0,0 +1,35 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1308572538(BasicNewsRecipe):
|
||||
title = u'Novinite.com'
|
||||
__author__ = 'Martin Tsanchev'
|
||||
description = 'Real time provider of the latest Bulgarian news in English'
|
||||
category = 'Business, Politics, Society, Sports, Crime, Lifestyle, World, People'
|
||||
language = 'en_BG'
|
||||
encoding = 'utf-8'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 10
|
||||
keep_only_tags = [dict(name='div', attrs={'id':'content'})]
|
||||
remove_tags = [dict(name='a', attrs={'class':'twitter-share-button'})]
|
||||
remove_tags_after = dict(id='textsize')
|
||||
no_stylesheets = True
|
||||
feeds = [(u'Business', u'http://www.novinite.com/services/news_rdf.php?category_id=1'),
|
||||
(u'Finance', u'http://www.novinite.com/services/news_rdf.php?category_id=15'),
|
||||
(u'Energy', u'http://www.novinite.com/services/news_rdf.php?category_id=16'),
|
||||
(u'Industry', u'http://www.novinite.com/services/news_rdf.php?category_id=17'),
|
||||
(u'Properties', u'http://www.novinite.com/services/news_rdf.php?category_id=18'),
|
||||
(u'Politics', u'http://www.novinite.com/services/news_rdf.php?category_id=2'),
|
||||
(u'Diplomacy', u'http://www.novinite.com/services/news_rdf.php?category_id=20'),
|
||||
(u'Defense', u'http://www.novinite.com/services/news_rdf.php?category_id=21'),
|
||||
(u'Bulgaria in EU', u'http://www.novinite.com/services/news_rdf.php?category_id=22'),
|
||||
(u'Domestic', u'http://www.novinite.com/services/news_rdf.php?category_id=23'),
|
||||
(u'Society', u'http://www.novinite.com/services/news_rdf.php?category_id=3'),
|
||||
(u'Environment', u'http://www.novinite.com/services/news_rdf.php?category_id=24'),
|
||||
(u'Education', u'http://www.novinite.com/services/news_rdf.php?category_id=25'),
|
||||
(u'Culture', u'http://www.novinite.com/services/news_rdf.php?category_id=26'),
|
||||
(u'Archaeology', u'http://www.novinite.com/services/news_rdf.php?category_id=34'),
|
||||
(u'Health', u'http://www.novinite.com/services/news_rdf.php?category_id=62'),
|
||||
(u'Sports', u'http://www.novinite.com/services/news_rdf.php?category_id=4'),
|
||||
(u'Crime', u'http://www.novinite.com/services/news_rdf.php?category_id=5'),
|
||||
(u'Lifestyle', u'http://www.novinite.com/services/news_rdf.php?category_id=6'),
|
||||
(u'World', u'http://www.novinite.com/services/news_rdf.php?category_id=30')]
|
43
recipes/patente_de_corso.recipe
Normal file
@ -0,0 +1,43 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Oscar Megia Lopez'
|
||||
'''
|
||||
perezreverte.com
|
||||
'''
|
||||
import re
|
||||
from calibre.web.feeds.recipes import BasicNewsRecipe
|
||||
|
||||
class PerezReverte(BasicNewsRecipe):
|
||||
title = u'Patente de Corso'
|
||||
__author__ = 'Oscar Megia Lopez'
|
||||
description = 'Arturo Perez Reverte'
|
||||
oldest_article = 90
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = True
|
||||
#delay = 1
|
||||
use_embedded_content = False
|
||||
encoding = 'utf8'
|
||||
publisher = 'Arturo Perez Reverte'
|
||||
category = 'Articulo'
|
||||
language = 'es'
|
||||
publication_type = 'Magazine'
|
||||
extra_css = ' body{ font-family: Verdana,Helvetica,Arial,sans-serif } .contentheading{font-weight: bold} .txt_articulo{display: block; padding: 0; border: 1px solid; width: 40%; font-size: small} .story-feature h2{text-align: center; text-transform: uppercase} '
|
||||
preprocess_regexps = [(re.compile(r'<!--.*?-->', re.DOTALL), lambda m: '')]
|
||||
conversion_options = {
|
||||
'comments' : description
|
||||
,'tags' : category
|
||||
,'language' : language
|
||||
,'publisher' : publisher
|
||||
,'linearize_tables': True
|
||||
}
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='h2', attrs={'class':['titular']}),
|
||||
dict(name='p', attrs={'class':['fecha']}),
|
||||
dict(name='div', attrs={'class':['bloqueTexto']})
|
||||
]
|
||||
|
||||
remove_attributes = ['width','height']
|
||||
|
||||
feeds = [
|
||||
('Patente de corso - Web oficial de Arturo Perez Reverte', 'http://www.perezreverte.com/rss/patentes-corso/')
|
||||
]
|
@ -1,12 +1,12 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class RzeczpospolitaRecipe(BasicNewsRecipe):
|
||||
__license__ = 'GPL v3'
|
||||
__license__ = 'GPL v3'
|
||||
__author__ = u'kwetal and Tomasz Dlugosz'
|
||||
language = 'pl'
|
||||
version = 1
|
||||
|
||||
title = u'Rzeczpospolita OnLine'
|
||||
title = u'Rzeczpospolita OnLine'
|
||||
publisher = u'Presspublica Sp.'
|
||||
category = u'News'
|
||||
description = u'Newspaper'
|
||||
@ -31,15 +31,19 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
|
||||
feeds.append(u'http://www.rp.pl/rss/8.html')
|
||||
|
||||
keep_only_tags =[]
|
||||
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'storyp'}))
|
||||
keep_only_tags.append(dict(name = 'div', attrs = {'id' : 'story'}))
|
||||
|
||||
remove_tags =[]
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'adk_0'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'socialTools'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleToolBoxTop'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'clr'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'share_bottom'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'copyright_law'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'recommendations'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'editorPicks'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'articleCopyrightText'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'id' : 'articleCopyrightButton'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'articleToolBoxBottom'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'more'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'editorPicks'}))
|
||||
remove_tags.append(dict(name = 'div', attrs = {'class' : 'addRecommendation'}))
|
||||
|
||||
extra_css = '''
|
||||
body {font-family: verdana, arial, helvetica, geneva, sans-serif ;}
|
||||
@ -62,3 +66,4 @@ class RzeczpospolitaRecipe(BasicNewsRecipe):
|
||||
forget, sep, index = rest.rpartition(',')
|
||||
|
||||
return start + '/' + index + '?print=tak'
|
||||
|
||||
|
27
recipes/the_clinic_online.recipe
Normal file
@ -0,0 +1,27 @@
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class AdvancedUserRecipe1313555075(BasicNewsRecipe):
|
||||
news = True
|
||||
title = u'The Clinic'
|
||||
__author__ = 'Alex Mitrani'
|
||||
description = u'Online version of Chilean satirical weekly'
|
||||
publisher = u'The Clinic'
|
||||
category = 'news, politics, Chile, rss'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
summary_length = 1000
|
||||
language = 'es_CL'
|
||||
|
||||
remove_javascript = True
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
remove_empty_feeds = True
|
||||
masthead_url = 'http://www.theclinic.cl/wp-content/themes/tc12m/css/ui/mainLogoTC-top.png'
|
||||
remove_tags_before = dict(name='article', attrs={'class':'scope bordered'})
|
||||
remove_tags_after = dict(name='div', attrs={'id':'commentsSection'})
|
||||
remove_tags = [dict(name='span', attrs={'class':'relTags'})
|
||||
,dict(name='div', attrs={'class':'articleActivity hdcol'})
|
||||
,dict(name='div', attrs={'id':'commentsSection'})
|
||||
]
|
||||
|
||||
feeds = [(u'The Clinic Online', u'http://www.theclinic.cl/feed/')]
|
@ -38,6 +38,7 @@ class WallStreetJournal(BasicNewsRecipe):
|
||||
dict(id=["articleTabs_tab_article", "articleTabs_tab_comments", "articleTabs_tab_interactive","articleTabs_tab_video","articleTabs_tab_map","articleTabs_tab_slideshow","articleTabs_tab_quotes","articleTabs_tab_document"]),
|
||||
{'class':['footer_columns','network','insetCol3wide','interactive','video','slideshow','map','insettip','insetClose','more_in', "insetContent", 'articleTools_bottom', 'aTools', "tooltip", "adSummary", "nav-inline"]},
|
||||
dict(rel='shortcut icon'),
|
||||
{'class':lambda x: x and 'sTools' in x},
|
||||
]
|
||||
remove_tags_after = [dict(id="article_story_body"), {'class':"article story"},]
|
||||
|
||||
|
@ -40,6 +40,7 @@ class WallStreetJournal(BasicNewsRecipe):
|
||||
dict(name='div', attrs={'data-flash-settings':True}),
|
||||
{'class':['insetContent embedType-interactive insetCol3wide','insetCol6wide','insettipUnit']},
|
||||
dict(rel='shortcut icon'),
|
||||
{'class':lambda x: x and 'sTools' in x},
|
||||
]
|
||||
remove_tags_after = [dict(id="article_story_body"), {'class':"article story"},]
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="{prefix}/static/browse/browse.css" />
|
||||
<link type="text/css" href="{prefix}/static/jquery_ui/css/humanity-custom/jquery-ui-1.8.5.custom.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" type="text/css" href="{prefix}/static/jquery.multiselect.css" />
|
||||
<link rel="apple-touch-icon" href="/static/calibre.png" />
|
||||
<link rel="apple-touch-icon" href="{prefix}/static/calibre.png" />
|
||||
|
||||
<script type="text/javascript" src="{prefix}/static/jquery.js"></script>
|
||||
<script type="text/javascript" src="{prefix}/static/jquery.corner.js"></script>
|
||||
|
@ -179,6 +179,9 @@ save_template_title_series_sorting = 'library_order'
|
||||
# changed. Changes to this tweak won't have an effect until the book is modified
|
||||
# in some way. If you enter an invalid pattern, it is silently ignored.
|
||||
# To disable use the expression: '^$'
|
||||
# This expression is designed for articles that are followed by spaces. If you
|
||||
# also need to match articles that are followed by other characters, for example L'
|
||||
# in French, use: r"^(A\s+|The\s+|An\s+|L')" instead.
|
||||
# Default: '^(A|The|An)\s+'
|
||||
title_sort_articles=r'^(A|The|An)\s+'
|
||||
|
||||
|
BIN
resources/images/keyboard-prefs.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
resources/images/languages.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
resources/images/mimetypes/djvu.png
Executable file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.9 KiB |
BIN
resources/images/mimetypes/xps.png
Executable file
After Width: | Height: | Size: 13 KiB |
BIN
resources/images/random.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
@ -21,3 +21,5 @@ vipy.session.add_content_browser('.r', ',r', 'Recipe',
|
||||
vipy.session.glob_based_iterator(os.path.join(project_dir, 'recipes', '*.recipe')),
|
||||
vipy.session.regexp_based_matcher(r'title\s*=\s*(?P<title>.+)', 'title', recipe_title_callback))
|
||||
EOFPY
|
||||
|
||||
nmap \log :enew<CR>:read ! bzr log -l 500 ../.. <CR>:e ../../Changelog.yaml<CR>:e constants.py<CR>
|
||||
|
2169
setup/iso639.xml
Normal file
@ -205,8 +205,8 @@ class Resources(Command):
|
||||
dest = self.j(self.RESOURCES, 'template-functions.json')
|
||||
function_dict = {}
|
||||
import inspect
|
||||
from calibre.utils.formatter_functions import all_builtin_functions
|
||||
for obj in all_builtin_functions:
|
||||
from calibre.utils.formatter_functions import formatter_functions
|
||||
for obj in formatter_functions.get_builtins().values():
|
||||
eval_func = inspect.getmembers(obj,
|
||||
lambda x: inspect.ismethod(x) and x.__name__ == 'evaluate')
|
||||
try:
|
||||
|
@ -273,10 +273,9 @@ class GetTranslations(Translations):
|
||||
class ISO639(Command):
|
||||
|
||||
description = 'Compile translations for ISO 639 codes'
|
||||
XML = '/usr/lib/python2.7/site-packages/pycountry/databases/iso639.xml'
|
||||
|
||||
def run(self, opts):
|
||||
src = self.XML
|
||||
src = self.j(self.d(self.SRC), 'setup', 'iso639.xml')
|
||||
if not os.path.exists(src):
|
||||
raise Exception(src + ' does not exist')
|
||||
dest = self.j(self.d(self.SRC), 'resources', 'localization',
|
||||
@ -290,20 +289,36 @@ class ISO639(Command):
|
||||
by_2 = {}
|
||||
by_3b = {}
|
||||
by_3t = {}
|
||||
m2to3 = {}
|
||||
m3to2 = {}
|
||||
m3bto3t = {}
|
||||
nm = {}
|
||||
codes2, codes3t, codes3b = set([]), set([]), set([])
|
||||
for x in root.xpath('//iso_639_entry'):
|
||||
name = x.get('name')
|
||||
two = x.get('iso_639_1_code', None)
|
||||
threeb = x.get('iso_639_2B_code')
|
||||
threet = x.get('iso_639_2T_code')
|
||||
if two is not None:
|
||||
by_2[two] = name
|
||||
codes2.add(two)
|
||||
by_3b[x.get('iso_639_2B_code')] = name
|
||||
by_3t[x.get('iso_639_2T_code')] = name
|
||||
m2to3[two] = threet
|
||||
m3to2[threeb] = m3to2[threet] = two
|
||||
by_3b[threeb] = name
|
||||
by_3t[threet] = name
|
||||
if threeb != threet:
|
||||
m3bto3t[threeb] = threet
|
||||
codes3b.add(x.get('iso_639_2B_code'))
|
||||
codes3t.add(x.get('iso_639_2T_code'))
|
||||
base_name = name.lower()
|
||||
nm[base_name] = threet
|
||||
simple_name = base_name.partition(';')[0].strip()
|
||||
if simple_name not in nm:
|
||||
nm[simple_name] = threet
|
||||
|
||||
from cPickle import dump
|
||||
x = {'by_2':by_2, 'by_3b':by_3b, 'by_3t':by_3t, 'codes2':codes2,
|
||||
'codes3b':codes3b, 'codes3t':codes3t}
|
||||
'codes3b':codes3b, 'codes3t':codes3t, '2to3':m2to3,
|
||||
'3to2':m3to2, '3bto3t':m3bto3t, 'name_map':nm}
|
||||
dump(x, open(dest, 'wb'), -1)
|
||||
|
||||
|
@ -4,7 +4,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = u'calibre'
|
||||
numeric_version = (0, 8, 12)
|
||||
numeric_version = (0, 8, 14)
|
||||
__version__ = u'.'.join(map(unicode, numeric_version))
|
||||
__author__ = u"Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
|
||||
|
@ -590,8 +590,9 @@ from calibre.ebooks.metadata.sources.openlibrary import OpenLibrary
|
||||
from calibre.ebooks.metadata.sources.isbndb import ISBNDB
|
||||
from calibre.ebooks.metadata.sources.overdrive import OverDrive
|
||||
from calibre.ebooks.metadata.sources.douban import Douban
|
||||
from calibre.ebooks.metadata.sources.ozon import Ozon
|
||||
|
||||
plugins += [GoogleBooks, Amazon, OpenLibrary, ISBNDB, OverDrive, Douban]
|
||||
plugins += [GoogleBooks, Amazon, OpenLibrary, ISBNDB, OverDrive, Douban, Ozon]
|
||||
|
||||
# }}}
|
||||
|
||||
@ -843,6 +844,12 @@ class ActionNextMatch(InterfaceActionBase):
|
||||
description = _('Find the next or previous match when searching in '
|
||||
'your calibre library in highlight mode')
|
||||
|
||||
class ActionPickRandom(InterfaceActionBase):
|
||||
name = 'Pick Random Book'
|
||||
actual_plugin = 'calibre.gui2.actions.random:PickRandomAction'
|
||||
description = _('Choose a random book from your calibre library')
|
||||
|
||||
|
||||
class ActionStore(InterfaceActionBase):
|
||||
name = 'Store'
|
||||
author = 'John Schember'
|
||||
@ -873,7 +880,7 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
||||
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
||||
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary,
|
||||
ActionCopyToLibrary, ActionTweakEpub, ActionNextMatch, ActionStore,
|
||||
ActionPluginUpdater]
|
||||
ActionPluginUpdater, ActionPickRandom]
|
||||
|
||||
# }}}
|
||||
|
||||
@ -1023,7 +1030,7 @@ class TemplateFunctions(PreferencesPlugin):
|
||||
category = 'Advanced'
|
||||
gui_category = _('Advanced')
|
||||
category_order = 5
|
||||
name_order = 4
|
||||
name_order = 5
|
||||
config_widget = 'calibre.gui2.preferences.template_functions'
|
||||
description = _('Create your own template functions')
|
||||
|
||||
@ -1086,6 +1093,17 @@ class Tweaks(PreferencesPlugin):
|
||||
config_widget = 'calibre.gui2.preferences.tweaks'
|
||||
description = _('Fine tune how calibre behaves in various contexts')
|
||||
|
||||
class Keyboard(PreferencesPlugin):
|
||||
name = 'Keyboard'
|
||||
icon = I('keyboard-prefs.png')
|
||||
gui_name = _('Keyboard')
|
||||
category = 'Advanced'
|
||||
gui_category = _('Advanced')
|
||||
category_order = 5
|
||||
name_order = 4
|
||||
config_widget = 'calibre.gui2.preferences.keyboard'
|
||||
description = _('Customize the keyboard shortcuts used by calibre')
|
||||
|
||||
class Misc(PreferencesPlugin):
|
||||
name = 'Misc'
|
||||
icon = I('exec.png')
|
||||
@ -1100,7 +1118,7 @@ class Misc(PreferencesPlugin):
|
||||
plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions,
|
||||
CommonOptions, OutputOptions, Adding, Saving, Sending, Plugboard,
|
||||
Email, Server, Plugins, Tweaks, Misc, TemplateFunctions,
|
||||
MetadataSources]
|
||||
MetadataSources, Keyboard]
|
||||
|
||||
#}}}
|
||||
|
||||
@ -1470,6 +1488,14 @@ class StoreWoblinkStore(StoreBase):
|
||||
headquarters = 'PL'
|
||||
formats = ['EPUB']
|
||||
|
||||
class XinXiiStore(StoreBase):
|
||||
name = 'XinXii'
|
||||
description = ''
|
||||
actual_plugin = 'calibre.gui2.store.stores.xinxii_plugin:XinXiiStore'
|
||||
|
||||
headquarters = 'DE'
|
||||
formats = ['EPUB', 'PDF']
|
||||
|
||||
class StoreZixoStore(StoreBase):
|
||||
name = 'Zixo'
|
||||
author = u'Tomasz Długosz'
|
||||
@ -1519,6 +1545,7 @@ plugins += [
|
||||
StoreWHSmithUKStore,
|
||||
StoreWizardsTowerBooksStore,
|
||||
StoreWoblinkStore,
|
||||
XinXiiStore,
|
||||
StoreZixoStore
|
||||
]
|
||||
|
||||
|
@ -40,6 +40,7 @@ class ANDROID(USBMS):
|
||||
0x41db : [0x216], 0x4285 : [0x216], 0x42a3 : [0x216],
|
||||
0x4286 : [0x216], 0x42b3 : [0x216], 0x42b4 : [0x216],
|
||||
0x7086 : [0x0226], 0x70a8: [0x9999], 0x42c4 : [0x216],
|
||||
0x70c6 : [0x226]
|
||||
},
|
||||
|
||||
# Sony Ericsson
|
||||
@ -47,7 +48,7 @@ class ANDROID(USBMS):
|
||||
|
||||
# Google
|
||||
0x18d1 : {
|
||||
0x0001 : [0x0223],
|
||||
0x0001 : [0x0223, 0x9999],
|
||||
0x4e11 : [0x0100, 0x226, 0x227],
|
||||
0x4e12 : [0x0100, 0x226, 0x227],
|
||||
0x4e21 : [0x0100, 0x226, 0x227],
|
||||
@ -63,6 +64,7 @@ class ANDROID(USBMS):
|
||||
0x6860 : [0x0400],
|
||||
0x6877 : [0x0400],
|
||||
0x689e : [0x0400],
|
||||
0xdeed : [0x0222],
|
||||
},
|
||||
|
||||
# Viewsonic
|
||||
@ -75,8 +77,11 @@ class ANDROID(USBMS):
|
||||
0x413c : { 0xb007 : [0x0100, 0x0224, 0x0226]},
|
||||
|
||||
# LG
|
||||
0x1004 : { 0x61cc : [0x100], 0x61ce : [0x100], 0x618e : [0x226,
|
||||
0x9999] },
|
||||
0x1004 : {
|
||||
0x61cc : [0x100],
|
||||
0x61ce : [0x100],
|
||||
0x618e : [0x226, 0x9999, 0x100]
|
||||
},
|
||||
|
||||
# Archos
|
||||
0x0e79 : {
|
||||
@ -128,11 +133,11 @@ class ANDROID(USBMS):
|
||||
'7', 'A956', 'A955', 'A43', 'ANDROID_PLATFORM', 'TEGRA_2',
|
||||
'MB860', 'MULTI-CARD', 'MID7015A', 'INCREDIBLE', 'A7EB', 'STREAK',
|
||||
'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612',
|
||||
'GT-S5830_CARD', 'GT-S5570_CARD']
|
||||
'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
|
||||
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',
|
||||
'__UMS_COMPOSITE', 'SGH-I997_CARD']
|
||||
'__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870']
|
||||
|
||||
OSX_MAIN_MEM = 'Android Device Main Memory'
|
||||
|
||||
|
@ -59,7 +59,7 @@ class BAMBOOK(DeviceConfig, DevicePlugin):
|
||||
|
||||
def reset(self, key='-1', log_packets=False, report_progress=None,
|
||||
detected_device=None) :
|
||||
self.open()
|
||||
self.open(None)
|
||||
|
||||
def open(self, library_uuid):
|
||||
# Make sure the Bambook library is ready
|
||||
|
@ -6,6 +6,7 @@ Created on 15 May 2010
|
||||
import os
|
||||
|
||||
from calibre.devices.usbms.driver import USBMS, BookList
|
||||
from calibre.ebooks import BOOK_EXTENSIONS
|
||||
|
||||
# This class is added to the standard device plugin chain, so that it can
|
||||
# be configured. It has invalid vendor_id etc, so it will never match a
|
||||
@ -16,8 +17,8 @@ class FOLDER_DEVICE_FOR_CONFIG(USBMS):
|
||||
description = _('Use an arbitrary folder as a device.')
|
||||
author = 'John Schember/Charles Haley'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
FORMATS = ['epub', 'fb2', 'mobi', 'azw', 'lrf', 'tcr', 'pmlz', 'lit',
|
||||
'rtf', 'rb', 'pdf', 'oeb', 'txt', 'pdb', 'prc']
|
||||
FORMATS = list(BOOK_EXTENSIONS)
|
||||
|
||||
VENDOR_ID = [0xffff]
|
||||
PRODUCT_ID = [0xffff]
|
||||
BCD = [0xffff]
|
||||
|
@ -64,7 +64,7 @@ class KINDLE(USBMS):
|
||||
|
||||
EBOOK_DIR_MAIN = 'documents'
|
||||
EBOOK_DIR_CARD_A = 'documents'
|
||||
DELETE_EXTS = ['.mbp','.tan','.pdr']
|
||||
DELETE_EXTS = ['.mbp', '.tan', '.pdr', '.ea', '.apnx', '.phl']
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
SUPPORTS_ANNOTATIONS = True
|
||||
|
||||
|
@ -333,8 +333,14 @@ class KOBO(USBMS):
|
||||
except Exception as e:
|
||||
if 'no such column' not in str(e):
|
||||
raise
|
||||
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0 ' \
|
||||
'where BookID is Null and ContentID =?',t)
|
||||
try:
|
||||
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\', ___PercentRead=0 ' \
|
||||
'where BookID is Null and ContentID =?',t)
|
||||
except Exception as e:
|
||||
if 'no such column' not in str(e):
|
||||
raise
|
||||
cursor.execute('update content set ReadStatus=0, FirstTimeReading = \'true\' ' \
|
||||
'where BookID is Null and ContentID =?',t)
|
||||
|
||||
|
||||
connection.commit()
|
||||
|
@ -252,8 +252,8 @@ class EEEREADER(USBMS):
|
||||
|
||||
EBOOK_DIR_MAIN = EBOOK_DIR_CARD_A = 'Book'
|
||||
|
||||
VENDOR_NAME = 'LINUX'
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET'
|
||||
VENDOR_NAME = ['LINUX', 'ASUS']
|
||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['FILE-STOR_GADGET', 'EEE_NOTE']
|
||||
|
||||
class ADAM(USBMS):
|
||||
|
||||
|
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
import os, shutil, time
|
||||
|
||||
from calibre.devices.errors import PathError
|
||||
from calibre.utils.filenames import case_preserving_open_file
|
||||
|
||||
class File(object):
|
||||
|
||||
@ -46,10 +47,8 @@ class CLI(object):
|
||||
path = os.path.join(path, infile.name)
|
||||
if not replace_file and os.path.exists(path):
|
||||
raise PathError('File already exists: ' + path)
|
||||
d = os.path.dirname(path)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
with open(path, 'w+b') as dest:
|
||||
dest, actual_path = case_preserving_open_file(path)
|
||||
with dest:
|
||||
try:
|
||||
shutil.copyfileobj(infile, dest)
|
||||
except IOError:
|
||||
@ -62,6 +61,7 @@ class CLI(object):
|
||||
#if not check_transfer(infile, dest): raise Exception('Transfer failed')
|
||||
if close:
|
||||
infile.close()
|
||||
return actual_path
|
||||
|
||||
def munge_path(self, path):
|
||||
if path.startswith('/') and not (path.startswith(self._main_prefix) or \
|
||||
|
@ -258,10 +258,10 @@ class USBMS(CLI, Device):
|
||||
for i, infile in enumerate(files):
|
||||
mdata, fname = metadata.next(), names.next()
|
||||
filepath = self.normalize_path(self.create_upload_path(path, mdata, fname))
|
||||
paths.append(filepath)
|
||||
if not hasattr(infile, 'read'):
|
||||
infile = self.normalize_path(infile)
|
||||
self.put_file(infile, filepath, replace_file=True)
|
||||
filepath = self.put_file(infile, filepath, replace_file=True)
|
||||
paths.append(filepath)
|
||||
try:
|
||||
self.upload_cover(os.path.dirname(filepath),
|
||||
os.path.splitext(os.path.basename(filepath))[0],
|
||||
|
@ -8,6 +8,7 @@ from various formats.
|
||||
'''
|
||||
|
||||
import traceback, os, re
|
||||
from cStringIO import StringIO
|
||||
from calibre import CurrentDir
|
||||
|
||||
class ConversionError(Exception):
|
||||
@ -27,8 +28,9 @@ class ParserError(ValueError):
|
||||
|
||||
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'txtz', 'text', 'htm', 'xhtm',
|
||||
'html', 'htmlz', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc',
|
||||
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
|
||||
'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'pmlz', 'mbp', 'tan', 'snb']
|
||||
'epub', 'fb2', 'djv', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
|
||||
'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'pmlz', 'mbp', 'tan', 'snb',
|
||||
'xps', 'oxps']
|
||||
|
||||
class HTMLRenderer(object):
|
||||
|
||||
@ -209,4 +211,45 @@ def unit_convert(value, base, font, dpi):
|
||||
result = value * 0.40
|
||||
return result
|
||||
|
||||
def generate_masthead(title, output_path=None, width=600, height=60):
|
||||
from calibre.ebooks.conversion.config import load_defaults
|
||||
from calibre.utils.fonts import fontconfig
|
||||
font_path = default_font = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
||||
recs = load_defaults('mobi_output')
|
||||
masthead_font_family = recs.get('masthead_font', 'Default')
|
||||
|
||||
if masthead_font_family != 'Default':
|
||||
masthead_font = fontconfig.files_for_family(masthead_font_family)
|
||||
# Assume 'normal' always in dict, else use default
|
||||
# {'normal': (path_to_font, friendly name)}
|
||||
if 'normal' in masthead_font:
|
||||
font_path = masthead_font['normal'][0]
|
||||
|
||||
if not font_path or not os.access(font_path, os.R_OK):
|
||||
font_path = default_font
|
||||
|
||||
try:
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
Image, ImageDraw, ImageFont
|
||||
except ImportError:
|
||||
import Image, ImageDraw, ImageFont
|
||||
|
||||
img = Image.new('RGB', (width, height), 'white')
|
||||
draw = ImageDraw.Draw(img)
|
||||
try:
|
||||
font = ImageFont.truetype(font_path, 48)
|
||||
except:
|
||||
font = ImageFont.truetype(default_font, 48)
|
||||
text = title.encode('utf-8')
|
||||
width, height = draw.textsize(text, font=font)
|
||||
left = max(int((width - width)/2.), 0)
|
||||
top = max(int((height - height)/2.), 0)
|
||||
draw.text((left, top), text, fill=(0,0,0), font=font)
|
||||
if output_path is None:
|
||||
f = StringIO()
|
||||
img.save(f, 'JPEG')
|
||||
return f.getvalue()
|
||||
else:
|
||||
with open(output_path, 'wb') as f:
|
||||
img.save(f, 'JPEG')
|
||||
|
||||
|
@ -557,7 +557,7 @@ OptionRecommendation(name='delete_blank_paragraphs',
|
||||
OptionRecommendation(name='format_scene_breaks',
|
||||
recommended_value=True, level=OptionRecommendation.LOW,
|
||||
help=_('Left aligned scene break markers are center aligned. '
|
||||
'Replace soft scene breaks that use multiple blank lines with'
|
||||
'Replace soft scene breaks that use multiple blank lines with '
|
||||
'horizontal rules.')),
|
||||
|
||||
OptionRecommendation(name='replace_scene_breaks',
|
||||
|
@ -47,8 +47,7 @@ PUBLICATION_METADATA_FIELDS = frozenset([
|
||||
# If None, means book
|
||||
'publication_type',
|
||||
'uuid', # A UUID usually of type 4
|
||||
'language', # the primary language of this book
|
||||
'languages', # ordered list
|
||||
'languages', # ordered list of languages in this publication
|
||||
'publisher', # Simple string, no special semantics
|
||||
# Absolute path to image file encoded in filesystem_encoding
|
||||
'cover',
|
||||
@ -109,7 +108,7 @@ STANDARD_METADATA_FIELDS = SOCIAL_METADATA_FIELDS.union(
|
||||
# Metadata fields that smart update must do special processing to copy.
|
||||
SC_FIELDS_NOT_COPIED = frozenset(['title', 'title_sort', 'authors',
|
||||
'author_sort', 'author_sort_map',
|
||||
'cover_data', 'tags', 'language',
|
||||
'cover_data', 'tags', 'languages',
|
||||
'identifiers'])
|
||||
|
||||
# Metadata fields that smart update should copy only if the source is not None
|
||||
|
@ -102,6 +102,7 @@ class Metadata(object):
|
||||
@param other: None or a metadata object
|
||||
'''
|
||||
_data = copy.deepcopy(NULL_VALUES)
|
||||
_data.pop('language')
|
||||
object.__setattr__(self, '_data', _data)
|
||||
if other is not None:
|
||||
self.smart_update(other)
|
||||
@ -136,6 +137,11 @@ class Metadata(object):
|
||||
_data = object.__getattribute__(self, '_data')
|
||||
if field in TOP_LEVEL_IDENTIFIERS:
|
||||
return _data.get('identifiers').get(field, None)
|
||||
if field == 'language':
|
||||
try:
|
||||
return _data.get('languages', [])[0]
|
||||
except:
|
||||
return NULL_VALUES['language']
|
||||
if field in STANDARD_METADATA_FIELDS:
|
||||
return _data.get(field, None)
|
||||
try:
|
||||
@ -175,6 +181,11 @@ class Metadata(object):
|
||||
if not val:
|
||||
val = copy.copy(NULL_VALUES.get('identifiers', None))
|
||||
self.set_identifiers(val)
|
||||
elif field == 'language':
|
||||
langs = []
|
||||
if val and val.lower() != 'und':
|
||||
langs = [val]
|
||||
_data['languages'] = langs
|
||||
elif field in STANDARD_METADATA_FIELDS:
|
||||
if val is None:
|
||||
val = copy.copy(NULL_VALUES.get(field, None))
|
||||
@ -553,9 +564,9 @@ class Metadata(object):
|
||||
for attr in TOP_LEVEL_IDENTIFIERS:
|
||||
copy_not_none(self, other, attr)
|
||||
|
||||
other_lang = getattr(other, 'language', None)
|
||||
if other_lang and other_lang.lower() != 'und':
|
||||
self.language = other_lang
|
||||
other_lang = getattr(other, 'languages', [])
|
||||
if other_lang and other_lang != ['und']:
|
||||
self.languages = list(other_lang)
|
||||
if not getattr(self, 'series', None):
|
||||
self.series_index = None
|
||||
|
||||
@ -706,8 +717,8 @@ class Metadata(object):
|
||||
fmt('Tags', u', '.join([unicode(t) for t in self.tags]))
|
||||
if self.series:
|
||||
fmt('Series', self.series + ' #%s'%self.format_series_index())
|
||||
if not self.is_null('language'):
|
||||
fmt('Language', self.language)
|
||||
if not self.is_null('languages'):
|
||||
fmt('Languages', ', '.join(self.languages))
|
||||
if self.rating is not None:
|
||||
fmt('Rating', self.rating)
|
||||
if self.timestamp is not None:
|
||||
@ -743,7 +754,7 @@ class Metadata(object):
|
||||
ans += [(_('Tags'), u', '.join([unicode(t) for t in self.tags]))]
|
||||
if self.series:
|
||||
ans += [(_('Series'), unicode(self.series) + ' #%s'%self.format_series_index())]
|
||||
ans += [(_('Language'), unicode(self.language))]
|
||||
ans += [(_('Languages'), u', '.join(self.languages))]
|
||||
if self.timestamp is not None:
|
||||
ans += [(_('Timestamp'), unicode(self.timestamp.isoformat(' ')))]
|
||||
if self.pubdate is not None:
|
||||
|
@ -11,7 +11,7 @@ from functools import partial
|
||||
from base64 import b64decode
|
||||
from lxml import etree
|
||||
from calibre.utils.date import parse_date
|
||||
from calibre import guess_all_extensions, prints, force_unicode
|
||||
from calibre import guess_type, guess_all_extensions, prints, force_unicode
|
||||
from calibre.ebooks.metadata import MetaInformation, check_isbn
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
|
||||
@ -147,6 +147,15 @@ def _parse_cover_data(root, imgid, mi):
|
||||
if elm_binary:
|
||||
mimetype = elm_binary[0].get('content-type', 'image/jpeg')
|
||||
mime_extensions = guess_all_extensions(mimetype)
|
||||
|
||||
if not mime_extensions and mimetype.startswith('image/'):
|
||||
prints("WARNING: Unsupported or misspelled mime-type '%s'. "\
|
||||
"Trying to recovery mime-type from id_ref='%s'" % (mimetype, imgid) )
|
||||
ctype = guess_type(imgid) # -> (mime-type, encoding)
|
||||
mimetype_fromid = ctype[0]
|
||||
if mimetype_fromid and mimetype_fromid.startswith('image/'):
|
||||
mime_extensions = guess_all_extensions(mimetype_fromid)
|
||||
|
||||
if mime_extensions:
|
||||
pic_data = elm_binary[0].text
|
||||
if pic_data:
|
||||
|
@ -38,17 +38,17 @@ def get_metadata_(src, encoding=None):
|
||||
if match:
|
||||
title = match.group(2)
|
||||
else:
|
||||
pat = re.compile('<title>([^<>]+?)</title>', re.IGNORECASE)
|
||||
match = pat.search(src)
|
||||
if match:
|
||||
title = match.group(1)
|
||||
if not title:
|
||||
for x in ('Title','DC.title','DCTERMS.title'):
|
||||
for x in ('DC.title','DCTERMS.title','Title'):
|
||||
pat = get_meta_regexp_(x)
|
||||
match = pat.search(src)
|
||||
if match:
|
||||
title = match.group(1)
|
||||
break
|
||||
if not title:
|
||||
pat = re.compile('<title>([^<>]+?)</title>', re.IGNORECASE)
|
||||
match = pat.search(src)
|
||||
if match:
|
||||
title = match.group(1)
|
||||
|
||||
# Author
|
||||
author = None
|
||||
@ -57,7 +57,7 @@ def get_metadata_(src, encoding=None):
|
||||
if match:
|
||||
author = match.group(2).replace(',', ';')
|
||||
else:
|
||||
for x in ('Author','DC.creator.aut','DCTERMS.creator.aut'):
|
||||
for x in ('Author','DC.creator.aut','DCTERMS.creator.aut', 'DC.creator'):
|
||||
pat = get_meta_regexp_(x)
|
||||
match = pat.search(src)
|
||||
if match:
|
||||
|
@ -67,10 +67,6 @@ def _metadata_from_formats(formats, force_read_metadata=False, pattern=None):
|
||||
|
||||
return mi
|
||||
|
||||
def is_recipe(filename):
|
||||
return filename.startswith('calibre') and \
|
||||
filename.rpartition('.')[0].endswith('_recipe_out')
|
||||
|
||||
def get_metadata(stream, stream_type='lrf', use_libprs_metadata=False,
|
||||
force_read_metadata=False, pattern=None):
|
||||
pos = 0
|
||||
@ -106,7 +102,7 @@ def _get_metadata(stream, stream_type, use_libprs_metadata,
|
||||
mi = MetaInformation(None, None)
|
||||
name = os.path.basename(getattr(stream, 'name', ''))
|
||||
base = metadata_from_filename(name, pat=pattern)
|
||||
if force_read_metadata or is_recipe(name) or prefs['read_file_metadata']:
|
||||
if force_read_metadata or prefs['read_file_metadata']:
|
||||
mi = get_file_type_metadata(stream, stream_type)
|
||||
if base.title == os.path.splitext(name)[0] and \
|
||||
base.is_null('authors') and base.is_null('isbn'):
|
||||
|
@ -19,7 +19,7 @@ from calibre.ebooks.metadata.toc import TOC
|
||||
from calibre.ebooks.metadata import string_to_authors, MetaInformation, check_isbn
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.utils.date import parse_date, isoformat
|
||||
from calibre.utils.localization import get_lang
|
||||
from calibre.utils.localization import get_lang, canonicalize_lang
|
||||
from calibre import prints, guess_type
|
||||
from calibre.utils.cleantext import clean_ascii_chars
|
||||
from calibre.utils.config import tweaks
|
||||
@ -515,6 +515,7 @@ class OPF(object): # {{{
|
||||
'(re:match(@opf:scheme, "calibre|libprs500", "i") or re:match(@scheme, "calibre|libprs500", "i"))]')
|
||||
uuid_id_path = XPath('descendant::*[re:match(name(), "identifier", "i") and '+
|
||||
'(re:match(@opf:scheme, "uuid", "i") or re:match(@scheme, "uuid", "i"))]')
|
||||
languages_path = XPath('descendant::*[local-name()="language"]')
|
||||
|
||||
manifest_path = XPath('descendant::*[re:match(name(), "manifest", "i")]/*[re:match(name(), "item", "i")]')
|
||||
manifest_ppath = XPath('descendant::*[re:match(name(), "manifest", "i")]')
|
||||
@ -523,7 +524,6 @@ class OPF(object): # {{{
|
||||
|
||||
title = MetadataField('title', formatter=lambda x: re.sub(r'\s+', ' ', x))
|
||||
publisher = MetadataField('publisher')
|
||||
language = MetadataField('language')
|
||||
comments = MetadataField('description')
|
||||
category = MetadataField('type')
|
||||
rights = MetadataField('rights')
|
||||
@ -930,6 +930,44 @@ class OPF(object): # {{{
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
|
||||
@dynamic_property
|
||||
def language(self):
|
||||
|
||||
def fget(self):
|
||||
ans = self.languages
|
||||
if ans:
|
||||
return ans[0]
|
||||
|
||||
def fset(self, val):
|
||||
self.languages = [val]
|
||||
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
|
||||
@dynamic_property
|
||||
def languages(self):
|
||||
|
||||
def fget(self):
|
||||
ans = []
|
||||
for match in self.languages_path(self.metadata):
|
||||
t = self.get_text(match)
|
||||
if t and t.strip():
|
||||
l = canonicalize_lang(t.strip())
|
||||
if l:
|
||||
ans.append(l)
|
||||
return ans
|
||||
|
||||
def fset(self, val):
|
||||
matches = self.languages_path(self.metadata)
|
||||
for x in matches:
|
||||
x.getparent().remove(x)
|
||||
|
||||
for lang in val:
|
||||
l = self.create_metadata_element('language')
|
||||
self.set_text(l, unicode(lang))
|
||||
|
||||
return property(fget=fget, fset=fset)
|
||||
|
||||
|
||||
@dynamic_property
|
||||
def book_producer(self):
|
||||
@ -1052,9 +1090,9 @@ class OPF(object): # {{{
|
||||
val = getattr(mi, attr, None)
|
||||
if val is not None and val != [] and val != (None, None):
|
||||
setattr(self, attr, val)
|
||||
lang = getattr(mi, 'language', None)
|
||||
if lang and lang != 'und':
|
||||
self.language = lang
|
||||
langs = getattr(mi, 'languages', [])
|
||||
if langs and langs != ['und']:
|
||||
self.languages = langs
|
||||
temp = self.to_book_metadata()
|
||||
temp.smart_update(mi, replace_metadata=replace_metadata)
|
||||
self._user_metadata_ = temp.get_all_user_metadata(True)
|
||||
@ -1202,10 +1240,11 @@ class OPFCreator(Metadata):
|
||||
dc_attrs={'id':__appname__+'_id'}))
|
||||
if getattr(self, 'pubdate', None) is not None:
|
||||
a(DC_ELEM('date', self.pubdate.isoformat()))
|
||||
lang = self.language
|
||||
if not lang or lang.lower() == 'und':
|
||||
lang = get_lang().replace('_', '-')
|
||||
a(DC_ELEM('language', lang))
|
||||
langs = self.languages
|
||||
if not langs or langs == ['und']:
|
||||
langs = [get_lang().replace('_', '-').partition('-')[0]]
|
||||
for lang in langs:
|
||||
a(DC_ELEM('language', lang))
|
||||
if self.comments:
|
||||
a(DC_ELEM('description', self.comments))
|
||||
if self.publisher:
|
||||
@ -1288,8 +1327,9 @@ def metadata_to_opf(mi, as_string=True):
|
||||
mi.book_producer = __appname__ + ' (%s) '%__version__ + \
|
||||
'[http://calibre-ebook.com]'
|
||||
|
||||
if not mi.language:
|
||||
mi.language = 'UND'
|
||||
if not mi.languages:
|
||||
lang = get_lang().replace('_', '-').partition('-')[0]
|
||||
mi.languages = [lang]
|
||||
|
||||
root = etree.fromstring(textwrap.dedent(
|
||||
'''
|
||||
@ -1339,8 +1379,10 @@ def metadata_to_opf(mi, as_string=True):
|
||||
factory(DC('identifier'), val, scheme=icu_upper(key))
|
||||
if mi.rights:
|
||||
factory(DC('rights'), mi.rights)
|
||||
factory(DC('language'), mi.language if mi.language and mi.language.lower()
|
||||
!= 'und' else get_lang().replace('_', '-'))
|
||||
for lang in mi.languages:
|
||||
if not lang or lang.lower() == 'und':
|
||||
continue
|
||||
factory(DC('language'), lang)
|
||||
if mi.tags:
|
||||
for tag in mi.tags:
|
||||
factory(DC('subject'), tag)
|
||||
|
@ -22,6 +22,7 @@ from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.library.comments import sanitize_comments_html
|
||||
from calibre.utils.date import parse_date
|
||||
from calibre.utils.localization import canonicalize_lang
|
||||
|
||||
class Worker(Thread): # Get details {{{
|
||||
|
||||
@ -106,10 +107,11 @@ class Worker(Thread): # Get details {{{
|
||||
r'([0-9.]+) (out of|von|su|étoiles sur) (\d+)( (stars|Sternen|stelle)){0,1}')
|
||||
|
||||
lm = {
|
||||
'en': ('English', 'Englisch'),
|
||||
'fr': ('French', 'Français'),
|
||||
'it': ('Italian', 'Italiano'),
|
||||
'de': ('German', 'Deutsch'),
|
||||
'eng': ('English', 'Englisch'),
|
||||
'fra': ('French', 'Français'),
|
||||
'ita': ('Italian', 'Italiano'),
|
||||
'deu': ('German', 'Deutsch'),
|
||||
'spa': ('Spanish', 'Espa\xf1ol', 'Espaniol'),
|
||||
}
|
||||
self.lang_map = {}
|
||||
for code, names in lm.iteritems():
|
||||
@ -374,8 +376,11 @@ class Worker(Thread): # Get details {{{
|
||||
def parse_language(self, pd):
|
||||
for x in reversed(pd.xpath(self.language_xpath)):
|
||||
if x.tail:
|
||||
ans = x.tail.strip()
|
||||
ans = self.lang_map.get(ans, None)
|
||||
raw = x.tail.strip()
|
||||
ans = self.lang_map.get(raw, None)
|
||||
if ans:
|
||||
return ans
|
||||
ans = canonicalize_lang(ans)
|
||||
if ans:
|
||||
return ans
|
||||
# }}}
|
||||
@ -388,7 +393,7 @@ class Amazon(Source):
|
||||
capabilities = frozenset(['identify', 'cover'])
|
||||
touched_fields = frozenset(['title', 'authors', 'identifier:amazon',
|
||||
'identifier:isbn', 'rating', 'comments', 'publisher', 'pubdate',
|
||||
'language'])
|
||||
'languages'])
|
||||
has_html_comments = True
|
||||
supports_gzip_transfer_encoding = True
|
||||
|
||||
|
@ -20,6 +20,7 @@ from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.utils.date import parse_date, utcnow
|
||||
from calibre.utils.cleantext import clean_ascii_chars
|
||||
from calibre.utils.localization import canonicalize_lang
|
||||
from calibre import as_unicode
|
||||
|
||||
NAMESPACES = {
|
||||
@ -95,7 +96,9 @@ def to_metadata(browser, log, entry_, timeout): # {{{
|
||||
return mi
|
||||
|
||||
mi.comments = get_text(extra, description)
|
||||
#mi.language = get_text(extra, language)
|
||||
lang = canonicalize_lang(get_text(extra, language))
|
||||
if lang:
|
||||
mi.language = lang
|
||||
mi.publisher = get_text(extra, publisher)
|
||||
|
||||
# ISBN
|
||||
@ -162,7 +165,7 @@ class GoogleBooks(Source):
|
||||
capabilities = frozenset(['identify', 'cover'])
|
||||
touched_fields = frozenset(['title', 'authors', 'tags', 'pubdate',
|
||||
'comments', 'publisher', 'identifier:isbn', 'rating',
|
||||
'identifier:google']) # language currently disabled
|
||||
'identifier:google', 'languages'])
|
||||
supports_gzip_transfer_encoding = True
|
||||
cached_cover_url_is_reliable = False
|
||||
|
||||
|
@ -484,6 +484,7 @@ def identify(log, abort, # {{{
|
||||
'publication dates')
|
||||
start_time = time.time()
|
||||
results = merge_identify_results(results, log)
|
||||
|
||||
log('We have %d merged results, merging took: %.2f seconds' %
|
||||
(len(results), time.time() - start_time))
|
||||
|
||||
|
@ -35,7 +35,7 @@ class ISBNDB(Source):
|
||||
|
||||
options = (
|
||||
Option('isbndb_key', 'string', None, _('IsbnDB key:'),
|
||||
_('To use isbndb.com you have to sign up for a free account'
|
||||
_('To use isbndb.com you have to sign up for a free account '
|
||||
'at isbndb.com and get an access key.')),
|
||||
)
|
||||
|
||||
|
@ -35,7 +35,7 @@ class OverDrive(Source):
|
||||
capabilities = frozenset(['identify', 'cover'])
|
||||
touched_fields = frozenset(['title', 'authors', 'tags', 'pubdate',
|
||||
'comments', 'publisher', 'identifier:isbn', 'series', 'series_index',
|
||||
'language', 'identifier:overdrive'])
|
||||
'languages', 'identifier:overdrive'])
|
||||
has_html_comments = True
|
||||
supports_gzip_transfer_encoding = False
|
||||
cached_cover_url_is_reliable = True
|
||||
@ -421,8 +421,10 @@ class OverDrive(Source):
|
||||
pass
|
||||
if lang:
|
||||
lang = lang[0].strip().lower()
|
||||
mi.language = {'english':'en', 'french':'fr', 'german':'de',
|
||||
'spanish':'es'}.get(lang, None)
|
||||
lang = {'english':'eng', 'french':'fra', 'german':'deu',
|
||||
'spanish':'spa'}.get(lang, None)
|
||||
if lang:
|
||||
mi.language = lang
|
||||
|
||||
if ebook_isbn:
|
||||
#print "ebook isbn is "+str(ebook_isbn[0])
|
||||
|
445
src/calibre/ebooks/metadata/sources/ozon.py
Normal file
@ -0,0 +1,445 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||
from xml.etree.ElementTree import _Element
|
||||
|
||||
__license__ = 'GPL 3'
|
||||
__copyright__ = '2011, Roman Mukhin <ramses_ru at hotmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
import urllib2
|
||||
import datetime
|
||||
from urllib import quote_plus
|
||||
from Queue import Queue, Empty
|
||||
from lxml import etree, html
|
||||
from lxml.etree import ElementBase
|
||||
from calibre import as_unicode
|
||||
|
||||
from calibre import prints
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
|
||||
from calibre.ebooks.metadata import check_isbn
|
||||
from calibre.ebooks.metadata.sources.base import Source
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
|
||||
class Ozon(Source):
|
||||
name = 'OZON.ru'
|
||||
description = _('Downloads metadata and covers from OZON.ru')
|
||||
|
||||
capabilities = frozenset(['identify', 'cover'])
|
||||
|
||||
touched_fields = frozenset(['title', 'authors', 'identifier:isbn', 'identifier:ozon',
|
||||
'publisher', 'pubdate', 'comments', 'series', 'rating', 'language'])
|
||||
# Test purpose only, test function does not like when sometimes some filed are empty
|
||||
#touched_fields = frozenset(['title', 'authors', 'identifier:isbn', 'identifier:ozon',
|
||||
# 'publisher', 'pubdate', 'comments'])
|
||||
|
||||
supports_gzip_transfer_encoding = True
|
||||
has_html_comments = True
|
||||
|
||||
ozon_url = 'http://www.ozon.ru'
|
||||
|
||||
# match any ISBN10/13. From "Regular Expressions Cookbook"
|
||||
isbnPattern = r'(?:ISBN(?:-1[03])?:? )?(?=[-0-9 ]{17}|'\
|
||||
'[-0-9X ]{13}|[0-9X]{10})(?:97[89][- ]?)?[0-9]{1,5}[- ]?'\
|
||||
'(?:[0-9]+[- ]?){2}[0-9X]'
|
||||
isbnRegex = re.compile(isbnPattern)
|
||||
|
||||
def get_book_url(self, identifiers): # {{{
|
||||
ozon_id = identifiers.get('ozon', None)
|
||||
res = None
|
||||
if ozon_id:
|
||||
url = '{}/context/detail/id/{}?partner={}'.format(self.ozon_url, urllib2.quote(ozon_id), _get_affiliateId())
|
||||
res = ('ozon', ozon_id, url)
|
||||
return res
|
||||
# }}}
|
||||
|
||||
def create_query(self, log, title=None, authors=None, identifiers={}): # {{{
|
||||
# div_book -> search only books, ebooks and audio books
|
||||
search_url = self.ozon_url + '/webservice/webservice.asmx/SearchWebService?searchContext=div_book&searchText='
|
||||
|
||||
isbn = _format_isbn(log, identifiers.get('isbn', None))
|
||||
# TODO: format isbn!
|
||||
qItems = set([isbn, title])
|
||||
if authors:
|
||||
qItems |= frozenset(authors)
|
||||
qItems.discard(None)
|
||||
qItems.discard('')
|
||||
qItems = map(_quoteString, qItems)
|
||||
|
||||
q = ' '.join(qItems).strip()
|
||||
log.info(u'search string: ' + q)
|
||||
|
||||
if isinstance(q, unicode):
|
||||
q = q.encode('utf-8')
|
||||
if not q:
|
||||
return None
|
||||
|
||||
search_url += quote_plus(q)
|
||||
log.debug(u'search url: %r'%search_url)
|
||||
|
||||
return search_url
|
||||
# }}}
|
||||
|
||||
def identify(self, log, result_queue, abort, title=None, authors=None, # {{{
|
||||
identifiers={}, timeout=30):
|
||||
if not self.is_configured():
|
||||
return
|
||||
query = self.create_query(log, title=title, authors=authors, identifiers=identifiers)
|
||||
if not query:
|
||||
err = 'Insufficient metadata to construct query'
|
||||
log.error(err)
|
||||
return err
|
||||
|
||||
try:
|
||||
raw = self.browser.open_novisit(query).read()
|
||||
|
||||
except Exception as e:
|
||||
log.exception(u'Failed to make identify query: %r'%query)
|
||||
return as_unicode(e)
|
||||
|
||||
try:
|
||||
parser = etree.XMLParser(recover=True, no_network=True)
|
||||
feed = etree.fromstring(xml_to_unicode(raw, strip_encoding_pats=True, assume_utf8=True)[0], parser=parser)
|
||||
entries = feed.xpath('//*[local-name() = "SearchItems"]')
|
||||
if entries:
|
||||
metadata = self.get_metadata(log, entries, title, authors, identifiers)
|
||||
self.get_all_details(log, metadata, abort, result_queue, identifiers, timeout)
|
||||
except Exception as e:
|
||||
log.exception('Failed to parse identify results')
|
||||
return as_unicode(e)
|
||||
|
||||
# }}}
|
||||
|
||||
def get_metadata(self, log, entries, title, authors, identifiers): # {{{
|
||||
title = unicode(title).upper() if title else ''
|
||||
authors = map(unicode.upper, map(unicode, authors)) if authors else None
|
||||
ozon_id = identifiers.get('ozon', None)
|
||||
|
||||
unk = unicode(_('Unknown')).upper()
|
||||
|
||||
if title == unk:
|
||||
title = None
|
||||
|
||||
if authors == [unk]:
|
||||
authors = None
|
||||
|
||||
def in_authors(authors, miauthors):
|
||||
for author in authors:
|
||||
for miauthor in miauthors:
|
||||
if author in miauthor: return True
|
||||
return None
|
||||
|
||||
def ensure_metadata_match(mi): # {{{
|
||||
match = True
|
||||
if title:
|
||||
mititle = unicode(mi.title).upper() if mi.title else ''
|
||||
match = title in mititle
|
||||
if match and authors:
|
||||
miauthors = map(unicode.upper, map(unicode, mi.authors)) if mi.authors else []
|
||||
match = in_authors(authors, miauthors)
|
||||
|
||||
if match and ozon_id:
|
||||
mozon_id = mi.identifiers['ozon']
|
||||
match = ozon_id == mozon_id
|
||||
|
||||
return match
|
||||
|
||||
metadata = []
|
||||
for i, entry in enumerate(entries):
|
||||
mi = self.to_metadata(log, entry)
|
||||
mi.source_relevance = i
|
||||
if ensure_metadata_match(mi):
|
||||
metadata.append(mi)
|
||||
# log.debug(u'added metadata %s %s. '%(mi.title, mi.authors))
|
||||
else:
|
||||
log.debug(u'skipped metadata %s %s. (does not match the query)'%(mi.title, mi.authors))
|
||||
return metadata
|
||||
# }}}
|
||||
|
||||
def get_all_details(self, log, metadata, abort, result_queue, identifiers, timeout): # {{{
|
||||
req_isbn = identifiers.get('isbn', None)
|
||||
|
||||
for mi in metadata:
|
||||
if abort.is_set():
|
||||
break
|
||||
try:
|
||||
ozon_id = mi.identifiers['ozon']
|
||||
|
||||
try:
|
||||
self.get_book_details(log, mi, timeout)
|
||||
except:
|
||||
log.exception(u'Failed to get details for metadata: %s'%mi.title)
|
||||
|
||||
all_isbns = getattr(mi, 'all_isbns', [])
|
||||
if req_isbn and all_isbns and check_isbn(req_isbn) not in all_isbns:
|
||||
log.debug(u'skipped, no requested ISBN %s found'%req_isbn)
|
||||
continue
|
||||
|
||||
for isbn in all_isbns:
|
||||
self.cache_isbn_to_identifier(isbn, ozon_id)
|
||||
|
||||
if mi.ozon_cover_url:
|
||||
self.cache_identifier_to_cover_url(ozon_id, mi.ozon_cover_url)
|
||||
|
||||
self.clean_downloaded_metadata(mi)
|
||||
result_queue.put(mi)
|
||||
except:
|
||||
log.exception(u'Failed to get details for metadata: %s'%mi.title)
|
||||
# }}}
|
||||
|
||||
def to_metadata(self, log, entry): # {{{
|
||||
xp_template = 'normalize-space(./*[local-name() = "{0}"]/text())'
|
||||
|
||||
title = entry.xpath(xp_template.format('Name'))
|
||||
author = entry.xpath(xp_template.format('Author'))
|
||||
mi = Metadata(title, author.split(','))
|
||||
|
||||
ozon_id = entry.xpath(xp_template.format('ID'))
|
||||
mi.identifiers = {'ozon':ozon_id}
|
||||
|
||||
mi.comments = entry.xpath(xp_template.format('Annotation'))
|
||||
|
||||
mi.ozon_cover_url = None
|
||||
cover = entry.xpath(xp_template.format('Picture'))
|
||||
if cover:
|
||||
mi.ozon_cover_url = _translateToBigCoverUrl(cover)
|
||||
|
||||
rating = entry.xpath(xp_template.format('ClientRatingValue'))
|
||||
if rating:
|
||||
try:
|
||||
#'rating', A floating point number between 0 and 10
|
||||
# OZON raion N of 5, calibre of 10, but there is a bug? in identify
|
||||
mi.rating = float(rating)
|
||||
except:
|
||||
pass
|
||||
rating
|
||||
return mi
|
||||
# }}}
|
||||
|
||||
def get_cached_cover_url(self, identifiers): # {{{
|
||||
url = None
|
||||
ozon_id = identifiers.get('ozon', None)
|
||||
if ozon_id is None:
|
||||
isbn = identifiers.get('isbn', None)
|
||||
if isbn is not None:
|
||||
ozon_id = self.cached_isbn_to_identifier(isbn)
|
||||
if ozon_id is not None:
|
||||
url = self.cached_identifier_to_cover_url(ozon_id)
|
||||
return url
|
||||
# }}}
|
||||
|
||||
def download_cover(self, log, result_queue, abort, title=None, authors=None, identifiers={}, timeout=30): # {{{
|
||||
cached_url = self.get_cached_cover_url(identifiers)
|
||||
if cached_url is None:
|
||||
log.debug('No cached cover found, running identify')
|
||||
rq = Queue()
|
||||
self.identify(log, rq, abort, title=title, authors=authors, identifiers=identifiers)
|
||||
if abort.is_set():
|
||||
return
|
||||
results = []
|
||||
while True:
|
||||
try:
|
||||
results.append(rq.get_nowait())
|
||||
except Empty:
|
||||
break
|
||||
results.sort(key=self.identify_results_keygen(title=title, authors=authors, identifiers=identifiers))
|
||||
for mi in results:
|
||||
cached_url = self.get_cached_cover_url(mi.identifiers)
|
||||
if cached_url is not None:
|
||||
break
|
||||
|
||||
if cached_url is None:
|
||||
log.info('No cover found')
|
||||
return
|
||||
|
||||
if abort.is_set():
|
||||
return
|
||||
|
||||
log.debug('Downloading cover from:', cached_url)
|
||||
try:
|
||||
cdata = self.browser.open_novisit(cached_url, timeout=timeout).read()
|
||||
if cdata:
|
||||
result_queue.put((self, cdata))
|
||||
except Exception as e:
|
||||
log.exception(u'Failed to download cover from: %s'%cached_url)
|
||||
return as_unicode(e)
|
||||
# }}}
|
||||
|
||||
def get_book_details(self, log, metadata, timeout): # {{{
|
||||
url = self.get_book_url(metadata.get_identifiers())[2]
|
||||
|
||||
raw = self.browser.open_novisit(url, timeout=timeout).read()
|
||||
doc = html.fromstring(raw)
|
||||
|
||||
# series
|
||||
xpt = u'normalize-space(//div[@class="frame_content"]//div[contains(normalize-space(text()), "Серия:")]//a/@title)'
|
||||
series = doc.xpath(xpt)
|
||||
if series:
|
||||
metadata.series = series
|
||||
|
||||
xpt = u'substring-after(//meta[@name="description"]/@content, "ISBN")'
|
||||
isbn_str = doc.xpath(xpt)
|
||||
if isbn_str:
|
||||
all_isbns = [check_isbn(isbn) for isbn in self.isbnRegex.findall(isbn_str) if check_isbn(isbn)]
|
||||
if all_isbns:
|
||||
metadata.all_isbns = all_isbns
|
||||
metadata.isbn = all_isbns[0]
|
||||
|
||||
xpt = u'//div[@class="frame_content"]//div[contains(normalize-space(text()), "Издатель")]//a[@title="Издательство"]'
|
||||
publishers = doc.xpath(xpt)
|
||||
if publishers:
|
||||
metadata.publisher = publishers[0].text
|
||||
|
||||
xpt = u'string(../text()[contains(., "г.")])'
|
||||
yearIn = publishers[0].xpath(xpt)
|
||||
if yearIn:
|
||||
matcher = re.search(r'\d{4}', yearIn)
|
||||
if matcher:
|
||||
year = int(matcher.group(0))
|
||||
# only year is available, so use 1-st of Jan
|
||||
metadata.pubdate = datetime.datetime(year, 1, 1) #<- failed comparation in identify.py
|
||||
#metadata.pubdate = datetime(year, 1, 1)
|
||||
xpt = u'substring-after(string(../text()[contains(., "Язык")]), ": ")'
|
||||
displLang = publishers[0].xpath(xpt)
|
||||
lang_code =_translageLanguageToCode(displLang)
|
||||
if lang_code:
|
||||
metadata.language = lang_code
|
||||
|
||||
# overwrite comments from HTML if any
|
||||
# tr/td[contains(.//text(), "От издателя")] -> does not work, why?
|
||||
xpt = u'//div[contains(@class, "detail")]//tr/td//text()[contains(., "От издателя")]'\
|
||||
u'/ancestor::tr[1]/following-sibling::tr[1]/td[contains(./@class, "description")][1]'
|
||||
comment_elem = doc.xpath(xpt)
|
||||
if comment_elem:
|
||||
comments = unicode(etree.tostring(comment_elem[0]))
|
||||
if comments:
|
||||
# cleanup root tag, TODO: remove tags like object/embeded
|
||||
comments = re.sub(r'^<td.+?>|</td>.+?$', u'', comments).strip()
|
||||
if comments:
|
||||
metadata.comments = comments
|
||||
else:
|
||||
log.debug('No book description found in HTML')
|
||||
# }}}
|
||||
|
||||
def _quoteString(str): # {{{
|
||||
return '"' + str + '"' if str and str.find(' ') != -1 else str
|
||||
# }}}
|
||||
|
||||
# TODO: make customizable
|
||||
def _translateToBigCoverUrl(coverUrl): # {{{
|
||||
# http://www.ozon.ru/multimedia/books_covers/small/1002986468.gif
|
||||
# http://www.ozon.ru/multimedia/books_covers/1002986468.jpg
|
||||
|
||||
m = re.match(r'^(.+\/)small\/(.+\.).+$', coverUrl)
|
||||
if m:
|
||||
coverUrl = m.group(1) + m.group(2) + 'jpg'
|
||||
return coverUrl
|
||||
# }}}
|
||||
|
||||
def _get_affiliateId(): # {{{
|
||||
import random
|
||||
|
||||
aff_id = 'romuk'
|
||||
# Use Kovid's affiliate id 30% of the time.
|
||||
if random.randint(1, 10) in (1, 2, 3):
|
||||
aff_id = 'kovidgoyal'
|
||||
return aff_id
|
||||
# }}}
|
||||
|
||||
# for now only RUS ISBN are supported
|
||||
#http://ru.wikipedia.org/wiki/ISBN_российских_издательств
|
||||
isbn_pat = re.compile(r"""
|
||||
^
|
||||
(\d{3})? # match GS1 Prefix for ISBN13
|
||||
(5) # group identifier for rRussian-speaking countries
|
||||
( # begin variable length for Publisher
|
||||
[01]\d{1}| # 2x
|
||||
[2-6]\d{2}| # 3x
|
||||
7\d{3}| # 4x (starting with 7)
|
||||
8[0-4]\d{2}| # 4x (starting with 8)
|
||||
9[2567]\d{2}| # 4x (starting with 9)
|
||||
99[26]\d{1}| # 4x (starting with 99)
|
||||
8[5-9]\d{3}| # 5x (starting with 8)
|
||||
9[348]\d{3}| # 5x (starting with 9)
|
||||
900\d{2}| # 5x (starting with 900)
|
||||
91[0-8]\d{2}| # 5x (starting with 91)
|
||||
90[1-9]\d{3}| # 6x (starting with 90)
|
||||
919\d{3}| # 6x (starting with 919)
|
||||
99[^26]\d{4} # 7x (starting with 99)
|
||||
) # end variable length for Publisher
|
||||
(\d+) # Title
|
||||
([\dX]) # Check digit
|
||||
$
|
||||
""", re.VERBOSE)
|
||||
|
||||
def _format_isbn(log, isbn): # {{{
|
||||
res = check_isbn(isbn)
|
||||
if res:
|
||||
m = isbn_pat.match(res)
|
||||
if m:
|
||||
res = '-'.join([g for g in m.groups() if g])
|
||||
else:
|
||||
log.error('cannot format isbn %s'%isbn)
|
||||
return res
|
||||
# }}}
|
||||
|
||||
def _translageLanguageToCode(displayLang): # {{{
|
||||
displayLang = unicode(displayLang).strip() if displayLang else None
|
||||
langTbl = { None: 'ru',
|
||||
u'Немецкий': 'de',
|
||||
u'Английский': 'en',
|
||||
u'Французский': 'fr',
|
||||
u'Итальянский': 'it',
|
||||
u'Испанский': 'es',
|
||||
u'Китайский': 'zh',
|
||||
u'Японский': 'ja' }
|
||||
return langTbl.get(displayLang, None)
|
||||
# }}}
|
||||
|
||||
if __name__ == '__main__': # tests {{{
|
||||
# To run these test use: calibre-debug -e src/calibre/ebooks/metadata/sources/ozon.py
|
||||
# comment some touched_fields before run thoses tests
|
||||
from calibre.ebooks.metadata.sources.test import (test_identify_plugin,
|
||||
title_test, authors_test, isbn_test)
|
||||
|
||||
|
||||
test_identify_plugin(Ozon.name,
|
||||
[
|
||||
|
||||
(
|
||||
{'identifiers':{'isbn': '9785916572629'} },
|
||||
[title_test(u'На все четыре стороны', exact=True),
|
||||
authors_test([u'А. А. Гилл'])]
|
||||
),
|
||||
(
|
||||
{'identifiers':{}, 'title':u'Der Himmel Kennt Keine Gunstlinge',
|
||||
'authors':[u'Erich Maria Remarque']},
|
||||
[title_test(u'Der Himmel Kennt Keine Gunstlinge', exact=True),
|
||||
authors_test([u'Erich Maria Remarque'])]
|
||||
),
|
||||
(
|
||||
{'identifiers':{ }, 'title':u'Метро 2033',
|
||||
'authors':[u'Дмитрий Глуховский']},
|
||||
[title_test(u'Метро 2033', exact=False)]
|
||||
),
|
||||
(
|
||||
{'identifiers':{'isbn': '9785170727209'}, 'title':u'Метро 2033',
|
||||
'authors':[u'Дмитрий Глуховский']},
|
||||
[title_test(u'Метро 2033', exact=True),
|
||||
authors_test([u'Дмитрий Глуховский']),
|
||||
isbn_test('9785170727209')]
|
||||
),
|
||||
(
|
||||
{'identifiers':{'isbn': '5-699-13613-4'}, 'title':u'Метро 2033',
|
||||
'authors':[u'Дмитрий Глуховский']},
|
||||
[title_test(u'Метро 2033', exact=True),
|
||||
authors_test([u'Дмитрий Глуховский'])]
|
||||
),
|
||||
(
|
||||
{'identifiers':{}, 'title':u'Метро',
|
||||
'authors':[u'Глуховский']},
|
||||
[title_test(u'Метро', exact=False)]
|
||||
),
|
||||
])
|
||||
# }}}
|
@ -18,6 +18,11 @@ from calibre.ebooks.mobi.utils import (decode_hex_number, decint,
|
||||
get_trailing_data, decode_tbs)
|
||||
from calibre.utils.magick.draw import identify_data
|
||||
|
||||
def format_bytes(byts):
|
||||
byts = bytearray(byts)
|
||||
byts = [hex(b)[2:] for b in byts]
|
||||
return ' '.join(byts)
|
||||
|
||||
# PalmDB {{{
|
||||
class PalmDOCAttributes(object):
|
||||
|
||||
@ -76,7 +81,7 @@ class PalmDB(object):
|
||||
self.ident = self.type + self.creator
|
||||
if self.ident not in (b'BOOKMOBI', b'TEXTREAD'):
|
||||
raise ValueError('Unknown book ident: %r'%self.ident)
|
||||
self.uid_seed, = struct.unpack(b'>I', self.raw[68:72])
|
||||
self.last_record_uid, = struct.unpack(b'>I', self.raw[68:72])
|
||||
self.next_rec_list_id = self.raw[72:76]
|
||||
|
||||
self.number_of_records, = struct.unpack(b'>H', self.raw[76:78])
|
||||
@ -97,7 +102,7 @@ class PalmDB(object):
|
||||
ans.append('Sort Info ID: %r'%self.sort_info_id)
|
||||
ans.append('Type: %r'%self.type)
|
||||
ans.append('Creator: %r'%self.creator)
|
||||
ans.append('UID seed: %r'%self.uid_seed)
|
||||
ans.append('Last record UID +1: %r'%self.last_record_uid)
|
||||
ans.append('Next record list id: %r'%self.next_rec_list_id)
|
||||
ans.append('Number of records: %s'%self.number_of_records)
|
||||
|
||||
@ -272,7 +277,8 @@ class MOBIHeader(object): # {{{
|
||||
self.first_image_index, = struct.unpack(b'>I', self.raw[108:112])
|
||||
self.huffman_record_offset, = struct.unpack(b'>I', self.raw[112:116])
|
||||
self.huffman_record_count, = struct.unpack(b'>I', self.raw[116:120])
|
||||
self.unknown2 = self.raw[120:128]
|
||||
self.datp_record_offset, = struct.unpack(b'>I', self.raw[120:124])
|
||||
self.datp_record_count, = struct.unpack(b'>I', self.raw[124:128])
|
||||
self.exth_flags, = struct.unpack(b'>I', self.raw[128:132])
|
||||
self.has_exth = bool(self.exth_flags & 0x40)
|
||||
self.has_drm_data = self.length >= 174 and len(self.raw) >= 180
|
||||
@ -314,7 +320,7 @@ class MOBIHeader(object): # {{{
|
||||
self.exth = EXTHHeader(self.raw[self.exth_offset:])
|
||||
|
||||
self.end_of_exth = self.exth_offset + self.exth.length
|
||||
self.bytes_after_exth = self.fullname_offset - self.end_of_exth
|
||||
self.bytes_after_exth = self.raw[self.end_of_exth:self.fullname_offset]
|
||||
|
||||
def __str__(self):
|
||||
ans = ['*'*20 + ' MOBI Header '+ '*'*20]
|
||||
@ -347,7 +353,8 @@ class MOBIHeader(object): # {{{
|
||||
ans.append('First Image index: %d'%self.first_image_index)
|
||||
ans.append('Huffman record offset: %d'%self.huffman_record_offset)
|
||||
ans.append('Huffman record count: %d'%self.huffman_record_count)
|
||||
ans.append('Unknown2: %r'%self.unknown2)
|
||||
ans.append('DATP record offset: %r'%self.datp_record_offset)
|
||||
ans.append('DATP record count: %r'%self.datp_record_count)
|
||||
ans.append('EXTH flags: %s (%s)'%(bin(self.exth_flags)[2:], self.has_exth))
|
||||
if self.has_drm_data:
|
||||
ans.append('Unknown3: %r'%self.unknown3)
|
||||
@ -379,7 +386,9 @@ class MOBIHeader(object): # {{{
|
||||
|
||||
if self.has_exth:
|
||||
ans += '\n\n' + str(self.exth)
|
||||
ans += '\n\nBytes after EXTH: %d'%self.bytes_after_exth
|
||||
ans += '\n\nBytes after EXTH (%d bytes): %s'%(
|
||||
len(self.bytes_after_exth),
|
||||
format_bytes(self.bytes_after_exth))
|
||||
|
||||
ans += '\nNumber of bytes after full name: %d' % (len(self.raw) - (self.fullname_offset +
|
||||
self.fullname_length))
|
||||
@ -406,6 +415,109 @@ class TagX(object): # {{{
|
||||
self.num_values, bin(self.bitmask), self.eof)
|
||||
# }}}
|
||||
|
||||
class SecondaryIndexHeader(object): # {{{
|
||||
|
||||
def __init__(self, record):
|
||||
self.record = record
|
||||
raw = self.record.raw
|
||||
#open('/t/index_header.bin', 'wb').write(raw)
|
||||
if raw[:4] != b'INDX':
|
||||
raise ValueError('Invalid Secondary Index Record')
|
||||
self.header_length, = struct.unpack('>I', raw[4:8])
|
||||
self.unknown1 = raw[8:16]
|
||||
self.index_type, = struct.unpack('>I', raw[16:20])
|
||||
self.index_type_desc = {0: 'normal', 2:
|
||||
'inflection', 6: 'calibre'}.get(self.index_type, 'unknown')
|
||||
self.idxt_start, = struct.unpack('>I', raw[20:24])
|
||||
self.index_count, = struct.unpack('>I', raw[24:28])
|
||||
self.index_encoding_num, = struct.unpack('>I', raw[28:32])
|
||||
self.index_encoding = {65001: 'utf-8', 1252:
|
||||
'cp1252'}.get(self.index_encoding_num, 'unknown')
|
||||
if self.index_encoding == 'unknown':
|
||||
raise ValueError(
|
||||
'Unknown index encoding: %d'%self.index_encoding_num)
|
||||
self.unknown2 = raw[32:36]
|
||||
self.num_index_entries, = struct.unpack('>I', raw[36:40])
|
||||
self.ordt_start, = struct.unpack('>I', raw[40:44])
|
||||
self.ligt_start, = struct.unpack('>I', raw[44:48])
|
||||
self.num_of_ligt_entries, = struct.unpack('>I', raw[48:52])
|
||||
self.num_of_cncx_blocks, = struct.unpack('>I', raw[52:56])
|
||||
self.unknown3 = raw[56:180]
|
||||
self.tagx_offset, = struct.unpack(b'>I', raw[180:184])
|
||||
if self.tagx_offset != self.header_length:
|
||||
raise ValueError('TAGX offset and header length disagree')
|
||||
self.unknown4 = raw[184:self.header_length]
|
||||
|
||||
tagx = raw[self.header_length:]
|
||||
if not tagx.startswith(b'TAGX'):
|
||||
raise ValueError('Invalid TAGX section')
|
||||
self.tagx_header_length, = struct.unpack('>I', tagx[4:8])
|
||||
self.tagx_control_byte_count, = struct.unpack('>I', tagx[8:12])
|
||||
tag_table = tagx[12:self.tagx_header_length]
|
||||
if len(tag_table) % 4 != 0:
|
||||
raise ValueError('Invalid Tag table')
|
||||
num_tagx_entries = len(tag_table) // 4
|
||||
self.tagx_entries = []
|
||||
for i in range(num_tagx_entries):
|
||||
self.tagx_entries.append(TagX(tag_table[i*4:(i+1)*4],
|
||||
self.tagx_control_byte_count))
|
||||
if self.tagx_entries and not self.tagx_entries[-1].is_eof:
|
||||
raise ValueError('TAGX last entry is not EOF')
|
||||
|
||||
idxt0_pos = self.header_length+self.tagx_header_length
|
||||
num = ord(raw[idxt0_pos])
|
||||
count_pos = idxt0_pos+1+num
|
||||
self.last_entry = raw[idxt0_pos+1:count_pos]
|
||||
self.ncx_count, = struct.unpack(b'>H', raw[count_pos:count_pos+2])
|
||||
|
||||
# There may be some alignment zero bytes between the end of the idxt0
|
||||
# and self.idxt_start
|
||||
idxt = raw[self.idxt_start:]
|
||||
if idxt[:4] != b'IDXT':
|
||||
raise ValueError('Invalid IDXT header')
|
||||
length_check, = struct.unpack(b'>H', idxt[4:6])
|
||||
if length_check != self.header_length + self.tagx_header_length:
|
||||
raise ValueError('Length check failed')
|
||||
if idxt[6:].replace(b'\0', b''):
|
||||
raise ValueError('Non null trailing bytes after IDXT')
|
||||
|
||||
|
||||
def __str__(self):
|
||||
ans = ['*'*20 + ' Secondary Index Header '+ '*'*20]
|
||||
a = ans.append
|
||||
def u(w):
|
||||
a('Unknown: %r (%d bytes) (All zeros: %r)'%(w,
|
||||
len(w), not bool(w.replace(b'\0', b'')) ))
|
||||
|
||||
a('Header length: %d'%self.header_length)
|
||||
u(self.unknown1)
|
||||
a('Index Type: %s (%d)'%(self.index_type_desc, self.index_type))
|
||||
a('Offset to IDXT start: %d'%self.idxt_start)
|
||||
a('Number of index records: %d'%self.index_count)
|
||||
a('Index encoding: %s (%d)'%(self.index_encoding,
|
||||
self.index_encoding_num))
|
||||
u(self.unknown2)
|
||||
a('Number of index entries: %d'% self.num_index_entries)
|
||||
a('ORDT start: %d'%self.ordt_start)
|
||||
a('LIGT start: %d'%self.ligt_start)
|
||||
a('Number of LIGT entries: %d'%self.num_of_ligt_entries)
|
||||
a('Number of cncx blocks: %d'%self.num_of_cncx_blocks)
|
||||
u(self.unknown3)
|
||||
a('TAGX offset: %d'%self.tagx_offset)
|
||||
u(self.unknown4)
|
||||
a('\n\n')
|
||||
a('*'*20 + ' TAGX Header (%d bytes)'%self.tagx_header_length+ '*'*20)
|
||||
a('Header length: %d'%self.tagx_header_length)
|
||||
a('Control byte count: %d'%self.tagx_control_byte_count)
|
||||
for i in self.tagx_entries:
|
||||
a('\t' + repr(i))
|
||||
a('Index of last IndexEntry in secondary index record: %s'% self.last_entry)
|
||||
a('Number of entries in the NCX: %d'% self.ncx_count)
|
||||
|
||||
return '\n'.join(ans)
|
||||
|
||||
# }}}
|
||||
|
||||
class IndexHeader(object): # {{{
|
||||
|
||||
def __init__(self, record):
|
||||
@ -455,12 +567,12 @@ class IndexHeader(object): # {{{
|
||||
self.tagx_control_byte_count))
|
||||
if self.tagx_entries and not self.tagx_entries[-1].is_eof:
|
||||
raise ValueError('TAGX last entry is not EOF')
|
||||
self.tagx_entries = self.tagx_entries[:-1]
|
||||
|
||||
idxt0_pos = self.header_length+self.tagx_header_length
|
||||
last_num, consumed = decode_hex_number(raw[idxt0_pos:])
|
||||
count_pos = idxt0_pos + consumed
|
||||
self.ncx_count, = struct.unpack(b'>H', raw[count_pos:count_pos+2])
|
||||
self.last_entry = last_num
|
||||
|
||||
if last_num != self.ncx_count - 1:
|
||||
raise ValueError('Last id number in the NCX != NCX count - 1')
|
||||
@ -473,9 +585,12 @@ class IndexHeader(object): # {{{
|
||||
length_check, = struct.unpack(b'>H', idxt[4:6])
|
||||
if length_check != self.header_length + self.tagx_header_length:
|
||||
raise ValueError('Length check failed')
|
||||
if idxt[6:].replace(b'\0', b''):
|
||||
raise ValueError('Non null trailing bytes after IDXT')
|
||||
|
||||
|
||||
def __str__(self):
|
||||
ans = ['*'*20 + ' Index Header '+ '*'*20]
|
||||
ans = ['*'*20 + ' Index Header (%d bytes)'%len(self.record.raw)+ '*'*20]
|
||||
a = ans.append
|
||||
def u(w):
|
||||
a('Unknown: %r (%d bytes) (All zeros: %r)'%(w,
|
||||
@ -503,6 +618,7 @@ class IndexHeader(object): # {{{
|
||||
a('Control byte count: %d'%self.tagx_control_byte_count)
|
||||
for i in self.tagx_entries:
|
||||
a('\t' + repr(i))
|
||||
a('Index of last IndexEntry in primary index record: %s'% self.last_entry)
|
||||
a('Number of entries in the NCX: %d'% self.ncx_count)
|
||||
|
||||
return '\n'.join(ans)
|
||||
@ -521,20 +637,30 @@ class Tag(object): # {{{
|
||||
3: ('label_offset', 'Offset to label in CNCX'),
|
||||
4: ('depth', 'Depth of this entry in TOC'),
|
||||
|
||||
11: ('secondary', '[unknown, unknown, '
|
||||
'tag type from TAGX in primary index header]'),
|
||||
|
||||
# The remaining tag types have to be interpreted subject to the type
|
||||
# of index entry they are present in
|
||||
}
|
||||
|
||||
INTERPRET_MAP = {
|
||||
'subchapter': {
|
||||
5 : ('Parent chapter index', 'parent_index')
|
||||
21 : ('Parent chapter index', 'parent_index')
|
||||
},
|
||||
|
||||
'article' : {
|
||||
5 : ('Class offset in cncx', 'class_offset'),
|
||||
21 : ('Parent section index', 'parent_index'),
|
||||
22 : ('Description offset in cncx', 'desc_offset'),
|
||||
23 : ('Author offset in cncx', 'author_offset'),
|
||||
69 : ('Offset from first image record num to the'
|
||||
' image record associated with this article',
|
||||
'image_index'),
|
||||
70 : ('Description offset in cncx', 'desc_offset'),
|
||||
71 : ('Image attribution offset in cncx',
|
||||
'image_attr_offset'),
|
||||
72 : ('Image caption offset in cncx',
|
||||
'image_caption_offset'),
|
||||
73 : ('Author offset in cncx', 'author_offset'),
|
||||
},
|
||||
|
||||
'chapter_with_subchapters' : {
|
||||
@ -546,6 +672,8 @@ class Tag(object): # {{{
|
||||
5 : ('Class offset in cncx', 'class_offset'),
|
||||
22 : ('First section index', 'first_child_index'),
|
||||
23 : ('Last section index', 'last_child_index'),
|
||||
69 : ('Offset from first image record num to masthead'
|
||||
' record', 'image_index'),
|
||||
},
|
||||
|
||||
'section' : {
|
||||
@ -558,21 +686,25 @@ class Tag(object): # {{{
|
||||
|
||||
|
||||
def __init__(self, tagx, vals, entry_type, cncx):
|
||||
self.value = vals if len(vals) > 1 else vals[0]
|
||||
self.value = vals if len(vals) > 1 else vals[0] if vals else None
|
||||
self.entry_type = entry_type
|
||||
tag_type = tagx.tag
|
||||
|
||||
self.cncx_value = None
|
||||
if tagx.tag in self.TAG_MAP:
|
||||
self.attr, self.desc = self.TAG_MAP[tagx.tag]
|
||||
if tag_type in self.TAG_MAP:
|
||||
self.attr, self.desc = self.TAG_MAP[tag_type]
|
||||
else:
|
||||
try:
|
||||
td = self.INTERPRET_MAP[entry_type]
|
||||
except:
|
||||
raise ValueError('Unknown entry type: %s'%entry_type)
|
||||
try:
|
||||
self.desc, self.attr = td[tagx.tag]
|
||||
self.desc, self.attr = td[tag_type]
|
||||
except:
|
||||
raise ValueError('Unknown tag: %d for entry type: %s'%(
|
||||
tagx.tag, entry_type))
|
||||
print ('Unknown tag value: %d'%tag_type)
|
||||
self.desc = '??Unknown (tag value: %d type: %s)'%(
|
||||
tag_type, entry_type)
|
||||
self.attr = 'unknown'
|
||||
if '_offset' in self.attr:
|
||||
self.cncx_value = cncx[self.value]
|
||||
|
||||
@ -593,6 +725,9 @@ class IndexEntry(object): # {{{
|
||||
'''
|
||||
|
||||
TYPES = {
|
||||
# Present in secondary index record
|
||||
0x01 : 'null',
|
||||
0x02 : 'publication_meta',
|
||||
# Present in book type files
|
||||
0x0f : 'chapter',
|
||||
0x6f : 'chapter_with_subchapters',
|
||||
@ -603,7 +738,8 @@ class IndexEntry(object): # {{{
|
||||
0x3f : 'article',
|
||||
}
|
||||
|
||||
def __init__(self, ident, entry_type, raw, cncx, tagx_entries, flags=0):
|
||||
def __init__(self, ident, entry_type, raw, cncx, tagx_entries,
|
||||
control_byte_count):
|
||||
self.index = ident
|
||||
self.raw = raw
|
||||
self.tags = []
|
||||
@ -615,13 +751,29 @@ class IndexEntry(object): # {{{
|
||||
try:
|
||||
self.entry_type = self.TYPES[entry_type]
|
||||
except KeyError:
|
||||
raise ValueError('Unknown Index Entry type: %s'%hex(entry_type))
|
||||
raise ValueError('Unknown Index Entry type: %s'%bin(entry_type))
|
||||
|
||||
if control_byte_count not in (1, 2):
|
||||
raise ValueError('Unknown control byte count: %d'%
|
||||
control_byte_count)
|
||||
|
||||
self.flags = 0
|
||||
|
||||
if control_byte_count == 2:
|
||||
self.flags = ord(raw[0])
|
||||
raw = raw[1:]
|
||||
|
||||
expected_tags = [tag for tag in tagx_entries if tag.bitmask &
|
||||
entry_type]
|
||||
|
||||
flags = self.flags
|
||||
for tag in expected_tags:
|
||||
vals = []
|
||||
|
||||
if tag.tag > 64:
|
||||
has_tag = flags & 0b1
|
||||
flags = flags >> 1
|
||||
if not has_tag: continue
|
||||
for i in range(tag.num_values):
|
||||
if not raw:
|
||||
raise ValueError('Index entry does not match TAGX header')
|
||||
@ -630,26 +782,11 @@ class IndexEntry(object): # {{{
|
||||
vals.append(val)
|
||||
self.tags.append(Tag(tag, vals, self.entry_type, cncx))
|
||||
|
||||
if flags & 0b10:
|
||||
# Look for optional description and author
|
||||
desc_tag = [t for t in tagx_entries if t.tag == 22]
|
||||
if desc_tag and raw:
|
||||
val, consumed = decint(raw)
|
||||
raw = raw[consumed:]
|
||||
if val:
|
||||
self.tags.append(Tag(desc_tag[0], [val], self.entry_type,
|
||||
cncx))
|
||||
if flags & 0b100:
|
||||
aut_tag = [t for t in tagx_entries if t.tag == 23]
|
||||
if aut_tag and raw:
|
||||
val, consumed = decint(raw)
|
||||
raw = raw[consumed:]
|
||||
if val:
|
||||
self.tags.append(Tag(aut_tag[0], [val], self.entry_type,
|
||||
cncx))
|
||||
|
||||
self.consumed = len(orig_raw) - len(raw)
|
||||
self.trailing_bytes = raw
|
||||
if self.trailing_bytes.replace(b'\0', b''):
|
||||
raise ValueError('%s has leftover bytes: %s'%(self, format_bytes(
|
||||
self.trailing_bytes)))
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
@ -701,11 +838,13 @@ class IndexEntry(object): # {{{
|
||||
return -1
|
||||
|
||||
def __str__(self):
|
||||
ans = ['Index Entry(index=%s, entry_type=%s (%s), length=%d, byte_size=%d)'%(
|
||||
self.index, self.entry_type, bin(self.entry_type_raw)[2:],
|
||||
ans = ['Index Entry(index=%s, entry_type=%s, flags=%s, '
|
||||
'length=%d, byte_size=%d)'%(
|
||||
self.index, self.entry_type, bin(self.flags)[2:],
|
||||
len(self.tags), self.byte_size)]
|
||||
for tag in self.tags:
|
||||
ans.append('\t'+str(tag))
|
||||
if tag.value is not None:
|
||||
ans.append('\t'+str(tag))
|
||||
if self.first_child_index != -1:
|
||||
ans.append('\tNumber of children: %d'%(self.last_child_index -
|
||||
self.first_child_index + 1))
|
||||
@ -715,6 +854,90 @@ class IndexEntry(object): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class SecondaryIndexRecord(object): # {{{
|
||||
|
||||
def __init__(self, record, index_header, cncx):
|
||||
self.record = record
|
||||
raw = self.record.raw
|
||||
|
||||
if raw[:4] != b'INDX':
|
||||
raise ValueError('Invalid Primary Index Record')
|
||||
|
||||
u = struct.unpack
|
||||
|
||||
self.header_length, = u('>I', raw[4:8])
|
||||
self.unknown1 = raw[8:12]
|
||||
self.header_type, = u('>I', raw[12:16])
|
||||
self.unknown2 = raw[16:20]
|
||||
self.idxt_offset, self.idxt_count = u(b'>II', raw[20:28])
|
||||
if self.idxt_offset < 192:
|
||||
raise ValueError('Unknown Index record structure')
|
||||
self.unknown3 = raw[28:36]
|
||||
self.unknown4 = raw[36:192] # Should be 156 bytes
|
||||
|
||||
self.index_offsets = []
|
||||
indices = raw[self.idxt_offset:]
|
||||
if indices[:4] != b'IDXT':
|
||||
raise ValueError("Invalid IDXT index table")
|
||||
indices = indices[4:]
|
||||
for i in range(self.idxt_count):
|
||||
off, = u(b'>H', indices[i*2:(i+1)*2])
|
||||
self.index_offsets.append(off-192)
|
||||
rest = indices[(i+1)*2:]
|
||||
if rest.replace(b'\0', ''): # There can be padding null bytes
|
||||
raise ValueError('Extra bytes after IDXT table: %r'%rest)
|
||||
|
||||
indxt = raw[192:self.idxt_offset]
|
||||
self.size_of_indxt_block = len(indxt)
|
||||
|
||||
self.indices = []
|
||||
for i, off in enumerate(self.index_offsets):
|
||||
try:
|
||||
next_off = self.index_offsets[i+1]
|
||||
except:
|
||||
next_off = len(indxt)
|
||||
num = ord(indxt[off])
|
||||
index = indxt[off+1:off+1+num]
|
||||
consumed = 1 + num
|
||||
entry_type = ord(indxt[off+consumed])
|
||||
pos = off+consumed+1
|
||||
idxe = IndexEntry(index, entry_type,
|
||||
indxt[pos:next_off], cncx,
|
||||
index_header.tagx_entries,
|
||||
index_header.tagx_control_byte_count)
|
||||
self.indices.append(idxe)
|
||||
|
||||
rest = indxt[pos+self.indices[-1].consumed:]
|
||||
if rest.replace(b'\0', b''): # There can be padding null bytes
|
||||
raise ValueError('Extra bytes after IDXT table: %r'%rest)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
ans = ['*'*20 + ' Secondary Index Record (%d bytes) '%len(self.record.raw)+ '*'*20]
|
||||
a = ans.append
|
||||
def u(w):
|
||||
a('Unknown: %r (%d bytes) (All zeros: %r)'%(w,
|
||||
len(w), not bool(w.replace(b'\0', b'')) ))
|
||||
a('Header length: %d'%self.header_length)
|
||||
u(self.unknown1)
|
||||
a('Unknown (header type? index record number? always 1?): %d'%self.header_type)
|
||||
u(self.unknown2)
|
||||
a('IDXT Offset (%d block size): %d'%(self.size_of_indxt_block,
|
||||
self.idxt_offset))
|
||||
a('IDXT Count: %d'%self.idxt_count)
|
||||
u(self.unknown3)
|
||||
u(self.unknown4)
|
||||
a('Index offsets: %r'%self.index_offsets)
|
||||
a('\nIndex Entries (%d entries):'%len(self.indices))
|
||||
for entry in self.indices:
|
||||
a(str(entry))
|
||||
a('')
|
||||
|
||||
|
||||
return '\n'.join(ans)
|
||||
|
||||
# }}}
|
||||
|
||||
class IndexRecord(object): # {{{
|
||||
|
||||
'''
|
||||
@ -724,6 +947,7 @@ class IndexRecord(object): # {{{
|
||||
|
||||
def __init__(self, record, index_header, cncx):
|
||||
self.record = record
|
||||
self.alltext = None
|
||||
raw = self.record.raw
|
||||
|
||||
if raw[:4] != b'INDX':
|
||||
@ -763,17 +987,15 @@ class IndexRecord(object): # {{{
|
||||
next_off = len(indxt)
|
||||
index, consumed = decode_hex_number(indxt[off:])
|
||||
entry_type = ord(indxt[off+consumed])
|
||||
d, flags = 1, 0
|
||||
if index_header.index_type == 6:
|
||||
flags = ord(indxt[off+consumed+d])
|
||||
d += 1
|
||||
pos = off+consumed+d
|
||||
self.indices.append(IndexEntry(index, entry_type,
|
||||
indxt[pos:next_off], cncx,
|
||||
index_header.tagx_entries, flags=flags))
|
||||
pos = off+consumed+1
|
||||
idxe = IndexEntry(index, entry_type,
|
||||
indxt[pos:next_off], cncx,
|
||||
index_header.tagx_entries,
|
||||
index_header.tagx_control_byte_count)
|
||||
self.indices.append(idxe)
|
||||
|
||||
rest = indxt[pos+self.indices[-1].consumed:]
|
||||
if rest.replace(b'\0', ''): # There can be padding null bytes
|
||||
if rest.replace(b'\0', b''): # There can be padding null bytes
|
||||
raise ValueError('Extra bytes after IDXT table: %r'%rest)
|
||||
|
||||
def get_parent(self, index):
|
||||
@ -803,13 +1025,23 @@ class IndexRecord(object): # {{{
|
||||
a('Index offsets: %r'%self.index_offsets)
|
||||
a('\nIndex Entries (%d entries):'%len(self.indices))
|
||||
for entry in self.indices:
|
||||
a(str(entry)+'\n')
|
||||
offset = entry.offset
|
||||
a(str(entry))
|
||||
t = self.alltext
|
||||
if offset is not None and self.alltext is not None:
|
||||
a('\tHTML before offset: %r'%t[offset-50:offset])
|
||||
a('\tHTML after offset: %r'%t[offset:offset+50])
|
||||
p = offset+entry.size
|
||||
a('\tHTML before end: %r'%t[p-50:p])
|
||||
a('\tHTML after end: %r'%t[p:p+50])
|
||||
|
||||
a('')
|
||||
|
||||
return '\n'.join(ans)
|
||||
|
||||
# }}}
|
||||
|
||||
class CNCX(object) : # {{{
|
||||
class CNCX(object): # {{{
|
||||
|
||||
'''
|
||||
Parses the records that contain the compiled NCX (all strings from the
|
||||
@ -826,8 +1058,16 @@ class CNCX(object) : # {{{
|
||||
while pos < len(raw):
|
||||
length, consumed = decint(raw[pos:])
|
||||
if length > 0:
|
||||
self.records[pos+record_offset] = raw[
|
||||
try:
|
||||
self.records[pos+record_offset] = raw[
|
||||
pos+consumed:pos+consumed+length].decode(codec)
|
||||
except:
|
||||
byts = raw[pos:]
|
||||
r = format_bytes(byts)
|
||||
print ('CNCX entry at offset %d has unknown format %s'%(
|
||||
pos+record_offset, r))
|
||||
self.records[pos+record_offset] = r
|
||||
pos = len(raw)
|
||||
pos += consumed+length
|
||||
record_offset += 0x10000
|
||||
|
||||
@ -849,6 +1089,7 @@ class TextRecord(object): # {{{
|
||||
self.trailing_data, self.raw = get_trailing_data(record.raw, extra_data_flags)
|
||||
raw_trailing_bytes = record.raw[len(self.raw):]
|
||||
self.raw = decompress(self.raw)
|
||||
|
||||
if 0 in self.trailing_data:
|
||||
self.trailing_data['multibyte_overlap'] = self.trailing_data.pop(0)
|
||||
if 1 in self.trailing_data:
|
||||
@ -857,6 +1098,11 @@ class TextRecord(object): # {{{
|
||||
self.trailing_data['uncrossable_breaks'] = self.trailing_data.pop(2)
|
||||
self.trailing_data['raw_bytes'] = raw_trailing_bytes
|
||||
|
||||
for typ, val in self.trailing_data.iteritems():
|
||||
if isinstance(typ, int):
|
||||
print ('Record %d has unknown trailing data of type: %d : %r'%
|
||||
(idx, typ, val))
|
||||
|
||||
self.idx = idx
|
||||
|
||||
def dump(self, folder):
|
||||
@ -890,7 +1136,7 @@ class BinaryRecord(object): # {{{
|
||||
self.raw = record.raw
|
||||
sig = self.raw[:4]
|
||||
name = '%06d'%idx
|
||||
if sig in (b'FCIS', b'FLIS', b'SRCS'):
|
||||
if sig in (b'FCIS', b'FLIS', b'SRCS', b'DATP'):
|
||||
name += '-' + sig.decode('ascii')
|
||||
elif sig == b'\xe9\x8e\r\n':
|
||||
name += '-' + 'EOF'
|
||||
@ -960,8 +1206,7 @@ class TBSIndexing(object): # {{{
|
||||
'(%d ends, %d complete, %d starts)')%tuple(map(len, (s+e+c, e,
|
||||
c, s))))
|
||||
byts = bytearray(r.trailing_data.get('indexing', b''))
|
||||
sbyts = tuple(hex(b)[2:] for b in byts)
|
||||
ans.append('TBS bytes: %s'%(' '.join(sbyts)))
|
||||
ans.append('TBS bytes: %s'%format_bytes(byts))
|
||||
for typ, entries in (('Ends', e), ('Complete', c), ('Starts', s)):
|
||||
if entries:
|
||||
ans.append('\t%s:'%typ)
|
||||
@ -979,8 +1224,7 @@ class TBSIndexing(object): # {{{
|
||||
tbs_type = 0
|
||||
is_periodical = self.doc_type in (257, 258, 259)
|
||||
if len(byts):
|
||||
outermost_index, extra, consumed = decode_tbs(byts, flag_size=4 if
|
||||
is_periodical else 3)
|
||||
outermost_index, extra, consumed = decode_tbs(byts, flag_size=3)
|
||||
byts = byts[consumed:]
|
||||
for k in extra:
|
||||
tbs_type |= k
|
||||
@ -988,8 +1232,14 @@ class TBSIndexing(object): # {{{
|
||||
ans.append('Outermost index: %d'%outermost_index)
|
||||
ans.append('Unknown extra start bytes: %s'%repr_extra(extra))
|
||||
if is_periodical: # Hierarchical periodical
|
||||
byts, a = self.interpret_periodical(tbs_type, byts,
|
||||
try:
|
||||
byts, a = self.interpret_periodical(tbs_type, byts,
|
||||
dat['geom'][0])
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
a = []
|
||||
print ('Failed to decode TBS bytes for record: %d'%r.idx)
|
||||
ans += a
|
||||
if byts:
|
||||
sbyts = tuple(hex(b)[2:] for b in byts)
|
||||
@ -1112,15 +1362,16 @@ class MOBIFile(object): # {{{
|
||||
self.records.append(Record(section(i), self.record_headers[i]))
|
||||
|
||||
self.mobi_header = MOBIHeader(self.records[0])
|
||||
self.huffman_record_nums = []
|
||||
|
||||
if 'huff' in self.mobi_header.compression.lower():
|
||||
huffrecs = [r.raw for r in
|
||||
xrange(self.mobi_header.huffman_record_offset,
|
||||
self.huffman_record_nums = list(xrange(self.mobi_header.huffman_record_offset,
|
||||
self.mobi_header.huffman_record_offset +
|
||||
self.mobi_header.huffman_record_count)]
|
||||
self.mobi_header.huffman_record_count))
|
||||
huffrecs = [self.records[r].raw for r in self.huffman_record_nums]
|
||||
from calibre.ebooks.mobi.huffcdic import HuffReader
|
||||
huffs = HuffReader(huffrecs)
|
||||
decompress = huffs.decompress
|
||||
decompress = huffs.unpack
|
||||
elif 'palmdoc' in self.mobi_header.compression.lower():
|
||||
from calibre.ebooks.compression.palmdoc import decompress_doc
|
||||
decompress = decompress_doc
|
||||
@ -1139,6 +1390,14 @@ class MOBIFile(object): # {{{
|
||||
self.index_header, self.cncx)
|
||||
self.indexing_record_nums = set(xrange(pir,
|
||||
pir+2+self.index_header.num_of_cncx_blocks))
|
||||
self.secondary_index_record = self.secondary_index_header = None
|
||||
sir = self.mobi_header.secondary_index_record
|
||||
if sir != 0xffffffff:
|
||||
self.secondary_index_header = SecondaryIndexHeader(self.records[sir])
|
||||
self.indexing_record_nums.add(sir)
|
||||
self.secondary_index_record = SecondaryIndexRecord(
|
||||
self.records[sir+1], self.secondary_index_header, self.cncx)
|
||||
self.indexing_record_nums.add(sir+1)
|
||||
|
||||
|
||||
ntr = self.mobi_header.number_of_text_records
|
||||
@ -1151,7 +1410,7 @@ class MOBIFile(object): # {{{
|
||||
min(len(self.records), ntr+1))]
|
||||
self.image_records, self.binary_records = [], []
|
||||
for i in xrange(fntbr, len(self.records)):
|
||||
if i in self.indexing_record_nums:
|
||||
if i in self.indexing_record_nums or i in self.huffman_record_nums:
|
||||
continue
|
||||
r = self.records[i]
|
||||
fmt = None
|
||||
@ -1181,35 +1440,19 @@ class MOBIFile(object): # {{{
|
||||
print (str(self.mobi_header).encode('utf-8'), file=f)
|
||||
# }}}
|
||||
|
||||
def inspect_mobi(path_or_stream, prefix='decompiled'):
|
||||
def inspect_mobi(path_or_stream, ddir=None): # {{{
|
||||
stream = (path_or_stream if hasattr(path_or_stream, 'read') else
|
||||
open(path_or_stream, 'rb'))
|
||||
f = MOBIFile(stream)
|
||||
ddir = prefix + '_' + os.path.splitext(os.path.basename(stream.name))[0]
|
||||
if ddir is None:
|
||||
ddir = 'decompiled_' + os.path.splitext(os.path.basename(stream.name))[0]
|
||||
try:
|
||||
shutil.rmtree(ddir)
|
||||
except:
|
||||
pass
|
||||
os.mkdir(ddir)
|
||||
os.makedirs(ddir)
|
||||
with open(os.path.join(ddir, 'header.txt'), 'wb') as out:
|
||||
f.print_header(f=out)
|
||||
if f.index_header is not None:
|
||||
with open(os.path.join(ddir, 'index.txt'), 'wb') as out:
|
||||
print(str(f.index_header), file=out)
|
||||
print('\n\n', file=out)
|
||||
print(str(f.cncx).encode('utf-8'), file=out)
|
||||
print('\n\n', file=out)
|
||||
print(str(f.index_record), file=out)
|
||||
with open(os.path.join(ddir, 'tbs_indexing.txt'), 'wb') as out:
|
||||
print(str(f.tbs_indexing), file=out)
|
||||
f.tbs_indexing.dump(ddir)
|
||||
|
||||
for tdir, attr in [('text', 'text_records'), ('images', 'image_records'),
|
||||
('binary', 'binary_records')]:
|
||||
tdir = os.path.join(ddir, tdir)
|
||||
os.mkdir(tdir)
|
||||
for rec in getattr(f, attr):
|
||||
rec.dump(tdir)
|
||||
|
||||
alltext = os.path.join(ddir, 'text.html')
|
||||
with open(alltext, 'wb') as of:
|
||||
@ -1224,8 +1467,36 @@ def inspect_mobi(path_or_stream, prefix='decompiled'):
|
||||
include_meta_content_type=True))
|
||||
|
||||
|
||||
if f.index_header is not None:
|
||||
f.index_record.alltext = alltext
|
||||
with open(os.path.join(ddir, 'index.txt'), 'wb') as out:
|
||||
print(str(f.index_header), file=out)
|
||||
print('\n\n', file=out)
|
||||
if f.secondary_index_header is not None:
|
||||
print(str(f.secondary_index_header).encode('utf-8'), file=out)
|
||||
print('\n\n', file=out)
|
||||
if f.secondary_index_record is not None:
|
||||
print(str(f.secondary_index_record).encode('utf-8'), file=out)
|
||||
print('\n\n', file=out)
|
||||
print(str(f.cncx).encode('utf-8'), file=out)
|
||||
print('\n\n', file=out)
|
||||
print(str(f.index_record), file=out)
|
||||
with open(os.path.join(ddir, 'tbs_indexing.txt'), 'wb') as out:
|
||||
print(str(f.tbs_indexing), file=out)
|
||||
f.tbs_indexing.dump(ddir)
|
||||
|
||||
for tdir, attr in [('text', 'text_records'), ('images', 'image_records'),
|
||||
('binary', 'binary_records')]:
|
||||
tdir = os.path.join(ddir, tdir)
|
||||
os.mkdir(tdir)
|
||||
for rec in getattr(f, attr):
|
||||
rec.dump(tdir)
|
||||
|
||||
|
||||
print ('Debug data saved to:', ddir)
|
||||
|
||||
# }}}
|
||||
|
||||
def main():
|
||||
inspect_mobi(sys.argv[1])
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Decompress MOBI files compressed with the Huff/cdic algorithm. Code thanks to darkninja
|
||||
and igorsk.
|
||||
@ -12,82 +16,92 @@ import struct
|
||||
|
||||
from calibre.ebooks.mobi import MobiError
|
||||
|
||||
class BitReader(object):
|
||||
|
||||
def __init__(self, data):
|
||||
self.data, self.pos, self.nbits = data + "\x00\x00\x00\x00", 0, len(data) * 8
|
||||
|
||||
def peek(self, n):
|
||||
r, g = 0, 0
|
||||
while g < n:
|
||||
r, g = (r << 8) | ord(self.data[(self.pos+g)>>3]), g + 8 - ((self.pos+g) & 7)
|
||||
return (r >> (g - n)) & ((1 << n) - 1)
|
||||
|
||||
def eat(self, n):
|
||||
self.pos += n
|
||||
return self.pos <= self.nbits
|
||||
|
||||
def left(self):
|
||||
return self.nbits - self.pos
|
||||
class Reader(object):
|
||||
|
||||
class HuffReader(object):
|
||||
|
||||
def __init__(self, huffs):
|
||||
self.huffs = huffs
|
||||
|
||||
if huffs[0][0:4] != 'HUFF' or huffs[0][4:8] != '\x00\x00\x00\x18':
|
||||
def __init__(self):
|
||||
self.q = struct.Struct(b'>Q').unpack_from
|
||||
|
||||
def load_huff(self, huff):
|
||||
if huff[0:8] != b'HUFF\x00\x00\x00\x18':
|
||||
raise MobiError('Invalid HUFF header')
|
||||
|
||||
if huffs[1][0:4] != 'CDIC' or huffs[1][4:8] != '\x00\x00\x00\x10':
|
||||
raise ValueError('Invalid CDIC header')
|
||||
|
||||
self.entry_bits, = struct.unpack('>L', huffs[1][12:16])
|
||||
off1,off2 = struct.unpack('>LL', huffs[0][16:24])
|
||||
self.dict1 = struct.unpack('<256L', huffs[0][off1:off1+256*4])
|
||||
self.dict2 = struct.unpack('<64L', huffs[0][off2:off2+64*4])
|
||||
self.dicts = huffs[1:]
|
||||
self.r = ''
|
||||
|
||||
def _unpack(self, bits, depth = 0):
|
||||
if depth > 32:
|
||||
raise MobiError('Corrupt file')
|
||||
|
||||
while bits.left():
|
||||
dw = bits.peek(32)
|
||||
v = self.dict1[dw >> 24]
|
||||
codelen = v & 0x1F
|
||||
off1, off2 = struct.unpack_from(b'>LL', huff, 8)
|
||||
|
||||
def dict1_unpack(v):
|
||||
codelen, term, maxcode = v&0x1f, v&0x80, v>>8
|
||||
assert codelen != 0
|
||||
code = dw >> (32 - codelen)
|
||||
r = (v >> 8)
|
||||
if not (v & 0x80):
|
||||
while code < self.dict2[(codelen-1)*2]:
|
||||
codelen += 1
|
||||
code = dw >> (32 - codelen)
|
||||
r = self.dict2[(codelen-1)*2+1]
|
||||
r -= code
|
||||
assert codelen != 0
|
||||
if not bits.eat(codelen):
|
||||
return
|
||||
dicno = r >> self.entry_bits
|
||||
off1 = 16 + (r - (dicno << self.entry_bits)) * 2
|
||||
dic = self.dicts[dicno]
|
||||
off2 = 16 + ord(dic[off1]) * 256 + ord(dic[off1+1])
|
||||
blen = ord(dic[off2]) * 256 + ord(dic[off2+1])
|
||||
slice = dic[off2+2:off2+2+(blen&0x7fff)]
|
||||
if blen & 0x8000:
|
||||
self.r += slice
|
||||
else:
|
||||
self._unpack(BitReader(slice), depth + 1)
|
||||
if codelen <= 8:
|
||||
assert term
|
||||
maxcode = ((maxcode + 1) << (32 - codelen)) - 1
|
||||
return (codelen, term, maxcode)
|
||||
self.dict1 = map(dict1_unpack, struct.unpack_from(b'>256L', huff, off1))
|
||||
|
||||
dict2 = struct.unpack_from(b'>64L', huff, off2)
|
||||
self.mincode, self.maxcode = (), ()
|
||||
for codelen, mincode in enumerate((0,) + dict2[0::2]):
|
||||
self.mincode += (mincode << (32 - codelen), )
|
||||
for codelen, maxcode in enumerate((0,) + dict2[1::2]):
|
||||
self.maxcode += (((maxcode + 1) << (32 - codelen)) - 1, )
|
||||
|
||||
self.dictionary = []
|
||||
|
||||
def load_cdic(self, cdic):
|
||||
if cdic[0:8] != b'CDIC\x00\x00\x00\x10':
|
||||
raise MobiError('Invalid CDIC header')
|
||||
phrases, bits = struct.unpack_from(b'>LL', cdic, 8)
|
||||
n = min(1<<bits, phrases-len(self.dictionary))
|
||||
h = struct.Struct(b'>H').unpack_from
|
||||
def getslice(off):
|
||||
blen, = h(cdic, 16+off)
|
||||
slice = cdic[18+off:18+off+(blen&0x7fff)]
|
||||
return (slice, blen&0x8000)
|
||||
self.dictionary += map(getslice, struct.unpack_from(b'>%dH' % n, cdic, 16))
|
||||
|
||||
def unpack(self, data):
|
||||
self.r = ''
|
||||
self._unpack(BitReader(data))
|
||||
return self.r
|
||||
|
||||
def decompress(self, sections):
|
||||
r = ''
|
||||
for data in sections:
|
||||
r += self.unpack(data)
|
||||
if r.endswith('#'):
|
||||
r = r[:-1]
|
||||
return r
|
||||
q = self.q
|
||||
|
||||
bitsleft = len(data) * 8
|
||||
data += b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
pos = 0
|
||||
x, = q(data, pos)
|
||||
n = 32
|
||||
|
||||
s = []
|
||||
while True:
|
||||
if n <= 0:
|
||||
pos += 4
|
||||
x, = q(data, pos)
|
||||
n += 32
|
||||
code = (x >> n) & ((1 << 32) - 1)
|
||||
|
||||
codelen, term, maxcode = self.dict1[code >> 24]
|
||||
if not term:
|
||||
while code < self.mincode[codelen]:
|
||||
codelen += 1
|
||||
maxcode = self.maxcode[codelen]
|
||||
|
||||
n -= codelen
|
||||
bitsleft -= codelen
|
||||
if bitsleft < 0:
|
||||
break
|
||||
|
||||
r = (maxcode - code) >> (32 - codelen)
|
||||
slice_, flag = self.dictionary[r]
|
||||
if not flag:
|
||||
self.dictionary[r] = None
|
||||
slice_ = self.unpack(slice_)
|
||||
self.dictionary[r] = (slice_, 1)
|
||||
s.append(slice_)
|
||||
return b''.join(s)
|
||||
|
||||
class HuffReader(object):
|
||||
|
||||
def __init__(self, huffs):
|
||||
self.reader = Reader()
|
||||
self.reader.load_huff(huffs[0])
|
||||
for cdic in huffs[1:]:
|
||||
self.reader.load_cdic(cdic)
|
||||
|
||||
def unpack(self, section):
|
||||
return self.reader.unpack(section)
|
||||
|
||||
|
||||
|
@ -4,6 +4,7 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from struct import pack
|
||||
from calibre.utils.localization import lang_as_iso639_1
|
||||
|
||||
lang_codes = {
|
||||
}
|
||||
@ -314,7 +315,8 @@ def iana2mobi(icode):
|
||||
subtags = list(icode.split('-'))
|
||||
while len(subtags) > 0:
|
||||
lang = subtags.pop(0).lower()
|
||||
if lang in IANA_MOBI:
|
||||
lang = lang_as_iso639_1(lang)
|
||||
if lang and lang in IANA_MOBI:
|
||||
langdict = IANA_MOBI[lang]
|
||||
break
|
||||
|
||||
|
@ -532,7 +532,7 @@ class MobiMLizer(object):
|
||||
bstate.pbreak = True
|
||||
if isblock:
|
||||
para = bstate.para
|
||||
if para is not None and para.text == u'\xa0':
|
||||
if para is not None and para.text == u'\xa0' and len(para) < 1:
|
||||
para.getparent().replace(para, etree.Element(XHTML('br')))
|
||||
bstate.para = None
|
||||
bstate.istate = None
|
||||
|
@ -50,13 +50,11 @@ class MOBIOutput(OutputFormatPlugin):
|
||||
help=_('When adding the Table of Contents to the book, add it at the start of the '
|
||||
'book instead of the end. Not recommended.')
|
||||
),
|
||||
OptionRecommendation(name='mobi_navpoints_only_deepest',
|
||||
recommended_value=False,
|
||||
help=_('When adding navpoints for the chapter-to-chapter'
|
||||
' navigation on the kindle, use only the lowest level '
|
||||
'of items in the TOC, instead of items at every level.')
|
||||
OptionRecommendation(name='extract_to', recommended_value=None,
|
||||
help=_('Extract the contents of the MOBI file to the'
|
||||
' specified directory. If the directory already '
|
||||
'exists, it will be deleted.')
|
||||
),
|
||||
|
||||
OptionRecommendation(name='kindlegen',
|
||||
recommended_value=False,
|
||||
help=('Use kindlegen (must be in your PATH) to generate the'
|
||||
@ -185,3 +183,8 @@ class MOBIOutput(OutputFormatPlugin):
|
||||
write_page_breaks_after_item=write_page_breaks_after_item)
|
||||
writer(oeb, output_path)
|
||||
|
||||
if opts.extract_to is not None:
|
||||
from calibre.ebooks.mobi.debug import inspect_mobi
|
||||
ddir = opts.extract_to
|
||||
inspect_mobi(output_path, ddir=ddir)
|
||||
|
||||
|
@ -859,16 +859,19 @@ class MobiReader(object):
|
||||
processed_records += list(range(self.book_header.huff_offset,
|
||||
self.book_header.huff_offset + self.book_header.huff_number))
|
||||
huff = HuffReader(huffs)
|
||||
self.mobi_html = huff.decompress(text_sections)
|
||||
unpack = huff.unpack
|
||||
|
||||
elif self.book_header.compression_type == '\x00\x02':
|
||||
for section in text_sections:
|
||||
self.mobi_html += decompress_doc(section)
|
||||
unpack = decompress_doc
|
||||
|
||||
elif self.book_header.compression_type == '\x00\x01':
|
||||
self.mobi_html = ''.join(text_sections)
|
||||
unpack = lambda x: x
|
||||
else:
|
||||
raise MobiError('Unknown compression algorithm: %s' % repr(self.book_header.compression_type))
|
||||
self.mobi_html = b''.join(map(unpack, text_sections))
|
||||
if self.mobi_html.endswith(b'#'):
|
||||
self.mobi_html = self.mobi_html[:-1]
|
||||
|
||||
if self.book_header.ancient and '<html' not in self.mobi_html[:300].lower():
|
||||
self.mobi_html = self.mobi_html.replace('\r ', '\n\n ')
|
||||
self.mobi_html = self.mobi_html.replace('\0', '')
|
||||
|
@ -13,9 +13,9 @@ First sequences:
|
||||
0b0110 : 80 2
|
||||
0b0111 : 80 2 80
|
||||
|
||||
Other sequences:
|
||||
0b0101 : 4 1a
|
||||
0b0001 : c b1
|
||||
0b0001 = 0
|
||||
0b0010 = 0
|
||||
0b0100 = 2
|
||||
|
||||
Opening record
|
||||
----------------
|
||||
|
@ -169,19 +169,26 @@ def get_trailing_data(record, extra_data_flags):
|
||||
:return: Trailing data, record - trailing data
|
||||
'''
|
||||
data = OrderedDict()
|
||||
for i in xrange(16, -1, -1):
|
||||
flag = 1 << i # 2**i
|
||||
if flag & extra_data_flags:
|
||||
if i == 0:
|
||||
# Only the first two bits are used for the size since there can
|
||||
# never be more than 3 trailing multibyte chars
|
||||
sz = (ord(record[-1]) & 0b11) + 1
|
||||
consumed = 1
|
||||
else:
|
||||
sz, consumed = decint(record, forward=False)
|
||||
flags = extra_data_flags >> 1
|
||||
|
||||
num = 0
|
||||
while flags:
|
||||
num += 1
|
||||
if flags & 0b1:
|
||||
sz, consumed = decint(record, forward=False)
|
||||
if sz > consumed:
|
||||
data[i] = record[-sz:-consumed]
|
||||
data[num] = record[-sz:-consumed]
|
||||
record = record[:-sz]
|
||||
flags >>= 1
|
||||
# Read multibyte chars if any
|
||||
if extra_data_flags & 0b1:
|
||||
# Only the first two bits are used for the size since there can
|
||||
# never be more than 3 trailing multibyte chars
|
||||
sz = (ord(record[-1]) & 0b11) + 1
|
||||
consumed = 1
|
||||
if sz > consumed:
|
||||
data[0] = record[-sz:-consumed]
|
||||
record = record[:-sz]
|
||||
return data, record
|
||||
|
||||
def encode_trailing_data(raw):
|
||||
|
@ -430,6 +430,7 @@ class MobiWriter(object):
|
||||
text.seek(npos)
|
||||
return data, overlap
|
||||
|
||||
# TBS {{{
|
||||
def _generate_flat_indexed_navpoints(self):
|
||||
# Assemble a HTMLRecordData instance for each HTML record
|
||||
# Return True if valid, False if invalid
|
||||
@ -1174,6 +1175,8 @@ class MobiWriter(object):
|
||||
|
||||
self._tbSequence = tbSequence
|
||||
|
||||
# }}}
|
||||
|
||||
def _evaluate_periodical_toc(self):
|
||||
'''
|
||||
Periodical:
|
||||
@ -1634,7 +1637,7 @@ class MobiWriter(object):
|
||||
now = int(time.time())
|
||||
nrecords = len(self._records)
|
||||
self._write(title, pack('>HHIIIIII', 0, 0, now, now, 0, 0, 0, 0),
|
||||
'BOOK', 'MOBI', pack('>IIH', nrecords, 0, nrecords))
|
||||
'BOOK', 'MOBI', pack('>IIH', (2*nrecords)-1, 0, nrecords))
|
||||
offset = self._tell() + (8 * nrecords) + 2
|
||||
for i, record in enumerate(self._records):
|
||||
self._write(pack('>I', offset), '\0', pack('>I', 2*i)[1:])
|
||||
@ -1707,8 +1710,6 @@ class MobiWriter(object):
|
||||
'''
|
||||
from calibre.ebooks.oeb.base import TOC
|
||||
items = list(self._oeb.toc.iterdescendants())
|
||||
if self.opts.mobi_navpoints_only_deepest:
|
||||
items = [i for i in items if i.depth == 1]
|
||||
offsets = {i:self._id_offsets.get(i.href, -1) for i in items if i.href}
|
||||
items = [i for i in items if offsets[i] > -1]
|
||||
items.sort(key=lambda i:offsets[i])
|
||||
|
@ -2,7 +2,7 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
from future_builtins import filter
|
||||
from future_builtins import filter, map
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
@ -14,8 +14,7 @@ from collections import OrderedDict, defaultdict
|
||||
|
||||
from calibre.ebooks.mobi.writer2 import RECORD_SIZE
|
||||
from calibre.ebooks.mobi.utils import (encint, encode_number_as_hex,
|
||||
encode_tbs, align_block, utf8_text, detect_periodical)
|
||||
|
||||
encode_tbs, align_block, utf8_text)
|
||||
|
||||
class CNCX(object): # {{{
|
||||
|
||||
@ -34,6 +33,10 @@ class CNCX(object): # {{{
|
||||
self.strings[item.title] = 0
|
||||
if is_periodical:
|
||||
self.strings[item.klass] = 0
|
||||
if item.author:
|
||||
self.strings[item.author] = 0
|
||||
if item.description:
|
||||
self.strings[item.description] = 0
|
||||
|
||||
self.records = []
|
||||
offset = 0
|
||||
@ -61,7 +64,79 @@ class CNCX(object): # {{{
|
||||
return self.strings[string]
|
||||
# }}}
|
||||
|
||||
class IndexEntry(object): # {{{
|
||||
class TAGX(object): # {{{
|
||||
|
||||
BITMASKS = {11:0b1}
|
||||
BITMASKS.update({x:(1 << i) for i, x in enumerate([1, 2, 3, 4, 5, 21, 22, 23])})
|
||||
BITMASKS.update({x:(1 << i) for i, x in enumerate([69, 70, 71, 72, 73])})
|
||||
|
||||
NUM_VALUES = defaultdict(lambda :1)
|
||||
NUM_VALUES[11] = 3
|
||||
NUM_VALUES[0] = 0
|
||||
|
||||
def __init__(self):
|
||||
self.byts = bytearray()
|
||||
|
||||
def add_tag(self, tag):
|
||||
buf = self.byts
|
||||
buf.append(tag)
|
||||
buf.append(self.NUM_VALUES[tag])
|
||||
# bitmask
|
||||
buf.append(self.BITMASKS[tag] if tag else 0)
|
||||
# eof
|
||||
buf.append(0 if tag else 1)
|
||||
|
||||
def header(self, control_byte_count):
|
||||
header = b'TAGX'
|
||||
# table length, control byte count
|
||||
header += pack(b'>II', 12+len(self.byts), control_byte_count)
|
||||
return header
|
||||
|
||||
@property
|
||||
def periodical(self):
|
||||
'''
|
||||
TAGX block for the Primary index header of a periodical
|
||||
'''
|
||||
list(map(self.add_tag, (1, 2, 3, 4, 5, 21, 22, 23, 0, 69, 70, 71, 72,
|
||||
73, 0)))
|
||||
return self.header(2) + bytes(self.byts)
|
||||
|
||||
@property
|
||||
def secondary(self):
|
||||
'''
|
||||
TAGX block for the secondary index header of a periodical
|
||||
'''
|
||||
list(map(self.add_tag, (11, 0)))
|
||||
return self.header(1) + bytes(self.byts)
|
||||
|
||||
|
||||
|
||||
class TAGX_BOOK(TAGX):
|
||||
BITMASKS = dict(TAGX.BITMASKS)
|
||||
BITMASKS.update({x:(1 << i) for i, x in enumerate([1, 2, 3, 4, 21, 22, 23])})
|
||||
|
||||
@property
|
||||
def hierarchical_book(self):
|
||||
'''
|
||||
TAGX block for the primary index header of a hierarchical book
|
||||
'''
|
||||
list(map(self.add_tag, (1, 2, 3, 4, 21, 22, 23, 0)))
|
||||
return self.header(1) + bytes(self.byts)
|
||||
|
||||
@property
|
||||
def flat_book(self):
|
||||
'''
|
||||
TAGX block for the primary index header of a flat book
|
||||
'''
|
||||
list(map(self.add_tag, (1, 2, 3, 4, 0)))
|
||||
return self.header(1) + bytes(self.byts)
|
||||
|
||||
|
||||
# }}}
|
||||
|
||||
# Index Entries {{{
|
||||
|
||||
class IndexEntry(object):
|
||||
|
||||
TAG_VALUES = {
|
||||
'offset': 1,
|
||||
@ -69,17 +144,21 @@ class IndexEntry(object): # {{{
|
||||
'label_offset': 3,
|
||||
'depth': 4,
|
||||
'class_offset': 5,
|
||||
'secondary': 11,
|
||||
'parent_index': 21,
|
||||
'first_child_index': 22,
|
||||
'last_child_index': 23,
|
||||
'image_index': 69,
|
||||
'desc_offset': 70,
|
||||
'author_offset': 73,
|
||||
}
|
||||
RTAG_MAP = {v:k for k, v in TAG_VALUES.iteritems()}
|
||||
|
||||
BITMASKS = [1, 2, 3, 4, 5, 21, 22, 23,]
|
||||
|
||||
def __init__(self, offset, label_offset, depth=0, class_offset=None):
|
||||
def __init__(self, offset, label_offset):
|
||||
self.offset, self.label_offset = offset, label_offset
|
||||
self.depth, self.class_offset = depth, class_offset
|
||||
self.depth, self.class_offset = 0, None
|
||||
self.control_byte_count = 1
|
||||
|
||||
self.length = 0
|
||||
self.index = 0
|
||||
@ -88,6 +167,10 @@ class IndexEntry(object): # {{{
|
||||
self.first_child_index = None
|
||||
self.last_child_index = None
|
||||
|
||||
self.image_index = None
|
||||
self.author_offset = None
|
||||
self.desc_offset = None
|
||||
|
||||
def __repr__(self):
|
||||
return ('IndexEntry(offset=%r, depth=%r, length=%r, index=%r,'
|
||||
' parent_index=%r)')%(self.offset, self.depth, self.length,
|
||||
@ -99,35 +182,6 @@ class IndexEntry(object): # {{{
|
||||
def fset(self, val): self.length = val
|
||||
return property(fget=fget, fset=fset, doc='Alias for length')
|
||||
|
||||
@classmethod
|
||||
def tagx_block(cls, for_periodical=True):
|
||||
buf = bytearray()
|
||||
|
||||
def add_tag(tag, num_values=1):
|
||||
buf.append(tag)
|
||||
buf.append(num_values)
|
||||
# bitmask
|
||||
buf.append(1 << (cls.BITMASKS.index(tag)))
|
||||
# eof
|
||||
buf.append(0)
|
||||
|
||||
for tag in xrange(1, 5):
|
||||
add_tag(tag)
|
||||
|
||||
if for_periodical:
|
||||
for tag in (5, 21, 22, 23):
|
||||
add_tag(tag)
|
||||
|
||||
# End of TAGX record
|
||||
for i in xrange(3): buf.append(0)
|
||||
buf.append(1)
|
||||
|
||||
header = b'TAGX'
|
||||
header += pack(b'>I', 12+len(buf)) # table length
|
||||
header += pack(b'>I', 1) # control byte count
|
||||
|
||||
return header + bytes(buf)
|
||||
|
||||
@property
|
||||
def next_offset(self):
|
||||
return self.offset + self.length
|
||||
@ -145,24 +199,100 @@ class IndexEntry(object): # {{{
|
||||
def entry_type(self):
|
||||
ans = 0
|
||||
for tag in self.tag_nums:
|
||||
ans |= (1 << self.BITMASKS.index(tag)) # 1 << x == 2**x
|
||||
ans |= TAGX.BITMASKS[tag]
|
||||
return ans
|
||||
|
||||
def attr_for_tag(self, tag):
|
||||
return self.RTAG_MAP[tag]
|
||||
|
||||
@property
|
||||
def bytestring(self):
|
||||
buf = StringIO()
|
||||
buf.write(encode_number_as_hex(self.index))
|
||||
if isinstance(self.index, int):
|
||||
buf.write(encode_number_as_hex(self.index))
|
||||
else:
|
||||
raw = bytearray(self.index.encode('ascii'))
|
||||
raw.insert(0, len(raw))
|
||||
buf.write(bytes(raw))
|
||||
et = self.entry_type
|
||||
buf.write(bytes(bytearray([et])))
|
||||
|
||||
if self.control_byte_count == 2:
|
||||
flags = 0
|
||||
for attr in ('image_index', 'desc_offset', 'author_offset'):
|
||||
val = getattr(self, attr)
|
||||
if val is not None:
|
||||
tag = self.TAG_VALUES[attr]
|
||||
bm = TAGX.BITMASKS[tag]
|
||||
flags |= bm
|
||||
buf.write(bytes(bytearray([flags])))
|
||||
|
||||
for tag in self.tag_nums:
|
||||
attr = self.RTAG_MAP[tag]
|
||||
attr = self.attr_for_tag(tag)
|
||||
val = getattr(self, attr)
|
||||
buf.write(encint(val))
|
||||
if isinstance(val, int):
|
||||
val = [val]
|
||||
for x in val:
|
||||
buf.write(encint(x))
|
||||
|
||||
if self.control_byte_count == 2:
|
||||
for attr in ('image_index', 'desc_offset', 'author_offset'):
|
||||
val = getattr(self, attr)
|
||||
if val is not None:
|
||||
buf.write(encint(val))
|
||||
|
||||
ans = buf.getvalue()
|
||||
return ans
|
||||
|
||||
class BookIndexEntry(IndexEntry):
|
||||
|
||||
@property
|
||||
def entry_type(self):
|
||||
tagx = TAGX_BOOK()
|
||||
ans = 0
|
||||
for tag in self.tag_nums:
|
||||
ans |= tagx.BITMASKS[tag]
|
||||
return ans
|
||||
|
||||
|
||||
class PeriodicalIndexEntry(IndexEntry):
|
||||
|
||||
def __init__(self, offset, label_offset, class_offset, depth):
|
||||
IndexEntry.__init__(self, offset, label_offset)
|
||||
self.depth = depth
|
||||
self.class_offset = class_offset
|
||||
self.control_byte_count = 2
|
||||
|
||||
class SecondaryIndexEntry(IndexEntry):
|
||||
|
||||
INDEX_MAP = {'author':73, 'caption':72, 'credit':71, 'description':70,
|
||||
'mastheadImage':69}
|
||||
|
||||
def __init__(self, index):
|
||||
IndexEntry.__init__(self, 0, 0)
|
||||
self.index = index
|
||||
|
||||
tag = self.INDEX_MAP[index]
|
||||
|
||||
# The values for this index entry
|
||||
# I dont know what the 5 means, it is not the number of entries
|
||||
self.secondary = [5 if tag == min(
|
||||
self.INDEX_MAP.itervalues()) else 0, 0, tag]
|
||||
|
||||
@property
|
||||
def tag_nums(self):
|
||||
yield 11
|
||||
|
||||
@property
|
||||
def entry_type(self):
|
||||
return 1
|
||||
|
||||
@classmethod
|
||||
def entries(cls):
|
||||
rmap = {v:k for k,v in cls.INDEX_MAP.iteritems()}
|
||||
for tag in sorted(rmap, reverse=True):
|
||||
yield cls(rmap[tag])
|
||||
|
||||
# }}}
|
||||
|
||||
class TBS(object): # {{{
|
||||
@ -323,16 +453,23 @@ class TBS(object): # {{{
|
||||
class Indexer(object): # {{{
|
||||
|
||||
def __init__(self, serializer, number_of_text_records,
|
||||
size_of_last_text_record, opts, oeb):
|
||||
size_of_last_text_record, masthead_offset, is_periodical,
|
||||
opts, oeb):
|
||||
self.serializer = serializer
|
||||
self.number_of_text_records = number_of_text_records
|
||||
self.text_size = (RECORD_SIZE * (self.number_of_text_records-1) +
|
||||
size_of_last_text_record)
|
||||
self.masthead_offset = masthead_offset
|
||||
self.secondary_record_offset = None
|
||||
|
||||
self.oeb = oeb
|
||||
self.log = oeb.log
|
||||
self.opts = opts
|
||||
|
||||
self.is_periodical = detect_periodical(self.oeb.toc, self.log)
|
||||
self.is_periodical = is_periodical
|
||||
if self.is_periodical and self.masthead_offset is None:
|
||||
raise ValueError('Periodicals must have a masthead')
|
||||
|
||||
self.log('Generating MOBI index for a %s'%('periodical' if
|
||||
self.is_periodical else 'book'))
|
||||
self.is_flat_periodical = False
|
||||
@ -343,6 +480,16 @@ class Indexer(object): # {{{
|
||||
|
||||
self.records = []
|
||||
|
||||
if self.is_periodical:
|
||||
# Ensure all articles have an author and description before
|
||||
# creating the CNCX
|
||||
for node in oeb.toc.iterdescendants():
|
||||
if node.klass == 'article':
|
||||
aut, desc = node.author, node.description
|
||||
if not aut: aut = _('Unknown')
|
||||
if not desc: desc = _('No details available')
|
||||
node.author, node.description = aut, desc
|
||||
|
||||
self.cncx = CNCX(oeb.toc, self.is_periodical)
|
||||
|
||||
if self.is_periodical:
|
||||
@ -354,12 +501,17 @@ class Indexer(object): # {{{
|
||||
self.records.insert(0, self.create_header())
|
||||
self.records.extend(self.cncx.records)
|
||||
|
||||
if is_periodical:
|
||||
self.secondary_record_offset = len(self.records)
|
||||
self.records.append(self.create_header(secondary=True))
|
||||
self.records.append(self.create_index_record(secondary=True))
|
||||
|
||||
self.calculate_trailing_byte_sequences()
|
||||
|
||||
def create_index_record(self): # {{{
|
||||
def create_index_record(self, secondary=False): # {{{
|
||||
header_length = 192
|
||||
buf = StringIO()
|
||||
indices = self.indices
|
||||
indices = list(SecondaryIndexEntry.entries()) if secondary else self.indices
|
||||
|
||||
# Write index entries
|
||||
offsets = []
|
||||
@ -399,9 +551,15 @@ class Indexer(object): # {{{
|
||||
return ans
|
||||
# }}}
|
||||
|
||||
def create_header(self): # {{{
|
||||
def create_header(self, secondary=False): # {{{
|
||||
buf = StringIO()
|
||||
tagx_block = IndexEntry.tagx_block(self.is_periodical)
|
||||
if secondary:
|
||||
tagx_block = TAGX().secondary
|
||||
else:
|
||||
tagx_block = (TAGX().periodical if self.is_periodical else
|
||||
(TAGX_BOOK().hierarchical_book if
|
||||
self.book_has_subchapters else
|
||||
TAGX_BOOK().flat_book))
|
||||
header_length = 192
|
||||
|
||||
# Ident 0 - 4
|
||||
@ -420,7 +578,7 @@ class Indexer(object): # {{{
|
||||
buf.write(pack(b'>I', 0)) # Filled in later
|
||||
|
||||
# Number of index records 24-28
|
||||
buf.write(pack(b'>I', len(self.records)))
|
||||
buf.write(pack(b'>I', 1 if secondary else len(self.records)))
|
||||
|
||||
# Index Encoding 28-32
|
||||
buf.write(pack(b'>I', 65001)) # utf-8
|
||||
@ -429,7 +587,8 @@ class Indexer(object): # {{{
|
||||
buf.write(b'\xff'*4)
|
||||
|
||||
# Number of index entries 36-40
|
||||
buf.write(pack(b'>I', len(self.indices)))
|
||||
indices = list(SecondaryIndexEntry.entries()) if secondary else self.indices
|
||||
buf.write(pack(b'>I', len(indices)))
|
||||
|
||||
# ORDT offset 40-44
|
||||
buf.write(pack(b'>I', 0))
|
||||
@ -441,7 +600,7 @@ class Indexer(object): # {{{
|
||||
buf.write(pack(b'>I', 0))
|
||||
|
||||
# Number of CNCX records 52-56
|
||||
buf.write(pack(b'>I', len(self.cncx.records)))
|
||||
buf.write(pack(b'>I', 0 if secondary else len(self.cncx.records)))
|
||||
|
||||
# Unknown 56-180
|
||||
buf.write(b'\0'*124)
|
||||
@ -455,10 +614,16 @@ class Indexer(object): # {{{
|
||||
# TAGX block
|
||||
buf.write(tagx_block)
|
||||
|
||||
num = len(self.indices)
|
||||
num = len(indices)
|
||||
|
||||
# The index of the last entry in the NCX
|
||||
buf.write(encode_number_as_hex(num-1))
|
||||
idx = indices[-1].index
|
||||
if isinstance(idx, int):
|
||||
idx = encode_number_as_hex(idx)
|
||||
else:
|
||||
idx = idx.encode('ascii')
|
||||
idx = (bytes(bytearray([len(idx)]))) + idx
|
||||
buf.write(idx)
|
||||
|
||||
# The number of entries in the NCX
|
||||
buf.write(pack(b'>H', num))
|
||||
@ -480,47 +645,98 @@ class Indexer(object): # {{{
|
||||
# }}}
|
||||
|
||||
def create_book_index(self): # {{{
|
||||
self.book_has_subchapters = False
|
||||
indices = []
|
||||
seen = set()
|
||||
seen, sub_seen = set(), set()
|
||||
id_offsets = self.serializer.id_offsets
|
||||
|
||||
for node in self.oeb.toc.iterdescendants():
|
||||
# Flatten toc to contain only chapters and subchapters
|
||||
# Anything deeper than a subchapter is made into a subchapter
|
||||
chapters = []
|
||||
for node in self.oeb.toc:
|
||||
try:
|
||||
offset = id_offsets[node.href]
|
||||
label = self.cncx[node.title]
|
||||
except:
|
||||
self.log.warn('TOC item %s not found in document'%node.href)
|
||||
self.log.warn('TOC item %s [%s] not found in document'%(
|
||||
node.title, node.href))
|
||||
continue
|
||||
|
||||
if offset in seen:
|
||||
continue
|
||||
seen.add(offset)
|
||||
index = IndexEntry(offset, label)
|
||||
indices.append(index)
|
||||
|
||||
indices.sort(key=lambda x:x.offset)
|
||||
subchapters = []
|
||||
chapters.append((offset, label, subchapters))
|
||||
|
||||
# Set lengths
|
||||
for i, index in enumerate(indices):
|
||||
try:
|
||||
next_offset = indices[i+1].offset
|
||||
except:
|
||||
next_offset = self.serializer.body_end_offset
|
||||
index.length = next_offset - index.offset
|
||||
for descendant in node.iterdescendants():
|
||||
try:
|
||||
offset = id_offsets[descendant.href]
|
||||
label = self.cncx[descendant.title]
|
||||
except:
|
||||
self.log.warn('TOC item %s [%s] not found in document'%(
|
||||
descendant.title, descendant.href))
|
||||
continue
|
||||
|
||||
# Remove empty nodes
|
||||
indices = [i for i in indices if i.length > 0]
|
||||
if offset in sub_seen:
|
||||
continue
|
||||
sub_seen.add(offset)
|
||||
subchapters.append((offset, label))
|
||||
|
||||
# Set index values
|
||||
for i, index in enumerate(indices):
|
||||
index.index = i
|
||||
subchapters.sort(key=lambda x:x[0])
|
||||
|
||||
# Set lengths again to close up any gaps left by filtering
|
||||
for i, index in enumerate(indices):
|
||||
try:
|
||||
next_offset = indices[i+1].offset
|
||||
except:
|
||||
next_offset = self.serializer.body_end_offset
|
||||
index.length = next_offset - index.offset
|
||||
chapters.sort(key=lambda x:x[0])
|
||||
|
||||
chapters = [(BookIndexEntry(x[0], x[1]), [
|
||||
BookIndexEntry(y[0], y[1]) for y in x[2]]) for x in chapters]
|
||||
|
||||
def set_length(indices):
|
||||
for i, index in enumerate(indices):
|
||||
try:
|
||||
next_offset = indices[i+1].offset
|
||||
except:
|
||||
next_offset = self.serializer.body_end_offset
|
||||
index.length = next_offset - index.offset
|
||||
|
||||
# Set chapter and subchapter lengths
|
||||
set_length([x[0] for x in chapters])
|
||||
for x in chapters:
|
||||
set_length(x[1])
|
||||
|
||||
# Remove empty chapters
|
||||
chapters = [x for x in chapters if x[0].length > 0]
|
||||
|
||||
# Remove invalid subchapters
|
||||
for i, x in enumerate(list(chapters)):
|
||||
chapter, subchapters = x
|
||||
ok_subchapters = []
|
||||
for sc in subchapters:
|
||||
if sc.offset < chapter.next_offset and sc.length > 0:
|
||||
ok_subchapters.append(sc)
|
||||
chapters[i] = (chapter, ok_subchapters)
|
||||
|
||||
# Reset chapter and subchapter lengths in case any were removed
|
||||
set_length([x[0] for x in chapters])
|
||||
for x in chapters:
|
||||
set_length(x[1])
|
||||
|
||||
# Set index and depth values
|
||||
indices = []
|
||||
for index, x in enumerate(chapters):
|
||||
x[0].index = index
|
||||
indices.append(x[0])
|
||||
|
||||
for chapter, subchapters in chapters:
|
||||
for sc in subchapters:
|
||||
index += 1
|
||||
sc.index = index
|
||||
sc.parent_index = chapter.index
|
||||
indices.append(sc)
|
||||
sc.depth = 1
|
||||
self.book_has_subchapters = True
|
||||
if subchapters:
|
||||
chapter.first_child_index = subchapters[0].index
|
||||
chapter.last_child_index = subchapters[-1].index
|
||||
|
||||
return indices
|
||||
|
||||
@ -536,11 +752,12 @@ class Indexer(object): # {{{
|
||||
|
||||
id_offsets = self.serializer.id_offsets
|
||||
|
||||
periodical = IndexEntry(periodical_node_offset,
|
||||
periodical = PeriodicalIndexEntry(periodical_node_offset,
|
||||
self.cncx[periodical_node.title],
|
||||
class_offset=self.cncx[periodical_node.klass])
|
||||
self.cncx[periodical_node.klass], 0)
|
||||
periodical.length = periodical_node_size
|
||||
periodical.first_child_index = 1
|
||||
periodical.image_index = self.masthead_offset
|
||||
|
||||
seen_sec_offsets = set()
|
||||
seen_art_offsets = set()
|
||||
@ -556,7 +773,7 @@ class Indexer(object): # {{{
|
||||
if offset in seen_sec_offsets:
|
||||
continue
|
||||
seen_sec_offsets.add(offset)
|
||||
section = IndexEntry(offset, label, class_offset=klass, depth=1)
|
||||
section = PeriodicalIndexEntry(offset, label, klass, 1)
|
||||
section.parent_index = 0
|
||||
for art in sec:
|
||||
try:
|
||||
@ -568,9 +785,11 @@ class Indexer(object): # {{{
|
||||
if offset in seen_art_offsets:
|
||||
continue
|
||||
seen_art_offsets.add(offset)
|
||||
article = IndexEntry(offset, label, class_offset=klass,
|
||||
depth=2)
|
||||
article = PeriodicalIndexEntry(offset, label, klass, 2)
|
||||
normalized_articles.append(article)
|
||||
article.author_offset = self.cncx[art.author]
|
||||
article.desc_offset = self.cncx[art.description]
|
||||
|
||||
if normalized_articles:
|
||||
normalized_articles.sort(key=lambda x:x.offset)
|
||||
normalized_sections.append((section, normalized_articles))
|
||||
|
@ -11,7 +11,7 @@ import re, random, time
|
||||
from cStringIO import StringIO
|
||||
from struct import pack
|
||||
|
||||
from calibre.ebooks import normalize
|
||||
from calibre.ebooks import normalize, generate_masthead
|
||||
from calibre.ebooks.oeb.base import OEB_RASTER_IMAGES
|
||||
from calibre.ebooks.mobi.writer2.serializer import Serializer
|
||||
from calibre.ebooks.compression.palmdoc import compress_doc
|
||||
@ -19,7 +19,7 @@ from calibre.ebooks.mobi.langcodes import iana2mobi
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
from calibre.ebooks.mobi.writer2 import (PALMDOC, UNCOMPRESSED, RECORD_SIZE)
|
||||
from calibre.ebooks.mobi.utils import (rescale_image, encint,
|
||||
encode_trailing_data, align_block)
|
||||
encode_trailing_data, align_block, detect_periodical)
|
||||
from calibre.ebooks.mobi.writer2.indexer import Indexer
|
||||
|
||||
EXTH_CODES = {
|
||||
@ -34,6 +34,12 @@ EXTH_CODES = {
|
||||
'rights': 109,
|
||||
'type': 111,
|
||||
'source': 112,
|
||||
'versionnumber': 114,
|
||||
'startreading': 116,
|
||||
'coveroffset': 201,
|
||||
'thumboffset': 202,
|
||||
'hasfakecover': 203,
|
||||
'lastupdatetime': 502,
|
||||
'title': 503,
|
||||
}
|
||||
|
||||
@ -77,13 +83,16 @@ class MobiWriter(object):
|
||||
self.write_content()
|
||||
|
||||
def generate_content(self):
|
||||
self.map_image_names()
|
||||
self.is_periodical = detect_periodical(self.oeb.toc, self.oeb.log)
|
||||
# Image records are stored in their own list, they are merged into the
|
||||
# main record list at the end
|
||||
self.generate_images()
|
||||
self.generate_text()
|
||||
# The uncrossable breaks trailing entries come before the indexing
|
||||
# trailing entries
|
||||
self.write_uncrossable_breaks()
|
||||
# Index records come after text records
|
||||
self.generate_index()
|
||||
self.write_uncrossable_breaks()
|
||||
# Image records come after index records
|
||||
self.generate_images()
|
||||
|
||||
# Indexing {{{
|
||||
def generate_index(self):
|
||||
@ -91,22 +100,18 @@ class MobiWriter(object):
|
||||
try:
|
||||
self.indexer = Indexer(self.serializer, self.last_text_record_idx,
|
||||
len(self.records[self.last_text_record_idx]),
|
||||
self.masthead_offset, self.is_periodical,
|
||||
self.opts, self.oeb)
|
||||
except:
|
||||
self.log.exception('Failed to generate MOBI index:')
|
||||
else:
|
||||
self.primary_index_record_idx = len(self.records)
|
||||
for i in xrange(len(self.records)):
|
||||
for i in xrange(self.last_text_record_idx + 1):
|
||||
if i == 0: continue
|
||||
tbs = self.indexer.get_trailing_byte_sequence(i)
|
||||
self.records[i] += encode_trailing_data(tbs)
|
||||
self.records.extend(self.indexer.records)
|
||||
|
||||
@property
|
||||
def is_periodical(self):
|
||||
return (self.primary_index_record_idx is None or not
|
||||
self.indexer.is_periodical)
|
||||
|
||||
# }}}
|
||||
|
||||
def write_uncrossable_breaks(self): # {{{
|
||||
@ -136,58 +141,54 @@ class MobiWriter(object):
|
||||
# }}}
|
||||
|
||||
# Images {{{
|
||||
def map_image_names(self):
|
||||
'''
|
||||
Map image names to record indices, ensuring that the masthead image if
|
||||
present has index number 1.
|
||||
'''
|
||||
index = 1
|
||||
self.images = images = {}
|
||||
mh_href = None
|
||||
|
||||
if 'masthead' in self.oeb.guide:
|
||||
mh_href = self.oeb.guide['masthead'].href
|
||||
images[mh_href] = 1
|
||||
index += 1
|
||||
|
||||
for item in self.oeb.manifest.values():
|
||||
if item.media_type in OEB_RASTER_IMAGES:
|
||||
if item.href == mh_href: continue
|
||||
images[item.href] = index
|
||||
index += 1
|
||||
|
||||
def generate_images(self):
|
||||
self.oeb.logger.info('Serializing images...')
|
||||
images = [(index, href) for href, index in self.images.iteritems()]
|
||||
images.sort()
|
||||
self.first_image_record = None
|
||||
for _, href in images:
|
||||
item = self.oeb.manifest.hrefs[href]
|
||||
oeb = self.oeb
|
||||
oeb.logger.info('Serializing images...')
|
||||
self.image_records = []
|
||||
self.image_map = {}
|
||||
|
||||
mh_href = self.masthead_offset = None
|
||||
if 'masthead' in oeb.guide:
|
||||
mh_href = oeb.guide['masthead'].href
|
||||
elif self.is_periodical:
|
||||
# Generate a default masthead
|
||||
data = generate_masthead(unicode(self.oeb.metadata('title')[0]))
|
||||
self.image_records.append(data)
|
||||
self.masthead_offset = 0
|
||||
|
||||
cover_href = self.cover_offset = self.thumbnail_offset = None
|
||||
if (oeb.metadata.cover and
|
||||
unicode(oeb.metadata.cover[0]) in oeb.manifest.ids):
|
||||
cover_id = unicode(oeb.metadata.cover[0])
|
||||
item = oeb.manifest.ids[cover_id]
|
||||
cover_href = item.href
|
||||
|
||||
for item in self.oeb.manifest.values():
|
||||
if item.media_type not in OEB_RASTER_IMAGES: continue
|
||||
try:
|
||||
data = rescale_image(item.data)
|
||||
except:
|
||||
self.oeb.logger.warn('Bad image file %r' % item.href)
|
||||
oeb.logger.warn('Bad image file %r' % item.href)
|
||||
continue
|
||||
else:
|
||||
self.image_map[item.href] = len(self.image_records)
|
||||
self.image_records.append(data)
|
||||
|
||||
if item.href == mh_href:
|
||||
self.masthead_offset = len(self.image_records) - 1
|
||||
elif item.href == cover_href:
|
||||
self.cover_offset = len(self.image_records) - 1
|
||||
try:
|
||||
data = rescale_image(item.data, dimen=MAX_THUMB_DIMEN,
|
||||
maxsizeb=MAX_THUMB_SIZE)
|
||||
except:
|
||||
oeb.logger.warn('Failed to generate thumbnail')
|
||||
else:
|
||||
self.image_records.append(data)
|
||||
self.thumbnail_offset = len(self.image_records) - 1
|
||||
finally:
|
||||
item.unload_data_from_memory()
|
||||
self.records.append(data)
|
||||
if self.first_image_record is None:
|
||||
self.first_image_record = len(self.records) - 1
|
||||
|
||||
def add_thumbnail(self, item):
|
||||
try:
|
||||
data = rescale_image(item.data, dimen=MAX_THUMB_DIMEN,
|
||||
maxsizeb=MAX_THUMB_SIZE)
|
||||
except IOError:
|
||||
self.oeb.logger.warn('Bad image file %r' % item.href)
|
||||
return None
|
||||
manifest = self.oeb.manifest
|
||||
id, href = manifest.generate('thumbnail', 'thumbnail.jpeg')
|
||||
manifest.add(id, href, 'image/jpeg', data=data)
|
||||
index = len(self.images) + 1
|
||||
self.images[href] = index
|
||||
self.records.append(data)
|
||||
return index
|
||||
|
||||
# }}}
|
||||
|
||||
@ -195,12 +196,13 @@ class MobiWriter(object):
|
||||
|
||||
def generate_text(self):
|
||||
self.oeb.logger.info('Serializing markup content...')
|
||||
self.serializer = Serializer(self.oeb, self.images,
|
||||
self.serializer = Serializer(self.oeb, self.image_map,
|
||||
write_page_breaks_after_item=self.write_page_breaks_after_item)
|
||||
text = self.serializer()
|
||||
self.text_length = len(text)
|
||||
text = StringIO(text)
|
||||
nrecords = 0
|
||||
records_size = 0
|
||||
|
||||
if self.compression != UNCOMPRESSED:
|
||||
self.oeb.logger.info(' Compressing markup content...')
|
||||
@ -214,9 +216,15 @@ class MobiWriter(object):
|
||||
data += pack(b'>B', len(overlap))
|
||||
|
||||
self.records.append(data)
|
||||
records_size += len(data)
|
||||
nrecords += 1
|
||||
|
||||
self.last_text_record_idx = nrecords
|
||||
self.first_non_text_record_idx = nrecords + 1
|
||||
# Pad so that the next records starts at a 4 byte boundary
|
||||
if records_size % 4 != 0:
|
||||
self.records.append(b'\x00'*(records_size % 4))
|
||||
self.first_non_text_record_idx += 1
|
||||
|
||||
def read_text_record(self, text):
|
||||
'''
|
||||
@ -273,9 +281,13 @@ class MobiWriter(object):
|
||||
def generate_record0(self): # MOBI header {{{
|
||||
metadata = self.oeb.metadata
|
||||
exth = self.build_exth()
|
||||
first_image_record = None
|
||||
if self.image_records:
|
||||
first_image_record = len(self.records)
|
||||
self.records.extend(self.image_records)
|
||||
last_content_record = len(self.records) - 1
|
||||
|
||||
# FCIS/FLIS (Seem to server no purpose)
|
||||
# FCIS/FLIS (Seems to serve no purpose)
|
||||
flis_number = len(self.records)
|
||||
self.records.append(
|
||||
b'FLIS\0\0\0\x08\0\x41\0\0\0\0\0\0\xff\xff\xff\xff\0\x01\0\x03\0\0\0\x03\0\0\0\x01'+
|
||||
@ -322,6 +334,8 @@ class MobiWriter(object):
|
||||
if self.indexer.is_flat_periodical:
|
||||
bt = 0x102
|
||||
elif self.indexer.is_periodical:
|
||||
# If you change this, remember to change the cdetype in the EXTH
|
||||
# header as well
|
||||
bt = 0x103
|
||||
|
||||
record0.write(pack(b'>IIIII',
|
||||
@ -331,14 +345,19 @@ class MobiWriter(object):
|
||||
record0.write(b'\xff' * 8)
|
||||
|
||||
# 0x20 - 0x23 : Secondary index record
|
||||
record0.write(pack(b'>I', 0xffffffff))
|
||||
sir = 0xffffffff
|
||||
if (self.primary_index_record_idx is not None and
|
||||
self.indexer.secondary_record_offset is not None):
|
||||
sir = (self.primary_index_record_idx +
|
||||
self.indexer.secondary_record_offset)
|
||||
record0.write(pack(b'>I', sir))
|
||||
|
||||
# 0x24 - 0x3f : Unknown
|
||||
record0.write(b'\xff' * 28)
|
||||
|
||||
# 0x40 - 0x43 : Offset of first non-text record
|
||||
record0.write(pack(b'>I',
|
||||
self.last_text_record_idx + 1))
|
||||
self.first_non_text_record_idx))
|
||||
|
||||
# 0x44 - 0x4b : title offset, title length
|
||||
record0.write(pack(b'>II',
|
||||
@ -354,8 +373,7 @@ class MobiWriter(object):
|
||||
# 0x58 - 0x5b : Format version
|
||||
# 0x5c - 0x5f : First image record number
|
||||
record0.write(pack(b'>II',
|
||||
6, self.first_image_record if self.first_image_record else
|
||||
len(self.records)-1))
|
||||
6, first_image_record if first_image_record else len(self.records)))
|
||||
|
||||
# 0x60 - 0x63 : First HUFF/CDIC record number
|
||||
# 0x64 - 0x67 : Number of HUFF/CDIC records
|
||||
@ -493,10 +511,14 @@ class MobiWriter(object):
|
||||
|
||||
# Write cdetype
|
||||
if self.is_periodical:
|
||||
# If you set the book type header field to 0x101 use NWPR here if
|
||||
# you use 0x103 use MAGZ
|
||||
data = b'MAGZ'
|
||||
else:
|
||||
data = b'EBOK'
|
||||
exth.write(pack(b'>II', 501, len(data)+8))
|
||||
exth.write(data)
|
||||
nrecs += 1
|
||||
exth.write(pack(b'>II', 501, len(data)+8))
|
||||
exth.write(data)
|
||||
nrecs += 1
|
||||
|
||||
# Add a publication date entry
|
||||
if oeb.metadata['date']:
|
||||
@ -504,34 +526,42 @@ class MobiWriter(object):
|
||||
elif oeb.metadata['timestamp']:
|
||||
datestr = str(oeb.metadata['timestamp'][0])
|
||||
|
||||
if datestr is not None:
|
||||
datestr = bytes(datestr)
|
||||
exth.write(pack(b'>II', EXTH_CODES['pubdate'], len(datestr) + 8))
|
||||
if datestr is None:
|
||||
raise ValueError("missing date or timestamp")
|
||||
|
||||
datestr = bytes(datestr)
|
||||
exth.write(pack(b'>II', EXTH_CODES['pubdate'], len(datestr) + 8))
|
||||
exth.write(datestr)
|
||||
nrecs += 1
|
||||
if self.is_periodical:
|
||||
exth.write(pack(b'>II', EXTH_CODES['lastupdatetime'], len(datestr) + 8))
|
||||
exth.write(datestr)
|
||||
nrecs += 1
|
||||
else:
|
||||
raise NotImplementedError("missing date or timestamp needed for mobi_periodical")
|
||||
|
||||
# Write the same creator info as kindlegen 1.2
|
||||
for code, val in [(204, 201), (205, 1), (206, 2), (207, 33307)]:
|
||||
exth.write(pack(b'>II', code, 12))
|
||||
exth.write(pack(b'>I', val))
|
||||
if self.is_periodical:
|
||||
# Pretend to be amazon's super secret periodical generator
|
||||
vals = {204:201, 205:2, 206:0, 207:101}
|
||||
else:
|
||||
# Pretend to be kindlegen 1.2
|
||||
vals = {204:201, 205:1, 206:2, 207:33307}
|
||||
for code, val in vals.iteritems():
|
||||
exth.write(pack(b'>III', code, 12, val))
|
||||
nrecs += 1
|
||||
|
||||
if (oeb.metadata.cover and
|
||||
unicode(oeb.metadata.cover[0]) in oeb.manifest.ids):
|
||||
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(b'>III', 0xc9, 0x0c, index))
|
||||
exth.write(pack(b'>III', 0xcb, 0x0c, 0))
|
||||
nrecs += 2
|
||||
index = self.add_thumbnail(item)
|
||||
if index is not None:
|
||||
exth.write(pack(b'>III', 0xca, 0x0c, index - 1))
|
||||
nrecs += 1
|
||||
if self.cover_offset is not None:
|
||||
exth.write(pack(b'>III', EXTH_CODES['coveroffset'], 12,
|
||||
self.cover_offset))
|
||||
exth.write(pack(b'>III', EXTH_CODES['hasfakecover'], 12, 0))
|
||||
nrecs += 2
|
||||
if self.thumbnail_offset is not None:
|
||||
exth.write(pack(b'>III', EXTH_CODES['thumboffset'], 12,
|
||||
self.thumbnail_offset))
|
||||
nrecs += 1
|
||||
|
||||
if self.serializer.start_offset is not None:
|
||||
exth.write(pack(b'>III', EXTH_CODES['startreading'], 12,
|
||||
self.serializer.start_offset))
|
||||
nrecs += 1
|
||||
|
||||
exth = exth.getvalue()
|
||||
trail = len(exth) % 4
|
||||
@ -550,7 +580,7 @@ class MobiWriter(object):
|
||||
now = int(time.time())
|
||||
nrecords = len(self.records)
|
||||
self.write(title, pack(b'>HHIIIIII', 0, 0, now, now, 0, 0, 0, 0),
|
||||
b'BOOK', b'MOBI', pack(b'>IIH', nrecords, 0, nrecords))
|
||||
b'BOOK', b'MOBI', pack(b'>IIH', (2*nrecords)-1, 0, nrecords))
|
||||
offset = self.tell() + (8 * nrecords) + 2
|
||||
for i, record in enumerate(self.records):
|
||||
self.write(pack(b'>I', offset), b'\0', pack(b'>I', 2*i)[1:])
|
||||
|
@ -39,6 +39,10 @@ class Serializer(object):
|
||||
self.logger = oeb.logger
|
||||
self.write_page_breaks_after_item = write_page_breaks_after_item
|
||||
|
||||
# If not None, this is a number pointing to the location at which to
|
||||
# open the MOBI file on the Kindle
|
||||
self.start_offset = None
|
||||
|
||||
# Mapping of hrefs (urlnormalized) to the offset in the buffer where
|
||||
# the resource pointed to by the href lives. Used at the end to fill in
|
||||
# the correct values into all filepos="..." links.
|
||||
@ -106,6 +110,7 @@ class Serializer(object):
|
||||
self.serialize_head()
|
||||
self.serialize_body()
|
||||
buf.write(b'</html>')
|
||||
self.end_offset = buf.tell()
|
||||
self.fixup_links()
|
||||
return buf.getvalue()
|
||||
|
||||
@ -144,6 +149,8 @@ class Serializer(object):
|
||||
buf.write(b'title="')
|
||||
self.serialize_text(ref.title, quot=True)
|
||||
buf.write(b'" ')
|
||||
if ref.title == 'start':
|
||||
self._start_href = ref.href
|
||||
self.serialize_href(ref.href)
|
||||
# Space required or won't work, I kid you not
|
||||
buf.write(b' />')
|
||||
@ -200,20 +207,18 @@ class Serializer(object):
|
||||
self.breaks.append(buf.tell() - 1)
|
||||
self.id_offsets[urlnormalize(item.href)] = buf.tell()
|
||||
if item.is_section_start:
|
||||
buf.write(b'<div>')
|
||||
buf.write(b'<a ></a> ')
|
||||
if item.is_article_start:
|
||||
buf.write(b'<div>')
|
||||
buf.write(b'<a ></a> <a ></a>')
|
||||
for elem in item.data.find(XHTML('body')):
|
||||
self.serialize_elem(elem, item)
|
||||
if item.is_article_end:
|
||||
# Kindle periodical article end marker
|
||||
buf.write(b'<div></div>')
|
||||
if self.write_page_breaks_after_item:
|
||||
buf.write(b'<mbp:pagebreak/>')
|
||||
if item.is_article_end:
|
||||
buf.write(b'</div>')
|
||||
# Kindle periodical article end marker
|
||||
buf.write(b'<a ></a> <a ></a>')
|
||||
if item.is_section_end:
|
||||
buf.write(b'</div>')
|
||||
buf.write(b' <a ></a>')
|
||||
self.anchor_offset = None
|
||||
|
||||
def serialize_elem(self, elem, item, nsrmap=NSRMAP):
|
||||
@ -283,6 +288,7 @@ class Serializer(object):
|
||||
buf = self.buf
|
||||
id_offsets = self.id_offsets
|
||||
for href, hoffs in self.href_offsets.items():
|
||||
is_start = (href and href == getattr(self, '_start_href', None))
|
||||
# Iterate over all filepos items
|
||||
if href not in id_offsets:
|
||||
self.logger.warn('Hyperlink target %r not found' % href)
|
||||
@ -290,6 +296,8 @@ class Serializer(object):
|
||||
href, _ = urldefrag(href)
|
||||
if href in self.id_offsets:
|
||||
ioff = self.id_offsets[href]
|
||||
if is_start:
|
||||
self.start_offset = ioff
|
||||
for hoff in hoffs:
|
||||
buf.seek(hoff)
|
||||
buf.write(b'%010d' % ioff)
|
||||
|
@ -61,9 +61,11 @@ def meta_info_to_oeb_metadata(mi, m, log, override_input_metadata=False):
|
||||
m.add('identifier', val, scheme=typ.upper())
|
||||
if override_input_metadata and not set_isbn:
|
||||
m.filter('identifier', lambda x: x.scheme.lower() == 'isbn')
|
||||
if not mi.is_null('language'):
|
||||
if not mi.is_null('languages'):
|
||||
m.clear('language')
|
||||
m.add('language', mi.language)
|
||||
for lang in mi.languages:
|
||||
if lang and lang.lower() not in ('und', ''):
|
||||
m.add('language', lang)
|
||||
if not mi.is_null('series_index'):
|
||||
m.clear('series_index')
|
||||
m.add('series_index', mi.format_series_index())
|
||||
|
@ -7,7 +7,6 @@ __docformat__ = 'restructuredtext en'
|
||||
import sys, struct, zlib, bz2, os
|
||||
|
||||
from calibre import guess_type
|
||||
from calibre.utils.filenames import ascii_filename
|
||||
|
||||
class FileStream:
|
||||
def IsBinary(self):
|
||||
@ -158,7 +157,7 @@ class SNBFile:
|
||||
f.fileBody = open(os.path.join(tdir,fileName), 'rb').read()
|
||||
f.fileName = fileName.replace(os.sep, '/')
|
||||
if isinstance(f.fileName, unicode):
|
||||
f.fileName = ascii_filename(f.fileName).encode('ascii')
|
||||
f.fileName = f.fileName.encode("ascii", "ignore");
|
||||
self.files.append(f)
|
||||
|
||||
def AppendBinary(self, fileName, tdir):
|
||||
@ -168,7 +167,7 @@ class SNBFile:
|
||||
f.fileBody = open(os.path.join(tdir,fileName), 'rb').read()
|
||||
f.fileName = fileName.replace(os.sep, '/')
|
||||
if isinstance(f.fileName, unicode):
|
||||
f.fileName = ascii_filename(f.fileName).encode('ascii')
|
||||
f.fileName = f.fileName.encode("ascii", "ignore");
|
||||
self.files.append(f)
|
||||
|
||||
def GetFileStream(self, fileName):
|
||||
|
@ -40,7 +40,7 @@ if isosx:
|
||||
gprefs.defaults['action-layout-toolbar-device'] = (
|
||||
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View',
|
||||
'Send To Device', None, None, 'Location Manager', None, None,
|
||||
'Fetch News', 'Save To Disk', 'Connect Share', None,
|
||||
'Fetch News', 'Store', 'Save To Disk', 'Connect Share', None,
|
||||
'Remove Books',
|
||||
)
|
||||
else:
|
||||
@ -55,7 +55,7 @@ else:
|
||||
gprefs.defaults['action-layout-toolbar-device'] = (
|
||||
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View',
|
||||
'Send To Device', None, None, 'Location Manager', None, None,
|
||||
'Fetch News', 'Save To Disk', 'Connect Share', None,
|
||||
'Fetch News', 'Save To Disk', 'Store', 'Connect Share', None,
|
||||
'Remove Books', None, 'Help', 'Preferences',
|
||||
)
|
||||
|
||||
@ -94,9 +94,10 @@ gprefs.defaults['book_display_fields'] = [
|
||||
('path', True), ('publisher', False), ('rating', False),
|
||||
('author_sort', False), ('sort', False), ('timestamp', False),
|
||||
('uuid', False), ('comments', True), ('id', False), ('pubdate', False),
|
||||
('last_modified', False), ('size', False),
|
||||
('last_modified', False), ('size', False), ('languages', False),
|
||||
]
|
||||
gprefs.defaults['default_author_link'] = 'http://en.wikipedia.org/w/index.php?search={author}'
|
||||
gprefs.defaults['preserve_date_on_ctl'] = True
|
||||
|
||||
# }}}
|
||||
|
||||
@ -169,7 +170,9 @@ def _config(): # {{{
|
||||
c.add_opt('scheduler_search_history', default=[],
|
||||
help='Search history for the recipe scheduler')
|
||||
c.add_opt('plugin_search_history', default=[],
|
||||
help='Search history for the recipe scheduler')
|
||||
help='Search history for the plugin preferences')
|
||||
c.add_opt('shortcuts_search_history', default=[],
|
||||
help='Search history for the keyboard preferences')
|
||||
c.add_opt('worker_limit', default=6,
|
||||
help=_(
|
||||
'Maximum number of simultaneous conversion/news download jobs. '
|
||||
@ -423,6 +426,10 @@ class FileIconProvider(QFileIconProvider):
|
||||
'rtf' : 'rtf',
|
||||
'odt' : 'odt',
|
||||
'snb' : 'snb',
|
||||
'djv' : 'djvu',
|
||||
'djvu' : 'djvu',
|
||||
'xps' : 'xps',
|
||||
'oxps' : 'xps',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
@ -8,9 +8,13 @@ __docformat__ = 'restructuredtext en'
|
||||
from functools import partial
|
||||
from zipfile import ZipFile
|
||||
|
||||
from PyQt4.Qt import QToolButton, QAction, QIcon, QObject
|
||||
from PyQt4.Qt import (QToolButton, QAction, QIcon, QObject, QMenu,
|
||||
QKeySequence)
|
||||
|
||||
from calibre import prints
|
||||
from calibre.gui2 import Dispatcher
|
||||
from calibre.gui2.keyboard import NameConflict
|
||||
|
||||
|
||||
class InterfaceAction(QObject):
|
||||
|
||||
@ -66,6 +70,14 @@ class InterfaceAction(QObject):
|
||||
#: shortcut must be a translated string if not None
|
||||
action_spec = ('text', 'icon', None, None)
|
||||
|
||||
#: If True, a menu is automatically created and added to self.qaction
|
||||
action_add_menu = False
|
||||
|
||||
#: If True, a clone of self.qaction is added to the menu of self.qaction
|
||||
#: If you want the text of this action to be different from that of
|
||||
#: self.qaction, set this variable to the new text
|
||||
action_menu_clone_qaction = False
|
||||
|
||||
#: Set of locations to which this action must not be added.
|
||||
#: See :attr:`all_locations` for a list of possible locations
|
||||
dont_add_to = frozenset([])
|
||||
@ -75,7 +87,8 @@ class InterfaceAction(QObject):
|
||||
dont_remove_from = frozenset([])
|
||||
|
||||
all_locations = frozenset(['toolbar', 'toolbar-device', 'context-menu',
|
||||
'context-menu-device', 'toolbar-child', 'menubar', 'menubar-device'])
|
||||
'context-menu-device', 'toolbar-child', 'menubar', 'menubar-device',
|
||||
'context-menu-cover-browser'])
|
||||
|
||||
#: Type of action
|
||||
#: 'current' means acts on the current view
|
||||
@ -94,8 +107,16 @@ class InterfaceAction(QObject):
|
||||
self.Dispatcher = partial(Dispatcher, parent=self)
|
||||
self.create_action()
|
||||
self.gui.addAction(self.qaction)
|
||||
self.gui.addAction(self.menuless_qaction)
|
||||
self.genesis()
|
||||
|
||||
@property
|
||||
def unique_name(self):
|
||||
bn = self.__class__.__name__
|
||||
if getattr(self.interface_action_base_plugin, 'name'):
|
||||
bn = self.interface_action_base_plugin.name
|
||||
return u'Interface Action: %s (%s)'%(bn, self.name)
|
||||
|
||||
def create_action(self, spec=None, attr='qaction'):
|
||||
if spec is None:
|
||||
spec = self.action_spec
|
||||
@ -104,20 +125,75 @@ class InterfaceAction(QObject):
|
||||
action = QAction(QIcon(I(icon)), text, self.gui)
|
||||
else:
|
||||
action = QAction(text, self.gui)
|
||||
action.setAutoRepeat(self.auto_repeat)
|
||||
text = tooltip if tooltip else text
|
||||
action.setToolTip(text)
|
||||
action.setStatusTip(text)
|
||||
action.setWhatsThis(text)
|
||||
action.setAutoRepeat(False)
|
||||
if shortcut:
|
||||
if isinstance(shortcut, list):
|
||||
action.setShortcuts(shortcut)
|
||||
else:
|
||||
action.setShortcut(shortcut)
|
||||
setattr(self, attr, action)
|
||||
if attr == 'qaction':
|
||||
mt = (action.text() if self.action_menu_clone_qaction is True else
|
||||
unicode(self.action_menu_clone_qaction))
|
||||
self.menuless_qaction = ma = QAction(action.icon(), mt, self.gui)
|
||||
ma.triggered.connect(action.trigger)
|
||||
for a in ((action, ma) if attr == 'qaction' else (action,)):
|
||||
a.setAutoRepeat(self.auto_repeat)
|
||||
text = tooltip if tooltip else text
|
||||
a.setToolTip(text)
|
||||
a.setStatusTip(text)
|
||||
a.setWhatsThis(text)
|
||||
shortcut_action = action
|
||||
desc = tooltip if tooltip else None
|
||||
if attr == 'qaction':
|
||||
shortcut_action = ma
|
||||
if shortcut is not None:
|
||||
keys = ((shortcut,) if isinstance(shortcut, basestring) else
|
||||
tuple(shortcut))
|
||||
|
||||
if spec[0] and not (attr=='qaction' and self.popup_type ==
|
||||
QToolButton.InstantPopup):
|
||||
try:
|
||||
self.gui.keyboard.register_shortcut(self.unique_name + ' - ' + attr,
|
||||
unicode(spec[0]), default_keys=keys,
|
||||
action=shortcut_action, description=desc,
|
||||
group=self.action_spec[0])
|
||||
except NameConflict as e:
|
||||
try:
|
||||
prints(unicode(e))
|
||||
except:
|
||||
pass
|
||||
shortcut_action.setShortcuts([QKeySequence(key,
|
||||
QKeySequence.PortableText) for key in keys])
|
||||
|
||||
|
||||
if attr is not None:
|
||||
setattr(self, attr, action)
|
||||
if attr == 'qaction' and self.action_add_menu:
|
||||
menu = QMenu()
|
||||
action.setMenu(menu)
|
||||
if self.action_menu_clone_qaction:
|
||||
menu.addAction(self.menuless_qaction)
|
||||
return action
|
||||
|
||||
def create_menu_action(self, menu, unique_name, text, icon=None, shortcut=None,
|
||||
description=None, triggered=None):
|
||||
ac = menu.addAction(text)
|
||||
if icon is not None:
|
||||
if not isinstance(icon, QIcon):
|
||||
icon = QIcon(I(icon))
|
||||
ac.setIcon(icon)
|
||||
keys = ()
|
||||
if shortcut is not None and shortcut is not False:
|
||||
keys = ((shortcut,) if isinstance(shortcut, basestring) else
|
||||
tuple(shortcut))
|
||||
unique_name = '%s : menu action : %s'%(self.unique_name, unique_name)
|
||||
if description is not None:
|
||||
ac.setToolTip(description)
|
||||
ac.setStatusTip(description)
|
||||
ac.setWhatsThis(description)
|
||||
|
||||
if shortcut is not False:
|
||||
self.gui.keyboard.register_shortcut(unique_name,
|
||||
unicode(text), default_keys=keys,
|
||||
action=ac, description=description, group=self.action_spec[0])
|
||||
if triggered is not None:
|
||||
ac.triggered.connect(triggered)
|
||||
return ac
|
||||
|
||||
def load_resources(self, names):
|
||||
'''
|
||||
If this plugin comes in a ZIP file (user added plugin), this method
|
||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QPixmap, QMenu, QTimer
|
||||
from PyQt4.Qt import QPixmap, QTimer
|
||||
|
||||
|
||||
from calibre.gui2 import error_dialog, choose_files, \
|
||||
@ -48,28 +48,29 @@ class AddAction(InterfaceAction):
|
||||
_('Add books to the calibre library/device from files on your computer')
|
||||
, _('A'))
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = _('Add books from a single directory')
|
||||
|
||||
def genesis(self):
|
||||
self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book)
|
||||
self.add_menu = QMenu()
|
||||
self.add_menu.addAction(_('Add books from a single directory'),
|
||||
self.add_books)
|
||||
self.add_menu.addAction(_('Add books from directories, including '
|
||||
self.add_menu = self.qaction.menu()
|
||||
ma = partial(self.create_menu_action, self.add_menu)
|
||||
ma('recursive-single', _('Add books from directories, including '
|
||||
'sub-directories (One book per directory, assumes every ebook '
|
||||
'file is the same book in a different format)'),
|
||||
'file is the same book in a different format)')).triggered.connect(
|
||||
self.add_recursive_single)
|
||||
self.add_menu.addAction(_('Add books from directories, including '
|
||||
ma('recursive-multiple', _('Add books from directories, including '
|
||||
'sub directories (Multiple books per directory, assumes every '
|
||||
'ebook file is a different book)'), self.add_recursive_multiple)
|
||||
'ebook file is a different book)')).triggered.connect(
|
||||
self.add_recursive_multiple)
|
||||
self.add_menu.addSeparator()
|
||||
self.add_menu.addAction(_('Add Empty book. (Book entry with no '
|
||||
'formats)'), self.add_empty, _('Shift+Ctrl+E'))
|
||||
self.add_menu.addAction(_('Add from ISBN'), self.add_from_isbn)
|
||||
ma('add-empty', _('Add Empty book. (Book entry with no formats)'),
|
||||
shortcut=_('Shift+Ctrl+E')).triggered.connect(self.add_empty)
|
||||
ma('add-isbn', _('Add from ISBN')).triggered.connect(self.add_from_isbn)
|
||||
self.add_menu.addSeparator()
|
||||
self.add_menu.addAction(_('Add files to selected book records'),
|
||||
self.add_formats, _('Shift+A'))
|
||||
ma('add-formats', _('Add files to selected book records'),
|
||||
triggered=self.add_formats, shortcut=_('Shift+A'))
|
||||
|
||||
self.qaction.setMenu(self.add_menu)
|
||||
self.qaction.triggered.connect(self.add_books)
|
||||
|
||||
def location_selected(self, loc):
|
||||
@ -83,7 +84,8 @@ class AddAction(InterfaceAction):
|
||||
view = self.gui.library_view
|
||||
rows = view.selectionModel().selectedRows()
|
||||
if not rows:
|
||||
return
|
||||
return error_dialog(self.gui, _('No books selected'),
|
||||
_('Cannot add files as no books are selected'), show=True)
|
||||
ids = [view.model().id(r) for r in rows]
|
||||
|
||||
if len(ids) > 1 and not question_dialog(self.gui,
|
||||
|
@ -8,13 +8,14 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMenu, Qt, QInputDialog, QToolButton
|
||||
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
|
||||
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize)
|
||||
|
||||
from calibre import isbytestring
|
||||
from calibre.constants import filesystem_encoding, iswindows
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2 import (gprefs, warning_dialog, Dispatcher, error_dialog,
|
||||
question_dialog, info_dialog, open_local_file)
|
||||
question_dialog, info_dialog, open_local_file, choose_dir)
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
@ -76,29 +77,83 @@ class LibraryUsageStats(object): # {{{
|
||||
self.write_stats()
|
||||
# }}}
|
||||
|
||||
class MovedDialog(QDialog): # {{{
|
||||
|
||||
def __init__(self, stats, location, parent=None):
|
||||
QDialog.__init__(self, parent)
|
||||
self.setWindowTitle(_('No library found'))
|
||||
self._l = l = QGridLayout(self)
|
||||
self.setLayout(l)
|
||||
self.stats, self.location = stats, location
|
||||
|
||||
loc = self.oldloc = location.replace('/', os.sep)
|
||||
self.header = QLabel(_('No existing calibre library was found at %s. '
|
||||
'If the library was moved, select its new location below. '
|
||||
'Otherwise calibre will forget this library.')%loc)
|
||||
self.header.setWordWrap(True)
|
||||
ncols = 2
|
||||
l.addWidget(self.header, 0, 0, 1, ncols)
|
||||
self.cl = QLabel('<br><b>'+_('New location of this library:'))
|
||||
l.addWidget(self.cl, 1, 0, 1, ncols)
|
||||
self.loc = QLineEdit(loc, self)
|
||||
l.addWidget(self.loc, 2, 0, 1, 1)
|
||||
self.cd = QToolButton(self)
|
||||
self.cd.setIcon(QIcon(I('document_open.png')))
|
||||
self.cd.clicked.connect(self.choose_dir)
|
||||
l.addWidget(self.cd, 2, 1, 1, 1)
|
||||
self.bb = QDialogButtonBox(self)
|
||||
b = self.bb.addButton(_('Library moved'), self.bb.AcceptRole)
|
||||
b.setIcon(QIcon(I('ok.png')))
|
||||
b = self.bb.addButton(_('Forget library'), self.bb.RejectRole)
|
||||
b.setIcon(QIcon(I('edit-clear.png')))
|
||||
self.bb.accepted.connect(self.accept)
|
||||
self.bb.rejected.connect(self.reject)
|
||||
l.addWidget(self.bb, 3, 0, 1, ncols)
|
||||
self.resize(self.sizeHint() + QSize(100, 50))
|
||||
|
||||
def choose_dir(self):
|
||||
d = choose_dir(self, 'library moved choose new loc',
|
||||
_('New library location'), default_dir=self.oldloc)
|
||||
if d is not None:
|
||||
self.loc.setText(d)
|
||||
|
||||
def reject(self):
|
||||
self.stats.remove(self.location)
|
||||
QDialog.reject(self)
|
||||
|
||||
def accept(self):
|
||||
newloc = unicode(self.loc.text())
|
||||
if not LibraryDatabase2.exists_at(newloc):
|
||||
error_dialog(self, _('No library found'),
|
||||
_('No existing calibre library found at %s')%newloc,
|
||||
show=True)
|
||||
return
|
||||
self.stats.rename(self.location, newloc)
|
||||
self.newloc = newloc
|
||||
QDialog.accept(self)
|
||||
# }}}
|
||||
|
||||
class ChooseLibraryAction(InterfaceAction):
|
||||
|
||||
name = 'Choose Library'
|
||||
action_spec = (_('%d books'), 'lt.png',
|
||||
action_spec = (_('Choose Library'), 'lt.png',
|
||||
_('Choose calibre library to work with'), None)
|
||||
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = _('Switch/create library...')
|
||||
|
||||
def genesis(self):
|
||||
self.base_text = _('%d books')
|
||||
self.count_changed(0)
|
||||
self.qaction.triggered.connect(self.choose_library,
|
||||
type=Qt.QueuedConnection)
|
||||
self.action_choose = self.menuless_qaction
|
||||
|
||||
self.stats = LibraryUsageStats()
|
||||
self.popup_type = (QToolButton.InstantPopup if len(self.stats.stats) > 1 else
|
||||
QToolButton.MenuButtonPopup)
|
||||
|
||||
self.create_action(spec=(_('Switch/create library...'), 'lt.png', None,
|
||||
None), attr='action_choose')
|
||||
self.action_choose.triggered.connect(self.choose_library,
|
||||
type=Qt.QueuedConnection)
|
||||
self.choose_menu = QMenu(self.gui)
|
||||
self.qaction.setMenu(self.choose_menu)
|
||||
|
||||
self.choose_menu = self.qaction.menu()
|
||||
|
||||
if not os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
|
||||
self.choose_menu.addAction(self.action_choose)
|
||||
@ -110,7 +165,7 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
self.delete_menu = QMenu(_('Remove library'))
|
||||
self.delete_menu_action = self.choose_menu.addMenu(self.delete_menu)
|
||||
|
||||
ac = self.create_action(spec=(_('Pick a random book'), 'catalog.png',
|
||||
ac = self.create_action(spec=(_('Pick a random book'), 'random.png',
|
||||
None, None), attr='action_pick_random')
|
||||
ac.triggered.connect(self.pick_random)
|
||||
self.choose_menu.addAction(ac)
|
||||
@ -152,10 +207,7 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
self.choose_menu.addMenu(self.maintenance_menu)
|
||||
|
||||
def pick_random(self, *args):
|
||||
import random
|
||||
pick = random.randint(0, self.gui.library_view.model().rowCount(None))
|
||||
self.gui.library_view.set_current_row(pick)
|
||||
self.gui.library_view.scroll_to_row(pick)
|
||||
self.gui.iactions['Pick Random Book'].pick_random()
|
||||
|
||||
def library_name(self):
|
||||
db = self.gui.library_view.model().db
|
||||
@ -344,14 +396,14 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
loc = location.replace('/', os.sep)
|
||||
exists = self.gui.library_view.model().db.exists_at(loc)
|
||||
if not exists:
|
||||
warning_dialog(self.gui, _('No library found'),
|
||||
_('No existing calibre library was found at %s.'
|
||||
' It will be removed from the list of known'
|
||||
' libraries.')%loc, show=True)
|
||||
self.stats.remove(location)
|
||||
d = MovedDialog(self.stats, location, self.gui)
|
||||
ret = d.exec_()
|
||||
self.build_menus()
|
||||
self.gui.iactions['Copy To Library'].build_menus()
|
||||
return
|
||||
if ret == d.Accepted:
|
||||
loc = d.newloc.replace('/', os.sep)
|
||||
else:
|
||||
return
|
||||
|
||||
prefs['library_path'] = loc
|
||||
#from calibre.utils.mem import memory
|
||||
@ -382,7 +434,7 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
self.switch_requested(self.qs_locations[idx])
|
||||
|
||||
def count_changed(self, new_count):
|
||||
text = self.action_spec[0]%new_count
|
||||
text = self.base_text%new_count
|
||||
a = self.qaction
|
||||
a.setText(text)
|
||||
tooltip = self.action_spec[2] + '\n\n' + text
|
||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QModelIndex, QMenu
|
||||
from PyQt4.Qt import QModelIndex
|
||||
|
||||
from calibre.gui2 import error_dialog, Dispatcher
|
||||
from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook
|
||||
@ -22,20 +22,22 @@ class ConvertAction(InterfaceAction):
|
||||
action_spec = (_('Convert books'), 'convert.png', None, _('C'))
|
||||
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
|
||||
def genesis(self):
|
||||
cm = QMenu()
|
||||
cm.addAction(_('Convert individually'), partial(self.convert_ebook,
|
||||
m = self.convert_menu = self.qaction.menu()
|
||||
cm = partial(self.create_menu_action, self.convert_menu)
|
||||
cm('convert-individual', _('Convert individually'),
|
||||
icon=self.qaction.icon(), triggered=partial(self.convert_ebook,
|
||||
False, bulk=False))
|
||||
cm.addAction(_('Bulk convert'),
|
||||
partial(self.convert_ebook, False, bulk=True))
|
||||
cm.addSeparator()
|
||||
ac = cm.addAction(
|
||||
_('Create a catalog of the books in your calibre library'))
|
||||
ac.triggered.connect(self.gui.iactions['Generate Catalog'].generate_catalog)
|
||||
self.qaction.setMenu(cm)
|
||||
cm('convert-bulk', _('Bulk convert'),
|
||||
triggered=partial(self.convert_ebook, False, bulk=True))
|
||||
m.addSeparator()
|
||||
cm('create-catalog',
|
||||
_('Create a catalog of the books in your calibre library'),
|
||||
icon='catalog.png', shortcut=False,
|
||||
triggered=self.gui.iactions['Generate Catalog'].generate_catalog)
|
||||
self.qaction.triggered.connect(self.convert_ebook)
|
||||
self.convert_menu = cm
|
||||
self.conversion_jobs = {}
|
||||
|
||||
def location_selected(self, loc):
|
||||
|
@ -9,12 +9,13 @@ import os
|
||||
from functools import partial
|
||||
from threading import Thread
|
||||
|
||||
from PyQt4.Qt import QMenu, QToolButton
|
||||
from PyQt4.Qt import QToolButton
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2 import error_dialog, Dispatcher, warning_dialog
|
||||
from calibre.gui2 import error_dialog, Dispatcher, warning_dialog, gprefs
|
||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
from calibre.utils.date import now
|
||||
|
||||
class Worker(Thread): # {{{
|
||||
|
||||
@ -55,6 +56,8 @@ class Worker(Thread): # {{{
|
||||
for i, x in enumerate(self.ids):
|
||||
mi = self.db.get_metadata(x, index_is_id=True, get_cover=True,
|
||||
cover_as_data=True)
|
||||
if not gprefs['preserve_date_on_ctl']:
|
||||
mi.timestamp = now()
|
||||
self.progress(i, mi.title)
|
||||
fmts = self.db.formats(x, index_is_id=True)
|
||||
if not fmts: fmts = []
|
||||
@ -65,14 +68,37 @@ class Worker(Thread): # {{{
|
||||
as_path=True)
|
||||
if p:
|
||||
paths.append(p)
|
||||
added = False
|
||||
automerged = False
|
||||
if prefs['add_formats_to_existing']:
|
||||
identical_book_list = newdb.find_identical_books(mi)
|
||||
if identical_book_list: # books with same author and nearly same title exist in newdb
|
||||
added = True
|
||||
automerged = True
|
||||
seen_fmts = set()
|
||||
for identical_book in identical_book_list:
|
||||
self.add_formats(identical_book, paths, newdb, replace=False)
|
||||
if not added:
|
||||
ib_fmts = newdb.formats(identical_book, index_is_id=True)
|
||||
if ib_fmts:
|
||||
seen_fmts |= set(ib_fmts.split(','))
|
||||
replace = gprefs['automerge'] == 'overwrite'
|
||||
self.add_formats(identical_book, paths, newdb,
|
||||
replace=replace)
|
||||
|
||||
if gprefs['automerge'] == 'new record':
|
||||
incoming_fmts = \
|
||||
set([os.path.splitext(path)[-1].replace('.',
|
||||
'').upper() for path in paths])
|
||||
|
||||
if incoming_fmts.intersection(seen_fmts):
|
||||
# There was at least one duplicate format
|
||||
# so create a new record and put the
|
||||
# incoming formats into it
|
||||
# We should arguably put only the duplicate
|
||||
# formats, but no real harm is done by having
|
||||
# all formats
|
||||
newdb.import_book(mi, paths, notify=False, import_hooks=False,
|
||||
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
|
||||
preserve_uuid=False)
|
||||
|
||||
if not automerged:
|
||||
newdb.import_book(mi, paths, notify=False, import_hooks=False,
|
||||
apply_import_tags=tweaks['add_new_book_tags_when_importing_books'],
|
||||
preserve_uuid=self.delete_after)
|
||||
@ -95,10 +121,10 @@ class CopyToLibraryAction(InterfaceAction):
|
||||
popup_type = QToolButton.InstantPopup
|
||||
dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
|
||||
def genesis(self):
|
||||
self.menu = QMenu(self.gui)
|
||||
self.qaction.setMenu(self.menu)
|
||||
self.menu = self.qaction.menu()
|
||||
|
||||
@property
|
||||
def stats(self):
|
||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMenu, QObject, QTimer
|
||||
from PyQt4.Qt import QObject, QTimer
|
||||
|
||||
from calibre.gui2 import error_dialog, question_dialog
|
||||
from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog
|
||||
@ -18,7 +18,7 @@ from calibre.utils.recycle_bin import can_recycle
|
||||
|
||||
single_shot = partial(QTimer.singleShot, 10)
|
||||
|
||||
class MultiDeleter(QObject):
|
||||
class MultiDeleter(QObject): # {{{
|
||||
|
||||
def __init__(self, gui, ids, callback):
|
||||
from calibre.gui2.dialogs.progress import ProgressDialog
|
||||
@ -77,32 +77,36 @@ class MultiDeleter(QObject):
|
||||
error_dialog(self.gui, _('Failed to delete'),
|
||||
_('Failed to delete some books, click the Show Details button'
|
||||
' for details.'), det_msg='\n\n'.join(msg), show=True)
|
||||
# }}}
|
||||
|
||||
class DeleteAction(InterfaceAction):
|
||||
|
||||
name = 'Remove Books'
|
||||
action_spec = (_('Remove books'), 'trash.png', None, 'Del')
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = _('Remove selected books')
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.delete_books)
|
||||
self.delete_menu = QMenu()
|
||||
self.delete_menu.addAction(_('Remove selected books'), self.delete_books)
|
||||
self.delete_menu.addAction(
|
||||
self.delete_menu = self.qaction.menu()
|
||||
m = partial(self.create_menu_action, self.delete_menu)
|
||||
m('delete-specific',
|
||||
_('Remove files of a specific format from selected books..'),
|
||||
self.delete_selected_formats)
|
||||
self.delete_menu.addAction(
|
||||
triggered=self.delete_selected_formats)
|
||||
m('delete-except',
|
||||
_('Remove all formats from selected books, except...'),
|
||||
self.delete_all_but_selected_formats)
|
||||
self.delete_menu.addAction(
|
||||
triggered=self.delete_all_but_selected_formats)
|
||||
m('delete-all',
|
||||
_('Remove all formats from selected books'),
|
||||
self.delete_all_formats)
|
||||
self.delete_menu.addAction(
|
||||
_('Remove covers from selected books'), self.delete_covers)
|
||||
triggered=self.delete_all_formats)
|
||||
m('delete-covers',
|
||||
_('Remove covers from selected books'),
|
||||
triggered=self.delete_covers)
|
||||
self.delete_menu.addSeparator()
|
||||
self.delete_menu.addAction(
|
||||
m('delete-matching',
|
||||
_('Remove matching books from device'),
|
||||
self.remove_matching_books_from_device)
|
||||
triggered=self.remove_matching_books_from_device)
|
||||
self.qaction.setMenu(self.delete_menu)
|
||||
self.delete_memory = {}
|
||||
|
||||
|
@ -60,6 +60,19 @@ class ShareConnMenu(QMenu): # {{{
|
||||
|
||||
self.email_actions = []
|
||||
|
||||
if hasattr(parent, 'keyboard'):
|
||||
r = parent.keyboard.register_shortcut
|
||||
prefix = 'Share/Connect Menu '
|
||||
gr = ConnectShareAction.action_spec[0]
|
||||
for attr in ('folder', 'bambook', 'itunes'):
|
||||
if not (iswindows or isosx) and attr == 'itunes':
|
||||
continue
|
||||
ac = getattr(self, 'connect_to_%s_action'%attr)
|
||||
r(prefix + attr, unicode(ac.text()), action=ac,
|
||||
group=gr)
|
||||
r(prefix+' content server', _('Start/stop content server'),
|
||||
action=self.toggle_server_action, group=gr)
|
||||
|
||||
def server_state_changed(self, running):
|
||||
text = _('Start Content Server')
|
||||
if running:
|
||||
|
@ -8,12 +8,12 @@ __docformat__ = 'restructuredtext en'
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import Qt, QMenu, QModelIndex, QTimer
|
||||
from PyQt4.Qt import QMenu, QModelIndex, QTimer
|
||||
|
||||
from calibre.gui2 import error_dialog, Dispatcher, question_dialog
|
||||
from calibre.gui2.dialogs.metadata_bulk import MetadataBulkDialog
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.dialogs.tag_list_editor import TagListEditor
|
||||
from calibre.gui2.dialogs.device_category_editor import DeviceCategoryEditor
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.ebooks.metadata import authors_to_string
|
||||
from calibre.utils.icu import sort_key
|
||||
@ -24,40 +24,41 @@ class EditMetadataAction(InterfaceAction):
|
||||
name = 'Edit Metadata'
|
||||
action_spec = (_('Edit metadata'), 'edit_input.png', None, _('E'))
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
|
||||
def genesis(self):
|
||||
self.create_action(spec=(_('Merge book records'), 'merge_books.png',
|
||||
None, _('M')), attr='action_merge')
|
||||
md = QMenu()
|
||||
md.addAction(_('Edit metadata individually'),
|
||||
partial(self.edit_metadata, False, bulk=False))
|
||||
md = self.qaction.menu()
|
||||
cm = partial(self.create_menu_action, md)
|
||||
cm('individual', _('Edit metadata individually'), icon=self.qaction.icon(),
|
||||
triggered=partial(self.edit_metadata, False, bulk=False))
|
||||
md.addSeparator()
|
||||
md.addAction(_('Edit metadata in bulk'),
|
||||
partial(self.edit_metadata, False, bulk=True))
|
||||
cm('bulk', _('Edit metadata in bulk'),
|
||||
triggered=partial(self.edit_metadata, False, bulk=True))
|
||||
md.addSeparator()
|
||||
md.addAction(_('Download metadata and covers'), self.download_metadata,
|
||||
Qt.ControlModifier+Qt.Key_D)
|
||||
cm('download', _('Download metadata and covers'),
|
||||
triggered=partial(self.download_metadata, ids=None),
|
||||
shortcut='Ctrl+D')
|
||||
self.metadata_menu = md
|
||||
|
||||
mb = QMenu()
|
||||
mb.addAction(_('Merge into first selected book - delete others'),
|
||||
self.merge_books)
|
||||
cm2 = partial(self.create_menu_action, mb)
|
||||
cm2('merge delete', _('Merge into first selected book - delete others'),
|
||||
triggered=self.merge_books)
|
||||
mb.addSeparator()
|
||||
mb.addAction(_('Merge into first selected book - keep others'),
|
||||
partial(self.merge_books, safe_merge=True),
|
||||
Qt.AltModifier+Qt.Key_M)
|
||||
cm2('merge keep', _('Merge into first selected book - keep others'),
|
||||
triggered=partial(self.merge_books, safe_merge=True),
|
||||
shortcut='Alt+M')
|
||||
mb.addSeparator()
|
||||
mb.addAction(_('Merge only formats into first selected book - delete others'),
|
||||
partial(self.merge_books, merge_only_formats=True),
|
||||
Qt.AltModifier+Qt.ShiftModifier+Qt.Key_M)
|
||||
cm2('merge formats', _('Merge only formats into first selected book - delete others'),
|
||||
triggered=partial(self.merge_books, merge_only_formats=True),
|
||||
shortcut='Alt+Shift+M')
|
||||
self.merge_menu = mb
|
||||
self.action_merge.setMenu(mb)
|
||||
md.addSeparator()
|
||||
md.addAction(self.action_merge)
|
||||
self.action_merge = cm('merge', _('Merge book records'), icon='merge_books.png',
|
||||
shortcut=_('M'), triggered=self.merge_books)
|
||||
self.action_merge.setMenu(mb)
|
||||
|
||||
self.qaction.triggered.connect(self.edit_metadata)
|
||||
self.qaction.setMenu(md)
|
||||
self.action_merge.triggered.connect(self.merge_books)
|
||||
|
||||
def location_selected(self, loc):
|
||||
enabled = loc == 'library'
|
||||
@ -417,7 +418,7 @@ class EditMetadataAction(InterfaceAction):
|
||||
db.set_custom(dest_id, dest_value, num=colnum)
|
||||
if db.field_metadata[key]['datatype'] in \
|
||||
('bool', 'int', 'float', 'rating', 'datetime') \
|
||||
and not dest_value:
|
||||
and dest_value is None:
|
||||
db.set_custom(dest_id, src_value, num=colnum)
|
||||
if db.field_metadata[key]['datatype'] == 'series' \
|
||||
and not dest_value:
|
||||
@ -441,7 +442,7 @@ class EditMetadataAction(InterfaceAction):
|
||||
def edit_device_collections(self, view, oncard=None):
|
||||
model = view.model()
|
||||
result = model.get_collections_with_ids()
|
||||
d = TagListEditor(self.gui, tag_to_match=None, data=result, key=sort_key)
|
||||
d = DeviceCategoryEditor(self.gui, tag_to_match=None, data=result, key=sort_key)
|
||||
d.exec_()
|
||||
if d.result() == d.Accepted:
|
||||
to_rename = d.to_rename # dict of new text to old ids
|
||||
|
@ -5,7 +5,9 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import QIcon, QMenu, Qt
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QIcon, Qt
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2.preferences.main import Preferences
|
||||
@ -16,24 +18,23 @@ class PreferencesAction(InterfaceAction):
|
||||
|
||||
name = 'Preferences'
|
||||
action_spec = (_('Preferences'), 'config.png', None, _('Ctrl+P'))
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = _('Change calibre behavior')
|
||||
|
||||
def genesis(self):
|
||||
pm = QMenu()
|
||||
pm.addAction(QIcon(I('config.png')), _('Preferences'), self.do_config)
|
||||
pm = self.qaction.menu()
|
||||
cm = partial(self.create_menu_action, pm)
|
||||
if isosx:
|
||||
pm.addAction(QIcon(I('config.png')), _('Change calibre behavior'), self.do_config)
|
||||
pm.addAction(QIcon(I('wizard.png')), _('Run welcome wizard'),
|
||||
self.gui.run_wizard)
|
||||
pm.addAction(QIcon(I('plugins/plugin_updater.png')),
|
||||
_('Get plugins to enhance calibre'), self.get_plugins)
|
||||
pm.addAction(QIcon(I('config.png')), _('Preferences'), self.do_config)
|
||||
cm('welcome wizard', _('Run welcome wizard'),
|
||||
icon='wizard.png', triggered=self.gui.run_wizard)
|
||||
cm('plugin updater', _('Get plugins to enhance calibre'),
|
||||
icon='plugins/plugin_updater.png', triggered=self.get_plugins)
|
||||
if not DEBUG:
|
||||
pm.addSeparator()
|
||||
ac = pm.addAction(QIcon(I('debug.png')), _('Restart in debug mode'),
|
||||
self.debug_restart)
|
||||
ac.setShortcut('Ctrl+Shift+R')
|
||||
self.gui.addAction(ac)
|
||||
cm('restart', _('Restart in debug mode'), icon='debug.png',
|
||||
triggered=self.debug_restart, shortcut='Ctrl+Shift+R')
|
||||
|
||||
self.qaction.setMenu(pm)
|
||||
self.preferences_menu = pm
|
||||
for x in (self.gui.preferences_action, self.qaction):
|
||||
x.triggered.connect(self.do_config)
|
||||
|
28
src/calibre/gui2/actions/random.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import random
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
class PickRandomAction(InterfaceAction):
|
||||
|
||||
name = 'Pick Random Book'
|
||||
action_spec = (_('Pick a random book'), 'random.png', 'Catalog builder', None)
|
||||
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.pick_random)
|
||||
|
||||
def pick_random(self):
|
||||
pick = random.randint(0, self.gui.library_view.model().rowCount(None))
|
||||
self.gui.library_view.set_current_row(pick)
|
||||
self.gui.library_view.scroll_to_row(pick)
|
||||
|
||||
|
@ -11,7 +11,7 @@ from calibre.gui2.actions import InterfaceAction
|
||||
class RestartAction(InterfaceAction):
|
||||
|
||||
name = 'Restart'
|
||||
action_spec = (_('&Restart'), None, None, _('Ctrl+R'))
|
||||
action_spec = (_('Restart'), None, None, _('Ctrl+R'))
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.restart)
|
||||
|
@ -38,25 +38,25 @@ class SaveToDiskAction(InterfaceAction):
|
||||
name = "Save To Disk"
|
||||
action_spec = (_('Save to disk'), 'save.png', None, _('S'))
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = True
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.save_to_disk)
|
||||
self.save_menu = QMenu()
|
||||
self.save_menu.addAction(_('Save to disk'), partial(self.save_to_disk,
|
||||
False))
|
||||
self.save_menu.addAction(_('Save to disk in a single directory'),
|
||||
partial(self.save_to_single_dir, False))
|
||||
self.save_menu.addAction(_('Save only %s format to disk')%
|
||||
self.save_menu = self.qaction.menu()
|
||||
cm = partial(self.create_menu_action, self.save_menu)
|
||||
cm('single dir', _('Save to disk in a single directory'),
|
||||
triggered=partial(self.save_to_single_dir, False))
|
||||
cm('single format', _('Save only %s format to disk')%
|
||||
prefs['output_format'].upper(),
|
||||
partial(self.save_single_format_to_disk, False))
|
||||
self.save_menu.addAction(
|
||||
triggered=partial(self.save_single_format_to_disk, False))
|
||||
cm('single dir and format',
|
||||
_('Save only %s format to disk in a single directory')%
|
||||
prefs['output_format'].upper(),
|
||||
partial(self.save_single_fmt_to_single_dir, False))
|
||||
triggered=partial(self.save_single_fmt_to_single_dir, False))
|
||||
self.save_sub_menu = SaveMenu(self.gui)
|
||||
self.save_sub_menu_action = self.save_menu.addMenu(self.save_sub_menu)
|
||||
self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk)
|
||||
self.qaction.setMenu(self.save_menu)
|
||||
|
||||
def location_selected(self, loc):
|
||||
enabled = loc == 'library'
|
||||
@ -115,10 +115,7 @@ class SaveToDiskAction(InterfaceAction):
|
||||
opts.save_cover = False
|
||||
opts.write_opf = False
|
||||
opts.template = opts.send_template
|
||||
if single_dir:
|
||||
opts.template = opts.template.split('/')[-1].strip()
|
||||
if not opts.template:
|
||||
opts.template = '{title} - {authors}'
|
||||
opts.single_dir = single_dir
|
||||
self._saver = Saver(self.gui, self.gui.library_view.model().db,
|
||||
Dispatcher(self._books_saved), rows, path, opts,
|
||||
spare_server=self.gui.spare_server)
|
||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMenu, QToolButton
|
||||
from PyQt4.Qt import QToolButton
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
@ -17,9 +17,10 @@ class SimilarBooksAction(InterfaceAction):
|
||||
action_spec = (_('Similar books...'), None, None, None)
|
||||
popup_type = QToolButton.InstantPopup
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
|
||||
def genesis(self):
|
||||
m = QMenu(self.gui)
|
||||
m = self.qaction.menu()
|
||||
for text, icon, target, shortcut in [
|
||||
(_('Books by same author'), 'user_profile.png', 'authors', _('Alt+A')),
|
||||
(_('Books in this series'), 'books_in_series.png', 'series',
|
||||
@ -31,7 +32,6 @@ class SimilarBooksAction(InterfaceAction):
|
||||
m.addAction(ac)
|
||||
ac.triggered.connect(partial(self.show_similar_books, target))
|
||||
self.qaction.setMenu(m)
|
||||
self.similar_menu = m
|
||||
|
||||
def show_similar_books(self, type, *args):
|
||||
search, join = [], ' '
|
||||
|
@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMenu, QIcon, QSize
|
||||
from PyQt4.Qt import QIcon, QSize
|
||||
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
@ -17,21 +17,28 @@ from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
class StoreAction(InterfaceAction):
|
||||
|
||||
name = 'Store'
|
||||
action_spec = (_('Get books'), 'store.png', None, None)
|
||||
action_spec = (_('Get books'), 'store.png', None, _('G'))
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = _('Search for ebooks')
|
||||
|
||||
def genesis(self):
|
||||
self.qaction.triggered.connect(self.do_search)
|
||||
self.store_menu = QMenu()
|
||||
self.load_menu()
|
||||
|
||||
def load_menu(self):
|
||||
self.store_menu.clear()
|
||||
self.store_menu.addAction(_('Search for ebooks'), self.search)
|
||||
self.store_menu.addAction(_('Search for this author'), self.search_author)
|
||||
self.store_menu.addAction(_('Search for this title'), self.search_title)
|
||||
self.store_menu.addAction(_('Search for this book'), self.search_author_title)
|
||||
self.store_menu = self.qaction.menu()
|
||||
cm = partial(self.create_menu_action, self.store_menu)
|
||||
for x, t in [('author', _('author')), ('title', _('title')),
|
||||
('book', _('book'))]:
|
||||
func = getattr(self, 'search_%s'%('author_title' if x == 'book'
|
||||
else x))
|
||||
ac = cm(x, _('Search for this %s'%t), triggered=func)
|
||||
setattr(self, 'action_search_by_'+x, ac)
|
||||
self.store_menu.addSeparator()
|
||||
self.store_list_menu = self.store_menu.addMenu(_('Stores'))
|
||||
self.load_menu()
|
||||
self.store_menu.addSeparator()
|
||||
cm('choose stores', _('Choose stores'), triggered=self.choose)
|
||||
|
||||
def load_menu(self):
|
||||
self.store_list_menu.clear()
|
||||
icon = QIcon()
|
||||
icon.addFile(I('donate.png'), QSize(16, 16))
|
||||
for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()):
|
||||
@ -39,9 +46,6 @@ class StoreAction(InterfaceAction):
|
||||
self.store_list_menu.addAction(icon, n, partial(self.open_store, p))
|
||||
else:
|
||||
self.store_list_menu.addAction(n, partial(self.open_store, p))
|
||||
self.store_menu.addSeparator()
|
||||
self.store_menu.addAction(_('Choose stores'), self.choose)
|
||||
self.qaction.setMenu(self.store_menu)
|
||||
|
||||
def do_search(self):
|
||||
return self.search()
|
||||
|
@ -6,8 +6,9 @@ __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, time
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import Qt, QMenu, QAction, pyqtSignal
|
||||
from PyQt4.Qt import Qt, QAction, pyqtSignal
|
||||
|
||||
from calibre.constants import isosx
|
||||
from calibre.gui2 import error_dialog, Dispatcher, question_dialog, config, \
|
||||
@ -35,45 +36,41 @@ class ViewAction(InterfaceAction):
|
||||
name = 'View'
|
||||
action_spec = (_('View'), 'view.png', None, _('V'))
|
||||
action_type = 'current'
|
||||
action_add_menu = True
|
||||
action_menu_clone_qaction = True
|
||||
|
||||
def genesis(self):
|
||||
self.persistent_files = []
|
||||
self.qaction.triggered.connect(self.view_book)
|
||||
self.view_menu = QMenu()
|
||||
ac = self.view_specific_action = QAction(_('View specific format'),
|
||||
self.gui)
|
||||
self.qaction.setMenu(self.view_menu)
|
||||
ac.setShortcut(Qt.AltModifier+Qt.Key_V)
|
||||
ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection)
|
||||
ac = self.view_action = QAction(self.qaction.icon(),
|
||||
self.qaction.text(), self.gui)
|
||||
ac.triggered.connect(self.view_book)
|
||||
ac = self.create_action(spec=(_('Read a random book'), 'catalog.png',
|
||||
None, None), attr='action_pick_random')
|
||||
ac.triggered.connect(self.view_random)
|
||||
ac = self.clear_history_action = QAction(
|
||||
_('Clear recently viewed list'), self.gui)
|
||||
ac.triggered.connect(self.clear_history)
|
||||
self.view_action = self.menuless_qaction
|
||||
self.view_menu = self.qaction.menu()
|
||||
cm = partial(self.create_menu_action, self.view_menu)
|
||||
self.view_specific_action = cm('specific', _('View specific format'),
|
||||
shortcut='Alt+V', triggered=self.view_specific_format)
|
||||
self.action_pick_random = cm('pick random', _('Read a random book'),
|
||||
icon='random.png', triggered=self.view_random)
|
||||
self.clear_sep1 = self.view_menu.addSeparator()
|
||||
self.clear_sep2 = self.view_menu.addSeparator()
|
||||
self.clear_history_action = cm('clear history',
|
||||
_('Clear recently viewed list'), triggered=self.clear_history)
|
||||
self.history_actions = [self.clear_sep1]
|
||||
|
||||
def initialization_complete(self):
|
||||
self.build_menus(self.gui.current_db)
|
||||
|
||||
def build_menus(self, db):
|
||||
self.view_menu.clear()
|
||||
self.view_menu.addAction(self.view_action)
|
||||
self.view_menu.addAction(self.view_specific_action)
|
||||
self.view_menu.addSeparator()
|
||||
self.view_menu.addAction(self.action_pick_random)
|
||||
for ac in self.history_actions:
|
||||
self.view_menu.removeAction(ac)
|
||||
self.history_actions = []
|
||||
history = db.prefs.get('gui_view_history', [])
|
||||
if history:
|
||||
self.view_menu.addSeparator()
|
||||
self.view_menu.insertAction(self.clear_sep2, self.clear_sep1)
|
||||
self.history_actions.append(self.clear_sep1)
|
||||
for id_, title in history:
|
||||
ac = HistoryAction(id_, title, self.view_menu)
|
||||
self.view_menu.addAction(ac)
|
||||
self.view_menu.insertAction(self.clear_sep2, ac)
|
||||
ac.view_historical.connect(self.view_historical)
|
||||
self.view_menu.addSeparator()
|
||||
self.view_menu.addAction(self.clear_history_action)
|
||||
self.history_actions.append(ac)
|
||||
|
||||
def clear_history(self):
|
||||
db = self.gui.current_db
|
||||
@ -207,7 +204,7 @@ class ViewAction(InterfaceAction):
|
||||
self._view_books([index])
|
||||
|
||||
def view_random(self, *args):
|
||||
self.gui.iactions['Choose Library'].pick_random()
|
||||
self.gui.iactions['Pick Random Book'].pick_random()
|
||||
self._view_books([self.gui.library_view.currentIndex()])
|
||||
|
||||
def _view_calibre_books(self, ids):
|
||||
|
@ -239,7 +239,7 @@ class DBAdder(QObject): # {{{
|
||||
|
||||
class Adder(QObject): # {{{
|
||||
|
||||
ADD_TIMEOUT = 600 # seconds
|
||||
ADD_TIMEOUT = 900 # seconds (15 minutes)
|
||||
|
||||
def __init__(self, parent, db, callback, spare_server=None):
|
||||
QObject.__init__(self, parent)
|
||||
|
@ -23,6 +23,8 @@ from calibre.gui2 import (config, open_local_file, open_url, pixmap_to_data,
|
||||
gprefs)
|
||||
from calibre.utils.icu import sort_key
|
||||
from calibre.utils.formatter import EvalFormatter
|
||||
from calibre.utils.date import is_date_undefined
|
||||
from calibre.utils.localization import calibre_langcode_to_name
|
||||
|
||||
def render_html(mi, css, vertical, widget, all_fields=False): # {{{
|
||||
table = render_data(mi, all_fields=all_fields,
|
||||
@ -151,6 +153,12 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
|
||||
authors.append(aut)
|
||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name,
|
||||
u' & '.join(authors))))
|
||||
elif field == 'languages':
|
||||
if not mi.languages:
|
||||
continue
|
||||
names = filter(None, map(calibre_langcode_to_name, mi.languages))
|
||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name,
|
||||
u', '.join(names))))
|
||||
else:
|
||||
val = mi.format_field(field)[-1]
|
||||
if val is None:
|
||||
@ -163,6 +171,10 @@ def render_data(mi, use_roman_numbers=True, all_fields=False):
|
||||
val = _('Book %(sidx)s of <span class="series_name">%(series)s</span>')%dict(
|
||||
sidx=fmt_sidx(sidx, use_roman=use_roman_numbers),
|
||||
series=prepare_string_for_xml(getattr(mi, field)))
|
||||
elif metadata['datatype'] == 'datetime':
|
||||
aval = getattr(mi, field)
|
||||
if is_date_undefined(aval):
|
||||
continue
|
||||
|
||||
ans.append((field, u'<td class="title">%s</td><td>%s</td>'%(name, val)))
|
||||
|
||||
|
@ -72,10 +72,11 @@ class HeuristicsWidget(Widget, Ui_Form):
|
||||
return True
|
||||
|
||||
def load_histories(self):
|
||||
val = unicode(self.opt_replace_scene_breaks.currentText())
|
||||
|
||||
self.opt_replace_scene_breaks.clear()
|
||||
self.opt_replace_scene_breaks.lineEdit().setText('')
|
||||
|
||||
val = unicode(self.opt_replace_scene_breaks.currentText())
|
||||
rssb_hist = gprefs.get('replace_scene_breaks_history', self.rssb_defaults)
|
||||
if val in rssb_hist:
|
||||
del rssb_hist[rssb_hist.index(val)]
|
||||
|
@ -6,11 +6,9 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import Qt
|
||||
|
||||
from calibre.gui2.convert.mobi_output_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
from calibre.gui2.widgets import FontFamilyModel
|
||||
|
||||
font_family_model = None
|
||||
|
||||
@ -26,11 +24,13 @@ class PluginWidget(Widget, Ui_Form):
|
||||
['prefer_author_sort', 'rescale_images', 'toc_title',
|
||||
'mobi_ignore_margins', 'mobi_toc_at_start',
|
||||
'dont_compress', 'no_inline_toc',
|
||||
'masthead_font','personal_doc', 'mobi_navpoints_only_deepest']
|
||||
'personal_doc']#, 'mobi_navpoints_only_deepest']
|
||||
)
|
||||
from calibre.utils.fonts import fontconfig
|
||||
self.db, self.book_id = db, book_id
|
||||
|
||||
'''
|
||||
from calibre.utils.fonts import fontconfig
|
||||
|
||||
global font_family_model
|
||||
if font_family_model is None:
|
||||
font_family_model = FontFamilyModel()
|
||||
@ -46,9 +46,11 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
self.font_family_model = font_family_model
|
||||
self.opt_masthead_font.setModel(self.font_family_model)
|
||||
'''
|
||||
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
||||
'''
|
||||
def set_value_handler(self, g, val):
|
||||
if unicode(g.objectName()) in 'opt_masthead_font':
|
||||
idx = -1
|
||||
@ -59,3 +61,4 @@ class PluginWidget(Widget, Ui_Form):
|
||||
g.setCurrentIndex(idx)
|
||||
return True
|
||||
return False
|
||||
'''
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>521</width>
|
||||
<height>331</height>
|
||||
<height>342</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -55,22 +55,12 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Kindle options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Periodical masthead font:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="opt_masthead_font"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
@ -101,7 +91,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="9" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@ -128,13 +118,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_mobi_navpoints_only_deepest">
|
||||
<property name="text">
|
||||
<string>Use only &lowest level of items in the TOC for chapter-to-chapter navigation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|