mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-10-31 10:37:00 -04:00 
			
		
		
		
	GwR patches for 'change_justification'
This commit is contained in:
		
						commit
						a8db54b6fa
					
				
							
								
								
									
										
											BIN
										
									
								
								resources/images/news/kurier.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/news/kurier.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 658 B | 
							
								
								
									
										
											BIN
										
									
								
								resources/images/news/virtualshackles.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/images/news/virtualshackles.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.5 KiB | 
| @ -119,5 +119,7 @@ class Guardian(BasicNewsRecipe): | ||||
|             raise NotImplementedError | ||||
| 
 | ||||
| 
 | ||||
|     def postprocess_html(self,soup,first): | ||||
|         return soup.findAll('html')[0] | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										50
									
								
								resources/recipes/kurier.recipe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								resources/recipes/kurier.recipe
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| __license__   = 'GPL v3' | ||||
| __copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>' | ||||
| ''' | ||||
| kurier.at | ||||
| ''' | ||||
| 
 | ||||
| from calibre.web.feeds.news import BasicNewsRecipe | ||||
| 
 | ||||
| class Kurier(BasicNewsRecipe): | ||||
|     title                 = 'Kurier' | ||||
|     __author__            = 'Darko Miletic' | ||||
|     description           = 'News from Austria' | ||||
|     publisher             = 'KURIER' | ||||
|     category              = 'news, politics, Austria' | ||||
|     oldest_article        = 2 | ||||
|     max_articles_per_feed = 200 | ||||
|     no_stylesheets        = True | ||||
|     encoding              = 'cp1252' | ||||
|     use_embedded_content  = False | ||||
|     language              = 'de_AT' | ||||
|     remove_empty_feeds    = True | ||||
|     publication_type      = 'newspaper' | ||||
|     extra_css             = ' body{font-family: Verdana,Helvetica,sans-serif } img{margin-bottom: 0.4em} .bild_us{font-size: x-small} ' | ||||
| 
 | ||||
|     conversion_options = { | ||||
|                           'comment'   : description | ||||
|                         , 'tags'      : category | ||||
|                         , 'publisher' : publisher | ||||
|                         , 'language'  : language | ||||
|                         } | ||||
| 
 | ||||
|     remove_tags = [dict(attrs={'class':['contenttabs','drucken','versenden','leserbrief','kommentieren','addthis_button']})] | ||||
|     keep_only_tags    = [dict(attrs={'id':'content'})] | ||||
|     remove_tags_after = dict(attrs={'id':'author'}) | ||||
|     remove_attributes = ['width','height'] | ||||
| 
 | ||||
|     feeds = [ | ||||
|                (u'Nachrichten', u'http://kurier.at/rss/nachrichten_nachrichten_rss.xml'   ) | ||||
|               ,(u'Techno'     , u'http://kurier.at/rss/techno_techno_rss.xml'   ) | ||||
|               ,(u'Wirtschaft' , u'http://kurier.at/rss/wirtschaft_wirtschaft_rss.xml'   ) | ||||
|               ,(u'Kultur'     , u'http://kurier.at/rss/kultur_kultur_rss.xml'   ) | ||||
|               ,(u'Freizeit'   , u'http://kurier.at/rss/freizeit_freizeit_rss.xml'   ) | ||||
|               ,(u'Wetter'     , u'http://kurier.at/rss/oewetter_rss.xml'   ) | ||||
|               ,(u'Verkehr'    , u'http://kurier.at/rss/verkehr_rss.xml'   ) | ||||
|             ] | ||||
| 
 | ||||
|     def preprocess_html(self, soup): | ||||
|         for item in soup.findAll(style=True): | ||||
|             del item['style'] | ||||
|         return self.adeify_images(soup) | ||||
| @ -22,21 +22,36 @@ class LaRepublica(BasicNewsRecipe): | ||||
|     language       = 'it' | ||||
|     timefmt        = '[%a, %d %b, %Y]' | ||||
| 
 | ||||
|     oldest_article = 1 | ||||
|     oldest_article = 5 | ||||
|     max_articles_per_feed = 100 | ||||
|     use_embedded_content  = False | ||||
|     recursion             = 10 | ||||
| 
 | ||||
|     remove_javascript = True | ||||
|     def get_article_url(self, article): | ||||
|         link = article.get('id', article.get('guid', None)) | ||||
|         if link is None: | ||||
|             return article | ||||
|         return link | ||||
| 
 | ||||
|     keep_only_tags     = [dict(name='div', attrs={'class':'articolo'}), | ||||
|                           dict(name='div', attrs={'class':'body-text'}), | ||||
|                           dict(name='div', attrs={'class':'page-content'}), | ||||
|                           dict(name='div', attrs={'id':'contA'}) | ||||
|                          ] | ||||
| 
 | ||||
|     keep_only_tags     = [dict(name='div', attrs={'class':'articolo'})] | ||||
| 
 | ||||
