emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* Org mode links: Open a PDF file at a given page and highlight a given string
@ 2021-03-02 20:07 Rodrigo Morales
  2021-03-02 22:36 ` Kyle Meyer
  2021-03-03  2:31 ` Juan Manuel Macías
  0 siblings, 2 replies; 8+ messages in thread
From: Rodrigo Morales @ 2021-03-02 20:07 UTC (permalink / raw)
  To: emacs-orgmode


I want to be able to

+ create a Org link to specific pages of a PDF. I've managed to
  accomplish this by setting the following value.

#+begin_src emacs-lisp :results silent
(setq org-file-apps
      '(("\\.pdf::\\([0-9]+\\)\\'" . "zathura -P %1 %s")))
#+end_src

The following links open the PDF at the given page.

[[file:~/Downloads/grub.pdf::10]]

[[file:~/Downloads/grub.pdf::20]]

[[file:~/Downloads/grub.pdf::30]]

+ create a Org link to specific pages of a PDF and highlight a given
  string.

#+begin_src emacs-lisp :results silent
(setq org-file-apps
      '(("\\.pdf::\\([0-9]+\\)::\\([^:]+\\)\\'" . "zathura -P %1 -f %2 %s")))
#+end_src

The following link must open the PDF at a given page and highlight the
given string. However, I'm getting the following error (see the
=#+begin_example= block below.)

[[file:~/Downloads/grub.pdf::95::do]]

#+begin_example
Debugger entered--Lisp error: (wrong-type-argument stringp nil)
  replace-match(nil t t "zathura -P 95 -f %2 /home/username/Downloads/grub....")
  org-open-file("~/Downloads/grub.pdf" nil nil "95::do")
  apply(org-open-file "~/Downloads/grub.pdf" nil (nil "95::do"))
  org-link-open((link (:type "file" :path "~/Downloads/grub.pdf" :format bracket :raw-link "file:~/Downloads/grub.pdf::95::do" :application nil :search-option "95::do" :begin 821 :end 858 :contents-begin nil :contents-end nil :post-blank 0 :parent (paragraph (:begin 821 :end 860 :contents-begin 821 :contents-end 859 :post-blank 1 :post-affiliated 821 :parent nil)))) nil)
  org-open-at-point(nil)
  funcall-interactively(org-open-at-point nil)
  call-interactively(org-open-at-point nil nil)
  command-execute(org-open-at-point)
#+end_example

Note that the following accomplishes what I'm looking for

#+begin_src bash
zathura -P 95 -f do ~/Downloads/grub.pdf
#+end_src

My question is: How can I get rid of that error so that the PDF is
opened at a given page and the string is highlighted?

PS: If you want a PDF to test the link presented in this message, you
can download the following PDF (564K). It was the smallest PDF in
gnu.org I was able to find.

#+begin_src bash :dir ~/Downloads :results silent
wget 'https://www.gnu.org/software/grub/manual/grub/grub.pdf'
#+end_src

-- 
Greetings,
Rodrigo Morales.

IRC: rdrg109 (freenode)


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-02 20:07 Org mode links: Open a PDF file at a given page and highlight a given string Rodrigo Morales
@ 2021-03-02 22:36 ` Kyle Meyer
  2021-03-03 12:37   ` Maxim Nikulin
  2021-09-27 16:39   ` Max Nikulin
  2021-03-03  2:31 ` Juan Manuel Macías
  1 sibling, 2 replies; 8+ messages in thread
From: Kyle Meyer @ 2021-03-02 22:36 UTC (permalink / raw)
  To: Rodrigo Morales; +Cc: emacs-orgmode

Rodrigo Morales writes:

[...]
> + create a Org link to specific pages of a PDF and highlight a given
>   string.
>
> #+begin_src emacs-lisp :results silent
> (setq org-file-apps
>       '(("\\.pdf::\\([0-9]+\\)::\\([^:]+\\)\\'" . "zathura -P %1 -f %2 %s")))
> #+end_src
>
> The following link must open the PDF at a given page and highlight the
> given string. However, I'm getting the following error (see the
> =#+begin_example= block below.)
>
> [[file:~/Downloads/grub.pdf::95::do]]
>
> #+begin_example
> Debugger entered--Lisp error: (wrong-type-argument stringp nil)
>   replace-match(nil t t "zathura -P 95 -f %2 /home/username/Downloads/grub....")

I haven't looked at this closely or tried to trigger the error, but an
in-flight patch is touching this area
(<https://orgmode.org/list/87mtw8fupl.fsf@kyleam.com>).  I've yet to
revisit it to address Maxim's helpful feedback, but I hope to soon and
will look at this error then too.

Thanks for reporting.


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-02 20:07 Org mode links: Open a PDF file at a given page and highlight a given string Rodrigo Morales
  2021-03-02 22:36 ` Kyle Meyer
@ 2021-03-03  2:31 ` Juan Manuel Macías
  2021-03-03 14:51   ` Maxim Nikulin
  1 sibling, 1 reply; 8+ messages in thread
From: Juan Manuel Macías @ 2021-03-03  2:31 UTC (permalink / raw)
  To: Rodrigo Morales; +Cc: orgmode

Hi Rodrigo,

Rodrigo Morales <moralesrodrigo1100@gmail.com> writes:

> I want to be able to
>
> + create a Org link to specific pages of a PDF. I've managed to
>   accomplish this by setting the following value.
> [ ... ]
> + create a Org link to specific pages of a PDF and highlight a given
>   string.

A possible alternative, which gives you more control over the link, is
`org-link-set-parameters'. For example:

#+begin_src emacs-lisp
  (org-link-set-parameters
   "pdf-pag"
   :follow (lambda (path)
	     (let ((pag (if (string-match "::\\([1-9]+\\):*:*\\(.*\\)" path)
			    (match-string 1 path)
			  (error "no pages")))
		   (clean-path (expand-file-name (replace-regexp-in-string "::.+" "" path)))
		   (str (when (string-match "::\\([1-9]+\\)::\\(.+\\)" path)
			  (match-string 2 path))))
	       (start-process-shell-command "zathura" nil (concat "zathura "
								  clean-path
								  " -P "
								  pag
								  (when str
								    (format " -f '%s' " str)))))))
#+end_src

And then:

#+begin_src org
  [[pdf-pag:~/Downloads/grub.pdf::95::do]]
#+end_src

Best regards,

Juan Manuel 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-02 22:36 ` Kyle Meyer
@ 2021-03-03 12:37   ` Maxim Nikulin
  2021-09-27 16:39   ` Max Nikulin
  1 sibling, 0 replies; 8+ messages in thread
From: Maxim Nikulin @ 2021-03-03 12:37 UTC (permalink / raw)
  To: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 1993 bytes --]

On 03/03/2021 05:36, Kyle Meyer wrote:
> Rodrigo Morales writes:
> 
> [...]
>> + create a Org link to specific pages of a PDF and highlight a given
>>    string.
>>
>> #+begin_src emacs-lisp :results silent
>> (setq org-file-apps
>>        '(("\\.pdf::\\([0-9]+\\)::\\([^:]+\\)\\'" . "zathura -P %1 -f %2 %s")))
>> #+end_src
>>
>> The following link must open the PDF at a given page and highlight the
>> given string. However, I'm getting the following error (see the
>> =#+begin_example= block below.)
>>
>> [[file:~/Downloads/grub.pdf::95::do]]

Correct link should be [[info:grub#true]] (a joke, at least a kind of).

Actually I never considered search string for highlighting of particular 
text, so thank you for the hint. Browsers could generate (mutually 
incompatible) link to particular rectangle. With xpopple (fork of old 
xpdf) it is possible to send command to select region.

However I think it is better to use logical links instead of page numbers:

    okular --find do ~/Downloads/grub.pdf'#true'
    xpdf ~/Downloads/grub.pdf +true

Unfortunately firefox replaces "#" to percent-encoded sequence in 
command line argument and could not find the file. It requires editing 
in address bar.

>> #+begin_example
>> Debugger entered--Lisp error: (wrong-type-argument stringp nil)
>>    replace-match(nil t t "zathura -P 95 -f %2 /home/username/Downloads/grub....")
> 
> I haven't looked at this closely or tried to trigger the error, but an
> in-flight patch is touching this area
> (<https://orgmode.org/list/87mtw8fupl.fsf@kyleam.com>).  I've yet to
> revisit it to address Maxim's helpful feedback, but I hope to soon and
> will look at this error then too.

Kyle, your patch works for such handlers. This case is another argument 
to replace all substitutions in a single pass. Global state (match data) 
is the source of the problem.

I am attaching informal single-line patch for quick plumbing. 
Alternative is to use save-match-data around string-match inside the loop.

[-- Attachment #2: org-open-file-multi-subst.patch --]
[-- Type: text/x-patch, Size: 568 bytes --]

diff --git a/lisp/org.el b/lisp/org.el
index fd6226702..f45adb308 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -8757,8 +8757,8 @@ If the file does not exist, throw an error."
       (save-match-data
 	(let ((match-index 1)
 	      (number-of-groups (- (/ (length link-match-data) 2) 1)))
-	  (set-match-data link-match-data)
 	  (while (<= match-index number-of-groups)
+	    (set-match-data link-match-data)
 	    (let ((regex (concat "%" (number-to-string match-index)))
 		  (replace-with (match-string match-index dlink)))
 	      (while (string-match regex cmd)

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-03  2:31 ` Juan Manuel Macías
@ 2021-03-03 14:51   ` Maxim Nikulin
  2021-03-03 16:11     ` Juan Manuel Macías
  0 siblings, 1 reply; 8+ messages in thread
From: Maxim Nikulin @ 2021-03-03 14:51 UTC (permalink / raw)
  To: emacs-orgmode

On 03/03/2021 09:31, Juan Manuel Macías wrote:
> 	       (start-process-shell-command "zathura" nil (concat "zathura "
> 								  clean-path
> 								  " -P "
> 								  pag
> 								  (when str
> 								    (format " -f '%s' " str)))))))

Please, do not forget to pass stings coming from user input through 
shell-quote-argument. There is combine-and-quote-strings function but 
its docstring tells that it is not safe enough. Ideally shell should be 
completely avoided in such cases and arguments should be passed as a 
list directly to exec. https://xkcd.com/327/



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-03 14:51   ` Maxim Nikulin
@ 2021-03-03 16:11     ` Juan Manuel Macías
  2021-03-05 13:02       ` Maxim Nikulin
  0 siblings, 1 reply; 8+ messages in thread
From: Juan Manuel Macías @ 2021-03-03 16:11 UTC (permalink / raw)
  To: Maxim Nikulin; +Cc: orgmode

Hi Maxim

Thanks for your advice, which I appreciate very much.

Maxim Nikulin <manikulin@gmail.com> writes:

> On 03/03/2021 09:31, Juan Manuel Macías wrote:
>> 	       (start-process-shell-command "zathura" nil (concat "zathura "
>> 								  clean-path
>> 								  " -P "
>> 								  pag
>> 								  (when str
>> 								    (format " -f '%s' " str)))))))
>
> Please, do not forget to pass stings coming from user input through
> shell-quote-argument. There is combine-and-quote-strings function but 
> its docstring tells that it is not safe enough. Ideally shell should
> be completely avoided in such cases and arguments should be passed as
> a list directly to exec. https://xkcd.com/327/

So, maybe it would look better like this (`start-process' instead of
`start-process-shell-command')?:

#+begin_src emacs-lisp
(org-link-set-parameters
 "pdf-pag"
 :follow (lambda (path)
	   (let ((pag (if (string-match "::\\([1-9]+\\):*:*\\(.*\\)" path)
			  (format "--page=%s" (match-string 1 path))
			(error "no pages")))
		 (clean-path (expand-file-name (replace-regexp-in-string "::.+" "" path)))
		 (str (when (string-match "::\\([1-9]+\\)::\\(.+\\)" path)
			(format "--find=%s" (match-string 2 path)))))
	     (if str
		 (start-process "zathura" nil "/usr/bin/zathura"
				clean-path
				pag
				str)
	       (start-process "zathura" nil "/usr/bin/zathura"
			      clean-path
			      pag)))))
#+end_src

Best reagards,

Juan Manuel 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-03 16:11     ` Juan Manuel Macías
@ 2021-03-05 13:02       ` Maxim Nikulin
  0 siblings, 0 replies; 8+ messages in thread
From: Maxim Nikulin @ 2021-03-05 13:02 UTC (permalink / raw)
  To: emacs-orgmode

On 03/03/2021 23:11, Juan Manuel Macías wrote:
> Maxim Nikulin writes:
>>
>> Please, do not forget to pass stings coming from user input through
>> shell-quote-argument.
> 
> So, maybe it would look better like this (`start-process' instead of
> `start-process-shell-command')?:

My intention was just to warn you against of opening a door
to shell injections.

Personally, as a workaround I would add an org-file-apps entry with
a single argument combining page and search string and would write
a tiny script that splits such argument and invokes the viewer.
I consider it as a better option in the sense of forward compatibility
since it allows to avoid custom link types. I expect that the bug
will be fixed soon.

I still suppose that it is serious limitation that such link format
does not support named link targets inside PDF files. Maybe the part
before second "::" could be considered as a named target
if it does not look like a number. I am unsure concerning search
strings containing "::", they may require more accurate regexp
or using e.g. percent encoding as in URLs.

> #+begin_src emacs-lisp
> (org-link-set-parameters
>   "pdf-pag"
>   :follow (lambda (path)
> 	   (let ((pag (if (string-match "::\\([1-9]+\\):*:*\\(.*\\)" path)
> 			  (format "--page=%s" (match-string 1 path))
> 			(error "no pages")))
> 		 (clean-path (expand-file-name (replace-regexp-in-string "::.+" "" path)))
> 		 (str (when (string-match "::\\([1-9]+\\)::\\(.+\\)" path)
> 			(format "--find=%s" (match-string 2 path)))))
> 	     (if str
> 		 (start-process "zathura" nil "/usr/bin/zathura"
> 				clean-path
> 				pag
> 				str)
> 	       (start-process "zathura" nil "/usr/bin/zathura"
> 			      clean-path
> 			      pag)))))
> #+end_src

If your are asking my opinion on your function, I think that the
variant with start-process is a better one. There is a low level
alternative make-process but it requires more code, so it is less
convenient. As to the style of lisp code, I am not a proper
person to make suggestions.

I suspect that your function has a problem with page numbers like 10.

I do not like repetition of the regexp and tend to think that minor
variations are unintended. On the other hand a variant I could offer
is not shorter despite just one regexp and just one call of a
match function.

#+begin_src elisp
   (defun wa-pdf-destination-zathura-args (target)
     (let ((suffix (string-match 
"::\\(?:\\([0-9]+\\)?\\(?:::\\(.+\\)\\)?\\|\\(.*\\)\\)$"
				target)))
       (if (not suffix)
	  (list (expand-file-name target))
	(let* ((invalid (match-string 3 target))
	       (file (cond
		      ((zerop suffix) (error "No file path in '%s'" target))
		      (invalid (error "Invalid destination within file: '%s'"
				      invalid))
		      (t (substring target 0 suffix))))
	       (page (match-string 1 target))
	       (search (match-string 2 target)))
	  (seq-remove #'null
		      (list (and page "--page") page
			    (and search "--find") search
			    (expand-file-name file)))))))

   (defun wa-launch-pdf-viewer (target)
     (let ((viewer "zathura")
	  (command (wa-pdf-destination-zathura-args target))
	  ;; Do not allocate a pty. Really required only if the application
	  ;; spawns background children and exits (xdg-open, gio open,
	  ;; kde-open5), see Emacs Bug #44824.
	  (process-connection-type nil))
       (apply #'start-process viewer "*Messages*" viewer command)))
#+end_src

#+begin_src elisp :results value list
   (mapcar #'wa-pdf-destination-zathura-args
	  '(
	    "~/Download/grub.pdf::95::do"
	    "file.pdf::95"
	    "file.pdf::::do"
	    "file.pdf"
	    ;; "::"
	    ;; "::95"
	    ;; "file.pdf::a"
	    ;; "file.pdf::95:do"
	    ))
#+end_src



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: Org mode links: Open a PDF file at a given page and highlight a given string
  2021-03-02 22:36 ` Kyle Meyer
  2021-03-03 12:37   ` Maxim Nikulin
@ 2021-09-27 16:39   ` Max Nikulin
  1 sibling, 0 replies; 8+ messages in thread
From: Max Nikulin @ 2021-09-27 16:39 UTC (permalink / raw)
  To: orgmode; +Cc: Kyle Meyer

On 03/03/2021 05:36, Kyle Meyer wrote:
> Rodrigo Morales writes:
> 
> [...]
>> + create a Org link to specific pages of a PDF and highlight a given
>>    string.
>>
>> #+begin_src emacs-lisp :results silent
>> (setq org-file-apps
>>        '(("\\.pdf::\\([0-9]+\\)::\\([^:]+\\)\\'" . "zathura -P %1 -f %2 %s")))
>> #+end_src
>>
>> The following link must open the PDF at a given page and highlight the
>> given string. However, I'm getting the following error (see the
>> =#+begin_example= block below.)
>>
>> [[file:~/Downloads/grub.pdf::95::do]]
>>
>> #+begin_example
>> Debugger entered--Lisp error: (wrong-type-argument stringp nil)
>>    replace-match(nil t t "zathura -P 95 -f %2 /home/username/Downloads/grub....")
> 
> I haven't looked at this closely or tried to trigger the error, but an
> in-flight patch is touching this area
> (<https://orgmode.org/list/87mtw8fupl.fsf@kyleam.com>).  I've yet to
> revisit it to address Maxim's helpful feedback, but I hope to soon and
> will look at this error then too.

I suppose, it deserves to be tracked on updates.orgmode.org 
("org-open-file & org-file-apps multiple substitution bug")


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-09-27 16:43 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-02 20:07 Org mode links: Open a PDF file at a given page and highlight a given string Rodrigo Morales
2021-03-02 22:36 ` Kyle Meyer
2021-03-03 12:37   ` Maxim Nikulin
2021-09-27 16:39   ` Max Nikulin
2021-03-03  2:31 ` Juan Manuel Macías
2021-03-03 14:51   ` Maxim Nikulin
2021-03-03 16:11     ` Juan Manuel Macías
2021-03-05 13:02       ` Maxim Nikulin

Code repositories for project(s) associated with this inbox:

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).