**Snit widget which wrapps the fast pdf viewer mupdf on X-Windows systems** This is an empty page. * provides an additional toolbar for easier usage of mupdf * additional features added to mupdf ** toolbar ** possibility to open new files in place ** file dialog for opening pdf, epub, comic book and fiction book files ** reopen accidentally closed files ** see: http://www.mupdf.com ** requires mupdf, xprop, [TkXext] ====== ############################################################################## # # Created By : Dr. Detlef Groth # Created : Mon Feb 12 16:55:44 2018 # Last Modified : <180213.1216> # # Description : A PDF viewer widget for X-Windows systems using the Tcl/Tk extension TkXext # for mupdf see http://www.mupdf.com # # # Notes : # # Requirements : mupdf and xprop # on fedora: dnf install mupdf xprop # # History : 0.1 initial release 2018-02-15 # ############################################################################## # # Copyright (c) 2018 Dr. Detlef Groth. # # License GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 # See: http://git.ghostscript.com/?p=mupdf.git;a=blob_plain;f=COPYING;hb=HEAD # See for background: http://artifex.com/licensing/ # As mupdf code is not included we might chose an other license, but I am not sure package require Tk package require TkXext package require snit package provide SnitXMupdf 0.1 snit::widget SnitXMupdf { variable app "" variable pid "" option -infile "" option -standalone false option -page 1 option -commandargs "" option -appname "" variable WinTitle "" variable DynTitle "" variable numPages 0 variable pnr 5 variable dpi 96 variable Closed true constructor {args} { $self configurelist $args set ok [auto_execok mupdf] if {$ok eq ""} { return -code error "please install mupdf" } pack [frame $win.controls] -padx 5 -pady 5 pack [label $win.controls.lb -textvariable [myvar DynTitle] -width 40] -side left -fill x -expand false pack [button $win.controls.fo -image fileopen-16 -command [mymethod ReopenWithNewFile] -relief groove -borderwidth 2] -side left -padx 2 pack [button $win.controls.rel -image reload-16 -command [mymethod reload] -relief groove -borderwidth 2] -side left -padx 2 pack [button $win.controls.bl -image playstart16 -command [mymethod goFirst] -relief groove -borderwidth 2] -side left -padx 2 pack [button $win.controls.bb -image nav1leftarrow16 -command [mymethod goBackward] -relief groove -borderwidth 2] -side left -padx 2 pack [button $win.controls.bf -image nav1rightarrow16 -command [mymethod goForward] -relief groove -borderwidth 2] -side left -padx 2 pack [button $win.controls.b1 -image playend16 -command [mymethod goLast] -relief groove -borderwidth 2] -side left -padx 2 pack [entry $win.controls.entry -textvariable [myvar pnr] -width 5] -side left -padx 5 bind $win.controls.entry [mymethod setPageBind] bind all [mymethod bindFocus %W] pack [button $win.controls.plus -image viewmag+16 -command [mymethod doPlus] -relief groove -borderwidth 2] -side left -padx 5 pack [button $win.controls.minus -image viewmag-16 -command [mymethod doMinus] -relief groove -borderwidth 2] -side left -padx 3 # install check $self LoadApp } typeconstructor { image create photo nav1downarrow16 -data { R0lGODlhEAAQAIAAAPwCBAQCBCH5BAEAAAAALAAAAAAQABAAAAIYhI+py+0P UZi0zmTtypflV0VdRJbm6fgFACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJv IHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0 cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs= } image create photo nav1uparrow16 -data { R0lGODlhEAAQAIAAAPwCBAQCBCH5BAEAAAAALAAAAAAQABAAAAIYhI+py+0P WwhxzmetzFpxnnxfRJbmufgFACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJv IHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0 cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs= } image create photo nav1leftarrow16 -data { R0lGODlhEAAQAIAAAP///wAAACH5BAEAAAAALAAAAAAQABAAAAIdhI+pyxqd woNGTmgvy9px/IEWBWRkKZ2oWrKu4hcAIf5oQ3JlYXRlZCBieSBCTVBUb0dJ RiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4LiBBbGwg cmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5jb20AOw== } image create photo nav1rightarrow16 -data { R0lGODlhEAAQAIAAAPwCBAQCBCH5BAEAAAAALAAAAAAQABAAAAIdhI+pyxCt woNHTmpvy3rxnnwQh1mUI52o6rCu6hcAIf5oQ3JlYXRlZCBieSBCTVBUb0dJ RiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4LiBBbGwg cmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5jb20AOw== } image create photo playend16 -data { R0lGODlhEAAQAIAAAPwCBAQCBCH5BAEAAAAALAAAAAAQABAAAAIjhI+py8Eb 3ENRggrxjRnrVIWcIoYd91FaenysMU6wTNeLXwAAIf5oQ3JlYXRlZCBieSBC TVBUb0dJRiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4 LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5j b20AOw== } image create photo playstart16 -data { R0lGODlhEAAQAIAAAPwCBAQCBCH5BAEAAAAALAAAAAAQABAAAAIjhI+pyxud wlNyguqkqRZh3h0gl43hpoElqlHt9UKw7NG27BcAIf5oQ3JlYXRlZCBieSBC TVBUb0dJRiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4 LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5j b20AOw== } image create photo viewmag+16 -data { R0lGODlhEAAQAIUAAPwCBCQmJDw+PAwODAQCBMza3NTm5MTW1HyChOTy9Mzq 7Kze5Kzm7OT29Oz6/Nzy9Lzu7JTW3GTCzLza3NTy9Nz29Ize7HTGzHzK1AwK DMTq7Kzq9JTi7HTW5HzGzMzu9KzS1IzW5Iza5FTK1ESyvLTa3HTK1GzGzGzG 1DyqtIzK1AT+/AQGBATCxHRydMTCxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaB QIAQEBAMhkikgFAwHAiC5FCASCQUCwYiKiU0HA9IRAIhSAcTSuXBsFwwk0wy YNBANpyOxPMxIzMgCyEiHSMkGCV+SAQQJicoJCllUgBUECEeKhAIBCuUSxMK IFArBIpJBCxmLQQuL6cAsLECrqeys7WxpqZdtK9Ct8C0fsHAZn5BACH+aENy ZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29y IDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cu ZGV2ZWxjb3IuY29tADs= } image create photo viewmag-16 -data { R0lGODlhEAAQAIUAAPwCBCQmJDw+PAwODAQCBMza3NTm5MTW1HyChOTy9Mzq 7Kze5Kzm7OT29Oz6/Nzy9Lzu7JTW3GTCzLza3NTy9Nz29Ize7HTGzHzK1AwK DMTq7Kzq9JTi7HTW5HzGzMzu9KzS1IzW5Iza5FTK1ESyvLTa3HTK1GzGzGzG 1DyqtIzK1AT+/AQGBATCxHRydMTCxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ+ QIAQEBAMhkikgFAwHAiC5FCASCQUCwYiKiU0HA9IRAIhSAcTSuXBsFwwk0wy YNBANpyOxPMxIzMgCyEiHSMkGCV+SAQQJicoJCllUgBUECEeKhAIBCuUSxMK IFArBIpJBCxmLQQuL6eUAFCusJSzr7Kmpl0CtLGLvbW2Zn5BACH+aENyZWF0 ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29yIDE5 OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cuZGV2 ZWxjb3IuY29tADs= } image create photo fileopen-16 -data { R0lGODlhEAAQAIUAAPwCBAQCBOSmZPzSnPzChPzGhPyuZEwyHExOTFROTFxa VFRSTMSGTPT29Ozu7Nze3NTS1MzKzMTGxLy6vLS2tLSytDQyNOTm5OTi5Ly+ vKyqrKSmpIyOjLR+RNTW1MzOzJyenGxqZBweHKSinJSWlExKTMTCxKyurGxu bBQSFAwKDJyanERCRERGRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaR QIBwGCgGhkhkEWA8HpNPojFJFU6ryitTiw0IBgRBkxsYFAiGtDodDZwPCERC EV8sEk0CI9FoOB4BEBESExQVFgEEBw8PFxcYEBIZGhscCEwdCxAPGA8eHxkU GyAhIkwHEREQqxEZExUjJCVWCBAZJhEmGRUnoygpQioZGxsnxsQrHByzQiJx z3EsLSwWpkJ+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9u IDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2 ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7 } image create photo reload-16 -data { R0lGODlhEAAQAIUAAPwCBCRaJBxWJBxOHBRGBCxeLLTatCSKFCymJBQ6BAwm BNzu3AQCBAQOBCRSJKzWrGy+ZDy+NBxSHFSmTBxWHLTWtCyaHCSSFCx6PETK NBQ+FBwaHCRKJMTixLy6vExOTKyqrFxaXDQyNDw+PBQSFHx6fCwuLJyenDQ2 NISChLSytJSSlFxeXAwODCQmJBweHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaB QIBQGBAMBALCcCksGA4IQkJBUDIDC6gVwGhshY5HlMn9DiCRL1MyYE8iiapa SKlALBdMRiPckDkdeXt9HgxkGhWDXB4fH4ZMGnxcICEiI45kQiQkDCUmJZsk mUIiJyiPQgyoQwwpH35LqqgMKiEjq5obqh8rLCMtowAkLqovuH5BACH+aENy ZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24gMi41DQqpIERldmVsQ29y IDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZlZC4NCmh0dHA6Ly93d3cu ZGV2ZWxjb3IuY29tADs= } image create photo actcross16 -data { R0lGODlhEAAQAIIAAASC/PwCBMQCBEQCBIQCBAAAAAAAAAAAACH5BAEAAAAA LAAAAAAQABAAAAMuCLrc/hCGFyYLQjQsquLDQ2ScEEJjZkYfyQKlJa2j7AQn MM7NfucLze1FLD78CQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJz aW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVz ZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7 } image create photo acthelp16 -data { R0lGODlhEAAQAIMAAPwCBAQ6XAQCBCyCvARSjAQ+ZGSm1ARCbEyWzESOxIy6 3ARalAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAQ/EEgQqhUz00GE Jx2WFUY3BZw5HYh4cu6mSkEy06B72LHkiYFST0NRLIaa4I0oQyZhTKInSq2e AlaaMAuYEv0RACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24g Mi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZl ZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs= } } method ReadPipe {chan} { set d [read $chan] foreach line [split $d \n] { if {[regexp {^WM_NAME.STRING. = "(.+)"} $line m title] } { set DynTitle $title if {[regexp { - ([0-9]+)/([0-9]+) \(([0-9]+) dpi} $title m pnr numPages dpi]} { set options(-page) $pnr } if {$options(-standalone)} { if {$options(-appname) ne ""} { wm title . "$options(-appname) - $DynTitle" } else { wm title . "$DynTitle" } } } } if {[eof $chan]} { fileevent $chan readable {} close $chan set Closed true } } method bindFocus {mwin} { catch {focus -force $mwin} } method getWinProperties {} { set xpin [open "|xprop -id 0x$app -spy" r] fconfigure $xpin -blocking 0 -buffering line fileevent $xpin readable [mymethod ReadPipe $xpin] } method ReopenWithNewFile {} { set types { {{Pdf Files} {.pdf} } {{Epub Files} {.epub} } {{Comic Book Files} {.cbz} } {{Fiction Book Files} {.fb2} } {{All Files} * } } set filename [tk_getOpenFile -filetypes $types] if {$filename != ""} { set options(-infile) [file nativename $filename] set options(-page) 1 if {[winfo exists $win.f]} { destroy $win.f } $self LoadApp } } method LoadApp {} { if {![winfo exists $win.f]} { frame $win.f -width 200 -height 200 -container 1 bind $win.f [mymethod LoadApp] bind $win.f [mymethod ReopenWithNewFile] } set pid [exec mupdf $options(-infile) $options(-page) &] set searchtitle [file tail $options(-infile)] set app [TkXext.find.window "${searchtitle}*"]; while {$app eq ""} { after 100 set app [TkXext.find.window "${searchtitle}*"]; if {[incr x] > 200} { break } } if {$app eq ""} { destroy $win.f return -code error "unable to find the mupdf window" } TkXext.reparent.window $app [winfo id $win.f] bind $win.f [list TkXext.resize.window $app %w %h] pack $win.f -fill both -expand 1 $self getWinProperties set Closed false } method reload {} { if {! $Closed } { catch { TkXext.focus $app ; after 200 TkXext.send.string "r" after 200 TkXext.send.string w } } else { $self LoadApp } } method getPage {} { return $pnr } method getPages {} { return $numPages } method getZoom {} { return $dpi } method setPageBind {} { $self setPage $pnr } method setPage {page} { TkXext.focus $app ; after 200 TkXext.send.string "${page}g" after 200 TkXext.send.string w } method loadFile {filename {page 1}} { return not implemented yet $self debug "loadFile $filename" TkXext.focus $app ; # this does not harm but makes the update TkXext.send.string w $self setPage $page } method goForward {} { TkXext.focus $app ; TkXext.send.string . } method goLast {} { TkXext.focus $app ; TkXext.send.string G } method goFirst {} { TkXext.focus $app ; TkXext.send.string 1g } method goBackward {} { TkXext.focus $app ; TkXext.send.string b } method doPlus {} { TkXext.focus $app ; TkXext.send.string "+" TkXext.send.string w } method doMinus {} { TkXext.focus $app ; TkXext.send.string "-" TkXext.send.string w } destructor { try { exec kill $pid } finally { try { # TkXext.delete.or.kill $app # did not work } } destroy $win } } if {$argv0 eq [info script]} { if {[llength $argv] == 0 } { puts "Usage SnitXMupdf.tcl filename" exit 0 } elseif {[llength $argv] == 1 } { SnitXMupdf .mpdf -infile [lindex $argv 0] -standalone true -appname SnitXMupdf pack .mpdf -side top -fill both -expand true } elseif {[llength $argv] == 2 } { SnitXMupdf .mpdf -infile [lindex $argv 0] -page [lindex $argv 1] -standalone true -appname SnitXMupdf pack .mpdf -side top -fill both -expand true } } ====== <>Application | Gui | Snit Widgets | Snit | Pdf