|     remove_tags        = [ | ||||
|                             dict(name=['object','link']), | ||||
|                             dict(name='span',attrs={'class':'linkindice'}), | ||||
|                             dict(name='div',attrs={'class':'bottom-mobile'}), | ||||
|                             dict(name='div',attrs={'id':['rssdiv','blocco']}) | ||||
|                             dict(name='div', attrs={'class':'bottom-mobile'}), | ||||
|                             dict(name='div', attrs={'id':['rssdiv','blocco']}), | ||||
|                             dict(name='div', attrs={'class':'utility'}), | ||||
|                             dict(name='div', attrs={'class':'generalbox'}) | ||||
|                          ] | ||||
|     remove_tags_after = [ | ||||
|                             dict(name='div',attrs={'id':'ugc_linkUpload'}) | ||||
|                         ] | ||||
| 
 | ||||
|     feeds          = [ | ||||
|                        (u'Repubblica Rilievo', u'http://www.repubblica.it/rss/homepage/rss2.0.xml'), | ||||
| @ -48,8 +63,12 @@ class LaRepublica(BasicNewsRecipe): | ||||
|                        (u'Repubblica Tecnologia', u'http://www.repubblica.it/rss/tecnologia/rss2.0.xml'), | ||||
|                        (u'Repubblica Scuola e Universita', u'http://www.repubblica.it/rss/scuola_e_universita/rss2.0.xml'), | ||||
|                        (u'Repubblica Ambiente', u'http://www.repubblica.it/rss/ambiente/rss2.0.xml'), | ||||
| 		       (u'Repubblica Cultura', u'http://www.repubblica.it/rss/spettacoli_e_cultura/rss2.0.xml'), | ||||
| 		       (u'Repubblica Persone', u'http://www.repubblica.it/rss/persone/rss2.0.xml'), | ||||
| 		       (u'Repubblica Sport', u'http://www.repubblica.it/rss/sport/rss2.0.xml'), | ||||
| 		       (u'Repubblica Calcio', u'http://www.repubblica.it/rss/sport/calcio/rss2.0.xml') | ||||
|                      ] | ||||
|                        (u'Repubblica Cultura', u'http://www.repubblica.it/rss/spettacoli_e_cultura/rss2.0.xml'), | ||||
|                        (u'Repubblica Persone', u'http://www.repubblica.it/rss/persone/rss2.0.xml'), | ||||
|                        (u'Repubblica Sport', u'http://www.repubblica.it/rss/sport/rss2.0.xml'), | ||||
|                        (u'Repubblica Calcio', u'http://www.repubblica.it/rss/sport/calcio/rss2.0.xml'), | ||||
|                        (u'Repubblica Motori', u'http://www.repubblica.it/rss/motori/rss2.0.xml'), | ||||
|                        (u'Repubblica Roma', u'http://roma.repubblica.it/rss/rss2.0.xml'), | ||||
|                        (u'Repubblica Torino', u'http://torino.repubblica.it/rss/rss2.0.xml') | ||||
|                       ] | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										18
									
								
								resources/recipes/npr_music_blogs.recipe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								resources/recipes/npr_music_blogs.recipe
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| from calibre.web.feeds.news import BasicNewsRecipe | ||||
| 
 | ||||
| class nprmusic(BasicNewsRecipe): | ||||
|     title = 'NPR Music Blogs' | ||||
|     __author__ = 'cix3' | ||||
|     timefmt = ' [%b %d, %Y]' | ||||
|     language = 'en' | ||||
| 
 | ||||
|     oldest_article = 30 | ||||
|     max_articles_per_feed = 100 | ||||
|     no_stylesheets = True | ||||
| 
 | ||||
|     remove_tags = [dict(name='div', attrs={'id':['logo', 'comments', 'related_objects', 'inset module', 'footer', 'strip_control', 'header', 'navigation']}), dict(name='hr'), dict(name='img')] | ||||
| 
 | ||||
|     feeds = [ | ||||
|         ('A Blog Supreme', 'http://www.npr.org/blogs/ablogsupreme/index.xml'), | ||||
|         ('All Songs Considered', 'http://www.npr.org/blogs/allsongs/index.xml'), | ||||
|         ('Monitor Mix', 'http://www.npr.org/blogs/monitormix/index.xml')] | ||||
| @ -377,8 +377,9 @@ class USAToday(BasicNewsRecipe): | ||||
|             if byline: | ||||
|                 byline['class'] = 'byline' | ||||
|                 # Replace comma with middot | ||||
|                 byline.contents[0].replaceWith(re.sub(","," ·", byline.renderContents())) | ||||
|                 return byline.renderContents() | ||||
|                 byline.contents[0].replaceWith(re.sub(u",", u" ·", | ||||
|                     byline.renderContents(encoding=None))) | ||||
|                 return byline.renderContents(encoding=None) | ||||
|             else : | ||||
|                 paras = soup.findAll(text=True) | ||||
|                 for para in paras: | ||||
|  | ||||
							
								
								
									
										33
									
								
								resources/recipes/virtualshackles.recipe
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								resources/recipes/virtualshackles.recipe
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | ||||
| __license__   = 'GPL v3' | ||||
| __copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>' | ||||
| ''' | ||||
| www.virtualshackles.com | ||||
| ''' | ||||
| 
 | ||||
