home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_866 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  16.5 KB  |  470 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import with_statement
  5. __license__ = 'GPL v3'
  6. __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
  7. __docformat__ = 'restructuredtext en'
  8. import os
  9. import shutil
  10. import traceback
  11. import textwrap
  12. import time
  13. import codecs
  14. from Queue import Empty
  15. from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation
  16. from calibre import extract, CurrentDir, prints
  17. from calibre.ptempfile import PersistentTemporaryDirectory
  18. from calibre.utils.ipc.server import Server
  19. from calibre.utils.ipc.job import ParallelJob
  20.  
  21. def extract_comic(path_to_comic_file):
  22.     tdir = PersistentTemporaryDirectory(suffix = '_comic_extract')
  23.     extract(path_to_comic_file, tdir)
  24.     return tdir
  25.  
  26.  
  27. def find_pages(dir, sort_on_mtime = False, verbose = False):
  28.     extensions = [
  29.         'jpeg',
  30.         'jpg',
  31.         'gif',
  32.         'png']
  33.     pages = []
  34.     for datum in os.walk(dir):
  35.         for name in datum[-1]:
  36.             path = os.path.join(datum[0], name)
  37.             if '__MACOSX' in path:
  38.                 continue
  39.             
  40.             for ext in extensions:
  41.                 if path.lower().endswith('.' + ext):
  42.                     pages.append(path)
  43.                     break
  44.                     continue
  45.             
  46.         
  47.     
  48.     if sort_on_mtime:
  49.         
  50.         comparator = lambda x, y: cmp(os.stat(x).st_mtime, os.stat(y).st_mtime)
  51.     else:
  52.         
  53.         comparator = lambda x, y: cmp(os.path.basename(x), os.path.basename(y))
  54.     pages.sort(cmp = comparator)
  55.     return pages
  56.  
  57.  
  58. class PageProcessor(list):
  59.     
  60.     def __init__(self, path_to_page, dest, opts, num):
  61.         list.__init__(self)
  62.         self.path_to_page = path_to_page
  63.         self.opts = opts
  64.         self.num = num
  65.         self.dest = dest
  66.         self.rotate = False
  67.         self.render()
  68.  
  69.     
  70.     def render(self):
  71.         Image = Image
  72.         import calibre.utils.magick
  73.         img = Image()
  74.         img.open(self.path_to_page)
  75.         (width, height) = img.size
  76.         if self.num == 0:
  77.             thumb = img.clone
  78.             thumb.thumbnail(60, 80)
  79.             thumb.save(os.path.join(self.dest, 'thumbnail.png'))
  80.         
  81.         self.pages = [
  82.             img]
  83.         if width > height:
  84.             if self.opts.landscape:
  85.                 self.rotate = True
  86.             else:
  87.                 split1 = img.clone
  88.                 split2 = img.clone
  89.                 half = int(width / 2)
  90.                 split1.crop(half - 1, height, 0, 0)
  91.                 split2.crop(half - 1, height, half, 0)
  92.                 self.pages = None if self.opts.right2left else [
  93.                     split1,
  94.                     split2]
  95.         
  96.         self.process_pages()
  97.  
  98.     
  99.     def process_pages(self):
  100.         PixelWand = PixelWand
  101.         import calibre.utils.magick
  102.         for i, wand in enumerate(self.pages):
  103.             pw = PixelWand()
  104.             pw.color = 'white'
  105.             wand.set_border_color(pw)
  106.             if self.rotate:
  107.                 wand.rotate(pw, -90)
  108.             
  109.             if not self.opts.disable_trim:
  110.                 wand.trim(1638375 / 100)
  111.             
  112.             wand.set_page(0, 0, 0, 0)
  113.             if not self.opts.dont_normalize:
  114.                 wand.normalize()
  115.             
  116.             (sizex, sizey) = wand.size
  117.             (SCRWIDTH, SCRHEIGHT) = self.opts.output_profile.comic_screen_size
  118.             if self.opts.keep_aspect_ratio:
  119.                 aspect = float(sizex) / float(sizey)
  120.                 if aspect <= float(SCRWIDTH) / float(SCRHEIGHT):
  121.                     newsizey = SCRHEIGHT
  122.                     newsizex = int(newsizey * aspect)
  123.                     deltax = (SCRWIDTH - newsizex) / 2
  124.                     deltay = 0
  125.                 else:
  126.                     newsizex = SCRWIDTH
  127.                     newsizey = int(newsizex / aspect)
  128.                     deltax = 0
  129.                     deltay = (SCRHEIGHT - newsizey) / 2
  130.                 wand.size = (newsizex, newsizey)
  131.                 wand.set_border_color(pw)
  132.                 wand.add_border(pw, deltax, deltay)
  133.             elif self.opts.wide:
  134.                 aspect = float(sizex) / float(sizey)
  135.                 screen_aspect = float(SCRWIDTH) / float(SCRHEIGHT)
  136.                 wscreenx = SCRHEIGHT + 25
  137.                 wscreeny = int(wscreenx / screen_aspect)
  138.                 if aspect <= screen_aspect:
  139.                     newsizey = wscreeny
  140.                     newsizex = int(newsizey * aspect)
  141.                     deltax = (wscreenx - newsizex) / 2
  142.                     deltay = 0
  143.                 else:
  144.                     newsizex = wscreenx
  145.                     newsizey = int(newsizex / aspect)
  146.                     deltax = 0
  147.                     deltay = (wscreeny - newsizey) / 2
  148.                 wand.size = (newsizex, newsizey)
  149.                 wand.set_border_color(pw)
  150.                 wand.add_border(pw, deltax, deltay)
  151.             else:
  152.                 wand.size = (SCRWIDTH, SCRHEIGHT)
  153.             if not self.opts.dont_sharpen:
  154.                 wand.sharpen(0, 1)
  155.             
  156.             if not self.opts.dont_grayscale:
  157.                 wand.type = 'GrayscaleType'
  158.             
  159.             if self.opts.despeckle:
  160.                 wand.despeckle()
  161.             
  162.             wand.quantize(self.opts.colors)
  163.             dest = '%d_%d.%s' % (self.num, i, self.opts.output_format)
  164.             dest = os.path.join(self.dest, dest)
  165.             wand.save(dest + '8')
  166.             os.rename(dest + '8', dest)
  167.             self.append(dest)
  168.         
  169.  
  170.  
  171.  
  172. def render_pages(tasks, dest, opts, notification = (lambda x, y: x)):
  173.     failures = []
  174.     pages = []
  175.     for num, path in tasks:
  176.         
  177.         try:
  178.             pages.extend(PageProcessor(path, dest, opts, num))
  179.             msg = _('Rendered %s') % path
  180.         except:
  181.             failures.append(path)
  182.             msg = _('Failed %s') % path
  183.             if opts.verbose:
  184.                 msg += '\n' + traceback.format_exc()
  185.             
  186.  
  187.         prints(msg)
  188.         notification(0.5, msg)
  189.     
  190.     return (pages, failures)
  191.  
  192.  
  193. class Progress(object):
  194.     
  195.     def __init__(self, total, update):
  196.         self.total = total
  197.         self.update = update
  198.         self.done = 0
  199.  
  200.     
  201.     def __call__(self, percent, msg = ''):
  202.         self.done += 1
  203.         self.update(float(self.done) / self.total, msg)
  204.  
  205.  
  206.  
  207. def process_pages(pages, opts, update, tdir):
  208.     progress = Progress(len(pages), update)
  209.     server = Server()
  210.     jobs = []
  211.     tasks = [ (p, os.path.join(tdir, os.path.basename(p))) for p in pages ]
  212.     tasks = server.split(pages)
  213.     for task in tasks:
  214.         jobs.append(ParallelJob('render_pages', '', progress, args = [
  215.             task,
  216.             tdir,
  217.             opts]))
  218.         server.add_job(jobs[-1])
  219.     
  220.     while True:
  221.         time.sleep(1)
  222.         running = False
  223.         for job in jobs:
  224.             while True:
  225.                 
  226.                 try:
  227.                     x = job.notifications.get_nowait()
  228.                     progress(*x)
  229.                 continue
  230.                 except Empty:
  231.                     []
  232.                     []
  233.                     []
  234.                     break
  235.                     continue
  236.                 
  237.  
  238.                 []<EXCEPTION MATCH>Empty
  239.             job.update()
  240.             if not job.is_finished:
  241.                 running = True
  242.                 continue
  243.             []
  244.         
  245.         if not running:
  246.             break
  247.             continue
  248.         []
  249.         continue
  250.         []
  251.     server.close()
  252.     ans = []
  253.     failures = []
  254.     for job in jobs:
  255.         if job.failed or job.result is None:
  256.             raise Exception(_('Failed to process comic: \n\n%s') % job.log_file.read())
  257.         job.result is None
  258.         (pages, failures_) = job.result
  259.         ans += pages
  260.         failures += failures_
  261.     
  262.     return (ans, failures)
  263.  
  264.  
  265. class ComicInput(InputFormatPlugin):
  266.     name = 'Comic Input'
  267.     author = 'Kovid Goyal'
  268.     description = 'Optimize comic files (.cbz, .cbr, .cbc) for viewing on portable devices'
  269.     file_types = set([
  270.         'cbz',
  271.         'cbr',
  272.         'cbc'])
  273.     is_image_collection = True
  274.     options = set([
  275.         OptionRecommendation(name = 'colors', recommended_value = 256, help = _('Number of colors for grayscale image conversion. Default: %default. Values of less than 256 may result in blurred text on your device if you are creating your comics in EPUB format.')),
  276.         OptionRecommendation(name = 'dont_normalize', recommended_value = False, help = _('Disable normalize (improve contrast) color range for pictures. Default: False')),
  277.         OptionRecommendation(name = 'keep_aspect_ratio', recommended_value = False, help = _('Maintain picture aspect ratio. Default is to fill the screen.')),
  278.         OptionRecommendation(name = 'dont_sharpen', recommended_value = False, help = _('Disable sharpening.')),
  279.         OptionRecommendation(name = 'disable_trim', recommended_value = False, help = _('Disable trimming of comic pages. For some comics, trimming might remove content as well as borders.')),
  280.         OptionRecommendation(name = 'landscape', recommended_value = False, help = _("Don't split landscape images into two portrait images")),
  281.         OptionRecommendation(name = 'wide', recommended_value = False, help = _('Keep aspect ratio and scale image using screen height as image width for viewing in landscape mode.')),
  282.         OptionRecommendation(name = 'right2left', recommended_value = False, help = _('Used for right-to-left publications like manga. Causes landscape pages to be split into portrait pages from right to left.')),
  283.         OptionRecommendation(name = 'despeckle', recommended_value = False, help = _('Enable Despeckle. Reduces speckle noise. May greatly increase processing time.')),
  284.         OptionRecommendation(name = 'no_sort', recommended_value = False, help = _("Don't sort the files found in the comic alphabetically by name. Instead use the order they were added to the comic.")),
  285.         OptionRecommendation(name = 'output_format', choices = [
  286.             'png',
  287.             'jpg'], recommended_value = 'png', help = _('The format that images in the created ebook are converted to. You can experiment to see which format gives you optimal size and look on your device.')),
  288.         OptionRecommendation(name = 'no_process', recommended_value = False, help = _('Apply no processing to the image')),
  289.         OptionRecommendation(name = 'dont_grayscale', recommended_value = False, help = _('Do not convert the image to grayscale (black and white)'))])
  290.     recommendations = set([
  291.         ('margin_left', 0, OptionRecommendation.HIGH),
  292.         ('margin_top', 0, OptionRecommendation.HIGH),
  293.         ('margin_right', 0, OptionRecommendation.HIGH),
  294.         ('margin_bottom', 0, OptionRecommendation.HIGH),
  295.         ('insert_blank_line', False, OptionRecommendation.HIGH),
  296.         ('remove_paragraph_spacing', False, OptionRecommendation.HIGH),
  297.         ('change_justification', 'left', OptionRecommendation.HIGH),
  298.         ('dont_split_on_pagebreaks', True, OptionRecommendation.HIGH),
  299.         ('chapter', None, OptionRecommendation.HIGH),
  300.         ('page_breaks_brefore', None, OptionRecommendation.HIGH),
  301.         ('use_auto_toc', False, OptionRecommendation.HIGH),
  302.         ('page_breaks_before', None, OptionRecommendation.HIGH),
  303.         ('disable_font_rescaling', True, OptionRecommendation.HIGH),
  304.         ('linearize_tables', False, OptionRecommendation.HIGH)])
  305.     
  306.     def get_comics_from_collection(self, stream):
  307.         zipextract = extract
  308.         import calibre.libunzip
  309.         tdir = PersistentTemporaryDirectory('_comic_collection')
  310.         zipextract(stream, tdir)
  311.         comics = []
  312.         CurrentDir(tdir).__enter__()
  313.         
  314.         try:
  315.             if not os.path.exists('comics.txt'):
  316.                 raise ValueError('%s is not a valid comic collection' % stream.name)
  317.             os.path.exists('comics.txt')
  318.             raw = open('comics.txt', 'rb').read()
  319.             if raw.startswith(codecs.BOM_UTF16_BE):
  320.                 raw = raw.decode('utf-16-be')[1:]
  321.             elif raw.startswith(codecs.BOM_UTF16_LE):
  322.                 raw = raw.decode('utf-16-le')[1:]
  323.             elif raw.startswith(codecs.BOM_UTF8):
  324.                 raw = raw.decode('utf-8')[1:]
  325.             else:
  326.                 raw = raw.decode('utf-8')
  327.             for line in raw.splitlines():
  328.                 line = line.strip()
  329.                 if not line:
  330.                     continue
  331.                 
  332.                 fname = line.partition(':')[0]
  333.                 title = line.partition(':')[-1]
  334.                 fname = os.path.join(tdir, *fname.split('/'))
  335.                 if not title:
  336.                     title = os.path.basename(fname).rpartition('.')[0]
  337.                 
  338.                 if os.access(fname, os.R_OK):
  339.                     comics.append([
  340.                         title,
  341.                         fname])
  342.                     continue
  343.         finally:
  344.             pass
  345.  
  346.         if not comics:
  347.             raise ValueError('%s has no comics' % stream.name)
  348.         comics
  349.         return comics
  350.  
  351.     
  352.     def get_pages(self, comic, tdir2):
  353.         tdir = extract_comic(comic)
  354.         new_pages = find_pages(tdir, sort_on_mtime = self.opts.no_sort, verbose = self.opts.verbose)
  355.         thumbnail = None
  356.         if not new_pages:
  357.             raise ValueError('Could not find any pages in the comic: %s' % comic)
  358.         new_pages
  359.         if self.opts.no_process:
  360.             n2 = []
  361.             for page in new_pages:
  362.                 n2.append(os.path.join(tdir2, os.path.basename(page)))
  363.                 shutil.copyfile(page, n2[-1])
  364.             
  365.             new_pages = n2
  366.         else:
  367.             (new_pages, failures) = process_pages(new_pages, self.opts, self.report_progress, tdir2)
  368.             if failures:
  369.                 self.log.warning('Could not process the following pages (run with --verbose to see why):')
  370.                 for f in failures:
  371.                     self.log.warning('\t', f)
  372.                 
  373.             
  374.             if not new_pages:
  375.                 raise ValueError('Could not find any valid pages in comic: %s' % comic)
  376.             new_pages
  377.             thumbnail = os.path.join(tdir2, 'thumbnail.' + self.opts.output_format.lower())
  378.             if not os.access(thumbnail, os.R_OK):
  379.                 thumbnail = None
  380.             
  381.         return new_pages
  382.  
  383.     
  384.     def get_images(self):
  385.         return self._images
  386.  
  387.     
  388.     def convert(self, stream, opts, file_ext, log, accelerators):
  389.         MetaInformation = MetaInformation
  390.         import calibre.ebooks.metadata
  391.         OPFCreator = OPFCreator
  392.         import calibre.ebooks.metadata.opf2
  393.         TOC = TOC
  394.         import calibre.ebooks.metadata.toc
  395.         self.opts = opts
  396.         self.log = log
  397.         if file_ext == 'cbc':
  398.             comics_ = self.get_comics_from_collection(stream)
  399.         else:
  400.             comics_ = [
  401.                 [
  402.                     'Comic',
  403.                     os.path.abspath(stream.name)]]
  404.         stream.close()
  405.         comics = []
  406.         for i, x in enumerate(comics_):
  407.             (title, fname) = x
  408.             cdir = None if len(comics_) > 1 else '.'
  409.             cdir = os.path.abspath(cdir)
  410.             if not os.path.exists(cdir):
  411.                 os.makedirs(cdir)
  412.             
  413.             pages = self.get_pages(fname, cdir)
  414.             if not pages:
  415.                 continue
  416.             
  417.             wrappers = self.create_wrappers(pages)
  418.             comics.append((title, pages, wrappers))
  419.         
  420.         if not comics:
  421.             raise ValueError('No comic pages found in %s' % stream.name)
  422.         comics
  423.         mi = MetaInformation(os.path.basename(stream.name).rpartition('.')[0], [
  424.             _('Unknown')])
  425.         opf = OPFCreator(os.path.abspath('.'), mi)
  426.         entries = []
  427.         
  428.         def href(x):
  429.             if len(comics) == 1:
  430.                 return os.path.basename(x)
  431.             return '/'.join(x.split(os.sep)[-2:])
  432.  
  433.         for comic in comics:
  434.             (pages, wrappers) = comic[1:]
  435.             [] += [] + [ (x, None) for x in map(href, pages) ]
  436.         
  437.         opf.create_manifest(entries)
  438.         spine = []
  439.         for comic in comics:
  440.             spine.extend(map(href, comic[2]))
  441.         
  442.         self._images = []
  443.         for comic in comics:
  444.             self._images.extend(comic[1])
  445.         
  446.         opf.create_spine(spine)
  447.         toc = TOC()
  448.         opf.set_toc(toc)
  449.         m = open('metadata.opf', 'wb')
  450.         n = open('toc.ncx', 'wb')
  451.         opf.render(m, n, 'toc.ncx')
  452.         return os.path.abspath('metadata.opf')
  453.  
  454.     
  455.     def create_wrappers(self, pages):
  456.         XHTML_NS = XHTML_NS
  457.         import calibre.ebooks.oeb.base
  458.         wrappers = []
  459.         WRAPPER = textwrap.dedent('        <html xmlns="%s">\n            <head>\n                <title>Page #%d</title>\n                <style type="text/css">\n                    @page { margin:0pt; padding: 0pt}\n                    body { margin: 0pt; padding: 0pt}\n                    div { text-align: center }\n                </style>\n            </head>\n            <body>\n                <div>\n                    <img src="%s" alt="comic page #%d" />\n                </div>\n            </body>\n        </html>\n        ')
  460.         dir = os.path.dirname(pages[0])
  461.         for i, page in enumerate(pages):
  462.             wrapper = WRAPPER % (XHTML_NS, i + 1, os.path.basename(page), i + 1)
  463.             page = os.path.join(dir, 'page_%d.xhtml' % (i + 1))
  464.             open(page, 'wb').write(wrapper)
  465.             wrappers.append(page)
  466.         
  467.         return wrappers
  468.  
  469.  
  470.