mpdf 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. #!/usr/bin/python3
  2. # -*- coding: utf-8 -*-
  3. import gi, sys, os, locale, cairo
  4. from gi.repository import Gio, Gtk, GLib, GdkPixbuf, Gdk, Poppler, GObject
  5. translate = {"tr":{0:"Sayfa Genişliği",
  6. 1:"Sayfa Yüksekliği",
  7. 2:"Orjinal Boyut",
  8. 3:"Pdf Dosyası Seçiniz",
  9. 4:"Yazı Kopyalandı",
  10. 5:"Sayfa No",
  11. 6:"Bulunan İfade Sayısı",
  12. 7:"Tüm Dökümanda Ara",
  13. 8:"Aranıyor",
  14. 9:"Milis Linux için Poppler ile yazılmış basit bir pdf görüntüleyici."},
  15. "en":{0:"Page Width",
  16. 1:"Page Height",
  17. 2:"Original Size",
  18. 3:"Select Pdf File",
  19. 4:"Text Copied",
  20. 5:"Page Number",
  21. 6:"Number of Expressions Found",
  22. 7:"Search All Documents",
  23. 8:"Searching",
  24. 9:"A simple pdf viewer for Milis Linux written in Poppler."}}
  25. l = locale.getdefaultlocale()
  26. l = l[0].split("_")[0]
  27. locales = list(translate.keys())
  28. if l not in locales:
  29. l = "en"
  30. _ = translate[l]
  31. class MPdf(Gtk.Window):
  32. def __init__(self):
  33. super(MPdf, self).__init__()
  34. self.scale_factor = 1
  35. self.is_full_screen = 0
  36. self.document = False
  37. self.selection = False
  38. self.selection_texts = [[],0]
  39. self.sizes = [_[0],_[1],_[2],"%75","%100","%125","%150","%200","%300","%400","%500"]
  40. self.h_bar = Gtk.HeaderBar()
  41. self.h_bar.set_title("MPdf")
  42. self.set_titlebar(self.h_bar)
  43. self.h_bar.set_show_close_button(True)
  44. self.open_pdf_file_button = Gtk.ToolButton()
  45. self.open_pdf_file_button.set_stock_id(Gtk.STOCK_FILE)
  46. self.open_pdf_file_button.connect("clicked", self.open_pdf_file)
  47. self.h_bar.pack_start(self.open_pdf_file_button)
  48. self.print_button = Gtk.ToolButton()
  49. self.print_button.connect("clicked",self.print_button_show)
  50. self.print_button.set_stock_id(Gtk.STOCK_PRINT)
  51. self.h_bar.pack_start(self.print_button)
  52. self.about_button = Gtk.ToolButton()
  53. self.about_button.connect("clicked",self.about_button_show)
  54. self.about_button.set_stock_id(Gtk.STOCK_ABOUT)
  55. self.h_bar.pack_end(self.about_button)
  56. self.pages_sb = Gtk.SpinButton(climb_rate=1, digits=0)
  57. self.pages_sb.set_wrap(True)
  58. self.h_bar.pack_start(self.pages_sb)
  59. self.max_page_lb = Gtk.Label()
  60. self.h_bar.pack_start(self.max_page_lb)
  61. self.search_entry = Gtk.SearchEntry()
  62. self.search_entry.connect("search-changed",self.change_search_entry)
  63. self.h_bar.pack_start(self.search_entry)
  64. self.spinner = Gtk.Spinner()
  65. self.spinner.start()
  66. self.h_bar.pack_start(self.spinner)
  67. self.search_up_button = Gtk.ToolButton()
  68. self.search_up_button.set_stock_id(Gtk.STOCK_GO_UP)
  69. self.search_up_button.connect("clicked", self.search_up_func)
  70. self.h_bar.pack_start(self.search_up_button)
  71. self.search_down_button = Gtk.ToolButton()
  72. self.search_down_button.set_stock_id(Gtk.STOCK_GO_DOWN)
  73. self.search_down_button.connect("clicked", self.search_down_func)
  74. self.h_bar.pack_start(self.search_down_button)
  75. self.scale_combo = Gtk.ComboBoxText.new_with_entry()
  76. self.scale_combo.connect("changed", self.scale_combo_changed)
  77. for size in self.sizes:
  78. self.scale_combo.append_text(size)
  79. self.h_bar.pack_end(self.scale_combo)
  80. self.scale_combo.set_active(2)
  81. self.info_bar = Gtk.Label()
  82. self.h_bar.pack_end(self.info_bar)
  83. h_box = Gtk.HBox()
  84. v_box = Gtk.VBox()
  85. v_box.pack_start(h_box, True, False, 0)
  86. self.scroll = Gtk.ScrolledWindow()
  87. self.scroll.connect("edge-overshot",self.scroll_max_position)
  88. self.scroll.connect("scroll-event", self.on_scroll)
  89. self.scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
  90. self.doc_viewer = Gtk.DrawingArea()
  91. self.doc_viewer.set_events(Gdk.EventMask.BUTTON_PRESS_MASK|Gdk.EventMask.BUTTON_MOTION_MASK|Gdk.EventMask.BUTTON_RELEASE_MASK|Gdk.EventMask.KEY_PRESS_MASK )
  92. self.doc_viewer.connect("draw", self.draw_pdf_file)
  93. self.doc_viewer.connect("button_press_event", self.selection_start)
  94. self.doc_viewer.connect("motion-notify-event", self.selection_move)
  95. self.doc_viewer.connect("button-release-event", self.selection_stop)
  96. self.scroll.add_with_viewport(self.doc_viewer)
  97. self.scroll.set_propagate_natural_width(True)
  98. self.scroll.set_propagate_natural_height(True)
  99. h_box.pack_start(self.scroll, True, False, 0)
  100. self.add(v_box)
  101. self.set_default_size(640,480)
  102. self.control_is_press = False
  103. self.connect('size-allocate', self.size_changed)
  104. self.connect("key-press-event",self.key_press)
  105. self.connect("key-release-event",self.key_release)
  106. def about_button_show(self, widget):
  107. about = Gtk.AboutDialog()
  108. about.set_program_name("MPdf")
  109. about.set_version("1.0")
  110. about.set_logo_icon_name("qpdfview")
  111. about.set_authors(["SonAkinci41"])
  112. about.set_comments(_[9])
  113. about.set_copyright("(c) 2021 Fatih Kaya <sonakinci41@gmail.com>")
  114. about.set_license(" GPL-3.0 License ")
  115. about.set_website("https://mls.akdeniz.edu.tr/git/sonakinci41/MPdf")
  116. about.run()
  117. about.destroy()
  118. def print_button_show(self, widget):
  119. if self.document:
  120. pd = Gtk.PrintOperation()
  121. pd.set_n_pages(self.document_pages)
  122. pd.connect("draw_page",self.draw_page)
  123. result = pd.run(Gtk.PrintOperationAction.PRINT_DIALOG, self)
  124. def draw_page(self, operation, print_ctx, page_num):
  125. cr = print_ctx.get_cairo_context()
  126. page = self.document.get_page(page_num)
  127. page.render(cr)
  128. def on_scroll(self,widget,scroll):
  129. if self.control_is_press:
  130. self.scale_factor -= scroll.delta_y
  131. self.scale_combo.get_child().set_text("% "+str(int(self.scale_factor*100)))
  132. return True
  133. else:
  134. return False
  135. def search_up_func(self, widget):
  136. if self.document:
  137. if (self.selection_texts[1] - 1) != -1:
  138. self.selection_texts = [self.selection_texts[0], self.selection_texts[1] - 1]
  139. self.doc_viewer.queue_draw()
  140. else:
  141. if (self.current_page - 1) != -1:
  142. self.search_next_or_prev("prev")
  143. self.scroll_focus_selected()
  144. def search_down_func(self, widget):
  145. if self.document:
  146. if (self.selection_texts[1] + 1) != len(self.selection_texts[0]):
  147. self.selection_texts = [self.selection_texts[0], self.selection_texts[1] + 1]
  148. self.doc_viewer.queue_draw()
  149. else:
  150. if (self.current_page + 1) != self.document_pages:
  151. self.search_next_or_prev("next")
  152. self.scroll_focus_selected()
  153. def scroll_focus_selected(self):
  154. if self.selection_texts[0] != []:
  155. w, he = self.get_size()
  156. v_adj = self.scroll.get_vadjustment()
  157. h_adj = self.scroll.get_hadjustment()
  158. s_rec = self.selection_texts[0][self.selection_texts[1]]
  159. h = ((s_rec.x1 + s_rec.x2) / 2) / self.doc_width
  160. v = (self.doc_height - ( (s_rec.y1 + s_rec.y2)) / 2) / self.doc_height
  161. v_adj.set_value(v*v_adj.get_upper()- (he / 2))
  162. self.scroll.set_vadjustment(v_adj)
  163. h_adj.set_value(h*h_adj.get_upper() - (w / 2))
  164. self.scroll.set_hadjustment(h_adj)
  165. def scroll_max_position(self, widget, pos):
  166. if pos == Gtk.PositionType.BOTTOM:
  167. if self.document and self.current_page != self.document_pages - 1:
  168. v_adj = self.scroll.get_vadjustment()
  169. v_adj.set_value(v_adj.get_lower())
  170. self.scroll.set_vadjustment(v_adj)
  171. self.pages_sb.set_value(self.current_page + 2)
  172. elif pos == Gtk.PositionType.TOP :
  173. if self.document and self.current_page != 0:
  174. v_adj = self.scroll.get_vadjustment()
  175. v_adj.set_value(v_adj.get_upper())
  176. self.scroll.set_vadjustment(v_adj)
  177. self.pages_sb.set_value(self.current_page)
  178. def key_release(self, widget, event):
  179. key_name = Gdk.keyval_name(event.keyval)
  180. if key_name == "Control_L" or key_name == "Control_R":
  181. self.control_is_press = False
  182. def key_press(self, widget, event):
  183. key_name = Gdk.keyval_name(event.keyval)
  184. if not self.search_entry.is_focus() and not self.pages_sb.is_focus() and not self.scale_combo.is_focus():
  185. if key_name == "Left":
  186. if self.document and self.current_page != 0:
  187. self.pages_sb.set_value(self.current_page)
  188. elif key_name == "Right":
  189. if self.document and self.current_page != self.document_pages - 1:
  190. self.pages_sb.set_value(self.current_page + 2)
  191. elif key_name == "Up":
  192. v_adj = self.scroll.get_vadjustment()
  193. v_val = v_adj.get_value()
  194. if v_val - 20 > v_adj.get_lower():
  195. v_val -= 20
  196. else:
  197. v_val = v_adj.get_upper()
  198. self.pages_sb.set_value(self.current_page)
  199. v_adj.set_value(v_val)
  200. self.scroll.set_vadjustment(v_adj)
  201. elif key_name == "Down":
  202. v_adj = self.scroll.get_vadjustment()
  203. v_val = v_adj.get_value()
  204. n_val = v_val + 20
  205. v_adj.set_value(n_val)
  206. self.scroll.set_vadjustment(v_adj)
  207. if self.scroll.get_vadjustment().get_value() != n_val and self.current_page != self.document_pages - 1:
  208. v_adj.set_value(v_adj.get_lower())
  209. self.scroll.set_vadjustment(v_adj)
  210. self.pages_sb.set_value(self.current_page + 2)
  211. elif key_name == "Control_L" or key_name == "Control_R":
  212. self.control_is_press = True
  213. def search_next_or_prev(self, next_prev):
  214. if next_prev == "next":
  215. self.search_page_number = self.current_page + 1
  216. else:
  217. self.search_page_number = self.current_page - 1
  218. text = self.search_entry.get_text().lower()
  219. self.spinner.show()
  220. GObject.timeout_add(10, self.search_thread,text,next_prev)
  221. def search_thread(self,text,next_prev):
  222. a_page = self.document.get_page(self.search_page_number)
  223. s_text = a_page.find_text(text)
  224. if len(s_text) != 0:
  225. self.spinner.hide()
  226. self.pages_sb.set_value(self.search_page_number+1)
  227. return False
  228. if next_prev == "next":
  229. self.search_page_number += 1
  230. else:
  231. self.search_page_number -= 1
  232. if self.search_page_number == self.document_pages or self.search_page_number == -1:
  233. self.spinner.hide()
  234. return False
  235. else:
  236. return True
  237. def change_search_entry(self,widget):
  238. text = widget.get_text().lower()
  239. if self.document and text != "":
  240. finding = self.active_page.find_text(text)
  241. self.selection_texts = [finding, 0]
  242. if finding == []:
  243. self.search_next_or_prev("next")
  244. self.doc_viewer.queue_draw()
  245. elif text == "":
  246. self.selection_texts = [[], 0]
  247. self.doc_viewer.queue_draw()
  248. #self.search_all_func(None)
  249. def selection_start(self,widget,pos):
  250. self.selection = [pos.x,pos.y,pos.x,pos.y]
  251. self.doc_viewer.queue_draw()
  252. def selection_move(self,widget,pos):
  253. self.selection[2] = pos.x
  254. self.selection[3] = pos.y
  255. self.doc_viewer.queue_draw()
  256. def selection_stop(self,widget,pos):
  257. self.selection[2] = pos.x
  258. self.selection[3] = pos.y
  259. self.doc_viewer.queue_draw()
  260. calc_rect = self.calc_selection(self.selection)
  261. p_rect = Poppler.Rectangle()
  262. p_rect.x1 = calc_rect[0]
  263. p_rect.x2 = calc_rect[0] + calc_rect[2]
  264. p_rect.y1 = calc_rect[1]
  265. p_rect.y2 = calc_rect[1] + calc_rect[3]
  266. s_text = self.active_page.get_selected_text(Poppler.SelectionStyle.GLYPH ,p_rect)
  267. if s_text != " " and s_text != "" and s_text != "\n":
  268. os.system("wl-copy '{}'".format(s_text))
  269. self.info_bar.set_text(_[4])
  270. GObject.timeout_add(2000, self.hide_info_bar)
  271. self.selection = False
  272. self.scroll.grab_focus()
  273. def hide_info_bar(self):
  274. self.info_bar.set_text("")
  275. return False
  276. def open_pdf_file(self,widget):
  277. dialog = Gtk.FileChooserDialog(_[3], self, Gtk.FileChooserAction.OPEN,
  278. (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
  279. filter_ = Gtk.FileFilter()
  280. filter_.set_name(_[2])
  281. filter_.add_pattern("*.pdf")
  282. dialog.add_filter(filter_)
  283. return_dialog = dialog.run()
  284. if return_dialog == Gtk.ResponseType.OK:
  285. self.load_pdf_file(dialog.get_filename())
  286. dialog.destroy()
  287. def size_changed(self, widget, alloc):
  288. if self.document:
  289. text = self.scale_combo.get_active_text()
  290. old_scale_factor = self.scale_factor
  291. w,h = self.get_size()
  292. if text == self.sizes[0]:
  293. self.scale_factor = w/self.doc_width
  294. elif text == self.sizes[1]:
  295. self.scale_factor = h/self.doc_height
  296. if old_scale_factor != self.scale_factor:
  297. self.doc_viewer.set_size_request(self.doc_width*self.scale_factor, self.doc_height*self.scale_factor)
  298. self.doc_viewer.queue_draw()
  299. def scale_combo_changed(self,widget):
  300. if self.document:
  301. text = widget.get_active_text()
  302. old_scale_factor = self.scale_factor
  303. w,h = self.get_size()
  304. if text == self.sizes[0]:
  305. self.scale_factor = w/self.doc_width
  306. elif text == self.sizes[1]:
  307. self.scale_factor = h/self.doc_height
  308. elif text == self.sizes[2]:
  309. self.scale_factor = 1
  310. elif "%" in text:
  311. text = text.replace("%","")
  312. text = text.replace(" ","")
  313. try:
  314. t = int(text)
  315. if t < 25:
  316. t = 25
  317. elif t > 750:
  318. t = 750
  319. self.scale_factor = t/100
  320. except:
  321. pass
  322. else:
  323. text = text.replace("%","")
  324. text = text.replace(" ","")
  325. try:
  326. t = int(text)
  327. if t < 25:
  328. t = 25
  329. elif t > 750:
  330. t = 750
  331. self.scale_factor = t/100
  332. except:
  333. pass
  334. if old_scale_factor != self.scale_factor:
  335. self.doc_viewer.set_size_request(self.doc_width*self.scale_factor, self.doc_height*self.scale_factor)
  336. self.doc_viewer.queue_draw()
  337. self.scroll.grab_focus()
  338. def load_pdf_file(self,f):
  339. self.current_file = f
  340. self.current_page = 0
  341. cache_file = os.path.expanduser("~/.config/MPdf/cache")
  342. self.settings = GLib.KeyFile()
  343. g_file = Gio.File.new_for_path(f)
  344. self.document = Poppler.Document.new_from_gfile(g_file, None, None)
  345. self.document_pages = self.document.get_n_pages()
  346. if os.path.exists(cache_file):
  347. self.settings.load_from_file(cache_file, GLib.KeyFileFlags.NONE)
  348. try:
  349. acts_ = self.settings.get_keys("pagenum")
  350. for key in acts_[0]:
  351. if f == key:
  352. self.current_page = self.settings.get_integer("pagenum",key)
  353. if self.current_page > self.document_pages:
  354. self.current_page = 0
  355. except:
  356. print("{} dosyasıyla ilgili kayıt yok".format(f))
  357. #Sayfa değiştirici
  358. ad = Gtk.Adjustment(0, 1, self.document_pages, 1, 0, 0)
  359. self.pages_sb.set_adjustment(ad)
  360. self.pages_sb.set_value(self.current_page+1)
  361. self.pages_sb.connect("value-changed",self.change_current_page)
  362. self.max_page_lb.set_text("/ {}".format(str(self.document_pages)))
  363. self.h_bar.set_title(os.path.split(f)[-1])
  364. #Aktif sayfayı ve boyutunu getirelim
  365. self.get_pdf_page()
  366. self.search_entry.set_text("")
  367. def change_current_page(self,widget):
  368. n_current_page = int(widget.get_text()) - 1
  369. if self.current_page != n_current_page:
  370. self.current_page = n_current_page
  371. self.get_pdf_page()
  372. finding = self.active_page.find_text(self.search_entry.get_text())
  373. self.selection_texts = [finding, 0]
  374. self.doc_viewer.queue_draw()
  375. self.scroll_focus_selected()
  376. def get_pdf_page(self):
  377. self.active_page = self.document.get_page(self.current_page)
  378. self.doc_width, self.doc_height = self.active_page.get_size()
  379. self.doc_viewer.set_size_request(self.doc_width*self.scale_factor, self.doc_height*self.scale_factor)
  380. self.doc_viewer.queue_draw()
  381. def draw_pdf_file(self, widget, cr):
  382. if self.document:
  383. cr.scale(self.scale_factor,self.scale_factor)
  384. cr.set_source_rgba(1, 1, 1, 1)
  385. cr.rectangle(0, 0, self.doc_width, self.doc_height)
  386. cr.fill()
  387. cr.set_source_rgba(0, 0, 0, 1)
  388. cr.rectangle(0, 0, self.doc_width, self.doc_height)
  389. cr.stroke()
  390. self.active_page.render_for_printing(cr)
  391. if self.selection:
  392. cr.set_source_rgba(0.5, 0.5, 0.5, 0.5)
  393. cr.rectangle(*self.calc_selection(self.selection))
  394. cr.fill()
  395. for s_text in self.selection_texts[0]:
  396. if s_text == self.selection_texts[0][self.selection_texts[1]]:
  397. cr.set_source_rgba(0.21, 0.68, 0.56, 0.5)
  398. else:
  399. cr.set_source_rgba(0.5, 0.5, 0.5, 0.5)
  400. cr.rectangle(*self.calc_selection([s_text.x1,self.doc_height-s_text.y1,s_text.x2,self.doc_height-s_text.y2],True))
  401. cr.fill()
  402. def calc_selection(self,selection,find=False):
  403. if selection[0] > selection[2]:
  404. start_x = selection[2]
  405. x_size = selection[0] - selection[2]
  406. else:
  407. start_x = selection[0]
  408. x_size = selection[2] - selection[0]
  409. if selection[1] > selection[3]:
  410. start_y = selection[3]
  411. y_size = selection[1] - selection[3]
  412. else:
  413. start_y = selection[1]
  414. y_size = selection[3] - selection[1]
  415. if find:
  416. return (start_x,start_y,x_size,y_size)
  417. else:
  418. return (start_x/self.scale_factor,start_y/self.scale_factor,x_size/self.scale_factor,y_size/self.scale_factor)
  419. apps = []
  420. def load_pdf_file(files):
  421. for f in files:
  422. f_type = os.path.splitext(f)
  423. if os.path.exists(f) and os.path.isfile(f) and f_type[-1] == ".pdf":
  424. app = MPdf()
  425. app.load_pdf_file(f)
  426. app.connect("delete-event", close_window)
  427. app.show_all()
  428. app.spinner.hide()
  429. apps.append(app)
  430. else:
  431. print("Dosya bulunamadı!")
  432. def close_window(win,x):
  433. cache_dir = os.path.expanduser("~/.config/MPdf")
  434. if not os.path.exists(cache_dir):
  435. os.mkdir(cache_dir)
  436. win.settings.set_integer("pagenum",win.current_file,win.current_page)
  437. win.settings.save_to_file(os.path.expanduser("~/.config/MPdf/cache"))
  438. apps.remove(win)
  439. if len(apps) == 0:
  440. Gtk.main_quit()
  441. if __name__ == '__main__':
  442. args = sys.argv
  443. if len(args) > 1:
  444. load_pdf_file(args[1:])
  445. else:
  446. app = MPdf()
  447. app.connect("delete-event", close_window)
  448. app.show_all()
  449. app.spinner.hide()
  450. apps.append(app)
  451. Gtk.main()