| from calibre.web.feeds.recipes import BasicNewsRecipe | ||||
| 
 | ||||
| class Virtualshackles(BasicNewsRecipe): | ||||
|     title                  = 'Virtual Shackles' | ||||
|     __author__             = 'Darko Miletic' | ||||
|     description            = "The adventures of Orion and Jack, making games they'd never play for people they don't like." | ||||
|     category               = 'virtual shackles, virtualshackles, games, webcomic, comic, video game, orion, jack' | ||||
|     oldest_article         = 10 | ||||
|     max_articles_per_feed  = 100 | ||||
|     no_stylesheets         = True | ||||
|     use_embedded_content   = True | ||||
|     encoding               = 'cp1252' | ||||
|     publisher              = 'Virtual Shackles' | ||||
|     language               = 'en' | ||||
|     publication_type       = 'comic' | ||||
| 
 | ||||
|     conversion_options = { | ||||
|                              'comments'  : description | ||||
|                             ,'tags'      : category | ||||
|                             ,'language'  : language | ||||
|                             ,'publisher' : publisher | ||||
|                          } | ||||
| 
 | ||||
|     feeds = [(u'Virtual Shackles', u'http://feeds2.feedburner.com/virtualshackles' )] | ||||
| 
 | ||||
|     def preprocess_html(self, soup): | ||||
|         return self.adeify_images(soup) | ||||
| @ -399,38 +399,49 @@ def my_unichr(num): | ||||
|     except ValueError: | ||||
|         return u'?' | ||||
| 
 | ||||
| def entity_to_unicode(match, exceptions=[], encoding='cp1252'): | ||||
| def entity_to_unicode(match, exceptions=[], encoding='cp1252', | ||||
|         result_exceptions={}): | ||||
|     ''' | ||||
|     @param match: A match object such that '&'+match.group(1)';' is the entity. | ||||
|     @param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' | ||||
|     @param encoding: The encoding to use to decode numeric entities between 128 and 256. | ||||
|     :param match: A match object such that '&'+match.group(1)';' is the entity. | ||||
| 
 | ||||
|     :param exceptions: A list of entities to not convert (Each entry is the name of the entity, for e.g. 'apos' or '#1234' | ||||
| 
 | ||||
|     :param encoding: The encoding to use to decode numeric entities between 128 and 256. | ||||
|     If None, the Unicode UCS encoding is used. A common encoding is cp1252. | ||||
| 
 | ||||
|     :param result_exceptions: A mapping of characters to entities. If the result | ||||
|     is in result_exceptions, result_exception[result] is returned instead. | ||||
|     Convenient way to specify exception for things like < or > that can be | ||||
|     specified by various actual entities. | ||||
|     ''' | ||||
|     def check(ch): | ||||
|         return result_exceptions.get(ch, ch) | ||||
| 
 | ||||
|     ent = match.group(1) | ||||
|     if ent in exceptions: | ||||
|         return '&'+ent+';' | ||||
|     if ent == 'apos': | ||||
|         return "'" | ||||
|         return check("'") | ||||
|     if ent == 'hellips': | ||||
|         ent = 'hellip' | ||||
|     if ent.startswith(u'#x'): | ||||
|     if ent.lower().startswith(u'#x'): | ||||
|         num = int(ent[2:], 16) | ||||
|         if encoding is None or num > 255: | ||||
|             return my_unichr(num) | ||||
|         return chr(num).decode(encoding) | ||||
|             return check(my_unichr(num)) | ||||
|         return check(chr(num).decode(encoding)) | ||||
|     if ent.startswith(u'#'): | ||||
|         try: | ||||
|             num = int(ent[1:]) | ||||
|         except ValueError: | ||||
|             return '&'+ent+';' | ||||
|         if encoding is None or num > 255: | ||||
|             return my_unichr(num) | ||||
|             return check(my_unichr(num)) | ||||
|         try: | ||||
|             return chr(num).decode(encoding) | ||||
|             return check(chr(num).decode(encoding)) | ||||
|         except UnicodeDecodeError: | ||||
|             return my_unichr(num) | ||||
|             return check(my_unichr(num)) | ||||
|     try: | ||||
|         return my_unichr(name2codepoint[ent]) | ||||
|         return check(my_unichr(name2codepoint[ent])) | ||||
|     except KeyError: | ||||
|         return '&'+ent+';' | ||||
| 
 | ||||
