Bladeren bron

Improve footnotes handling in exporters

* lisp/org-exp.el (org-export-footnotes-markers,
  org-export-footnotes-data): new variables.
  (org-export-preprocess-string): use a more explicit argument.

* lisp/org-html.el (org-export-as-html): initialize new variables.

* lisp/org-docbook.el (org-export-as-docbook): initialize new variables.

* lisp/org-latex.el (org-export-latex-footmark-seen): new variable.
  (org-export-as-latex): initialize new variables.
  (org-export-latex-preprocess): allow to export two or more footnotes
  in a row. Also permit to have footnotes refering to previously
  defined footnotes.

* lisp/org-ascii.el (org-export-as-ascii): feed org-footnote-normalize
  with data so it can normalize footnotes before first headline, or
  footnotes with their definition outside exported region.
Nicolas Goaziou 14 jaren geleden
bovenliggende
commit
ab9c52fd79
6 gewijzigde bestanden met toevoegingen van 88 en 64 verwijderingen
  1. 2 0
      lisp/org-ascii.el
  2. 2 0
      lisp/org-docbook.el
  3. 5 1
      lisp/org-exp.el
  4. 24 9
      lisp/org-footnote.el
  5. 2 0
      lisp/org-html.el
  6. 53 54
      lisp/org-latex.el

+ 2 - 0
lisp/org-ascii.el

@@ -291,6 +291,8 @@ publishing directory."
 	  (buffer-substring
 	   (if (org-region-active-p) (region-beginning) (point-min))
 	   (if (org-region-active-p) (region-end) (point-max))))
