home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_859 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  16.6 KB  |  477 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 = '#ffffff'
  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.             if dest.lower().endswith('.png'):
  166.                 dest += '8'
  167.             
  168.             wand.save(dest)
  169.             if dest.endswith('8'):
  170.                 dest = dest[:-1]
  171.                 os.rename(dest + '8', dest)
  172.             
  173.             self.append(dest)
  174.         
  175.  
  176.  
  177.  
  178. def render_pages(tasks, dest, opts, notification = (lambda x, y: x)):
  179.     failures = []
  180.     pages = []
  181.     for num, path in tasks:
  182.         
  183.         try:
  184.             pages.extend(PageProcessor(path, dest, opts, num))
  185.             msg = _('Rendered %s') % path
  186.         except:
  187.             failures.append(path)
  188.             msg = _('Failed %s') % path
  189.             if opts.verbose:
  190.                 msg += '\n' + traceback.format_exc()
  191.             
  192.  
  193.         prints(msg)
  194.         notification(0.5, msg)
  195.     
  196.     return (pages, failures)
  197.  
  198.  
  199. class Progress(object):
  200.     
  201.     def __init__(self, total, update):
  202.         self.total = total
  203.         self.update = update
  204.         self.done = 0
  205.  
  206.     
  207.     def __call__(self, percent, msg = ''):
  208.         self.done += 1
  209.         self.update(float(self.done) / self.total, msg)
  210.  
  211.  
  212.  
  213. def process_pages(pages, opts, update, tdir):
  214.     progress = Progress(len(pages), update)
  215.     server = Server()
  216.     jobs = []
  217.     tasks = [ (p, os.path.join(tdir, os.path.basename(p))) for p in pages ]
  218.     tasks = server.split(pages)
  219.     for task in tasks:
  220.         jobs.append(ParallelJob('render_pages', '', progress, args = [
  221.             task,
  222.             tdir,
  223.             opts]))
  224.         server.add_job(jobs[-1])
  225.     
  226.     while True:
  227.         time.sleep(1)
  228.         running = False
  229.         for job in jobs:
  230.             while True:
  231.                 
  232.                 try:
  233.                     x = job.notifications.get_nowait()
  234.                     progress(*x)
  235.                 continue
  236.                 except Empty:
  237.                     []
  238.                     []
  239.                     []
  240.                     break
  241.                     continue
  242.                 
  243.  
  244.                 []<EXCEPTION MATCH>Empty
  245.             job.update()
  246.             if not job.is_finished:
  247.                 running = True
  248.                 continue
  249.             []
  250.         
  251.         if not running:
  252.             break
  253.             continue
  254.         []
  255.         continue
  256.         []
  257.     server.close()
  258.     ans = []
  259.     failures = []
  260.     for job in jobs:
  261.         if job.failed or job.result is None:
  262.             raise Exception(_('Failed to process comic: \n\n%s') % job.log_file.read())
  263.         job.result is None
  264.         (pages, failures_) = job.result
  265.         ans += pages
  266.         failures += failures_
  267.     
  268.     return (ans, failures)
  269.  
  270.  
  271. class ComicInput(InputFormatPlugin):
  272.     name = 'Comic Input'
  273.     author = 'Kovid Goyal'
  274.     description = 'Optimize comic files (.cbz, .cbr, .cbc) for viewing on portable devices'
  275.     file_types = set([
  276.         'cbz',
  277.         'cbr',
  278.         'cbc'])
  279.     is_image_collection = True
  280.     core_usage = -1
  281.     options = set([
  282.         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.')),
  283.         OptionRecommendation(name = 'dont_normalize', recommended_value = False, help = _('Disable normalize (improve contrast) color range for pictures. Default: False')),
  284.         OptionRecommendation(name = 'keep_aspect_ratio', recommended_value = False, help = _('Maintain picture aspect ratio. Default is to fill the screen.')),
  285.         OptionRecommendation(name = 'dont_sharpen', recommended_value = False, help = _('Disable sharpening.')),
  286.         OptionRecommendation(name = 'disable_trim', recommended_value = False, help = _('Disable trimming of comic pages. For some comics, trimming might remove content as well as borders.')),
  287.         OptionRecommendation(name = 'landscape', recommended_value = False, help = _("Don't split landscape images into two portrait images")),
  288.         OptionRecommendation(name = 'wide', recommended_value = False, help = _('Keep aspect ratio and scale image using screen height as image width for viewing in landscape mode.')),
  289.         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.')),
  290.         OptionRecommendation(name = 'despeckle', recommended_value = False, help = _('Enable Despeckle. Reduces speckle noise. May greatly increase processing time.')),
  291.         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.")),
  292.         OptionRecommendation(name = 'output_format', choices = [
  293.             'png',
  294.             '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.')),
  295.         OptionRecommendation(name = 'no_process', recommended_value = False, help = _('Apply no processing to the image')),
  296.         OptionRecommendation(name = 'dont_grayscale', recommended_value = False, help = _('Do not convert the image to grayscale (black and white)'))])
  297.     recommendations = set([
  298.         ('margin_left', 0, OptionRecommendation.HIGH),
  299.         ('margin_top', 0, OptionRecommendation.HIGH),
  300.         ('margin_right', 0, OptionRecommendation.HIGH),
  301.         ('margin_bottom', 0, OptionRecommendation.HIGH),
  302.         ('insert_blank_line', False, OptionRecommendation.HIGH),
  303.         ('remove_paragraph_spacing', False, OptionRecommendation.HIGH),
  304.         ('change_justification', 'left', OptionRecommendation.HIGH),
  305.         ('dont_split_on_pagebreaks', True, OptionRecommendation.HIGH),
  306.         ('chapter', None, OptionRecommendation.HIGH),
  307.         ('page_breaks_brefore', None, OptionRecommendation.HIGH),
  308.         ('use_auto_toc', False, OptionRecommendation.HIGH),
  309.         ('page_breaks_before', None, OptionRecommendation.HIGH),
  310.         ('disable_font_rescaling', True, OptionRecommendation.HIGH),
  311.         ('linearize_tables', False, OptionRecommendation.HIGH)])
  312.     
  313.     def get_comics_from_collection(self, stream):
  314.         zipextract = extract
  315.         import calibre.libunzip
  316.         tdir = PersistentTemporaryDirectory('_comic_collection')
  317.         zipextract(stream, tdir)
  318.         comics = []
  319.         CurrentDir(tdir).__enter__()
  320.         
  321.         try:
  322.             if not os.path.exists('comics.txt'):
  323.                 raise ValueError('%s is not a valid comic collection' % stream.name)
  324.             os.path.exists('comics.txt')
  325.             raw = open('comics.txt', 'rb').read()
  326.             if raw.startswith(codecs.BOM_UTF16_BE):
  327.                 raw = raw.decode('utf-16-be')[1:]
  328.             elif raw.startswith(codecs.BOM_UTF16_LE):
  329.                 raw = raw.decode('utf-16-le')[1:]
  330.             elif raw.startswith(codecs.BOM_UTF8):
  331.                 raw = raw.decode('utf-8')[1:]
  332.             else:
  333.                 raw = raw.decode('utf-8')
  334.             for line in raw.splitlines():
  335.                 line = line.strip()
  336.                 if not line:
  337.                     continue
  338.                 
  339.                 fname = line.partition(':')[0]
  340.                 title = line.partition(':')[-1]
  341.                 fname = os.path.join(tdir, *fname.split('/'))
  342.                 if not title:
  343.                     title = os.path.basename(fname).rpartition('.')[0]
  344.                 
  345.                 if os.access(fname, os.R_OK):
  346.                     comics.append([
  347.                         title,
  348.                         fname])
  349.                     continue
  350.         finally:
  351.             pass
  352.  
  353.         if not comics:
  354.             raise ValueError('%s has no comics' % stream.name)
  355.         comics
  356.         return comics
  357.  
  358.     
  359.     def get_pages(self, comic, tdir2):
  360.         tdir = extract_comic(comic)
  361.         new_pages = find_pages(tdir, sort_on_mtime = self.opts.no_sort, verbose = self.opts.verbose)
  362.         thumbnail = None
  363.         if not new_pages:
  364.             raise ValueError('Could not find any pages in the comic: %s' % comic)
  365.         new_pages
  366.         if self.opts.no_process:
  367.             n2 = []
  368.             for page in new_pages:
  369.                 n2.append(os.path.join(tdir2, os.path.basename(page)))
  370.                 shutil.copyfile(page, n2[-1])
  371.             
  372.             new_pages = n2
  373.         else:
  374.             (new_pages, failures) = process_pages(new_pages, self.opts, self.report_progress, tdir2)
  375.             if failures:
  376.                 self.log.warning('Could not process the following pages (run with --verbose to see why):')
  377.                 for f in failures:
  378.                     self.log.warning('\t', f)
  379.                 
  380.             
  381.             if not new_pages:
  382.                 raise ValueError('Could not find any valid pages in comic: %s' % comic)
  383.             new_pages
  384.             thumbnail = os.path.join(tdir2, 'thumbnail.' + self.opts.output_format.lower())
  385.             if not os.access(thumbnail, os.R_OK):
  386.                 thumbnail = None
  387.             
  388.         return new_pages
  389.  
  390.     
  391.     def get_images(self):
  392.         return self._images
  393.  
  394.     
  395.     def convert(self, stream, opts, file_ext, log, accelerators):
  396.         MetaInformation = MetaInformation
  397.         import calibre.ebooks.metadata
  398.         OPFCreator = OPFCreator
  399.         import calibre.ebooks.metadata.opf2
  400.         TOC = TOC
  401.         import calibre.ebooks.metadata.toc
  402.         self.opts = opts
  403.         self.log = log
  404.         if file_ext == 'cbc':
  405.             comics_ = self.get_comics_from_collection(stream)
  406.         else:
  407.             comics_ = [
  408.                 [
  409.                     'Comic',
  410.                     os.path.abspath(stream.name)]]
  411.         stream.close()
  412.         comics = []
  413.         for i, x in enumerate(comics_):
  414.             (title, fname) = x
  415.             cdir = None if len(comics_) > 1 else '.'
  416.             cdir = os.path.abspath(cdir)
  417.             if not os.path.exists(cdir):
  418.                 os.makedirs(cdir)
  419.             
  420.             pages = self.get_pages(fname, cdir)
  421.             if not pages:
  422.                 continue
  423.             
  424.             wrappers = self.create_wrappers(pages)
  425.             comics.append((title, pages, wrappers))
  426.         
  427.         if not comics:
  428.             raise ValueError('No comic pages found in %s' % stream.name)
  429.         comics
  430.         mi = MetaInformation(os.path.basename(stream.name).rpartition('.')[0], [
  431.             _('Unknown')])
  432.         opf = OPFCreator(os.path.abspath('.'), mi)
  433.         entries = []
  434.         
  435.         def href(x):
  436.             if len(comics) == 1:
  437.                 return os.path.basename(x)
  438.             return '/'.join(x.split(os.sep)[-2:])
  439.  
  440.         for comic in comics:
  441.             (pages, wrappers) = comic[1:]
  442.             [] += [] + [ (x, None) for x in map(href, pages) ]
  443.         
  444.         opf.create_manifest(entries)
  445.         spine = []
  446.         for comic in comics:
  447.             spine.extend(map(href, comic[2]))
  448.         
  449.         self._images = []
  450.         for comic in comics:
  451.             self._images.extend(comic[1])
  452.         
  453.         opf.create_spine(spine)
  454.         toc = TOC()
  455.         opf.set_toc(toc)
  456.         m = open('metadata.opf', 'wb')
  457.         n = open('toc.ncx', 'wb')
  458.         opf.render(m, n, 'toc.ncx')
  459.         return os.path.abspath('metadata.opf')
  460.  
  461.     
  462.     def create_wrappers(self, pages):
  463.         XHTML_NS = XHTML_NS
  464.         import calibre.ebooks.oeb.base
  465.         wrappers = []
  466.         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        ')
  467.         dir = os.path.dirname(pages[0])
  468.         for i, page in enumerate(pages):
  469.             wrapper = WRAPPER % (XHTML_NS, i + 1, os.path.basename(page), i + 1)
  470.             page = os.path.join(dir, 'page_%d.xhtml' % (i + 1))
  471.             open(page, 'wb').write(wrapper)
  472.             wrappers.append(page)
  473.         
  474.         return wrappers
  475.  
  476.  
  477.