|  | ||||
| @ -444,7 +444,7 @@ from calibre.devices.eslick.driver import ESLICK | ||||
| from calibre.devices.nuut2.driver import NUUT2 | ||||
| from calibre.devices.iriver.driver import IRIVER_STORY | ||||
| from calibre.devices.binatone.driver import README | ||||
| from calibre.devices.hanvon.driver import N516, EB511 | ||||
| from calibre.devices.hanvon.driver import N516, EB511, ALEX | ||||
| from calibre.devices.edge.driver import EDGE | ||||
| from calibre.devices.teclast.driver import TECLAST_K3 | ||||
| from calibre.devices.sne.driver import SNE | ||||
| @ -526,7 +526,8 @@ plugins += [ | ||||
|     ELONEX, | ||||
|     TECLAST_K3, | ||||
|     EDGE, | ||||
|     SNE | ||||
|     SNE, | ||||
|     ALEX | ||||
| ] | ||||
| plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ | ||||
|                                         x.__name__.endswith('MetadataReader')] | ||||
|  | ||||
| @ -34,6 +34,22 @@ class N516(USBMS): | ||||
|     EBOOK_DIR_MAIN = 'e_book' | ||||
|     SUPPORTS_SUB_DIRS = True | ||||
| 
 | ||||
| class ALEX(N516): | ||||
| 
 | ||||
|     name = 'Alex driver' | ||||
|     gui_name = 'SpringDesign Alex' | ||||
|     description    = _('Communicate with the SpringDesign Alex eBook reader.') | ||||
|     author         = 'Kovid Goyal' | ||||
| 
 | ||||
|     FORMATS     = ['epub', 'pdf'] | ||||
|     VENDOR_NAME      = 'ALEX' | ||||
|     WINDOWS_MAIN_MEM = 'READER' | ||||
| 
 | ||||
|     MAIN_MEMORY_VOLUME_LABEL  = 'Alex Internal Memory' | ||||
| 
 | ||||
|     EBOOK_DIR_MAIN = 'eBooks' | ||||
|     SUPPORTS_SUB_DIRS = True | ||||
| 
 | ||||
| class EB511(USBMS): | ||||
|     name           = 'Elonex EB 511 driver' | ||||
|     gui_name       = 'EB 511' | ||||
|  | ||||
| @ -322,7 +322,7 @@ class ComicInput(InputFormatPlugin): | ||||
|         ('margin_bottom', 0, OptionRecommendation.HIGH), | ||||
|         ('insert_blank_line', False, OptionRecommendation.HIGH), | ||||
|         ('remove_paragraph_spacing',  False, OptionRecommendation.HIGH), | ||||
|         ('dont_justify', True, OptionRecommendation.HIGH), | ||||
|         ('change_justification', 'left', OptionRecommendation.HIGH), | ||||
|         ('dont_split_on_pagebreaks', True, OptionRecommendation.HIGH), | ||||
|         ('chapter', None, OptionRecommendation.HIGH), | ||||
|         ('page_breaks_brefore', None, OptionRecommendation.HIGH), | ||||
|  | ||||
| @ -124,7 +124,7 @@ def add_pipeline_options(parser, plumber): | ||||
|                       'linearize_tables', | ||||
|                       'extra_css', | ||||
|                       'margin_top', 'margin_left', 'margin_right', | ||||
|                       'margin_bottom', 'dont_justify', | ||||
|                       'margin_bottom', 'change_justification', | ||||
|                       'insert_blank_line', 'remove_paragraph_spacing','remove_paragraph_spacing_indent_size', | ||||
|                       'asciiize', 'remove_header', 'header_regex', | ||||
|                       'remove_footer', 'footer_regex', | ||||
|  | ||||
| @ -299,12 +299,13 @@ OptionRecommendation(name='margin_right', | ||||
|         help=_('Set the right margin in pts. Default is %default. ' | ||||
|             'Note: 72 pts equals 1 inch')), | ||||
| 
 | ||||
| OptionRecommendation(name='dont_justify', | ||||
|         recommended_value=False, level=OptionRecommendation.LOW, | ||||
|         help=_('Do not force text to be justified in output. Whether text ' | ||||
|             'is actually displayed justified or not depends on whether ' | ||||
|             'the ebook format and reading device support justification.') | ||||
|         ), | ||||
| OptionRecommendation(name='change_justification', | ||||
|         recommended_value='original', level=OptionRecommendation.LOW, | ||||
|         choices=['left','justify','original'], | ||||
|         help=_('Specify optional justification override. A value of ' | ||||
|                '"left" or "justify" overrides default justification.' | ||||
|                'A value of ' | ||||
|                '"original" uses existing alignment.')), | ||||
| 
 | ||||
| OptionRecommendation(name='remove_paragraph_spacing', | ||||
|         recommended_value=False, level=OptionRecommendation.LOW, | ||||
|  | ||||
| @ -130,7 +130,7 @@ class LRFOutput(OutputFormatPlugin): | ||||
|     ]) | ||||
| 
 | ||||
|     recommendations = set([ | ||||
|         ('dont_justify', True, OptionRecommendation.HIGH), | ||||
|         ('change_justification', 'original', OptionRecommendation.HIGH), | ||||
|         ]) | ||||
| 
 | ||||