+	 (org-export-footnotes-markers nil)
+	 (org-export-footnotes-data (org-footnote-all-labels 'with-defs))
 	 (lines (org-split-string
 		 (org-export-preprocess-string
 		  region

+ 2 - 0
lisp/org-docbook.el

@@ -519,6 +519,8 @@ publishing directory."
 	  (buffer-substring
 	   (if region-p (region-beginning) (point-min))
 	   (if region-p (region-end) (point-max))))
+	 (org-export-footnotes-markers nil)
+	 (org-export-footnotes-data (org-footnote-all-labels 'with-defs))
 	 (lines
 	  (org-split-string
 	   (org-export-preprocess-string

+ 5 - 1
lisp/org-exp.el

@@ -623,6 +623,10 @@ table.el tables."
 (defvar org-last-level nil) ; dynamically scoped variable
 (defvar org-min-level nil) ; dynamically scoped variable
 (defvar org-levels-open nil) ; dynamically scoped parameter
+(defvar org-export-footnotes-markers nil
+  "Alist of markers used for footnotes, along with their associated definition.")
+(defvar org-export-footnotes-data nil
+  "Alist of labels used in buffers, along with their associated definition.")
 
 (defconst org-export-plist-vars
   '((:link-up		      nil	  org-export-html-link-up)
@@ -1153,7 +1157,7 @@ on this string to produce the exported version."
 
       ;; Normalize footnotes
       (when (plist-get parameters :footnotes)
-	(org-footnote-normalize nil t))
+	(org-footnote-normalize nil 'pre-process-p))
 
       ;; Find all headings and compute the targets for them
       (setq target-alist (org-export-define-heading-targets target-alist))

+ 24 - 9
lisp/org-footnote.el

@@ -309,22 +309,37 @@ If no footnote is found, return nil."
     (concat "fn:" label))
    (t label)))
 
-(defun org-footnote-all-labels ()
-  "Return list with all defined foot labels used in the buffer."
-  (let (rtn l ref)
+(defun org-footnote-all-labels (&optional with-defs)
+  "Return list with all defined foot labels used in the buffer.
+
+If WITH-DEFS is non-nil, also associate the definition to each
+label.  The function will then return an alist whose key is label
+and value definition."
+  (let (rtn
+	(push-to-rtn
+	 (function
+	  ;; Depending on WITH-DEFS, store label or (label . def) of
+	  ;; footnote reference/definition given as argument in RTN.
+	  (lambda (el)
+	    (let ((lbl (car el)))
+	      (push (if with-defs (cons lbl (nth 3 el)) lbl) rtn))))))
     (save-excursion
       (save-restriction
 	(widen)
 	;; Find all labels found in definitions.
 	(goto-char (point-min))
-	(while (re-search-forward org-footnote-definition-re nil t)
-	  (setq l (org-match-string-no-properties 2))
-	  (and l (add-to-list 'rtn l)))
+	(let (def)
+	  (while (re-search-forward org-footnote-definition-re nil t)
+	    (when (setq def (org-footnote-at-definition-p))
+	      (funcall push-to-rtn def))))
 	;; Find all labels found in references.
 	(goto-char (point-min))
-	(while (and (setq ref (org-footnote-get-next-reference))
-		    (goto-char (nth 2 ref)))
-	  (and (car ref) (add-to-list 'rtn (car ref))))))
+	(let (ref)
+	  (while (setq ref (org-footnote-get-next-reference))
+	    (goto-char (nth 2 ref))
+	    (and (car ref)		; ignore anonymous footnotes
+		 (not (funcall (if with-defs #'assoc #'member) (car ref) rtn))
+		 (funcall push-to-rtn ref))))))
     rtn))
 
 (defun org-footnote-unique-label (&optional current)

+ 2 - 0
lisp/org-html.el

@@ -1177,6 +1177,8 @@ PUB-DIR is set, use this as the publishing directory."
 	   (if region-p (region-beginning) (point-min))
 	   (if region-p (region-end) (point-max))))
 	 (org-export-have-math nil)
+	 (org-export-footnotes-markers nil)
+	 (org-export-footnotes-data (org-footnote-all-labels 'with-defs))
 	 (lines
 	  (org-split-string
 	   (org-export-preprocess-string

+ 53 - 54
lisp/org-latex.el

@@ -65,6 +65,8 @@
 (defvar org-export-latex-display-custom-times nil)
 (defvar org-export-latex-all-targets-re nil)
 (defvar org-export-latex-add-level 0)
+(defvar org-export-latex-footmark-seen nil
+  "List of footnotes markers seen so far by exporter.")
 (defvar org-export-latex-sectioning "")
 (defvar org-export-latex-sectioning-depth 0)
 (defvar org-export-latex-special-keyword-regexp
@@ -766,7 +768,10 @@ when PUB-DIR is set, use this as the publishing directory."
 			     '(:org-license-to-kill nil))))
   (org-update-radio-target-regexp)
   (org-export-latex-set-initial-vars ext-plist arg)
-  (setq org-export-opt-plist org-export-latex-options-plist)
+  (setq org-export-opt-plist org-export-latex-options-plist
+	org-export-footnotes-data (org-footnote-all-labels 'with-defs)
+	org-export-footnotes-markers nil
+	org-export-latex-footmark-seen nil)
   (org-install-letbind)
   (run-hooks 'org-export-latex-after-initial-vars-hook)
   (let* ((wcf (current-window-configuration))
@@ -2412,60 +2417,54 @@ The conversion is made depending of STRING-BEFORE and STRING-AFTER."
     (org-if-unprotected
      (replace-match "")))
 
-  ;; When converting to LaTeX, replace footnotes
-  ;; FIXME: don't protect footnotes from conversion
-  (when (plist-get org-export-latex-options-plist :footnotes)
+  ;; When converting to LaTeX, replace footnotes.
+  (when (plist-get opt-plist :footnotes)
     (goto-char (point-min))
-    (while (and (re-search-forward "\\[\\([0-9]+\\)\\]" nil t)
-		(not (equal (char-before (match-beginning 0)) ?\])))
-      (org-if-unprotected
-       (when (and (save-match-data
-		    (save-excursion (beginning-of-line)
-				    (looking-at "[^:|#]")))
-		  (not (org-in-verbatim-emphasis)))
-	 (let ((foot-beg (match-beginning 0))
-	       (foot-end (match-end 0))
-	       (foot-prefix (match-string 0))
-	       footnote footnote-rpl)
-	   (save-excursion
-	     (if (not (re-search-forward (concat "^" (regexp-quote foot-prefix))
-					 nil t))
-		 (replace-match (org-export-latex-protect-string
-				 (concat "$^{" (match-string 1) "}$")))
-	       (replace-match "")
-	       (let* ((end (save-excursion
-			     (if (re-search-forward "^$\\|^#.*$\\|\\[[0-9]+\\]" nil t)
-				 (match-beginning 0) (point-max))))
-		      (body (org-trim (buffer-substring (point) end))))
-		 ;; Fix for footnotes ending on a link or a list.
-		 (setq footnote
-		       (concat body
-			       (if (string-match "ORG-LIST-END-MARKER\\'" body)
-				   "\n" " ")))
-		 (delete-region (point) end))
-	       (goto-char foot-beg)
-	       (delete-region foot-beg foot-end)
-	       (unless (null footnote)
-		 (setq footnote-rpl (format "\\footnote{%s}" footnote))
-		 (add-text-properties 0 10 '(org-protected t) footnote-rpl)
-		 (add-text-properties (1- (length footnote-rpl))
-				      (length footnote-rpl)
-				      '(org-protected t) footnote-rpl)
-		 (put-text-property 0 (length footnote-rpl)
-				    'original-indentation 1000 footnote-rpl)
-		 (if (org-on-heading-p)
-		     (setq footnote-rpl
-			   (concat (org-export-latex-protect-string "\\protect")
-				   footnote-rpl)))
-		 (insert footnote-rpl)))
-	     )))))
-
-    ;; Remove footnote section tag for LaTeX
-    (goto-char (point-min))
-    (while (re-search-forward
-	    (concat "^" footnote-section-tag-regexp) nil t)
-      (org-if-unprotected
-       (replace-match "")))))
+    (let (ref mark-max)
+      (while (setq ref (org-footnote-get-next-reference))
+	(let* ((beg (nth 1 ref))
+	       (lbl (string-to-number (car ref)))
+	       (def (or (cdr (assoc lbl org-export-footnotes-markers)) "")))
+	  ;; Fix body for footnotes ending on a link or a list and
+	  ;; remove definition from buffer.
+	  (setq def
+		(concat def
+			(if (string-match "ORG-LIST-END-MARKER\\'" def)
+			    "\n" " ")))
+	  (org-footnote-delete-definitions (number-to-string lbl))
+	  ;; Compute string to insert (FNOTE), and protect the outside
+	  ;; macro from further transformation. When footnote at point
+	  ;; is referring to a previously defined footnote, use
+	  ;; \footnotemark. Otherwise, use \footnote.
+	  (let ((fnote (if (memq lbl org-export-latex-footmark-seen)
+			   (org-export-latex-protect-string
+			    (format "\\footnotemark[%d]" lbl))
+			 (push lbl org-export-latex-footmark-seen)
+			 (concat (org-export-latex-protect-string "\\footnote{")
+				 def
+				 (org-export-latex-protect-string "}")))))
+	    (when (org-on-heading-p)
+	      (setq fnote
+		    (concat (org-export-latex-protect-string "\\protect") fnote)))
+	    ;; Replace footnote reference with FOOTNOTE.
+	    ;; `save-excursion' is required if there are two footnotes
+	    ;; in a row. In that case, point would be left at the
+	    ;; beginning of the second one, and
+	    ;; `org-footnote-get-next-reference' would then skip it.
+	    (goto-char beg)
+	    (delete-region beg (nth 2 ref))
+	    (save-excursion (insert fnote)))))))
+
+  ;; Remove footnote section tag for LaTeX
+  (goto-char (point-min))
+  (while (re-search-forward
+	  (concat "^" footnote-section-tag-regexp) nil t)
+    (org-if-unprotected
+     (replace-match "")))
+  ;; Remove any left-over footnote definition.
+  (goto-char (point-min))
+  (mapc (lambda (fn) (org-footnote-delete-definitions (car fn)))
+	org-export-footnotes-data))
 
 (defun org-export-latex-fix-inputenc ()
   "Set the coding system in inputenc to what the buffer is."