Package list sugar-read-activity / a630dac
New upstream version 119 Jonas Smedegaard 4 years ago
93 changed file(s) with 2255 addition(s) and 2028 deletion(s). Raw diff Collapse all Expand all
0 119
1
2 * New translations (Chris Leonard, et al)
3 * Remove EPUB support to remove WebKit 3.0 API dependency (James Cameron)
4 * Replace BeautifulSoup dependency with ElementTree (James Cameron)
5 * Remove unused screenshots (James Cameron)
6 * Remove some unnecessary errors from log (James Cameron)
7
08 118
19
210 * Trim headers, synch to Pootle (Chris Leonard)
22 bundle_id = org.laptop.sugar.ReadActivity
33 icon = activity-read
44 exec = sugar-activity readactivity.ReadActivity
5 activity_version = 118
6 mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff;application/epub+zip;text/plain;application/zip;application/x-cbz
5 activity_version = 119
6 mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff;text/plain;application/zip;application/x-cbz
77 license = GPLv2+
88 summary = Use this activity when you are ready to read! Remember to flip your computer around to feel like you are really holding a book!
99 categories = language documents media system
+0
-7
activity/mimetypes.xml less more
0 <?xml version="1.0" encoding="UTF-8"?>
1 <mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
2 <mime-type type="application/epub+zip">
3 <comment xml:lang="en">Epub document</comment>
4 <glob pattern="*.epub"/>
5 </mime-type>
6 </mime-info>
4343
4444 empty_widgets.add(vbox)
4545 empty_widgets.show_all()
46 logging.error('Showing empty Panel')
4746 activity.set_canvas(empty_widgets)
+0
-310
epubadapter.py less more
0 from gi.repository import GObject
1 import logging
2
3 import epubview
4
5 # import speech
6
7 from cStringIO import StringIO
8
9 _logger = logging.getLogger('read-activity')
10
11
12 class EpubViewer(epubview.EpubView):
13
14 def __init__(self):
15 epubview.EpubView.__init__(self)
16
17 def setup(self, activity):
18 self.set_screen_dpi(activity.dpi)
19 self.connect('selection-changed',
20 activity._view_selection_changed_cb)
21
22 activity._hbox.pack_start(self, True, True, 0)
23 self.show_all()
24 self._modified_files = []
25
26 # text to speech initialization
27 self.current_word = 0
28 self.word_tuples = []
29
30 def load_document(self, file_path):
31 self.set_document(EpubDocument(self, file_path.replace('file://', '')))
32 # speech.highlight_cb = self.highlight_next_word
33 # speech.reset_cb = self.reset_text_to_speech
34 # speech.end_text_cb = self.get_more_text
35
36 def load_metadata(self, activity):
37
38 self.metadata = activity.metadata
39
40 if not self.metadata['title_set_by_user'] == '1':
41 title = self._epub._info._get_title()
42 if title:
43 self.metadata['title'] = title
44 if 'Read_zoom' in self.metadata:
45 try:
46 logging.error('Loading zoom %s', self.metadata['Read_zoom'])
47 self.set_zoom(float(self.metadata['Read_zoom']))
48 except:
49 pass
50
51 def update_metadata(self, activity):
52 self.metadata = activity.metadata
53 logging.error('Saving zoom %s', self.get_zoom())
54 self.metadata['Read_zoom'] = self.get_zoom()
55
56 def zoom_to_width(self):
57 pass
58
59 def zoom_to_best_fit(self):
60 pass
61
62 def zoom_to_actual_size(self):
63 pass
64
65 def can_zoom_to_width(self):
66 return False
67
68 def can_highlight(self):
69 return True
70
71 def show_highlights(self, page):
72 # we save the highlights in the page as html
73 pass
74
75 def toggle_highlight(self, highlight):
76 self._view.set_editable(True)
77
78 if highlight:
79 self._view.execute_script(
80 'document.execCommand("backColor", false, "yellow");')
81 else:
82 # need remove the highlight nodes
83 js = """
84 var selObj = window.getSelection();
85 var range = selObj.getRangeAt(0);
86 var node = range.startContainer;
87 while (node.parentNode != null) {
88 if (node.localName == "span") {
89 if (node.hasAttributes()) {
90 var attrs = node.attributes;
91 for(var i = attrs.length - 1; i >= 0; i--) {
92 if (attrs[i].name == "style" &&
93 attrs[i].value == "background-color: yellow;") {
94 node.removeAttribute("style");
95 break;
96 };
97 };
98 };
99 };
100 node = node.parentNode;
101 };"""
102 self._view.execute_script(js)
103
104 self._view.set_editable(False)
105 # mark the file as modified
106 current_file = self.get_current_file()
107 logging.error('file %s was modified', current_file)
108 if current_file not in self._modified_files:
109 self._modified_files.append(current_file)
110 GObject.idle_add(self._save_page)
111
112 def _save_page(self):
113 oldtitle = self._view.get_title()
114 self._view.execute_script(
115 "document.title=document.documentElement.innerHTML;")
116 html = self._view.get_title()
117 file_path = self.get_current_file().replace('file:///', '/')
118 logging.error(html)
119 with open(file_path, 'w') as fd:
120 header = """<?xml version="1.0" encoding="utf-8" standalone="no"?>
121 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
122 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
123 <html xmlns="http://www.w3.org/1999/xhtml">"""
124 fd.write(header)
125 fd.write(html)
126 fd.write('</html>')
127 self._view.execute_script('document.title=%s;' % oldtitle)
128
129 def save(self, file_path):
130 if self._modified_files:
131 self._epub.write(file_path)
132 return True
133
134 return False
135
136 def in_highlight(self):
137 # Verify if the selection already exist or the cursor
138 # is in a highlighted area
139 page_title = self._view.get_title()
140 js = """
141 var selObj = window.getSelection();
142 var range = selObj.getRangeAt(0);
143 var node = range.startContainer;
144 var onHighlight = false;
145 while (node.parentNode != null) {
146 if (node.localName == "span") {
147 if (node.hasAttributes()) {
148 var attrs = node.attributes;
149 for(var i = attrs.length - 1; i >= 0; i--) {
150 if (attrs[i].name == "style" &&
151 attrs[i].value == "background-color: yellow;") {
152 onHighlight = true;
153 };
154 };
155 };
156 };
157 node = node.parentNode;
158 };
159 document.title=onHighlight;"""
160 self._view.execute_script(js)
161 on_highlight = self._view.get_title() == 'true'
162 self._view.execute_script('document.title = "%s";' % page_title)
163 # the second parameter is only used in the text backend
164 return on_highlight, None
165
166 def can_do_text_to_speech(self):
167 return False
168
169 def can_rotate(self):
170 return False
171
172 def get_marked_words(self):
173 "Adds a mark between each word of text."
174 i = self.current_word
175 file_str = StringIO()
176 file_str.write('<speak> ')
177 end_range = i + 40
178 if end_range > len(self.word_tuples):
179 end_range = len(self.word_tuples)
180 for word_tuple in self.word_tuples[self.current_word:end_range]:
181 file_str.write('<mark name="' + str(i) + '"/>' +
182 word_tuple[2].encode('utf-8'))
183 i = i + 1
184 self.current_word = i
185 file_str.write('</speak>')
186 return file_str.getvalue()
187
188 def get_more_text(self):
189 pass
190 """
191 if self.current_word < len(self.word_tuples):
192 speech.stop()
193 more_text = self.get_marked_words()
194 speech.play(more_text)
195 else:
196 if speech.reset_buttons_cb is not None:
197 speech.reset_buttons_cb()
198 """
199
200 def reset_text_to_speech(self):
201 self.current_word = 0
202
203 def highlight_next_word(self, word_count):
204 pass
205 """
206 TODO: disabled because javascript can't be executed
207 with the velocity needed
208 self.current_word = word_count
209 self._view.highlight_next_word()
210 return True
211 """
212
213 def connect_zoom_handler(self, handler):
214 self._zoom_handler = handler
215 self._view_notify_zoom_handler = \
216 self.connect('notify::scale', handler)
217 return self._view_notify_zoom_handler
218
219 def connect_page_changed_handler(self, handler):
220 self.connect('page-changed', handler)
221
222 def _try_load_page(self, n):
223 if self._ready:
224 self._load_page(n)
225 return False
226 else:
227 return True
228
229 def set_screen_dpi(self, dpi):
230 return
231
232 def find_set_highlight_search(self, set_highlight_search):
233 self._view.set_highlight_text_matches(set_highlight_search)
234
235 def set_current_page(self, n):
236 # When the book is being loaded, calling this does not help
237 # In such a situation, we go into a loop and try to load the
238 # supplied page when the book has loaded completely
239 n += 1
240 if self._ready:
241 self._load_page(n)
242 else:
243 GObject.timeout_add(200, self._try_load_page, n)
244
245 def get_current_page(self):
246 return int(self._loaded_page) - 1
247
248 def get_current_link(self):
249 # the _loaded_filename include all the path,
250 # need only the part included in the link
251 return self._loaded_filename[len(self._epub._tempdir) + 1:]
252
253 def update_toc(self, activity):
254 if self._epub.has_document_links():
255 activity.show_navigator_button()
256 activity.set_navigator_model(self._epub.get_links_model())
257 return True
258 else:
259 return False
260
261 def get_link_iter(self, current_link):
262 """
263 Returns the iter related to a link
264 """
265 link_iter = self._epub.get_links_model().get_iter_first()
266
267 while link_iter is not None and \
268 self._epub.get_links_model().get_value(link_iter, 1) \
269 != current_link:
270 link_iter = self._epub.get_links_model().iter_next(link_iter)
271 return link_iter
272
273 def find_changed(self, job, page=None):
274 self._find_changed(job)
275
276 def handle_link(self, link):
277 self._load_file(link)
278
279 def setup_find_job(self, text, updated_cb):
280 self._find_job = JobFind(document=self._epub,
281 start_page=0, n_pages=self.get_pagecount(),
282 text=text, case_sensitive=False)
283 self._find_updated_handler = self._find_job.connect('updated',
284 updated_cb)
285 return self._find_job, self._find_updated_handler
286
287
288 class EpubDocument(epubview.Epub):
289
290 def __init__(self, view, docpath):
291 epubview.Epub.__init__(self, docpath)
292 self._page_cache = view
293
294 def get_n_pages(self):
295 return int(self._page_cache.get_pagecount())
296
297 def has_document_links(self):
298 return True
299
300 def get_links_model(self):
301 return self.get_toc_model()
302
303
304 class JobFind(epubview.JobFind):
305
306 def __init__(self, document, start_page, n_pages, text,
307 case_sensitive=False):
308 epubview.JobFind.__init__(self, document, start_page, n_pages, text,
309 case_sensitive=False)
+0
-20
epubview/__init__.py less more
0 # Copyright 2009 One Laptop Per Child
1 # Author: Sayamindu Dasgupta <sayamindu@laptop.org>
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
17 from epub import _Epub as Epub
18 from epubview import _View as EpubView
19 from jobs import _JobFind as JobFind
+0
-202
epubview/epub.py less more
0 # Copyright 2009 One Laptop Per Child
1 # Author: Sayamindu Dasgupta <sayamindu@laptop.org>
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
17 import zipfile
18 import tempfile
19 import os
20 import xml.etree.ElementTree as etree
21 import shutil
22 import logging
23
24 import navmap
25 import epubinfo
26
27
28 class _Epub(object):
29
30 def __init__(self, _file):
31 """
32 _file: can be either a path to a file (a string) or a file-like object.
33 """
34 self._file = _file
35 self._zobject = None
36 self._opfpath = None
37 self._ncxpath = None
38 self._basepath = None
39 self._tempdir = tempfile.mkdtemp()
40
41 if not self._verify():
42 print 'Warning: This does not seem to be a valid epub file'
43
44 self._get_opf()
45 self._get_ncx()
46
47 ncxfile = self._zobject.open(self._ncxpath)
48 opffile = self._zobject.open(self._opfpath)
49 self._navmap = navmap.NavMap(opffile, ncxfile, self._basepath)
50
51 opffile = self._zobject.open(self._opfpath)
52 self._info = epubinfo.EpubInfo(opffile)
53
54 self._unzip()
55
56 def _unzip(self):
57 # This is broken upto python 2.7
58 # self._zobject.extractall(path = self._tempdir)
59 orig_cwd = os.getcwd()
60 os.chdir(self._tempdir)
61 for name in self._zobject.namelist():
62 # Some weird zip file entries start with a slash,
63 # and we don't want to write to the root directory
64 try:
65 if name.startswith(os.path.sep):
66 name = name[1:]
67 if name.endswith(os.path.sep) or name.endswith('\\'):
68 os.makedirs(name)
69 except:
70 logging.error('ERROR unziping %s', name)
71 else:
72 self._zobject.extract(name)
73 os.chdir(orig_cwd)
74
75 def _get_opf(self):
76 containerfile = self._zobject.open('META-INF/container.xml')
77
78 tree = etree.parse(containerfile)
79 root = tree.getroot()
80
81 r_id = './/{urn:oasis:names:tc:opendocument:xmlns:container}rootfile'
82 for element in root.iterfind(r_id):
83 if element.get('media-type') == 'application/oebps-package+xml':
84 self._opfpath = element.get('full-path')
85
86 if self._opfpath.rpartition('/')[0]:
87 self._basepath = self._opfpath.rpartition('/')[0] + '/'
88 else:
89 self._basepath = ''
90
91 containerfile.close()
92
93 def _get_ncx(self):
94 opffile = self._zobject.open(self._opfpath)
95
96 tree = etree.parse(opffile)
97 root = tree.getroot()
98
99 spine = root.find('.//{http://www.idpf.org/2007/opf}spine')
100 tocid = spine.get('toc')
101
102 for element in root.iterfind('.//{http://www.idpf.org/2007/opf}item'):
103 if element.get('id') == tocid:
104 self._ncxpath = self._basepath + element.get('href')
105
106 opffile.close()
107
108 def _verify(self):
109 '''
110 Method to crudely check to verify that what we
111 are dealing with is a epub file or not
112 '''
113 if isinstance(self._file, basestring):
114 if not os.path.exists(self._file):
115 return False
116
117 self._zobject = zipfile.ZipFile(self._file)
118
119 if 'mimetype' not in self._zobject.namelist():
120 return False
121
122 mtypefile = self._zobject.open('mimetype')
123 mimetype = mtypefile.readline()
124
125 # Some files seem to have trailing characters
126 if not mimetype.startswith('application/epub+zip'):
127 return False
128
129 return True
130
131 def get_toc_model(self):
132 '''
133 Returns a GtkTreeModel representation of the
134 Epub table of contents
135 '''
136 return self._navmap.get_gtktreestore()
137
138 def get_flattoc(self):
139 '''
140 Returns a flat (linear) list of files to be
141 rendered.
142 '''
143 return self._navmap.get_flattoc()
144
145 def get_basedir(self):
146 '''
147 Returns the base directory where the contents of the
148 epub has been unzipped
149 '''
150 return self._tempdir
151
152 def get_info(self):
153 '''
154 Returns a EpubInfo object title
155 '''
156 return self._info.title
157
158 def write(self, file_path):
159 '''Create the ZIP archive.
160 The mimetype must be the first file in the archive
161 and it must not be compressed.'''
162
163 # The EPUB must contain the META-INF and mimetype files at the root, so
164 # we'll create the archive in the working directory first
165 # and move it later
166 current_dir = os.getcwd()
167 os.chdir(self._tempdir)
168
169 # Open a new zipfile for writing
170 epub = zipfile.ZipFile(file_path, 'w')
171
172 # Add the mimetype file first and set it to be uncompressed
173 epub.write('mimetype', compress_type=zipfile.ZIP_STORED)
174
175 # For the remaining paths in the EPUB, add all of their files
176 # using normal ZIP compression
177 self._scan_dir('.', epub)
178
179 epub.close()
180 os.chdir(current_dir)
181
182 def _scan_dir(self, path, epub_file):
183 for p in os.listdir(path):
184 logging.error('add file %s', p)
185 if os.path.isdir(os.path.join(path, p)):
186 self._scan_dir(os.path.join(path, p), epub_file)
187 else:
188 if p != 'mimetype':
189 epub_file.write(
190 os.path.join(path, p),
191 compress_type=zipfile.ZIP_DEFLATED)
192
193 def close(self):
194 '''
195 Cleans up (closes open zip files and deletes
196 uncompressed content of Epub.
197 Please call this when a file is being closed or during
198 application exit.
199 '''
200 self._zobject.close()
201 shutil.rmtree(self._tempdir)
+0
-114
epubview/epubinfo.py less more
0 import xml.etree.ElementTree as etree
1
2
3 class EpubInfo():
4
5 # TODO: Cover the entire DC range
6
7 def __init__(self, opffile):
8 self._tree = etree.parse(opffile)
9 self._root = self._tree.getroot()
10 self._e_metadata = self._root.find(
11 '{http://www.idpf.org/2007/opf}metadata')
12
13 self.title = self._get_title()
14 self.creator = self._get_creator()
15 self.date = self._get_date()
16 self.subject = self._get_subject()
17 self.source = self._get_source()
18 self.rights = self._get_rights()
19 self.identifier = self._get_identifier()
20 self.language = self._get_language()
21 self.summary = self._get_description()
22 self.cover_image = self._get_cover_image()
23
24 def _get_data(self, tagname):
25 element = self._e_metadata.find(tagname)
26 return element.text
27
28 def _get_title(self):
29 try:
30 ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}title')
31 except AttributeError:
32 return None
33
34 return ret
35
36 def _get_description(self):
37 try:
38 ret = self._get_data(
39 './/{http://purl.org/dc/elements/1.1/}description')
40 except AttributeError:
41 return None
42
43 return ret
44
45 def _get_creator(self):
46 try:
47 ret = self._get_data(
48 './/{http://purl.org/dc/elements/1.1/}creator')
49 except AttributeError:
50 return None
51 return ret
52
53 def _get_date(self):
54 # TODO: iter
55 try:
56 ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}date')
57 except AttributeError:
58 return None
59
60 return ret
61
62 def _get_source(self):
63 try:
64 ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}source')
65 except AttributeError:
66 return None
67
68 return ret
69
70 def _get_rights(self):
71 try:
72 ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}rights')
73 except AttributeError:
74 return None
75
76 return ret
77
78 def _get_identifier(self):
79 # TODO: iter
80 element = self._e_metadata.find(
81 './/{http://purl.org/dc/elements/1.1/}identifier')
82
83 if element is not None:
84 return {'id': element.get('id'), 'value': element.text}
85 else:
86 return None
87
88 def _get_language(self):
89 try:
90 ret = self._get_data(
91 './/{http://purl.org/dc/elements/1.1/}language')
92 except AttributeError:
93 return None
94
95 return ret
96
97 def _get_subject(self):
98 try:
99 subjectlist = []
100 for element in self._e_metadata.iterfind(
101 './/{http://purl.org/dc/elements/1.1/}subject'):
102 subjectlist.append(element.text)
103 except AttributeError:
104 return None
105
106 return subjectlist
107
108 def _get_cover_image(self):
109 element = self._e_metadata.find('{http://www.idpf.org/2007/opf}meta')
110 if element is not None and element.get('name') == 'cover':
111 return element.get('content')
112 else:
113 return None
+0
-712
epubview/epubview.py less more
0 # Copyright 2009 One Laptop Per Child
1 # Author: Sayamindu Dasgupta <sayamindu@laptop.org>
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
17 from gi.repository import Gtk
18 from gi.repository import GObject
19 from gi.repository import Gdk
20 import widgets
21
22 import logging
23 import os.path
24 import math
25 import shutil
26
27 from jobs import _JobPaginator as _Paginator
28
29 LOADING_HTML = '''
30 <div style="width:100%;height:100%;text-align:center;padding-top:50%;">
31 <h1>Loading...</h1>
32 </div>
33 '''
34
35
36 class _View(Gtk.HBox):
37
38 __gproperties__ = {
39 'scale': (GObject.TYPE_FLOAT, 'the zoom level',
40 'the zoom level of the widget',
41 0.5, 4.0, 1.0, GObject.PARAM_READWRITE),
42 }
43 __gsignals__ = {
44 'page-changed': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
45 ([int, int])),
46 'selection-changed': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
47 ([])),
48 }
49
50 def __init__(self):
51 GObject.threads_init()
52 Gtk.HBox.__init__(self)
53
54 self.connect("destroy", self._destroy_cb)
55
56 self._ready = False
57 self._paginator = None
58 self._loaded_page = -1
59 self._file_loaded = True
60 # self._old_scrollval = -1
61 self._loaded_filename = None
62 self._pagecount = -1
63 self.__going_fwd = True
64 self.__going_back = False
65 self.__page_changed = False
66 self._has_selection = False
67 self.scale = 1.0
68 self._epub = None
69 self._findjob = None
70 self.__in_search = False
71 self.__search_fwd = True
72 self._filelist = None
73 self._internal_link = None
74
75 self._sw = Gtk.ScrolledWindow()
76 self._view = widgets._WebView()
77 self._view.load_string(LOADING_HTML, 'text/html', 'utf-8', '/')
78 settings = self._view.get_settings()
79 settings.props.default_font_family = 'DejaVu LGC Serif'
80 settings.props.enable_plugins = False
81 settings.props.default_encoding = 'utf-8'
82 self._view.connect('load-finished', self._view_load_finished_cb)
83 self._view.connect('scroll-event', self._view_scroll_event_cb)
84 self._view.connect('key-press-event', self._view_keypress_event_cb)
85 self._view.connect('selection-changed',
86 self._view_selection_changed_cb)
87 self._view.connect_after('populate-popup',
88 self._view_populate_popup_cb)
89 self._view.connect('touch-change-page', self.__touch_page_changed_cb)
90
91 self._sw.add(self._view)
92 self._v_vscrollbar = self._sw.get_vscrollbar()
93 self._v_scrollbar_value_changed_cb_id = self._v_vscrollbar.connect(
94 'value-changed', self._v_scrollbar_value_changed_cb)
95 self._scrollbar = Gtk.VScrollbar()
96 self._scrollbar_change_value_cb_id = self._scrollbar.connect(
97 'change-value', self._scrollbar_change_value_cb)
98
99 overlay = Gtk.Overlay()
100 hbox = Gtk.HBox()
101 overlay.add(hbox)
102 hbox.add(self._sw)
103
104 self._scrollbar.props.halign = Gtk.Align.END
105 self._scrollbar.props.valign = Gtk.Align.FILL
106 overlay.add_overlay(self._scrollbar)
107
108 self.pack_start(overlay, True, True, 0)
109
110 self._view.set_can_default(True)
111 self._view.set_can_focus(True)
112
113 def map_cp(widget):
114 widget.setup_touch()
115 widget.disconnect(self._setup_handle)
116
117 self._setup_handle = self._view.connect('map', map_cp)
118
119 def set_document(self, epubdocumentinstance):
120 '''
121 Sets document (should be a Epub instance)
122 '''
123 self._epub = epubdocumentinstance
124 GObject.idle_add(self._paginate)
125
126 def do_get_property(self, property):
127 if property.name == 'has-selection':
128 return self._has_selection
129 elif property.name == 'scale':
130 return self.scale
131 else:
132 raise AttributeError('unknown property %s' % property.name)
133
134 def do_set_property(self, property, value):
135 if property.name == 'scale':
136 self.__set_zoom(value)
137 else:
138 raise AttributeError('unknown property %s' % property.name)
139
140 def get_has_selection(self):
141 '''
142 Returns True if any part of the content is selected
143 '''
144 return self._view.can_copy_clipboard()
145
146 def get_zoom(self):
147 '''
148 Returns the current zoom level
149 '''
150 return self.get_property('scale') * 100.0
151
152 def set_zoom(self, value):
153 '''
154 Sets the current zoom level
155 '''
156 scrollbar_pos = self.get_vertical_pos()
157 self._view.set_zoom_level(value / 100.0)
158 self.set_vertical_pos(scrollbar_pos)
159
160 def _get_scale(self):
161 '''
162 Returns the current zoom level
163 '''
164 return self.get_property('scale')
165
166 def _set_scale(self, value):
167 '''
168 Sets the current zoom level
169 '''
170 self.set_property('scale', value)
171
172 def zoom_in(self):
173 '''
174 Zooms in (increases zoom level by 0.1)
175 '''
176 if self.can_zoom_in():
177 scrollbar_pos = self.get_vertical_pos()
178 self._set_scale(self._get_scale() + 0.1)
179 self.set_vertical_pos(scrollbar_pos)
180 return True
181 else:
182 return False
183
184 def zoom_out(self):
185 '''
186 Zooms out (decreases zoom level by 0.1)
187 '''
188 if self.can_zoom_out():
189 scrollbar_pos = self.get_vertical_pos()
190 self._set_scale(self._get_scale() - 0.1)
191 self.set_vertical_pos(scrollbar_pos)
192 return True
193 else:
194 return False
195
196 def get_vertical_pos(self):
197 """
198 Used to save the scrolled position and restore when needed
199 """
200 return self._v_vscrollbar.get_adjustment().get_value()
201
202 def set_vertical_pos(self, position):
203 """
204 Used to save the scrolled position and restore when needed
205 """
206 self._v_vscrollbar.get_adjustment().set_value(position)
207
208 def can_zoom_in(self):
209 '''
210 Returns True if it is possible to zoom in further
211 '''
212 if self.scale < 4:
213 return True
214 else:
215 return False
216
217 def can_zoom_out(self):
218 '''
219 Returns True if it is possible to zoom out further
220 '''
221 if self.scale > 0.5:
222 return True
223 else:
224 return False
225
226 def get_current_page(self):
227 '''
228 Returns the currently loaded page
229 '''
230 return self._loaded_page
231
232 def get_current_file(self):
233 '''
234 Returns the currently loaded XML file
235 '''
236 # return self._loaded_filename
237 if self._paginator:
238 return self._paginator.get_file_for_pageno(self._loaded_page)
239 else:
240 return None
241
242 def get_pagecount(self):
243 '''
244 Returns the pagecount of the loaded file
245 '''
246 return self._pagecount
247
248 def set_current_page(self, n):
249 '''
250 Loads page number n
251 '''
252 if n < 1 or n > self._pagecount:
253 return False
254 self._load_page(n)
255 return True
256
257 def next_page(self):
258 '''
259 Loads next page if possible
260 Returns True if transition to next page is possible and done
261 '''
262 if self._loaded_page == self._pagecount:
263 return False
264 self._load_next_page()
265 return True
266
267 def previous_page(self):
268 '''
269 Loads previous page if possible
270 Returns True if transition to previous page is possible and done
271 '''
272 if self._loaded_page == 1:
273 return False
274 self._load_prev_page()
275 return True
276
277 def scroll(self, scrolltype, horizontal):
278 '''
279 Scrolls through the pages.
280 Scrolling is horizontal if horizontal is set to True
281 Valid scrolltypes are:
282 Gtk.ScrollType.PAGE_BACKWARD, Gtk.ScrollType.PAGE_FORWARD,
283 Gtk.ScrollType.STEP_BACKWARD, Gtk.ScrollType.STEP_FORWARD
284 Gtk.ScrollType.STEP_START and Gtk.ScrollType.STEP_STOP
285 '''
286 if scrolltype == Gtk.ScrollType.PAGE_BACKWARD:
287 self.__going_back = True
288 self.__going_fwd = False
289 if not self._do_page_transition():
290 self._view.move_cursor(Gtk.MovementStep.PAGES, -1)
291 elif scrolltype == Gtk.ScrollType.PAGE_FORWARD:
292 self.__going_back = False
293 self.__going_fwd = True
294 if not self._do_page_transition():
295 self._view.move_cursor(Gtk.MovementStep.PAGES, 1)
296 elif scrolltype == Gtk.ScrollType.STEP_BACKWARD:
297 self.__going_fwd = False
298 self.__going_back = True
299 if not self._do_page_transition():
300 self._view.move_cursor(Gtk.MovementStep.DISPLAY_LINES, -1)
301 elif scrolltype == Gtk.ScrollType.STEP_FORWARD:
302 self.__going_fwd = True
303 self.__going_back = False
304 if not self._do_page_transition():
305 self._view.move_cursor(Gtk.MovementStep.DISPLAY_LINES, 1)
306 elif scrolltype == Gtk.ScrollType.START:
307 self.__going_back = True
308 self.__going_fwd = False
309 if not self._do_page_transition():
310 self.set_current_page(1)
311 elif scrolltype == Gtk.ScrollType.END:
312 self.__going_back = False
313 self.__going_fwd = True
314 if not self._do_page_transition():
315 self.set_current_page(self._pagecount - 1)
316 else:
317 print ('Got unsupported scrolltype %s' % str(scrolltype))
318
319 def __touch_page_changed_cb(self, widget, forward):
320 if forward:
321 self.scroll(Gtk.ScrollType.PAGE_FORWARD, False)
322 else:
323 self.scroll(Gtk.ScrollType.PAGE_BACKWARD, False)
324
325 def copy(self):
326 '''
327 Copies the current selection to clipboard.
328 '''
329 self._view.copy_clipboard()
330
331 def find_next(self):
332 '''
333 Highlights the next matching item for current search
334 '''
335 self._view.grab_focus()
336
337 if self._view.search_text(self._findjob.get_search_text(),
338 self._findjob.get_case_sensitive(),
339 True, False):
340 return
341 else:
342 path = os.path.join(self._epub.get_basedir(),
343 self._findjob.get_next_file())
344 self.__in_search = True
345 self.__search_fwd = True
346 self._load_file(path)
347
348 def find_previous(self):
349 '''
350 Highlights the previous matching item for current search
351 '''
352 self._view.grab_focus()
353
354 if self._view.search_text(self._findjob.get_search_text(),
355 self._findjob.get_case_sensitive(),
356 False, False):
357 return
358 else:
359 path = os.path.join(self._epub.get_basedir(),
360 self._findjob.get_prev_file())
361 self.__in_search = True
362 self.__search_fwd = False
363 self._load_file(path)
364
365 def _find_changed(self, job):
366 self._view.grab_focus()
367 self._findjob = job
368 self._mark_found_text()
369 self.find_next()
370
371 def _mark_found_text(self):
372 self._view.unmark_text_matches()
373 self._view.mark_text_matches(
374 self._findjob.get_search_text(),
375 case_sensitive=self._findjob.get_case_sensitive(), limit=0)
376 self._view.set_highlight_text_matches(True)
377
378 def __set_zoom(self, value):
379 self._view.set_zoom_level(value)
380 self.scale = value
381
382 def _view_populate_popup_cb(self, view, menu):
383 menu.destroy() # HACK
384 return
385
386 def _view_selection_changed_cb(self, view):
387 self.emit('selection-changed')
388
389 def _view_keypress_event_cb(self, view, event):
390 name = Gdk.keyval_name(event.keyval)
391 if name == 'Page_Down' or name == 'Down':
392 self.__going_back = False
393 self.__going_fwd = True
394 elif name == 'Page_Up' or name == 'Up':
395 self.__going_back = True
396 self.__going_fwd = False
397
398 self._do_page_transition()
399
400 def _view_scroll_event_cb(self, view, event):
401 if event.direction == Gdk.ScrollDirection.DOWN:
402 self.__going_back = False
403 self.__going_fwd = True
404 elif event.direction == Gdk.ScrollDirection.UP:
405 self.__going_back = True
406 self.__going_fwd = False
407
408 self._do_page_transition()
409
410 def _do_page_transition(self):
411 if self.__going_fwd:
412 if self._v_vscrollbar.get_value() >= \
413 self._v_vscrollbar.props.adjustment.props.upper - \
414 self._v_vscrollbar.props.adjustment.props.page_size:
415 self._load_page(self._loaded_page + 1)
416 return True
417 elif self.__going_back:
418 if self._v_vscrollbar.get_value() == \
419 self._v_vscrollbar.props.adjustment.props.lower:
420 self._load_page(self._loaded_page - 1)
421 return True
422
423 return False
424
425 def _view_load_finished_cb(self, v, frame):
426
427 self._file_loaded = True
428 filename = self._view.props.uri.replace('file://', '')
429 if os.path.exists(filename.replace('xhtml', 'xml')):
430 # Hack for making javascript work
431 filename = filename.replace('xhtml', 'xml')
432
433 filename = filename.split('#')[0] # Get rid of anchors
434
435 if self._loaded_page < 1 or filename is None:
436 return False
437
438 self._loaded_filename = filename
439
440 remfactor = self._paginator.get_remfactor_for_file(filename)
441 pages = self._paginator.get_pagecount_for_file(filename)
442 extra = int(math.ceil(
443 remfactor * self._view.get_page_height() / (pages - remfactor)))
444 if extra > 0:
445 self._view.add_bottom_padding(extra)
446
447 if self.__in_search:
448 self._mark_found_text()
449 self._view.search_text(self._findjob.get_search_text(),
450 self._findjob.get_case_sensitive(),
451 self.__search_fwd, False)
452 self.__in_search = False
453 else:
454 if self.__going_back:
455 # We need to scroll to the last page
456 self._scroll_page_end()
457 else:
458 self._scroll_page()
459
460 # process_file = True
461 if self._internal_link is not None:
462 self._view.go_to_link(self._internal_link)
463 vertical_pos = \
464 self._view.get_vertical_position_element(self._internal_link)
465 # set the page number based in the vertical position
466 initial_page = self._paginator.get_base_pageno_for_file(filename)
467 self._loaded_page = initial_page + int(
468 vertical_pos / self._paginator.get_single_page_height())
469
470 # There are epub files, created with Calibre,
471 # where the link in the index points to the end of the previos
472 # file to the needed chapter.
473 # if the link is at the bottom of the page, we open the next file
474 one_page_height = self._paginator.get_single_page_height()
475 self._internal_link = None
476 if vertical_pos > self._view.get_page_height() - one_page_height:
477 logging.error('bottom page link, go to next file')
478 next_file = self._paginator.get_next_filename(filename)
479 if next_file is not None:
480 logging.error('load next file %s', next_file)
481 self.__in_search = False
482 self.__going_back = False
483 # process_file = False
484 GObject.idle_add(self._load_file, next_file)
485
486 # if process_file:
487 # # prepare text to speech
488 # html_file = open(self._loaded_filename)
489 # soup = BeautifulSoup.BeautifulSoup(html_file)
490 # body = soup.find('body')
491 # tags = body.findAll(text=True)
492 # self._all_text = ''.join([tag for tag in tags])
493 # self._prepare_text_to_speech(self._all_text)
494
495 def _prepare_text_to_speech(self, page_text):
496 i = 0
497 j = 0
498 word_begin = 0
499 word_end = 0
500 ignore_chars = [' ', '\n', u'\r', '_', '[', '{', ']', '}', '|',
501 '<', '>', '*', '+', '/', '\\']
502 ignore_set = set(ignore_chars)
503 self.word_tuples = []
504 len_page_text = len(page_text)
505 while i < len_page_text:
506 if page_text[i] not in ignore_set:
507 word_begin = i
508 j = i
509 while j < len_page_text and page_text[j] not in ignore_set:
510 j = j + 1
511 word_end = j
512 i = j
513 word_tuple = (word_begin, word_end,
514 page_text[word_begin: word_end])
515 if word_tuple[2] != u'\r':
516 self.word_tuples.append(word_tuple)
517 i = i + 1
518
519 def _scroll_page_end(self):
520 v_upper = self._v_vscrollbar.props.adjustment.props.upper
521 # v_page_size = self._v_vscrollbar.props.adjustment.props.page_size
522 self._v_vscrollbar.set_value(v_upper)
523
524 def _scroll_page(self):
525 pageno = self._loaded_page
526
527 v_upper = self._v_vscrollbar.props.adjustment.props.upper
528 v_page_size = self._v_vscrollbar.props.adjustment.props.page_size
529
530 scrollfactor = self._paginator.get_scrollfactor_pos_for_pageno(pageno)
531 self._v_vscrollbar.set_value((v_upper - v_page_size) * scrollfactor)
532
533 def _paginate(self):
534 filelist = []
535 for i in self._epub._navmap.get_flattoc():
536 filelist.append(os.path.join(self._epub._tempdir, i))
537 # init files info
538 self._filelist = filelist
539 self._paginator = _Paginator(filelist)
540 self._paginator.connect('paginated', self._paginated_cb)
541
542 def get_filelist(self):
543 return self._filelist
544
545 def get_tempdir(self):
546 return self._epub._tempdir
547
548 def _load_next_page(self):
549 self._load_page(self._loaded_page + 1)
550
551 def _load_prev_page(self):
552 self._load_page(self._loaded_page - 1)
553
554 def _v_scrollbar_value_changed_cb(self, scrollbar):
555 if self._loaded_page < 1:
556 return
557 scrollval = scrollbar.get_value()
558 scroll_upper = self._v_vscrollbar.props.adjustment.props.upper
559 scroll_page_size = self._v_vscrollbar.props.adjustment.props.page_size
560
561 if self.__going_fwd and not self._loaded_page == self._pagecount:
562 if self._paginator.get_file_for_pageno(self._loaded_page) != \
563 self._paginator.get_file_for_pageno(self._loaded_page + 1):
564 # We don't need this if the next page is in another file
565 return
566
567 scrollfactor_next = \
568 self._paginator.get_scrollfactor_pos_for_pageno(
569 self._loaded_page + 1)
570 if scrollval > 0:
571 scrollfactor = scrollval / (scroll_upper - scroll_page_size)
572 else:
573 scrollfactor = 0
574 if scrollfactor >= scrollfactor_next:
575 self._on_page_changed(self._loaded_page, self._loaded_page + 1)
576 elif self.__going_back and self._loaded_page > 1:
577 if self._paginator.get_file_for_pageno(self._loaded_page) != \
578 self._paginator.get_file_for_pageno(self._loaded_page - 1):
579 return
580
581 scrollfactor_cur = \
582 self._paginator.get_scrollfactor_pos_for_pageno(
583 self._loaded_page)
584 if scrollval > 0:
585 scrollfactor = scrollval / (scroll_upper - scroll_page_size)
586 else:
587 scrollfactor = 0
588
589 if scrollfactor <= scrollfactor_cur:
590 self._on_page_changed(self._loaded_page, self._loaded_page - 1)
591
592 def _on_page_changed(self, oldpage, pageno):
593 if oldpage == pageno:
594 return
595 self.__page_changed = True
596 self._loaded_page = pageno
597 self._scrollbar.handler_block(self._scrollbar_change_value_cb_id)
598 self._scrollbar.set_value(pageno)
599 self._scrollbar.handler_unblock(self._scrollbar_change_value_cb_id)
600 # the indexes in read activity are zero based
601 self.emit('page-changed', (oldpage - 1), (pageno - 1))
602
603 def _load_page(self, pageno):
604 if pageno > self._pagecount or pageno < 1:
605 # TODO: Cause an exception
606 return
607 if self._loaded_page == pageno:
608 return
609
610 filename = self._paginator.get_file_for_pageno(pageno)
611 filename = filename.replace('file://', '')
612
613 if filename != self._loaded_filename:
614 self._loaded_filename = filename
615 if not self._file_loaded:
616 # wait until the file is loaded
617 return
618 self._file_loaded = False
619
620 """
621 TODO: disabled because javascript can't be executed
622 with the velocity needed
623 # Copy javascript to highligth text to speech
624 destpath, destname = os.path.split(filename.replace('file://', ''))
625 shutil.copy('./epubview/highlight_words.js', destpath)
626 self._insert_js_reference(filename.replace('file://', ''),
627 destpath)
628 IMPORTANT: Find a way to do this without modify the files
629 now text highlight is implemented and the epub file is saved
630 """
631
632 if filename.endswith('xml'):
633 dest = filename.replace('xml', 'xhtml')
634 if not os.path.exists(dest):
635 os.symlink(filename, dest)
636 self._view.load_uri('file://' + dest)
637 else:
638 self._view.load_uri('file://' + filename)
639 else:
640 self._loaded_page = pageno
641 self._scroll_page()
642 self._on_page_changed(self._loaded_page, pageno)
643
644 def _insert_js_reference(self, file_name, path):
645 js_reference = '<script type="text/javascript" ' + \
646 'src="./highlight_words.js"></script>'
647 o = open(file_name + '.tmp', 'a')
648 for line in open(file_name):
649 line = line.replace('</head>', js_reference + '</head>')
650 o.write(line + "\n")
651 o.close()
652 shutil.copy(file_name + '.tmp', file_name)
653
654 def _load_file(self, path):
655 self._internal_link = None
656 if path.find('#') > -1:
657 self._internal_link = path[path.find('#'):]
658 path = path[:path.find('#')]
659
660 for filepath in self._filelist:
661 if filepath.endswith(path):
662 self._view.load_uri('file://' + filepath)
663 oldpage = self._loaded_page
664 self._loaded_page = \
665 self._paginator.get_base_pageno_for_file(filepath)
666 self._scroll_page()
667 self._on_page_changed(oldpage, self._loaded_page)
668 break
669
670 def _scrollbar_change_value_cb(self, range, scrolltype, value):
671 if scrolltype == Gtk.ScrollType.STEP_FORWARD:
672 self.__going_fwd = True
673 self.__going_back = False
674 if not self._do_page_transition():
675 self._view.move_cursor(Gtk.MovementStep.DISPLAY_LINES, 1)
676 elif scrolltype == Gtk.ScrollType.STEP_BACKWARD:
677 self.__going_fwd = False
678 self.__going_back = True
679 if not self._do_page_transition():
680 self._view.move_cursor(Gtk.MovementStep.DISPLAY_LINES, -1)
681 elif scrolltype == Gtk.ScrollType.JUMP or \
682 scrolltype == Gtk.ScrollType.PAGE_FORWARD or \
683 scrolltype == Gtk.ScrollType.PAGE_BACKWARD:
684 if value > self._scrollbar.props.adjustment.props.upper:
685 self._load_page(self._pagecount)
686 else:
687 self._load_page(round(value))
688 else:
689 print 'Warning: unknown scrolltype %s with value %f' \
690 % (str(scrolltype), value)
691
692 # FIXME: This should not be needed here
693 self._scrollbar.set_value(self._loaded_page)
694
695 if self.__page_changed:
696 self.__page_changed = False
697 return False
698 else:
699 return True
700
701 def _paginated_cb(self, object):
702 self._ready = True
703
704 self._pagecount = self._paginator.get_total_pagecount()
705 self._scrollbar.set_range(1.0, self._pagecount - 1.0)
706 self._scrollbar.set_increments(1.0, 1.0)
707 self._view.grab_focus()
708 self._view.grab_default()
709
710 def _destroy_cb(self, widget):
711 self._epub.close()
+0
-89
epubview/highlight_words.js less more
0 var parentElement;
1 var actualChild;
2 var actualWord;
3 var words;
4 var originalNode = null;
5 var modifiedNode = null;
6
7 function trim(s) {
8 s = ( s || '' ).replace( /^\s+|\s+$/g, '' );
9 return s.replace(/[\n\r\t]/g,' ');
10 }
11
12
13 function init() {
14 parentElement = document.getElementsByTagName("body")[0];
15 actualChild = new Array();
16 actualWord = 0;
17 actualChild.push(0);
18 }
19
20 function highLightNextWordInt() {
21 var nodeList = parentElement.childNodes;
22 ini_posi = actualChild[actualChild.length - 1];
23 for (var i=ini_posi; i < nodeList.length; i++) {
24 var node = nodeList[i];
25 if ((node.nodeName == "#text") && (trim(node.nodeValue) != '')) {
26 node_text = trim(node.nodeValue);
27 words = node_text.split(" ");
28 if (actualWord < words.length) {
29 originalNode = document.createTextNode(node.nodeValue);
30
31 prev_text = '';
32 for (var p1 = 0; p1 < actualWord; p1++) {
33 prev_text = prev_text + words[p1] + " ";
34 }
35 var textNode1 = document.createTextNode(prev_text);
36 var textNode2 = document.createTextNode(words[actualWord]+" ");
37 post_text = '';
38 for (var p2 = actualWord + 1; p2 < words.length; p2++) {
39 post_text = post_text + words[p2] + " ";
40 }
41 var textNode3 = document.createTextNode(post_text);
42 var newParagraph = document.createElement('p');
43 var boldNode = document.createElement('b');
44 boldNode.appendChild(textNode2);
45 newParagraph.appendChild(textNode1);
46 newParagraph.appendChild(boldNode);
47 newParagraph.appendChild(textNode3);
48
49 parentElement.insertBefore(newParagraph, node);
50 parentElement.removeChild(node);
51 modifiedNode = newParagraph;
52
53 actualWord = actualWord + 1;
54 if (actualWord >= words.length) {
55 actualChild.pop();
56 actualChild[actualChild.length - 1] = actualChild[actualChild.length - 1] + 2;
57 actualWord = 0;
58 parentElement = parentElement.parentNode;
59 }
60 }
61 throw "exit";
62 } else {
63 if (node.childNodes.length > 0) {
64 parentElement = node;
65 actualChild.push(0);
66 actualWord = 0;
67 highLightNextWordInt();
68 actualChild.pop();
69 }
70 }
71 }
72 return;
73 }
74
75
76 function highLightNextWord() {
77 if (typeof parentElement == "undefined") {
78 init();
79 }
80 if (originalNode != null) {
81 modifiedNode.parentNode.insertBefore(originalNode, modifiedNode);
82 modifiedNode.parentNode.removeChild(modifiedNode);
83 }
84 try {
85 highLightNextWordInt();
86 } catch(er) {
87 }
88 }
+0
-319
epubview/jobs.py less more
0 # Copyright 2009 One Laptop Per Child
1 # Author: Sayamindu Dasgupta <sayamindu@laptop.org>
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
17
18 from gi.repository import GObject
19 from gi.repository import Gtk
20 import widgets
21 import math
22 import os.path
23 import BeautifulSoup
24
25 import threading
26
27 PAGE_WIDTH = 135
28 PAGE_HEIGHT = 216
29
30
31 def _pixel_to_mm(pixel, dpi):
32 inches = pixel / dpi
33 return int(inches / 0.03937)
34
35
36 def _mm_to_pixel(mm, dpi):
37 inches = mm * 0.03937
38 return int(inches * dpi)
39
40
41 class SearchThread(threading.Thread):
42
43 def __init__(self, obj):
44 threading.Thread.__init__(self)
45 self.obj = obj
46 self.stopthread = threading.Event()
47
48 def _start_search(self):
49 for entry in self.obj.flattoc:
50 if self.stopthread.isSet():
51 break
52 filepath = os.path.join(self.obj._document.get_basedir(), entry)
53 f = open(filepath)
54 if self._searchfile(f):
55 self.obj._matchfilelist.append(entry)
56 f.close()
57
58 self.obj._finished = True
59 GObject.idle_add(self.obj.emit, 'updated')
60
61 return False
62
63 def _searchfile(self, fileobj):
64 soup = BeautifulSoup.BeautifulSoup(fileobj)
65 body = soup.find('body')
66 tags = body.findChildren(True)
67 for tag in tags:
68 if tag.string is not None:
69 if tag.string.lower().find(self.obj._text.lower()) > -1:
70 return True
71
72 return False
73
74 def run(self):
75 self._start_search()
76
77 def stop(self):
78 self.stopthread.set()
79
80
81 class _JobPaginator(GObject.GObject):
82
83 __gsignals__ = {
84 'paginated': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ([])),
85 }
86
87 def __init__(self, filelist):
88 GObject.GObject.__init__(self)
89
90 self._filelist = filelist
91 self._filedict = {}
92 self._pagemap = {}
93
94 self._bookheight = 0
95 self._count = 0
96 self._pagecount = 0
97
98 # TODO
99 """
100 self._screen = Gdk.Screen.get_default()
101 self._old_fontoptions = self._screen.get_font_options()
102 options = cairo.FontOptions()
103 options.set_hint_style(cairo.HINT_STYLE_MEDIUM)
104 options.set_antialias(cairo.ANTIALIAS_GRAY)
105 options.set_subpixel_order(cairo.SUBPIXEL_ORDER_DEFAULT)
106 options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
107 self._screen.set_font_options(options)
108 """
109
110 self._temp_win = Gtk.Window()
111 self._temp_view = widgets._WebView(only_to_measure=True)
112
113 settings = self._temp_view.get_settings()
114 settings.props.default_font_family = 'DejaVu LGC Serif'
115 settings.props.sans_serif_font_family = 'DejaVu LGC Sans'
116 settings.props.serif_font_family = 'DejaVu LGC Serif'
117 settings.props.monospace_font_family = 'DejaVu LGC Sans Mono'
118 settings.props.enforce_96_dpi = True
119 # FIXME: This does not seem to work
120 # settings.props.auto_shrink_images = False
121 settings.props.enable_plugins = False
122 settings.props.default_font_size = 12
123 settings.props.default_monospace_font_size = 10
124 settings.props.default_encoding = 'utf-8'
125
126 sw = Gtk.ScrolledWindow()
127 sw.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
128 self._dpi = 96
129 self._single_page_height = _mm_to_pixel(PAGE_HEIGHT, self._dpi)
130 sw.set_size_request(_mm_to_pixel(PAGE_WIDTH, self._dpi),
131 self._single_page_height)
132 sw.add(self._temp_view)
133 self._temp_win.add(sw)
134 self._temp_view.connect('load-finished', self._page_load_finished_cb)
135
136 self._temp_win.show_all()
137 self._temp_win.unmap()
138
139 self._temp_view.open(self._filelist[self._count])
140
141 def get_single_page_height(self):
142 """
143 Returns the height in pixels of a single page
144 """
145 return self._single_page_height
146
147 def get_next_filename(self, actual_filename):
148 for n in range(len(self._filelist)):
149 filename = self._filelist[n]
150 if filename == actual_filename:
151 if n < len(self._filelist):
152 return self._filelist[n + 1]
153 return None
154
155 def _page_load_finished_cb(self, v, frame):
156 f = v.get_main_frame()
157 pageheight = v.get_page_height()
158
159 if pageheight <= self._single_page_height:
160 pages = 1
161 else:
162 pages = pageheight / float(self._single_page_height)
163 for i in range(1, int(math.ceil(pages) + 1)):
164 if pages - i < 0:
165 pagelen = (pages - math.floor(pages)) / pages
166 else:
167 pagelen = 1 / pages
168 self._pagemap[float(self._pagecount + i)] = \
169 (f.props.uri, (i - 1) / math.ceil(pages), pagelen)
170
171 self._pagecount += int(math.ceil(pages))
172 self._filedict[f.props.uri.replace('file://', '')] = \
173 (math.ceil(pages), math.ceil(pages) - pages)
174 self._bookheight += pageheight
175
176 if self._count + 1 >= len(self._filelist):
177 # TODO
178 # self._screen.set_font_options(self._old_fontoptions)
179 self.emit('paginated')
180 GObject.idle_add(self._cleanup)
181 else:
182 self._count += 1
183 self._temp_view.open(self._filelist[self._count])
184
185 def _cleanup(self):
186 self._temp_win.destroy()
187
188 def get_file_for_pageno(self, pageno):
189 '''
190 Returns the file in which pageno occurs
191 '''
192 return self._pagemap[pageno][0]
193
194 def get_scrollfactor_pos_for_pageno(self, pageno):
195 '''
196 Returns the position scrollfactor (fraction) for pageno
197 '''
198 return self._pagemap[pageno][1]
199
200 def get_scrollfactor_len_for_pageno(self, pageno):
201 '''
202 Returns the length scrollfactor (fraction) for pageno
203 '''
204 return self._pagemap[pageno][2]
205
206 def get_pagecount_for_file(self, filename):
207 '''
208 Returns the number of pages in file
209 '''
210 return self._filedict[filename][0]
211
212 def get_base_pageno_for_file(self, filename):
213 '''
214 Returns the pageno which begins in filename
215 '''
216 for key in self._pagemap.keys():
217 if self._pagemap[key][0].replace('file://', '') == filename:
218 return key
219
220 return None
221
222 def get_remfactor_for_file(self, filename):
223 '''
224 Returns the remainder
225 factor (1 - fraction length of last page in file)
226 '''
227 return self._filedict[filename][1]
228
229 def get_total_pagecount(self):
230 '''
231 Returns the total pagecount for the Epub file
232 '''
233 return self._pagecount
234
235 def get_total_height(self):
236 '''
237 Returns the total height of the Epub in pixels
238 '''
239 return self._bookheight
240
241
242 class _JobFind(GObject.GObject):
243 __gsignals__ = {
244 'updated': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ([])),
245 }
246
247 def __init__(self, document, start_page, n_pages, text,
248 case_sensitive=False):
249 """
250 Only case_sensitive=False is implemented
251 """
252 GObject.GObject.__init__(self)
253
254 self._finished = False
255 self._document = document
256 self._start_page = start_page
257 self._n_pages = n_pages
258 self._text = text
259 self._case_sensitive = case_sensitive
260 self.flattoc = self._document.get_flattoc()
261 self._matchfilelist = []
262 self._current_file_index = 0
263 self.threads = []
264
265 s_thread = SearchThread(self)
266 self.threads.append(s_thread)
267 s_thread.start()
268
269 def cancel(self):
270 '''
271 Cancels the search job
272 '''
273 for s_thread in self.threads:
274 s_thread.stop()
275
276 def is_finished(self):
277 '''
278 Returns True if the entire search job has been finished
279 '''
280 return self._finished
281
282 def get_next_file(self):
283 '''
284 Returns the next file which has the search pattern
285 '''
286 self._current_file_index += 1
287 try:
288 path = self._matchfilelist[self._current_file_index]
289 except IndexError:
290 self._current_file_index = 0
291 path = self._matchfilelist[self._current_file_index]
292
293 return path
294
295 def get_prev_file(self):
296 '''
297 Returns the previous file which has the search pattern
298 '''
299 self._current_file_index -= 1
300 try:
301 path = self._matchfilelist[self._current_file_index]
302 except IndexError:
303 self._current_file_index = -1
304 path = self._matchfilelist[self._current_file_index]
305
306 return path
307
308 def get_search_text(self):
309 '''
310 Returns the search text
311 '''
312 return self._text
313
314 def get_case_sensitive(self):
315 '''
316 Returns True if the search is case-sensitive
317 '''
318 return self._case_sensitive
+0
-102
epubview/navmap.py less more
0 import xml.etree.ElementTree as etree
1 from gi.repository import Gtk
2
3
4 class NavPoint(object):
5
6 def __init__(self, label, contentsrc, children=[]):
7 self._label = label
8 self._contentsrc = contentsrc
9 self._children = children
10
11 def get_label(self):
12 return self._label
13
14 def get_contentsrc(self):
15 return self._contentsrc
16
17 def get_children(self):
18 return self._children
19
20
21 class NavMap(object):
22 def __init__(self, opffile, ncxfile, basepath):
23 self._basepath = basepath
24 self._opffile = opffile
25 self._tree = etree.parse(ncxfile)
26 self._root = self._tree.getroot()
27 self._gtktreestore = Gtk.TreeStore(str, str)
28 self._flattoc = []
29
30 self._populate_flattoc()
31 self._populate_toc()
32
33 def _populate_flattoc(self):
34 tree = etree.parse(self._opffile)
35 root = tree.getroot()
36
37 itemmap = {}
38 manifest = root.find('.//{http://www.idpf.org/2007/opf}manifest')
39 for element in manifest.iterfind('{http://www.idpf.org/2007/opf}item'):
40 itemmap[element.get('id')] = element
41
42 spine = root.find('.//{http://www.idpf.org/2007/opf}spine')
43 for element in spine.iterfind('{http://www.idpf.org/2007/opf}itemref'):
44 idref = element.get('idref')
45 href = itemmap[idref].get('href')
46 self._flattoc.append(self._basepath + href)
47
48 self._opffile.close()
49
50 def _populate_toc(self):
51 navmap = self._root.find(
52 '{http://www.daisy.org/z3986/2005/ncx/}navMap')
53 for navpoint in navmap.iterfind(
54 './{http://www.daisy.org/z3986/2005/ncx/}navPoint'):
55 self._process_navpoint(navpoint)
56
57 def _gettitle(self, navpoint):
58 text = navpoint.find(
59 './{http://www.daisy.org/z3986/2005/ncx/}' +
60 'navLabel/{http://www.daisy.org/z3986/2005/ncx/}text')
61 return text.text
62
63 def _getcontent(self, navpoint):
64 text = navpoint.find(
65 './{http://www.daisy.org/z3986/2005/ncx/}content')
66 if text is not None:
67 return self._basepath + text.get('src')
68 else:
69 return ""
70
71 def _process_navpoint(self, navpoint, parent=None):
72 title = self._gettitle(navpoint)
73 content = self._getcontent(navpoint)
74
75 # print title, content
76
77 iter = self._gtktreestore.append(parent, [title, content])
78 # self._flattoc.append((title, content))
79
80 childnavpointlist = list(navpoint.iterfind(
81 './{http://www.daisy.org/z3986/2005/ncx/}navPoint'))
82
83 if len(childnavpointlist):
84 for childnavpoint in childnavpointlist:
85 self._process_navpoint(childnavpoint, parent=iter)
86 else:
87 return
88
89 def get_gtktreestore(self):
90 '''
91 Returns a GtkTreeModel representation of the
92 Epub table of contents
93 '''
94 return self._gtktreestore
95
96 def get_flattoc(self):
97 '''
98 Returns a flat (linear) list of files to be
99 rendered.
100 '''
101 return self._flattoc
+0
-115
epubview/widgets.py less more
0 import logging
1
2 from gi.repository import WebKit
3 from gi.repository import Gdk
4 from gi.repository import GObject
5
6
7 class _WebView(WebKit.WebView):
8
9 __gsignals__ = {
10 'touch-change-page': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE,
11 ([bool])), }
12
13 def __init__(self, only_to_measure=False):
14 WebKit.WebView.__init__(self)
15 self._only_to_measure = only_to_measure
16
17 def setup_touch(self):
18 self.get_window().set_events(
19 self.get_window().get_events() | Gdk.EventMask.TOUCH_MASK)
20 self.connect('event', self.__event_cb)
21
22 def __event_cb(self, widget, event):
23 if event.type == Gdk.EventType.TOUCH_BEGIN:
24 x = event.touch.x
25 view_width = widget.get_allocation().width
26 if x > view_width * 3 / 4:
27 self.emit('touch-change-page', True)
28 elif x < view_width * 1 / 4:
29 self.emit('touch-change-page', False)
30
31 def get_page_height(self):
32 '''
33 Gets height (in pixels) of loaded (X)HTML page.
34 This is done via javascript at the moment
35 '''
36 hide_scrollbar_js = ''
37 if self._only_to_measure:
38 hide_scrollbar_js = \
39 'document.documentElement.style.overflow = "hidden";'
40
41 oldtitle = self.get_main_frame().get_title()
42
43 js = """
44 document.documentElement.style.margin = "50px";
45 if (document.body == null) {
46 document.title = 0;
47 } else {
48 %s
49 document.title=Math.max(document.body.scrollHeight,
50 document.body.offsetHeight,
51 document.documentElement.clientHeight,
52 document.documentElement.scrollHeight,
53 document.documentElement.offsetHeight);
54 };
55 """ % hide_scrollbar_js
56 self.execute_script(js)
57 ret = self.get_main_frame().get_title()
58 logging.error('get_page_height %s', ret)
59 self.execute_script('document.title=%s;' % oldtitle)
60 try:
61 return int(ret)
62 except ValueError:
63 return 0
64
65 def add_bottom_padding(self, incr):
66 '''
67 Adds incr pixels of padding to the end of the loaded (X)HTML page.
68 This is done via javascript at the moment
69 '''
70 js = """
71 var newdiv = document.createElement("div");
72 newdiv.style.height = "%dpx";
73 document.body.appendChild(newdiv);
74 """ % incr
75 self.execute_script(js)
76
77 def highlight_next_word(self):
78 '''
79 Highlight next word (for text to speech)
80 '''
81 self.execute_script('highLightNextWord();')
82
83 def go_to_link(self, id_link):
84 self.execute_script('window.location.href = "%s";' % id_link)
85
86 def get_vertical_position_element(self, id_link):
87 '''
88 Get the vertical position of a element, in pixels
89 '''
90 # remove the first '#' char
91 id_link = id_link[1:]
92 oldtitle = self.get_main_frame().get_title()
93 js = """
94 obj = document.getElementById('%s');
95 var top = 0;
96 if(obj.offsetParent) {
97 while(1) {
98 top += obj.offsetTop;
99 if(!obj.offsetParent) {
100 break;
101 };
102 obj = obj.offsetParent;
103 };
104 } else if(obj.y) {
105 top += obj.y;
106 };
107 document.title=top;""" % id_link
108 self.execute_script(js)
109 ret = self.get_main_frame().get_title()
110 self.execute_script('document.title=%s;' % oldtitle)
111 try:
112 return int(ret)
113 except ValueError:
114 return 0
77 msgstr ""
88 "Project-Id-Version: PACKAGE VERSION\n"
99 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2017-06-01 08:19+1000\n"
10 "POT-Creation-Date: 2017-08-12 13:27+1000\n"
1111 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1212 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1313 "Language-Team: LANGUAGE <LL@li.org>\n"
117117 msgid "All the information related with this bookmark will be lost"
118118 msgstr ""
119119
120 #: readactivity.py:954
120 #: readactivity.py:953
121121 msgid "Receiving book..."
122122 msgstr ""
123123
124 #: readactivity.py:1032 readactivity.py:1227
124 #: readactivity.py:1027 readactivity.py:1222
125125 #, python-format
126126 msgid "%s (Page %d)"
127127 msgstr ""
224224 #: comicadapter.py:70
225225 msgid "No readable images were found"
226226 msgstr "Calere maromo kwane pe onwongere"
227
228 #, python-format
229 #~ msgid "Page %d"
230 #~ msgstr "Potbuk %d"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
221229 #: comicadapter.py:70
222230 msgid "No readable images were found"
223231 msgstr ""
232
233 #~ msgid "%"
234 #~ msgstr "%"
235
236 #~ msgid "Choose document"
237 #~ msgstr "Kies dokument"
238
239 #~ msgid "Edit"
240 #~ msgstr "Redigeer"
241
242 #~ msgid "View"
243 #~ msgstr "Besigtig"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
8 # SOME DESCRIPTIVE TITLE.
9 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
10 # This file is distributed under the same license as the PACKAGE package.
11 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
12 # SOME DESCRIPTIVE TITLE.
13 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
14 # This file is distributed under the same license as the PACKAGE package.
15 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
16 # SOME DESCRIPTIVE TITLE.
17 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
18 # This file is distributed under the same license as the PACKAGE package.
19 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
020 # SOME DESCRIPTIVE TITLE.
121 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
222 # This file is distributed under the same license as the PACKAGE package.
222242 #: comicadapter.py:70
223243 msgid "No readable images were found"
224244 msgstr ""
245
246 #~ msgid "%"
247 #~ msgstr "%"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
03 # translation of read-activity.po to Arabic
14 # Khaled Hosny <khaledhosny@eglug.org>, 2007, 2011.
25 # Ahmed Mansour <atphalix@users.arabeyes.org>, 2008.
225228 #: comicadapter.py:70
226229 msgid "No readable images were found"
227230 msgstr ""
231
232 #, python-format
233 #~ msgid "Page %d"
234 #~ msgstr "الصفحه %d"
235
236 #, python-format
237 #~ msgid "Page %(current)i of %(total_pages)i"
238 #~ msgstr "صفحة %(current)i من %(total_pages)i"
239
240 #~ msgid "%"
241 #~ msgstr "%"
242
243 #~ msgid "pitch adjusted"
244 #~ msgstr "ضُبِطت الشَّدة"
245
246 #~ msgid "rate adjusted"
247 #~ msgstr "ضُبِطت السرعة"
248
249 #~ msgid "Choose document"
250 #~ msgstr "اختر الوثيقة"
251
252 #, python-format
253 #~ msgid "Page %i of %i"
254 #~ msgstr "صفحة %i من %i"
255
256 #~ msgid "Edit"
257 #~ msgstr "تحرير"
258
259 #~ msgid "View"
260 #~ msgstr "عرض"
261
262 #~ msgid "Read Activity"
263 #~ msgstr "نشاط القراءة"
258258 #: comicadapter.py:70
259259 msgid "No readable images were found"
260260 msgstr ""
261
262 #, python-format
263 #~ msgid "Page %d"
264 #~ msgstr "%d jakhuni laphi"
265
266 # "Página
267 #, python-format
268 #~ msgid "Page %(current)i of %(total_pages)i"
269 #~ msgstr "Laphi %(current)i qipatsti %(total_pages)i"
270
271 # "%"
272 #~ msgid "%"
273 #~ msgstr "%"
274
275 # "tono ajustado"
276 #~ msgid "pitch adjusted"
277 #~ msgstr "istaña jitinakayaña"
278
279 # "velocidad ajustada"
280 #~ msgid "rate adjusted"
281 #~ msgstr "katakikuna jitinakayaña"
282
283 # "Escoger documento"
284 #~ msgid "Choose document"
285 #~ msgstr "Wakichaña qatukaña"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
222230 #: comicadapter.py:70
223231 msgid "No readable images were found"
224232 msgstr ""
233
234 #~ msgid "%"
235 #~ msgstr "%"
236
237 #~ msgid "Edit"
238 #~ msgstr "Редактиране"
239
240 #~ msgid "View"
241 #~ msgstr "Изглед"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
221229 #: comicadapter.py:70
222230 msgid "No readable images were found"
223231 msgstr ""
232
233 #~ msgid "%"
234 #~ msgstr "%"
235
236 #~ msgid "Choose document"
237 #~ msgstr "নথি বেছে নাও"
238
239 #, python-format
240 #~ msgid "Page %i of %i"
241 #~ msgstr "%i এর %i তম পৃষ্ঠা"
242
243 #~ msgid "Edit"
244 #~ msgstr "সম্পাদনা"
245
246 #~ msgid "View"
247 #~ msgstr "প্রদর্শন"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
225233 #: comicadapter.py:70
226234 msgid "No readable images were found"
227235 msgstr ""
236
237 #~ msgid "%"
238 #~ msgstr "%"
239
240 #~ msgid "Edit"
241 #~ msgstr "সম্পাদন"
242
243 #~ msgid "View"
244 #~ msgstr "প্রদর্শন"
221221 #: comicadapter.py:70
222222 msgid "No readable images were found"
223223 msgstr ""
224
225 #~ msgid "%"
226 #~ msgstr "%"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
225233 #: comicadapter.py:70
226234 msgid "No readable images were found"
227235 msgstr ""
236
237 #, python-format
238 #~ msgid "Page %d"
239 #~ msgstr "Pàgina %d"
240
241 #~ msgid "%"
242 #~ msgstr "%"
243
244 #~ msgid "Choose document"
245 #~ msgstr "Triar document"
246
247 #, python-format
248 #~ msgid "Page %i of %i"
249 #~ msgstr "Pàgina %i sobre %i"
250
251 #~ msgid "Edit"
252 #~ msgstr "Editar"
253
254 #~ msgid "View"
255 #~ msgstr "Veure"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
221229 #: comicadapter.py:70
222230 msgid "No readable images were found"
223231 msgstr ""
232
233 #~ msgid "%"
234 #~ msgstr "%"
235
236 #~ msgid "Choose document"
237 #~ msgstr "Vybrat dokument"
238
239 #~ msgid "Edit"
240 #~ msgstr "Upravit"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
223231 #: comicadapter.py:70
224232 msgid "No readable images were found"
225233 msgstr ""
234
235 #, python-format
236 #~ msgid "Page %d"
237 #~ msgstr "Side %d"
238
239 #, python-format
240 #~ msgid "Page %(current)i of %(total_pages)i"
241 #~ msgstr "Side %(current)i af %(total_pages)i"
242
243 #~ msgid "%"
244 #~ msgstr "%"
245
246 #~ msgid "pitch adjusted"
247 #~ msgstr "tonehøjde tilpasset"
248
249 #~ msgid "rate adjusted"
250 #~ msgstr "hastighed tilpasset"
251
252 #~ msgid "Choose document"
253 #~ msgstr "Vælg dokument"
254
255 #, python-format
256 #~ msgid "Page %i of %i"
257 #~ msgstr "Side %i af %i"
258
259 #~ msgid "Edit"
260 #~ msgstr "Redigér"
261
262 #~ msgid "View"
263 #~ msgstr "Vis"
00 # SOME DESCRIPTIVE TITLE.
11 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # SOME DESCRIPTIVE TITLE.
4 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
5 # This file is distributed under the same license as the PACKAGE package.
6 # SOME DESCRIPTIVE TITLE.
7 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
8 # This file is distributed under the same license as the PACKAGE package.
9 # SOME DESCRIPTIVE TITLE.
10 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
11 # This file is distributed under the same license as the PACKAGE package.
12 # SOME DESCRIPTIVE TITLE.
13 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
14 # This file is distributed under the same license as the PACKAGE package.
15 # SOME DESCRIPTIVE TITLE.
16 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
17 # This file is distributed under the same license as the PACKAGE package.
18 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
20 # This file is distributed under the same license as the PACKAGE package.
21 # SOME DESCRIPTIVE TITLE.
22 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
23 # This file is distributed under the same license as the PACKAGE package.
24 # SOME DESCRIPTIVE TITLE.
25 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
26 # This file is distributed under the same license as the PACKAGE package.
27 # SOME DESCRIPTIVE TITLE.
28 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
29 # This file is distributed under the same license as the PACKAGE package.
30 # SOME DESCRIPTIVE TITLE.
31 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
32 # This file is distributed under the same license as the PACKAGE package.
33 # SOME DESCRIPTIVE TITLE.
34 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
35 # This file is distributed under the same license as the PACKAGE package.
36 # SOME DESCRIPTIVE TITLE.
37 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
38 # This file is distributed under the same license as the PACKAGE package.
239 # This file is distributed under the same license as the PACKAGE package.
340 # Fabian Affolter <fab@fedoraproject.org>, 2007.
441 # Markus Schlager <m.slg@gmx.de>, 2012.
227264 #: comicadapter.py:70
228265 msgid "No readable images were found"
229266 msgstr "Es wurden keine lesbaren Bilder gefunden"
267
268 #, python-format
269 #~ msgid "Page %d"
270 #~ msgstr "Seite %d"
271
272 #, python-format
273 #~ msgid "Page %(current)i of %(total_pages)i"
274 #~ msgstr "Seite %(current)i von %(total_pages)i"
275
276 #~ msgid "%"
277 #~ msgstr "%"
278
279 #~ msgid "pitch adjusted"
280 #~ msgstr "Tonhöhe angepasst"
281
282 #~ msgid "rate adjusted"
283 #~ msgstr "Rate angepasst"
284
285 #~ msgid "Choose document"
286 #~ msgstr "Dokument auswählen"
287
288 #, python-format
289 #~ msgid "Page %i of %i"
290 #~ msgstr "Seite %i von %i"
291
292 #~ msgid "Edit"
293 #~ msgstr "Bearbeiten"
294
295 #~ msgid "View"
296 #~ msgstr "Ansehen"
297
298 #~ msgid "Read Activity"
299 #~ msgstr "Lese-Aktivitäten"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
220228 #: comicadapter.py:70
221229 msgid "No readable images were found"
222230 msgstr ""
231
232 #~ msgid "%"
233 #~ msgstr "%"
234
235 #~ msgid "Edit"
236 #~ msgstr "ཞུན་དག"
237
238 #~ msgid "View"
239 #~ msgstr "མཐོང་སྣང་།"
0 # Greek translation of Read project.
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
8 # Greek translation of Xbook project.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
311 # Simos Xenitellis <simos.lists@googlemail.com>, 2007.
221229 #: comicadapter.py:70
222230 msgid "No readable images were found"
223231 msgstr ""
232
233 #, python-format
234 #, python-format,
235 #~ msgid "Page %(current)i of %(total_pages)i"
236 #~ msgstr "Σελίδα %(current)i από %(total_pages)i"
237
238 #~ msgid "%"
239 #~ msgstr "%"
240
241 #~ msgid "pitch adjusted"
242 #~ msgstr "ο τόνος ρυθμίστηκε"
243
244 #~ msgid "rate adjusted"
245 #~ msgstr "ο ρυθμός ρυθμίστηκε"
246
247 #~ msgid "Choose document"
248 #~ msgstr "Επιλογή εγγράφου"
249
250 #, python-format
251 #~ msgid "Page %i of %i"
252 #~ msgstr "Σελίδα %i από %i"
253
254 #~ msgid "Edit"
255 #~ msgstr "Επεξεργασία"
256
257 #~ msgid "View"
258 #~ msgstr "Προβολή"
259
260 #~ msgid "Read Activity"
261 #~ msgstr "Ανάγνωση δραστηριότητας"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
224232 #: comicadapter.py:70
225233 msgid "No readable images were found"
226234 msgstr "No readable images were found"
235
236 #, python-format
237 #~ msgid "Page %d"
238 #~ msgstr "Page %d"
239
240 #, python-format
241 #~ msgid "Page %(current)i of %(total_pages)i"
242 #~ msgstr "Page %(current)i of %(total_pages)i"
243
244 #~ msgid "%"
245 #~ msgstr "%"
246
247 #~ msgid "pitch adjusted"
248 #~ msgstr "pitch adjusted"
249
250 #~ msgid "rate adjusted"
251 #~ msgstr "rate adjusted"
252
253 #~ msgid "Choose document"
254 #~ msgstr "Choose document"
255
256 #, python-format
257 #~ msgid "Page %i of %i"
258 #~ msgstr "Page %i of %i"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
224232 #: comicadapter.py:70
225233 msgid "No readable images were found"
226234 msgstr "No readable images were found"
235
236 #, python-format
237 #~ msgid "Page %d"
238 #~ msgstr "Page %d"
239
240 #, python-format
241 #~ msgid "Page %(current)i of %(total_pages)i"
242 #~ msgstr "Page %(current)i of %(total_pages)i"
243
244 #~ msgid "%"
245 #~ msgstr "%"
246
247 #~ msgid "pitch adjusted"
248 #~ msgstr "pitch adjusted"
249
250 #~ msgid "rate adjusted"
251 #~ msgstr "rate adjusted"
252
253 #~ msgid "Choose document"
254 #~ msgstr "Choose document"
255
256 #, python-format
257 #~ msgid "Page %i of %i"
258 #~ msgstr "Page %i of %i"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
8 # SOME DESCRIPTIVE TITLE.
9 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
10 # This file is distributed under the same license as the PACKAGE package.
11 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
12 # SOME DESCRIPTIVE TITLE.
13 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
14 # This file is distributed under the same license as the PACKAGE package.
15 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
16 # SOME DESCRIPTIVE TITLE.
17 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
18 # This file is distributed under the same license as the PACKAGE package.
19 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
020 # Spanish translations for PACKAGE package.
121 # Copyright (C) 2007 THE PACKAGE'S COPYRIGHT HOLDER
222 # This file is distributed under the same license as the PACKAGE package.
224244 #: comicadapter.py:70
225245 msgid "No readable images were found"
226246 msgstr "No se encontraron imágenes legibles"
247
248 #, python-format
249 #~ msgid "Page %d"
250 #~ msgstr "Página %d"
251
252 #, python-format
253 #~ msgid "Page %(current)i of %(total_pages)i"
254 #~ msgstr "Página %(current)i de %(total_pages)i"
255
256 #~ msgid "%"
257 #~ msgstr "%"
258
259 #~ msgid "pitch adjusted"
260 #~ msgstr "tono ajustado"
261
262 #~ msgid "rate adjusted"
263 #~ msgstr "velocidad ajustada"
264
265 #~ msgid "Choose document"
266 #~ msgstr "Escoger documento"
267
268 #, python-format
269 #~ msgid "Page %i of %i"
270 #~ msgstr "Página %i de %i"
271
272 #~ msgid "Edit"
273 #~ msgstr "Editar"
274
275 #~ msgid "View"
276 #~ msgstr "Ver"
277
278 #~ msgid "Read Activity"
279 #~ msgstr "Actividad Leer"
280
281 #~ msgid "Open a document to read"
282 #~ msgstr "Abrir un documento para leer"
283
284 #~ msgid "All supported formats"
285 #~ msgstr "Todos los formatos soportados"
286
287 #~ msgid "All files"
288 #~ msgstr "Todos los ficheros"
289
290 #~ msgid "Open"
291 #~ msgstr "Abrir"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
225233 #: comicadapter.py:70
226234 msgid "No readable images were found"
227235 msgstr ""
236
237 #, python-format
238 #~ msgid "Page %d"
239 #~ msgstr "صفحه‌ی %d"
240
241 #~ msgid "%"
242 #~ msgstr "٪"
243
244 #~ msgid "Edit"
245 #~ msgstr "ویراستن"
246
247 #~ msgid "View"
248 #~ msgstr "دیدن"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
221229 #: comicadapter.py:70
222230 msgid "No readable images were found"
223231 msgstr ""
232
233 #~ msgid "%"
234 #~ msgstr "%"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # translation of xbook.master.po to Français
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
224232 #: comicadapter.py:70
225233 msgid "No readable images were found"
226234 msgstr "Aucune image lisible n'a été trouvée"
235
236 #, python-format
237 #~ msgid "Page %d"
238 #~ msgstr "Page %d"
239
240 #, python-format
241 #~ msgid "Page %(current)i of %(total_pages)i"
242 #~ msgstr "Page %(current)i sur %(total_pages)i"
243
244 #~ msgid "%"
245 #~ msgstr "%"
246
247 #~ msgid "pitch adjusted"
248 #~ msgstr "hauteur ajustée"
249
250 #~ msgid "rate adjusted"
251 #~ msgstr "fréquence ajustée"
252
253 #~ msgid "Choose document"
254 #~ msgstr "Choisir un document"
255
256 #, python-format
257 #~ msgid "Page %i of %i"
258 #~ msgstr "Page %i sur %i"
259
260 #~ msgid "Edit"
261 #~ msgstr "Éditer"
262
263 #~ msgid "View"
264 #~ msgstr "Afficher"
265
266 #~ msgid "Read Activity"
267 #~ msgstr "Lecture"
224224 #: comicadapter.py:70
225225 msgid "No readable images were found"
226226 msgstr "Ndojetopái ta'anga hesakãva"
227
228 #, python-format
229 #~ msgid "Page %d"
230 #~ msgstr "Kuatia rogue %d."
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
614 "Project-Id-Version: PACKAGE VERSION\n"
715 "Report-Msgid-Bugs-To: \n"
816 "POT-Creation-Date: 2017-03-24 17:39+1100\n"
9 "PO-Revision-Date: 2016-11-13 12:53+0000\n"
17 "PO-Revision-Date: 2017-04-23 18:07+0000\n"
1018 "Last-Translator: Yaron <sh.yaron@gmail.com>\n"
1119 "Language-Team: LANGUAGE <LL@li.org>\n"
1220 "Language: he\n"
1523 "Content-Transfer-Encoding: 8bit\n"
1624 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
1725 "X-Generator: Pootle 2.5.1.1\n"
18 "X-POOTLE-MTIME: 1479041587.000000\n"
26 "X-POOTLE-MTIME: 1492970823.000000\n"
1927
2028 #: activity/activity.info:2
2129 msgid "Read"
4351
4452 #: readdialog.py:147
4553 msgid "<b>Author</b>:"
46 msgstr ""
54 msgstr "<b>מחבר</b>:"
4755
4856 #: readdialog.py:163
4957 msgid "<b>Details</b>:"
6371
6472 #: readactivity.py:379
6573 msgid "Please wait"
66 msgstr ""
74 msgstr "נא להמתין"
6775
6876 #: readactivity.py:380
6977 msgid "Starting connection..."
70 msgstr ""
78 msgstr "החיבור נוצר…"
7179
7280 #: readactivity.py:388
7381 msgid "No book"
115123
116124 #: readactivity.py:954
117125 msgid "Receiving book..."
118 msgstr ""
126 msgstr "הספר מתקבל…"
119127
120128 #: readactivity.py:1032 readactivity.py:1227
121129 #, python-format
122130 msgid "%s (Page %d)"
123 msgstr ""
131 msgstr "%s (עמוד %d)"
124132
125133 #: readtoolbar.py:61
126134 msgid "Previous"
176184
177185 #: readtoolbar.py:252
178186 msgid "Rotate left"
179 msgstr ""
187 msgstr "הטייה שמאלה"
180188
181189 #: readtoolbar.py:258
182190 msgid "Rotate right"
183 msgstr ""
191 msgstr "הטייה ימינה"
184192
185193 #: readtoolbar.py:324
186194 msgid "Show Tray"
219227
220228 #: comicadapter.py:69
221229 msgid "Can not read Comic Book Archive"
222 msgstr ""
230 msgstr "לא ניתן לקרוא את ארכיון ספרי הקומיקס"
223231
224232 #: comicadapter.py:70
225233 msgid "No readable images were found"
226 msgstr ""
234 msgstr "לא נמצאו תמונות שניתן לקרוא"
235
236 #, python-format
237 #~ msgid "Page %d"
238 #~ msgstr "עמוד %d"
239
240 #, fuzzy
241 #~ msgid "%"
242 #~ msgstr "%"
243
244 #, fuzzy
245 #~ msgid "Edit"
246 #~ msgstr "עריכה"
247
248 #, fuzzy
249 #~ msgid "View"
250 #~ msgstr "צפייה"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
8 # SOME DESCRIPTIVE TITLE.
9 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
10 # This file is distributed under the same license as the PACKAGE package.
11 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
012 # translation of read-activity.po to Hindi
113 # G Karunakar <karunakar@indlinux.org>, 2007.
214 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
224236 #: comicadapter.py:70
225237 msgid "No readable images were found"
226238 msgstr "कोई छवियां नहीं मिलीं"
239
240 #, python-format
241 #~ msgid "Page %d"
242 #~ msgstr "पृष्ठ %d"
243
244 #, python-format
245 #, python-format,
246 #~ msgid "Page %(current)i of %(total_pages)i"
247 #~ msgstr "पेज %(current)i मैं %(total_pages)i"
248
249 #~ msgid "%"
250 #~ msgstr "%"
251
252 #~ msgid "pitch adjusted"
253 #~ msgstr "पिच समायोजित"
254
255 #~ msgid "rate adjusted"
256 #~ msgstr "दर समायोजित"
257
258 #~ msgid "Choose document"
259 #~ msgstr "दस्तावेज़ चुनें"
260
261 #, python-format
262 #~ msgid "Page %i of %i"
263 #~ msgstr "पृष्ठ %i में से: %i"
264
265 #~ msgid "Edit"
266 #~ msgstr "संपादन"
267
268 #~ msgid "View"
269 #~ msgstr "दृश्य"
270
271 #~ msgid "Read Activity"
272 #~ msgstr "पढ़ने के क्रियाकलाप"
0 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
1 # This file is distributed under the same license as the Read package.
2 # Božidar Putanec <bozidarp@yahoo.com>, 2017.
3 msgid ""
4 msgstr ""
5 "Project-Id-Version: Read\n"
6 "Report-Msgid-Bugs-To: \n"
7 "POT-Creation-Date: 2017-03-24 17:39+1100\n"
8 "PO-Revision-Date: 2017-05-16 15:02-0700\n"
9 "Last-Translator: Božidar Putanec <bozidarp@yahoo.com>\n"
10 "Language-Team: Croatian <LL@li.org>\n"
11 "Language: hr\n"
12 "MIME-Version: 1.0\n"
13 "Content-Type: text/plain; charset=UTF-8\n"
14 "Content-Transfer-Encoding: 8bit\n"
15 "X-Generator: Poedit 2.0.1\n"
16 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
17 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
18
19 #: activity/activity.info:2
20 msgid "Read"
21 msgstr "Čitamo"
22
23 #: activity/activity.info:3
24 msgid ""
25 "Use this activity when you are ready to read! Remember to flip your computer "
26 "around to feel like you are really holding a book!"
27 msgstr ""
28 "Otvori ovu aktivnost kad si spreman i želiš čitati. Koristi računalo kao "
29 "knjigu!"
30
31 #: readdialog.py:52
32 msgid "Cancel"
33 msgstr "Odustajem"
34
35 #: readdialog.py:58
36 msgid "Ok"
37 msgstr "U redu"
38
39 #: readdialog.py:120
40 msgid "<b>Title</b>:"
41 msgstr "<b>Naslov</b>:"
42
43 #: readdialog.py:147
44 msgid "<b>Author</b>:"
45 msgstr "<b>Autor</b>:"
46
47 #: readdialog.py:163
48 msgid "<b>Details</b>:"
49 msgstr "<b>Pojedinosti</b>:"
50
51 #: evinceadapter.py:92
52 msgid "URL from Read"
53 msgstr "URL od „Čitamo“"
54
55 #: speechtoolbar.py:57
56 msgid "Play / Pause"
57 msgstr "Kreni/Stani"
58
59 #: speechtoolbar.py:65
60 msgid "Stop"
61 msgstr "Stop"
62
63 #: readactivity.py:379
64 msgid "Please wait"
65 msgstr "Pričekaj"
66
67 #: readactivity.py:380
68 msgid "Starting connection..."
69 msgstr "Započinje povezivanje..."
70
71 #: readactivity.py:388
72 msgid "No book"
73 msgstr "Nema knjige"
74
75 #: readactivity.py:388
76 msgid "Choose something to read"
77 msgstr "Odaberi nešto za čitanje"
78
79 #: readactivity.py:393
80 msgid "Back"
81 msgstr "Natrag"
82
83 #: readactivity.py:397
84 msgid "Previous page"
85 msgstr "Prethodna stranica"
86
87 #: readactivity.py:399
88 msgid "Previous bookmark"
89 msgstr "Prethodna knjižna oznaka"
90
91 #: readactivity.py:411
92 msgid "Forward"
93 msgstr "Naprijed"
94
95 #: readactivity.py:415
96 msgid "Next page"
97 msgstr "Sljedeća stranica"
98
99 #: readactivity.py:417
100 msgid "Next bookmark"
101 msgstr "Sljedeća knjižna oznaka"
102
103 #: readactivity.py:468
104 msgid "Index"
105 msgstr "Indeks"
106
107 #: readactivity.py:564
108 msgid "Delete bookmark"
109 msgstr "Ukloni knjižnu oznaku"
110
111 #: readactivity.py:565
112 msgid "All the information related with this bookmark will be lost"
113 msgstr "Sve informacije vezane uz ovu knjižnu oznaku biti će izgubljene"
114
115 #: readactivity.py:954
116 msgid "Receiving book..."
117 msgstr "Preuzimanje knjige..."
118
119 #: readactivity.py:1032 readactivity.py:1227
120 #, python-format
121 msgid "%s (Page %d)"
122 msgstr "%s (Stranica %d)"
123
124 #: readtoolbar.py:61
125 msgid "Previous"
126 msgstr "Prethodna"
127
128 #: readtoolbar.py:68
129 msgid "Next"
130 msgstr "Sljedeća"
131
132 #: readtoolbar.py:79
133 msgid "Highlight"
134 msgstr "Isticanje"
135
136 #: readtoolbar.py:160
137 msgid "Find first"
138 msgstr "Nađi prvi"
139
140 #: readtoolbar.py:166
141 msgid "Find previous"
142 msgstr "Nađi prethodni"
143
144 #: readtoolbar.py:168
145 msgid "Find next"
146 msgstr "Nađi sljedeći"
147
148 #: readtoolbar.py:188
149 msgid "Table of contents"
150 msgstr "Sadržaj"
151
152 #: readtoolbar.py:197
153 msgid "Zoom out"
154 msgstr "Smanjivanje"
155
156 #: readtoolbar.py:203
157 msgid "Zoom in"
158 msgstr "Povećavanje"
159
160 #: readtoolbar.py:209
161 msgid "Zoom to width"
162 msgstr "Povećaj do širine"
163
164 #: readtoolbar.py:215
165 msgid "Zoom to fit"
166 msgstr "Povećaj da stane"
167
168 #: readtoolbar.py:221
169 msgid "Actual size"
170 msgstr "Stvarna veličina"
171
172 #: readtoolbar.py:232
173 msgid "Fullscreen"
174 msgstr "Po cijelom zaslonu"
175
176 #: readtoolbar.py:252
177 msgid "Rotate left"
178 msgstr "Rotiraj nalijevo"
179
180 #: readtoolbar.py:258
181 msgid "Rotate right"
182 msgstr "Rotiraj nadesno"
183
184 #: readtoolbar.py:324
185 msgid "Show Tray"
186 msgstr "Pokaži traku"
187
188 #: readtoolbar.py:326
189 msgid "Hide Tray"
190 msgstr "Sakrij traku"
191
192 #: linkbutton.py:133
193 msgid "Go to Bookmark"
194 msgstr "Idi na knjižnu oznaku"
195
196 #: linkbutton.py:139
197 msgid "Remove"
198 msgstr "Ukloni"
199
200 #: bookmarkview.py:107
201 #, python-format
202 msgid "Bookmark added by %(user)s %(time)s"
203 msgstr "Knjižnu oznaku je dodao %(user)s %(time)s"
204
205 #: bookmarkview.py:143 bookmarkview.py:192
206 msgid "Add notes for bookmark: "
207 msgstr "Dodaj bilješku za knjižnu oznaku: "
208
209 #: bookmarkview.py:188
210 #, python-format
211 msgid "%s's bookmark"
212 msgstr "knjižna oznaka od %s"
213
214 #: bookmarkview.py:189
215 #, python-format
216 msgid "Bookmark for page %d"
217 msgstr "Knjižna oznaka za stranicu %d"
218
219 #: comicadapter.py:69
220 msgid "Can not read Comic Book Archive"
221 msgstr "Nije moguće čitati Comic Book arhivu"
222
223 #: comicadapter.py:70
224 msgid "No readable images were found"
225 msgstr "Nijedna čitljiva slika nije nađena"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
222230 #: comicadapter.py:70
223231 msgid "No readable images were found"
224232 msgstr ""
233
234 #~ msgid "%"
235 #~ msgstr "%"
236
237 #~ msgid "Edit"
238 #~ msgstr "Edite"
239
240 #~ msgid "View"
241 #~ msgstr "Vizyalize"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
222230 #: comicadapter.py:70
223231 msgid "No readable images were found"
224232 msgstr ""
233
234 #~ msgid "%"
235 #~ msgstr "%"
236
237 #~ msgid "Choose document"
238 #~ msgstr "Dokumentum kiválasztása"
239
240 #, python-format
241 #~ msgid "Page %i of %i"
242 #~ msgstr "Oldal %i ból %i"
222222 #: comicadapter.py:70
223223 msgid "No readable images were found"
224224 msgstr ""
225
226 #, python-format
227 #, python-format, ,
228 #~ msgid "Page %(current)i of %(total_pages)i"
229 #~ msgstr "Walxeklek %(current)i a xi ti %(total_pages)i"
230
231 #~ msgid "%"
232 #~ msgstr "%"
233
234 #~ msgid "pitch adjusted"
235 #~ msgstr "janidhtaláb t'ojojodh"
236
237 # tasa ajustada (tasa se refiere a un nivel, índice)
238 # Ejemplo: "Tasa de natalidad"="ïndice de natalidad"
239 #~ msgid "rate adjusted"
240 #~ msgstr "bajudh adhiktalab"
241
242 #~ msgid "Choose document"
243 #~ msgstr "Takuy dhuchadh úw"
244
245 #, python-format
246 #~ msgid "Page %i of %i"
247 #~ msgstr "Walxeklek %i a xi ti %i"
224224 #: comicadapter.py:70
225225 msgid "No readable images were found"
226226 msgstr ""
227
228 #, python-format
229 #~ msgid "Page %d"
230 #~ msgstr "Էջ %d"
231
232 #, python-format
233 #~ msgid "Page %(current)i of %(total_pages)i"
234 #~ msgstr "Էջ %(current)i of %(total_pages)i"
235
236 #~ msgid "%"
237 #~ msgstr "%"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
224232 #: comicadapter.py:70
225233 msgid "No readable images were found"
226234 msgstr "Tidak ditemukan citra yang dapat dibaca"
235
236 #, python-format
237 #~ msgid "Page %d"
238 #~ msgstr "Halaman %d"
239
240 #, python-format
241 #~ msgid "Page %(current)i of %(total_pages)i"
242 #~ msgstr "Halaman %(current)i dari %(total_pages)i"
243
244 #~ msgid "%"
245 #~ msgstr "%"
246
247 #~ msgid "Choose document"
248 #~ msgstr "Pilih dokumen"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
224232 #: comicadapter.py:70
225233 msgid "No readable images were found"
226234 msgstr "Engar læsilegar myndir fundust"
235
236 #, python-format
237 #~ msgid "Page %d"
238 #~ msgstr "Síða %d"
239
240 #~ msgid "%"
241 #~ msgstr "%"
242
243 #~ msgid "Edit"
244 #~ msgstr "Breyta"
245
246 #~ msgid "View"
247 #~ msgstr "Sýna"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
210 # This file is distributed under the same license as the PACKAGE package.
225233 #: comicadapter.py:70
226234 msgid "No readable images were found"
227235 msgstr ""
236
237 #, python-format
238 #~ msgid "Page %d"
239 #~ msgstr "Pagina %d"
240
241 #, python-format
242 #~ msgid "Page %(current)i of %(total_pages)i"
243 #~ msgstr "Pagina %(current)i di %(total_pages)i"
244
245 #~ msgid "%"
246 #~ msgstr "%"
247
248 #~ msgid "pitch adjusted"
249 #~ msgstr "Regolazione altezza"
250
251 #~ msgid "rate adjusted"
252 #~ msgstr "Regolazione della frequenza"
253
254 #~ msgid "Choose document"
255 #~ msgstr "Scegli documento"
256
257 #, python-format
258 #~ msgid "Page %i of %i"
259 #~ msgstr "Pagina %i di %i"
260
261 #~ msgid "Edit"
262 #~ msgstr "Modifica"
263
264 #~ msgid "View"
265 #~ msgstr "Vista"
266
267 #, fuzzy
268 #~ msgid "Read Activity"
269 #~ msgstr "Attività di lettura"
0 # SOME DESCRIPTIVE TITLE.
1 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # This file is distributed under the same license as the PACKAGE package.
3 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # SOME DESCRIPTIVE TITLE.
5 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
6 # This file is distributed under the same license as the PACKAGE package.
7 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
08 # SOME DESCRIPTIVE TITLE.
19