|     def convert_images(self, pages, opts, wide): | ||||
|  | ||||
| @ -303,7 +303,12 @@ class MobiReader(object): | ||||
|         for pat in ENCODING_PATS: | ||||
|             self.processed_html = pat.sub('', self.processed_html) | ||||
|         e2u = functools.partial(entity_to_unicode, | ||||
|             exceptions=['lt', 'gt', 'amp', 'apos', 'quot', '#60', '#62']) | ||||
|             result_exceptions={ | ||||
|                 '<' : u'<', | ||||
|                 '>' : u'>', | ||||
|                 '&' : u'&', | ||||
|                 '"' : u'"', | ||||
|                 "'" : u'''}) | ||||
|         self.processed_html = re.sub(r'&(\S+?);', e2u, | ||||
|             self.processed_html) | ||||
|         self.extract_images(processed_records, output_dir) | ||||
| @ -619,6 +624,7 @@ class MobiReader(object): | ||||
|                 opf.cover = None | ||||
| 
 | ||||
|         cover = opf.cover | ||||
|         cover_copied = None | ||||
|         if cover is not None: | ||||
|             cover = cover.replace('/', os.sep) | ||||
|             if os.path.exists(cover): | ||||
| @ -626,13 +632,19 @@ class MobiReader(object): | ||||
|                 if os.path.exists(ncover): | ||||
|                     os.remove(ncover) | ||||
|                 shutil.copyfile(cover, ncover) | ||||
|             opf.cover = ncover.replace(os.sep, '/') | ||||
|                 cover_copied = os.path.abspath(ncover) | ||||
|                 opf.cover = ncover.replace(os.sep, '/') | ||||
| 
 | ||||
|         manifest = [(htmlfile, 'application/xhtml+xml'), | ||||
|             (os.path.abspath('styles.css'), 'text/css')] | ||||
|         bp = os.path.dirname(htmlfile) | ||||
|         added = set([]) | ||||
|         for i in getattr(self, 'image_names', []): | ||||
|             manifest.append((os.path.join(bp, 'images/', i), 'image/jpeg')) | ||||
|             path = os.path.join(bp, 'images', i) | ||||
|             added.add(path) | ||||
|             manifest.append((path, 'image/jpeg')) | ||||
|         if cover_copied is not None: | ||||
|             manifest.append((cover_copied, 'image/jpeg')) | ||||
| 
 | ||||
|         opf.create_manifest(manifest) | ||||
|         opf.create_spine([os.path.basename(htmlfile)]) | ||||
|  | ||||
| @ -212,11 +212,12 @@ class EbookIterator(object): | ||||
| 
 | ||||
|         cover = self.opf.cover | ||||
|         if self.ebook_ext in ('lit', 'mobi', 'prc', 'opf') and cover: | ||||
|             cfile = os.path.join(os.path.dirname(self.spine[0]), | ||||
|                     'calibre_iterator_cover.html') | ||||
|             chtml = (TITLEPAGE%cover).encode('utf-8') | ||||
|             cfile = os.path.join(self.base, 'calibre_iterator_cover.html') | ||||
|             chtml = (TITLEPAGE%os.path.relpath(cover, self.base).replace(os.sep, | ||||
|                 '/')).encode('utf-8') | ||||
|             open(cfile, 'wb').write(chtml) | ||||
|             self.spine[0:0] = [SpineItem(cfile)] | ||||
|             self.spine[0:0] = [SpineItem(cfile, | ||||
|                 mime_type='application/xhtml+xml')] | ||||
|             self.delete_on_exit.append(cfile) | ||||
| 
 | ||||
|         if self.opf.path_to_html_toc is not None and \ | ||||
|  | ||||
| @ -318,8 +318,8 @@ class Stylizer(object): | ||||
|         if text == 'inherit': | ||||
|             style['text-align'] = 'inherit' | ||||
|         else: | ||||
|             if text in ('left', 'justify'): | ||||
|                 val = 'left' if self.opts.dont_justify else 'justify' | ||||
|             if text in ('left', 'justify') and self.opts.change_justification in ('left', 'justify'): | ||||
|                 val = self.opts.change_justification | ||||
|                 style['text-align'] = val | ||||
|             else: | ||||
|                 style['text-align'] = text | ||||
|  | ||||
| @ -138,8 +138,8 @@ class CSSFlattener(object): | ||||
|                     float(self.context.margin_left)) | ||||
|             bs.append('margin-right : %fpt'%\ | ||||
|                     float(self.context.margin_right)) | ||||
|             bs.append('text-align: '+ \ | ||||
|                     ('left' if self.context.dont_justify else 'justify')) | ||||
|             if self.context.change_justification != 'original': | ||||
|                 bs.append('text-align: '+ self.context.change_justification) | ||||
|             body.set('style', '; '.join(bs)) | ||||
|             stylizer = Stylizer(html, item.href, self.oeb, self.context, profile, | ||||
|                     user_css=self.context.extra_css, | ||||
|  | ||||
| @ -19,7 +19,7 @@ class LookAndFeelWidget(Widget, Ui_Form): | ||||
| 
 | ||||
|     def __init__(self, parent, get_option, get_help, db=None, book_id=None): | ||||
|         Widget.__init__(self, parent, 'look_and_feel', | ||||
|                 ['dont_justify', 'extra_css', 'base_font_size', | ||||
|                 ['change_justification', 'extra_css', 'base_font_size', | ||||
|                     'font_size_mapping', 'line_height', | ||||
|                     'linearize_tables', | ||||
|                     'disable_font_rescaling', 'insert_blank_line', | ||||
|  | ||||
| @ -84,7 +84,7 @@ | ||||
|         <string>...</string> | ||||
|        </property> | ||||
|        <property name="icon"> | ||||
|         <iconset resource="../../../../resources/images.qrc"> | ||||
|         <iconset> | ||||
|          <normaloff>:/images/wizard.svg</normaloff>:/images/wizard.svg</iconset> | ||||
|        </property> | ||||
|        <property name="iconSize"> | ||||
| @ -181,21 +181,7 @@ | ||||
|      </item> | ||||
|     </layout> | ||||
|    </item> | ||||
|    <item row="6" column="0"> | ||||
|     <widget class="QCheckBox" name="opt_insert_blank_line"> | ||||
|      <property name="text"> | ||||
|       <string>Insert &blank line</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="7" column="0"> | ||||
|     <widget class="QCheckBox" name="opt_dont_justify"> | ||||
|      <property name="text"> | ||||
|       <string>No text &justification</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="8" column="0"> | ||||
|     <widget class="QCheckBox" name="opt_linearize_tables"> | ||||
|      <property name="text"> | ||||
|       <string>&Linearize tables</string> | ||||
| @ -221,6 +207,42 @@ | ||||
|      </layout> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="8" column="0"> | ||||
|     <widget class="QCheckBox" name="opt_insert_blank_line"> | ||||
|      <property name="text"> | ||||
|       <string>Insert &blank line</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="6" column="0"> | ||||
|     <widget class="QLabel" name="label_5"> | ||||
|      <property name="text"> | ||||
|       <string>Text justification:</string> | ||||
|      </property> | ||||
|     </widget> | ||||
|    </item> | ||||
|    <item row="6" column="2"> | ||||
|     <widget class="QComboBox" name="opt_change_justification"> | ||||
|      <property name="currentIndex"> | ||||
|       <number>2</number> | ||||
|      </property> | ||||
|      <item> | ||||
|       <property name="text"> | ||||
|        <string>justify</string> | ||||
|       </property> | ||||
|      </item> | ||||
|      <item> | ||||
|       <property name="text"> | ||||
|        <string>left</string> | ||||
|       </property> | ||||
|      </item> | ||||
|      <item> | ||||
|       <property name="text"> | ||||
|        <string>original</string> | ||||
|       </property> | ||||
|      </item> | ||||
|     </widget> | ||||
|    </item> | ||||
|   </layout> | ||||
|  </widget> | ||||
|  <resources> | ||||
|  | ||||
| @ -29,6 +29,7 @@ class SchedulerDialog(QDialog, Ui_Dialog): | ||||
|         self.recipe_model.do_refresh() | ||||
| 
 | ||||
|         self.search = SearchBox2(self) | ||||
|         self.search.setMinimumContentsLength(25) | ||||
|         self.search.initialize('scheduler_search_history') | ||||
|         self.recipe_box.layout().insertWidget(0, self.search) | ||||
|         self.connect(self.search, SIGNAL('search(PyQt_PyObject,PyQt_PyObject)'), | ||||
|  | ||||
| @ -7,9 +7,9 @@ from math import cos, sin, pi | ||||
| from contextlib import closing | ||||
| 
 | ||||
| from PyQt4.QtGui import QTableView, QAbstractItemView, QColor, \ | ||||
|                         QItemDelegate, QPainterPath, QLinearGradient, QBrush, \ | ||||
|                         QPen, QStyle, QPainter, \ | ||||
|                         QImage, QApplication, QMenu, \ | ||||
|                         QPainterPath, QLinearGradient, QBrush, \ | ||||
|                         QPen, QStyle, QPainter, QStyleOptionViewItemV4, \ | ||||
|                         QImage, QMenu, \ | ||||
|                         QStyledItemDelegate, QCompleter | ||||
| from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, pyqtSignal, \ | ||||
|                          SIGNAL, QObject, QSize, QModelIndex, QDate | ||||
| @ -28,14 +28,15 @@ from calibre.ebooks.metadata import string_to_authors, fmt_sidx, \ | ||||
| from calibre.utils.config import tweaks | ||||
| from calibre.utils.date import dt_factory, qt_to_dt, isoformat | ||||
| 
 | ||||
| class LibraryDelegate(QItemDelegate): | ||||
| class LibraryDelegate(QStyledItemDelegate): | ||||
|     COLOR    = QColor("blue") | ||||
|     SIZE     = 16 | ||||
|     PEN      = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) | ||||
| 
 | ||||
|     def __init__(self, parent): | ||||
|         QItemDelegate.__init__(self, parent) | ||||
|         QStyledItemDelegate.__init__(self, parent) | ||||
|         self._parent = parent | ||||
|         self.dummy = QModelIndex() | ||||
|         self.star_path = QPainterPath() | ||||
|         self.star_path.moveTo(90, 50) | ||||
|         for i in range(1, 5): | ||||
| @ -54,6 +55,9 @@ class LibraryDelegate(QItemDelegate): | ||||
|         return QSize(5*(self.SIZE), self.SIZE+4) | ||||
| 
 | ||||
|     def paint(self, painter, option, index): | ||||
|         style = self._parent.style() | ||||
|         option = QStyleOptionViewItemV4(option) | ||||
|         self.initStyleOption(option, self.dummy) | ||||
|         num = index.model().data(index, Qt.DisplayRole).toInt()[0] | ||||
|         def draw_star(): | ||||
|             painter.save() | ||||
| @ -66,11 +70,10 @@ class LibraryDelegate(QItemDelegate): | ||||
| 
 | ||||
|         painter.save() | ||||
|         if hasattr(QStyle, 'CE_ItemViewItem'): | ||||
|             QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, | ||||
|             style.drawControl(QStyle.CE_ItemViewItem, option, | ||||
|                     painter, self._parent) | ||||
|         elif option.state & QStyle.State_Selected: | ||||
|             painter.fillRect(option.rect, option.palette.highlight()) | ||||
|         self.drawFocus(painter, option, option.rect) | ||||
|         try: | ||||
|             painter.setRenderHint(QPainter.Antialiasing) | ||||
|             painter.setClipRect(option.rect) | ||||
| @ -89,7 +92,7 @@ class LibraryDelegate(QItemDelegate): | ||||
|         painter.restore() | ||||
| 
 | ||||
|     def createEditor(self, parent, option, index): | ||||
|         sb = QItemDelegate.createEditor(self, parent, option, index) | ||||
|         sb = QStyledItemDelegate.createEditor(self, parent, option, index) | ||||
|         sb.setMinimum(0) | ||||
|         sb.setMaximum(5) | ||||
|         return sb | ||||
|  | ||||
| @ -194,6 +194,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): | ||||
|         self.tool_bar2.insertSeparator(self.action_find_next) | ||||
|         self.setFocusPolicy(Qt.StrongFocus) | ||||
|         self.search = SearchBox2(self) | ||||
|         self.search.setMinimumContentsLength(20) | ||||
|         self.search.initialize('viewer_search_history') | ||||
|         self.search.setToolTip(_('Search for text in book')) | ||||
|         self.search.setMinimumWidth(200) | ||||
|  | ||||
| @ -551,7 +551,8 @@ class LineEditECM(object): | ||||
|         self.setText(unicode(self.text()).swapcase()) | ||||
| 
 | ||||
|     def title_case(self): | ||||
|         self.setText(unicode(self.text()).title()) | ||||
|         from calibre.utils.titlecase import titlecase | ||||
|         self.setText(titlecase(unicode(self.text()))) | ||||
| 
 | ||||
| 
 | ||||
| class EnLineEdit(LineEditECM, QLineEdit): | ||||
|  | ||||
| @ -123,10 +123,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): | ||||
|         self.is_case_sensitive = not iswindows and not isosx and \ | ||||
|             not os.path.exists(self.dbpath.replace('metadata.db', 'MeTAdAtA.dB')) | ||||
|         SchemaUpgrade.__init__(self) | ||||
|         CustomColumns.__init__(self) | ||||
|         self.initialize_dynamic() | ||||
| 
 | ||||
|     def initialize_dynamic(self): | ||||
|         CustomColumns.__init__(self) | ||||
|         template = '''\ | ||||
|                 (SELECT {query} FROM books_{table}_link AS link INNER JOIN | ||||
|                     {table} ON(link.{link_col}={table}.id) WHERE link.book=books.id) | ||||
| @ -1428,6 +1428,7 @@ books_series_link      feeds | ||||
|             os.remove(self.dbpath) | ||||
|             shutil.copyfile(dest, self.dbpath) | ||||
|             self.connect() | ||||
|             self.initialize_dynamic() | ||||
|             self.refresh() | ||||
|         if os.path.exists(dest): | ||||
|             os.remove(dest) | ||||
|  | ||||
| @ -81,7 +81,7 @@ Device Integration | ||||
| 
 | ||||
| What devices does |app| support? | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
| At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. | ||||
| At the moment |app| has full support for the SONY PRS 300/500/505/600/700/900, Barnes & Noble Nook, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Entourage Edge, Longshine ShineBook, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Italica, eClicto, Iriver Story, Airis dBook, Hanvon N515, Binatone Readme, Teclast K3, SpringDesign Alex, various Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk. | ||||
| 
 | ||||
| How can I help get my device supported in |app|? | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
							
								
								
									
										94
									
								
								src/calibre/utils/titlecase.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								src/calibre/utils/titlecase.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,94 @@ | ||||
| #!/usr/bin/env python | ||||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| """ | ||||
| Original Perl version by: John Gruber http://daringfireball.net/ 10 May 2008 | ||||
| Python version by Stuart Colville http://muffinresearch.co.uk | ||||
| License: http://www.opensource.org/licenses/mit-license.php | ||||
| """ | ||||
| 
 | ||||
| import re | ||||
| 
 | ||||
| __all__ = ['titlecase'] | ||||
| __version__ = '0.5' | ||||
| 
 | ||||
| SMALL = 'a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\.?|via|vs\.?' | ||||
| PUNCT = r"""!"#$%&'‘()*+,\-./:;?@[\\\]_`{|}~""" | ||||
| 
 | ||||
| SMALL_WORDS = re.compile(r'^(%s)$' % SMALL, re.I) | ||||
| INLINE_PERIOD = re.compile(r'[a-z][.][a-z]', re.I) | ||||
| UC_ELSEWHERE = re.compile(r'[%s]*?[a-zA-Z]+[A-Z]+?' % PUNCT) | ||||
| CAPFIRST = re.compile(r"^[%s]*?([A-Za-z])" % PUNCT) | ||||
| SMALL_FIRST = re.compile(r'^([%s]*)(%s)\b' % (PUNCT, SMALL), re.I) | ||||
| SMALL_LAST = re.compile(r'\b(%s)[%s]?$' % (SMALL, PUNCT), re.I) | ||||
| SUBPHRASE = re.compile(r'([:.;?!][ ])(%s)' % SMALL) | ||||
| APOS_SECOND = re.compile(r"^[dol]{1}['‘]{1}[a-z]+$", re.I) | ||||
| ALL_CAPS = re.compile(r'^[A-Z\s%s]+$' % PUNCT) | ||||
| UC_INITIALS = re.compile(r"^(?:[A-Z]{1}\.{1}|[A-Z]{1}\.{1}[A-Z]{1})+$") | ||||
| MAC_MC = re.compile(r"^([Mm]a?c)(\w+)") | ||||
| 
 | ||||
| def titlecase(text): | ||||
| 
 | ||||
|     """ | ||||
|     Titlecases input text | ||||
| 
 | ||||
|     This filter changes all words to Title Caps, and attempts to be clever | ||||
|     about *un*capitalizing SMALL words like a/an/the in the input. | ||||
| 
 | ||||
|     The list of "SMALL words" which are not capped comes from | ||||
|     the New York Times Manual of Style, plus 'vs' and 'v'. | ||||
| 
 | ||||
|     """ | ||||
| 
 | ||||
|     all_caps = ALL_CAPS.match(text) | ||||
| 
 | ||||
|     words = re.split('\s', text) | ||||
|     line = [] | ||||
|     for word in words: | ||||
|         if all_caps: | ||||
|             if UC_INITIALS.match(word): | ||||
|                 line.append(word) | ||||
|                 continue | ||||
|             else: | ||||
|                 word = word.lower() | ||||
| 
 | ||||
|         if APOS_SECOND.match(word): | ||||
|             word = word.replace(word[0], word[0].upper()) | ||||
|             word = word.replace(word[2], word[2].upper()) | ||||
|             line.append(word) | ||||
|             continue | ||||
|         if INLINE_PERIOD.search(word) or UC_ELSEWHERE.match(word): | ||||
|             line.append(word) | ||||
|             continue | ||||
|         if SMALL_WORDS.match(word): | ||||
|             line.append(word.lower()) | ||||
|             continue | ||||
| 
 | ||||
|         match = MAC_MC.match(word) | ||||
|         if match: | ||||
|             line.append("%s%s" % (match.group(1).capitalize(), | ||||
|                                   match.group(2).capitalize())) | ||||
|             continue | ||||
| 
 | ||||
|         hyphenated = [] | ||||
|         for item in word.split('-'): | ||||
|             hyphenated.append(CAPFIRST.sub(lambda m: m.group(0).upper(), item)) | ||||
|         line.append("-".join(hyphenated)) | ||||
| 
 | ||||
| 
 | ||||
|     result = " ".join(line) | ||||
| 
 | ||||
|     result = SMALL_FIRST.sub(lambda m: '%s%s' % ( | ||||
|         m.group(1), | ||||
|         m.group(2).capitalize() | ||||
|     ), result) | ||||
| 
 | ||||
|     result = SMALL_LAST.sub(lambda m: m.group(0).capitalize(), result) | ||||
| 
 | ||||
|     result = SUBPHRASE.sub(lambda m: '%s%s' % ( | ||||
|         m.group(1), | ||||
|         m.group(2).capitalize() | ||||
|     ), result) | ||||
| 
 | ||||
|     return result | ||||
| 
 | ||||
| @ -113,7 +113,7 @@ class NewsItem(NewsTreeItem): | ||||
|         return NONE | ||||
| 
 | ||||
|     def __cmp__(self, other): | ||||
|         return cmp(self.title, getattr(other, 'title', '')) | ||||
|         return cmp(self.title.lower(), getattr(other, 'title', '').lower()) | ||||
| 
 | ||||
| class RecipeModel(QAbstractItemModel, SearchQueryParser): | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user