PageRenderTime 38ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/Read-99/epubview/jobs.py

#
Python | 309 lines | 245 code | 35 blank | 29 comment | 11 complexity | 975e97f256ac4b2422ac5b6c08251e41 MD5 | raw file
Possible License(s): GPL-2.0
  1. # Copyright 2009 One Laptop Per Child
  2. # Author: Sayamindu Dasgupta <sayamindu@laptop.org>
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  17. from gi.repository import GObject
  18. from gi.repository import Gtk
  19. from gi.repository import Gdk
  20. import widgets
  21. import cairo
  22. import math
  23. import os.path
  24. import BeautifulSoup
  25. import epub
  26. import threading
  27. PAGE_WIDTH = 135
  28. PAGE_HEIGHT = 216
  29. def _pixel_to_mm(pixel, dpi):
  30. inches = pixel / dpi
  31. return int(inches / 0.03937)
  32. def _mm_to_pixel(mm, dpi):
  33. inches = mm * 0.03937
  34. return int(inches * dpi)
  35. class SearchThread(threading.Thread):
  36. def __init__(self, obj):
  37. threading.Thread.__init__(self)
  38. self.obj = obj
  39. self.stopthread = threading.Event()
  40. def _start_search(self):
  41. for entry in self.obj.flattoc:
  42. if self.stopthread.isSet():
  43. break
  44. filepath = os.path.join(self.obj._document.get_basedir(), entry)
  45. f = open(filepath)
  46. if self._searchfile(f):
  47. self.obj._matchfilelist.append(entry)
  48. f.close()
  49. Gdk.threads_enter()
  50. self.obj._finished = True
  51. self.obj.emit('updated')
  52. Gdk.threads_leave()
  53. return False
  54. def _searchfile(self, fileobj):
  55. soup = BeautifulSoup.BeautifulSoup(fileobj)
  56. body = soup.find('body')
  57. tags = body.findChildren(True)
  58. for tag in tags:
  59. if not tag.string is None:
  60. if tag.string.find(self.obj._text) > -1:
  61. return True
  62. return False
  63. def run(self):
  64. self._start_search()
  65. def stop(self):
  66. self.stopthread.set()
  67. class _JobPaginator(GObject.GObject):
  68. __gsignals__ = {
  69. 'paginated': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ([])),
  70. }
  71. def __init__(self, filelist):
  72. GObject.GObject.__init__(self)
  73. self._filelist = filelist
  74. self._filedict = {}
  75. self._pagemap = {}
  76. self._bookheight = 0
  77. self._count = 0
  78. self._pagecount = 0
  79. #TODO
  80. """
  81. self._screen = Gdk.Screen.get_default()
  82. self._old_fontoptions = self._screen.get_font_options()
  83. options = cairo.FontOptions()
  84. options.set_hint_style(cairo.HINT_STYLE_MEDIUM)
  85. options.set_antialias(cairo.ANTIALIAS_GRAY)
  86. options.set_subpixel_order(cairo.SUBPIXEL_ORDER_DEFAULT)
  87. options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
  88. self._screen.set_font_options(options)
  89. """
  90. self._temp_win = Gtk.Window()
  91. self._temp_view = widgets._WebView(only_to_measure=True)
  92. settings = self._temp_view.get_settings()
  93. settings.props.default_font_family = 'DejaVu LGC Serif'
  94. settings.props.sans_serif_font_family = 'DejaVu LGC Sans'
  95. settings.props.serif_font_family = 'DejaVu LGC Serif'
  96. settings.props.monospace_font_family = 'DejaVu LGC Sans Mono'
  97. settings.props.enforce_96_dpi = True
  98. #FIXME: This does not seem to work
  99. #settings.props.auto_shrink_images = False
  100. settings.props.enable_plugins = False
  101. settings.props.default_font_size = 12
  102. settings.props.default_monospace_font_size = 10
  103. settings.props.default_encoding = 'utf-8'
  104. sw = Gtk.ScrolledWindow()
  105. sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
  106. self._dpi = 96
  107. sw.set_size_request(_mm_to_pixel(PAGE_WIDTH, self._dpi),
  108. _mm_to_pixel(PAGE_HEIGHT, self._dpi))
  109. sw.add(self._temp_view)
  110. self._temp_win.add(sw)
  111. self._temp_view.connect('load-finished', self._page_load_finished_cb)
  112. self._temp_win.show_all()
  113. self._temp_win.unmap()
  114. self._temp_view.open(self._filelist[self._count])
  115. def _page_load_finished_cb(self, v, frame):
  116. f = v.get_main_frame()
  117. pageheight = v.get_page_height()
  118. if pageheight <= _mm_to_pixel(PAGE_HEIGHT, self._dpi):
  119. pages = 1
  120. else:
  121. pages = pageheight / float(_mm_to_pixel(PAGE_HEIGHT, self._dpi))
  122. for i in range(1, int(math.ceil(pages) + 1)):
  123. if pages - i < 0:
  124. pagelen = (pages - math.floor(pages)) / pages
  125. else:
  126. pagelen = 1 / pages
  127. self._pagemap[float(self._pagecount + i)] = \
  128. (f.props.uri, (i - 1) / math.ceil(pages), pagelen)
  129. self._pagecount += int(math.ceil(pages))
  130. self._filedict[f.props.uri.replace('file://', '')] = \
  131. (math.ceil(pages), math.ceil(pages) - pages)
  132. self._bookheight += pageheight
  133. if self._count + 1 >= len(self._filelist):
  134. # TODO
  135. #self._screen.set_font_options(self._old_fontoptions)
  136. self.emit('paginated')
  137. GObject.idle_add(self._cleanup)
  138. else:
  139. self._count += 1
  140. self._temp_view.open(self._filelist[self._count])
  141. def _cleanup(self):
  142. self._temp_win.destroy()
  143. def get_file_for_pageno(self, pageno):
  144. '''
  145. Returns the file in which pageno occurs
  146. '''
  147. return self._pagemap[pageno][0]
  148. def get_scrollfactor_pos_for_pageno(self, pageno):
  149. '''
  150. Returns the position scrollfactor (fraction) for pageno
  151. '''
  152. return self._pagemap[pageno][1]
  153. def get_scrollfactor_len_for_pageno(self, pageno):
  154. '''
  155. Returns the length scrollfactor (fraction) for pageno
  156. '''
  157. return self._pagemap[pageno][2]
  158. def get_pagecount_for_file(self, filename):
  159. '''
  160. Returns the number of pages in file
  161. '''
  162. return self._filedict[filename][0]
  163. def get_base_pageno_for_file(self, filename):
  164. '''
  165. Returns the pageno which begins in filename
  166. '''
  167. for key in self._pagemap.keys():
  168. if self._pagemap[key][0].replace('file://', '') == filename:
  169. return key
  170. return None
  171. def get_remfactor_for_file(self, filename):
  172. '''
  173. Returns the remainder
  174. factor (1 - fraction length of last page in file)
  175. '''
  176. return self._filedict[filename][1]
  177. def get_total_pagecount(self):
  178. '''
  179. Returns the total pagecount for the Epub file
  180. '''
  181. return self._pagecount
  182. def get_total_height(self):
  183. '''
  184. Returns the total height of the Epub in pixels
  185. '''
  186. return self._bookheight
  187. class _JobFind(GObject.GObject):
  188. __gsignals__ = {
  189. 'updated': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ([])),
  190. }
  191. def __init__(self, document, start_page, n_pages, text,
  192. case_sensitive=False):
  193. GObject.GObject.__init__(self)
  194. Gdk.threads_init()
  195. self._finished = False
  196. self._document = document
  197. self._start_page = start_page
  198. self._n_pages = n_pages
  199. self._text = text
  200. self._case_sensitive = case_sensitive
  201. self.flattoc = self._document.get_flattoc()
  202. self._matchfilelist = []
  203. self._current_file_index = 0
  204. self.threads = []
  205. s_thread = SearchThread(self)
  206. self.threads.append(s_thread)
  207. s_thread.start()
  208. def cancel(self):
  209. '''
  210. Cancels the search job
  211. '''
  212. for s_thread in self.threads:
  213. s_thread.stop()
  214. def is_finished(self):
  215. '''
  216. Returns True if the entire search job has been finished
  217. '''
  218. return self._finished
  219. def get_next_file(self):
  220. '''
  221. Returns the next file which has the search pattern
  222. '''
  223. self._current_file_index += 1
  224. try:
  225. path = self._matchfilelist[self._current_file_index]
  226. except IndexError:
  227. self._current_file_index = 0
  228. path = self._matchfilelist[self._current_file_index]
  229. return path
  230. def get_prev_file(self):
  231. '''
  232. Returns the previous file which has the search pattern
  233. '''
  234. self._current_file_index -= 1
  235. try:
  236. path = self._matchfilelist[self._current_file_index]
  237. except IndexError:
  238. self._current_file_index = -1
  239. path = self._matchfilelist[self._current_file_index]
  240. return path
  241. def get_search_text(self):
  242. '''
  243. Returns the search text
  244. '''
  245. return self._text
  246. def get_case_sensitive(self):
  247. '''
  248. Returns True if the search is case-sensitive
  249. '''
  250. return self._case_sensitive