Fix to Longstanding Pdf-Tools Bug

This fix addresses a long standing bug in PDF-Tools that affects editing Annotations:

https://github.com/politza/pdf-tools/issues/294

Add the fix to your Dot Emacs.

;; Fix for pdf-tools annotation editing bug

(defun pdf-annot-edit-contents-commit-with-retry ()
  "Save annotation with automatic retry on 'No such annotation' error."
  (interactive)
  (condition-case err
      ;; Try the normal save first
      (pdf-annot-edit-contents-finalize t)
    (error
     (if (string-match-p "No such annotation" (error-message-string err))
         (progn
           (message "Annotation ID became stale, refreshing buffer...")
           ;; Revert the PDF buffer to refresh annotation IDs
           (with-current-buffer (pdf-annot-get-buffer pdf-annot-edit-contents--annotation)
             (revert-buffer t t))
           (sit-for 0.5) ; Brief pause for buffer refresh
           ;; Try to find the annotation again and save
           (condition-case err2
               (progn
                 (pdf-annot-edit-contents-finalize t)
                 (message "Annotation saved after buffer refresh"))
             (error
              (message "Failed to save annotation: %s" (error-message-string err2))
              (when (y-or-n-p "Keep edit buffer open? ")
                (pdf-annot-edit-contents-finalize nil)))))
       ;; For other errors, just show the message
       (error "Failed to save annotation: %s" (error-message-string err))))))

;; Fix for annotation deletion with stale IDs
(defun pdf-annot-delete-with-retry (a)
  "Delete annotation with automatic retry on 'No such annotation' error."
  (interactive
   (list (pdf-annot-read-annotation
          "Click on the annotation you wish to delete")))
  (condition-case err
      ;; Try normal deletion first
      (progn
        (with-current-buffer (pdf-annot-get-buffer a)
          (pdf-info-delannot (pdf-annot-get-id a))
          (set-buffer-modified-p t)
          (pdf-annot-run-modified-hooks :delete a))
        (when (called-interactively-p 'any)
          (message "Annotation deleted")))
    (error
     (if (string-match-p "No such annotation" (error-message-string err))
         (progn
           (message "Annotation ID became stale, refreshing buffer...")
           ;; Revert the PDF buffer to refresh annotation IDs
           (with-current-buffer (pdf-annot-get-buffer a)
             (revert-buffer t t))
           (sit-for 0.5)
           ;; Try to find and delete the annotation by position
           (let* ((page (pdf-annot-get a 'page))
                  (edges (pdf-annot-get a 'edges))
                  (buffer (pdf-annot-get-buffer a))
                  (found-annot nil))
             (with-current-buffer buffer
               (let ((current-annots (pdf-annot-getannots page nil buffer)))
                 (catch 'found
                   (dolist (annot current-annots)
                     (when (equal (pdf-annot-get annot 'edges) edges)
                       (setq found-annot annot)
                       (throw 'found annot)))))
               (if found-annot
                   (progn
                     (pdf-info-delannot (pdf-annot-get-id found-annot))
                     (set-buffer-modified-p t)
                     (pdf-annot-run-modified-hooks :delete found-annot)
                     (message "Annotation deleted after buffer refresh"))
                 (message "Could not find annotation to delete - it may have already been removed")))))
       ;; For other errors, just propagate them
       (error "Failed to delete annotation: %s" (error-message-string err))))))

;; Cursor positioning fix for annotation editing
(defun pdf-annot-edit-contents-position-cursor ()
  "Position cursor at end of annotation content when editing starts."
  (when (and (bound-and-true-p pdf-annot-edit-contents-minor-mode)
             pdf-annot-edit-contents--annotation)
    (goto-char (point-max))))

;; Add hook to position cursor at end when edit buffer is set up
(advice-add 'pdf-annot-edit-contents-noselect :after 
            (lambda (&rest _args)
              (with-current-buffer (get-buffer-create 
                                   (format "*Edit Annotation %s*" 
                                          (buffer-name (pdf-annot-get-buffer pdf-annot-edit-contents--annotation))))
                (goto-char (point-max)))))

;; Override both functions
(advice-add 'pdf-annot-edit-contents-commit :override 
            #'pdf-annot-edit-contents-commit-with-retry)

(advice-add 'pdf-annot-delete :override 
            #'pdf-annot-delete-with-retry)

;; Remove any previous advice
(advice-remove 'pdf-annot-edit-contents-save-annotation 
               #'pdf-annot-edit-contents-save-annotation-fixed)

(message "PDF-tools annotation fix loaded. Will auto-retry on 'No such annotation' errors for both editing and deletion. Cursor will be positioned at end of text when editing.")

Return to Home