فهرست منبع

org-e-texinfo: Provide the ability to export to .texinfo from Org-Mode

* contrib/lisp/org-e-texinfo.el: Provide the ability to generate .texinfo and .info files from an Org-Mode file.

Export can be called using either (org-e-texinfo-export-to-texinfo) to generate a .texinfo file, or (org-e-texinfo-export-to-info) to additionally compile the .texinfo file into a .info file.
Jonathan Leech-Pepin 12 سال پیش
والد
کامیت
eff62b3843
1فایلهای تغییر یافته به همراه1848 افزوده شده و 0 حذف شده
  1. 1848 0
      contrib/lisp/org-e-texinfo.el

+ 1848 - 0
contrib/lisp/org-e-texinfo.el

@@ -0,0 +1,1848 @@
+;;; org-e-texinfo.el --- Texinfo Back-End For Org Export Engine
+
+;; Author: Jonathan Leech-Pepin <jonathan.leechpepin at gmail dot com>
+;; Keywords: outlines, hypermedia, calendar, wp
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; This library implements a Texinfo back-end for Org generic
+;; exporter.
+;;
+;; To test it, run
+;;
+;;   M-: (org-export-to-buffer 'e-texinfo "*Test e-texinfo*") RET
+;;
+;; in an org-mode buffer then switch to the buffer to see the Texinfo
+;; export.  See contrib/lisp/org-export.el for more details on how
+;; this exporter works.
+;;
+;; It introduces eight new buffer keywords: "TEXINFO_CLASS",
+;; "TEXINFO_FILENAME", "TEXINFO_HEADER", "TEXINFO_DIR_CATEGORY",
+;; "TEXINFO_DIR_TITLE", "TEXINFO_DIR_DESC" "SUBTITLE" and "SUBAUTHOR".
+;;
+;; To include inline code snippets (for example for generating @kbd{}
+;; and @key{} commands), the following export-snippet keys are
+;; accepted:
+;; 
+;;     info
+;;     e-info
+;;     e-texinfo
+;;
+;; You can add them for export snippets via any of the below:
+;;
+;;    (add-to-list 'org-export-snippet-translation-alist
+;;                 '("e-info" . "e-texinfo"))
+;;    (add-to-list 'org-export-snippet-translation-alist
+;;                 '("e-texinfo" . "e-texinfo"))
+;;    (add-to-list 'org-export-snippet-translation-alist
+;;                 '("info" . "e-texinfo"))
+;; 
+
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'org-export)
+
+(defvar orgtbl-exp-regexp)
+
+
+;;; Define Back-End
+
+(defvar org-e-texinfo-translate-alist
+  '((babel-call . org-e-texinfo-babel-call)
+    (bold . org-e-texinfo-bold)
+    (center-block . org-e-texinfo-center-block)
+    (clock . org-e-texinfo-clock)
+    (code . org-e-texinfo-code)
+    (comment . org-e-texinfo-comment)
+    (comment-block . org-e-texinfo-comment-block)
+    (drawer . org-e-texinfo-drawer)
+    (dynamic-block . org-e-texinfo-dynamic-block)
+    (entity . org-e-texinfo-entity)
+    (example-block . org-e-texinfo-example-block)
+    (export-block . org-e-texinfo-export-block)
+    (export-snippet . org-e-texinfo-export-snippet)
+    (fixed-width . org-e-texinfo-fixed-width)
+    (footnote-definition . org-e-texinfo-footnote-definition)
+    (footnote-reference . org-e-texinfo-footnote-reference)
+    (headline . org-e-texinfo-headline)
+    (horizontal-rule . org-e-texinfo-horizontal-rule)
+    (inline-babel-call . org-e-texinfo-inline-babel-call)
+    (inline-src-block . org-e-texinfo-inline-src-block)
+    (inlinetask . org-e-texinfo-inlinetask)
+    (italic . org-e-texinfo-italic)
+    (item . org-e-texinfo-item)
+    (keyword . org-e-texinfo-keyword)
+    (latex-environment . org-e-texinfo-latex-environment)
+    (latex-fragment . org-e-texinfo-latex-fragment)
+    (line-break . org-e-texinfo-line-break)
+    (link . org-e-texinfo-link)
+    (macro . org-e-texinfo-macro)
+    (paragraph . org-e-texinfo-paragraph)
+    (plain-list . org-e-texinfo-plain-list)
+    (plain-text . org-e-texinfo-plain-text)
+    (planning . org-e-texinfo-planning)
+    (property-drawer . org-e-texinfo-property-drawer)
+    (quote-block . org-e-texinfo-quote-block)
+    (quote-section . org-e-texinfo-quote-section)
+    (radio-target . org-e-texinfo-radio-target)
+    (section . org-e-texinfo-section)
+    (special-block . org-e-texinfo-special-block)
+    (src-block . org-e-texinfo-src-block)
+    (statistics-cookie . org-e-texinfo-statistics-cookie)
+    (strike-through . org-e-texinfo-strike-through)
+    (subscript . org-e-texinfo-subscript)
+    (superscript . org-e-texinfo-superscript)
+    (table . org-e-texinfo-table)
+    (table-cell . org-e-texinfo-table-cell)
+    (table-row . org-e-texinfo-table-row)
+    (target . org-e-texinfo-target)
+    (template . org-e-texinfo-template)
+    (timestamp . org-e-texinfo-timestamp)
+    (underline . org-e-texinfo-underline)
+    (verbatim . org-e-texinfo-verbatim)
+    (verse-block . org-e-texinfo-verse-block))
+  "Alist between element or object types and translators.")
+
+(defconst org-e-texinfo-options-alist
+  '((:texinfo-filename "TEXINFO_FILENAME" nil org-e-texinfo-filename t)
+    (:texinfo-class "TEXINFO_CLASS" nil org-e-texinfo-default-class t)
+    (:texinfo-header "TEXINFO_HEADER" nil nil newline)
+    (:subtitle "SUBTITLE" nil nil newline)
+    (:subauthor "SUBAUTHOR" nil nil newline)
+    (:texinfo-dircat "TEXINFO_DIR_CATEGORY" nil nil t)
+    (:texinfo-dirtitle "TEXINFO_DIR_TITLE" nil nil t)
+    (:texinfo-dirdesc "TEXINFO_DIR_DESC" nil nil t))
+  "Alist between Texinfo export properties and ways to set them.
+See `org-export-options-alist' for more information on the
+structure of the values.
+
+SUBAUTHOR and SUBTITLE are for the inclusion of additional author
+and title information beyond the initial variable.")
+
+(defconst org-e-texinfo-filters-alist
+  '((:filter-headline . org-e-texinfo-filter-section-blank-lines)
+    (:filter-section . org-e-texinfo-filter-section-blank-lines))
+  "Alist between filters keywords and back-end specific filters.
+  See `org-export-filters-alist' for more information")
+
+
+
+;;; Internal Variables
+
+;; Add TEXINFO to the list of available of available export blocks.
+(add-to-list 'org-element-block-name-alist
+	     '("TEXINFO" . org-element-export-block-parser))
+
+;;; User Configurable Variables
+
+(defgroup org-export-e-texinfo nil
+  "Options for exporting Org mode files to Texinfo."
+  :tag "Org Export Texinfo"
+  :group 'org-export)
+
+
+;;;; Preamble
+
+(defcustom org-e-texinfo-filename nil
+  "Default filename for texinfo output."
+  :group 'org-export-e-texinfo
+  :type '(string :tag "Export Filename"))
+
+(defcustom org-e-texinfo-default-class "info"
+  "The default Texinfo class."
+  :group 'org-export-e-texinfo
+  :type '(string :tag "Texinfo class"))
+
+(defcustom org-e-texinfo-classes
+  '(("info"
+     "\\input texinfo    @c -*- texinfo -*-"
+     ("@chapter %s" . "@unnumbered %s")
+     ("@section %s" . "@unnumberedsec %s")
+     ("@subsection %s" . "@unnumberedsubsec %s")
+     ("@subsubsection %s" . "@unnumberedsubsubsec %s")))
+  "Alist of Texinfo classes and associated header and structure.
+If #+Texinfo_CLASS is set in the buffer, use its value and the
+associated information.  Here is the structure of each cell:
+
+  \(class-name
+    header-string
+    \(numbered-section . unnumbered-section\)
+    ...\)
+
+The sectioning structure
+------------------------
+
+The sectioning structure of the class is given by the elements
+following the header string.  For each sectioning level, a number
+of strings is specified.  A %s formatter is mandatory in each
+section string and will be replaced by the title of the section.
+
+Instead of a list of sectioning commands, you can also specify
+a function name.  That function will be called with two
+parameters, the \(reduced) level of the headline, and a predicate
+non-nil when the headline should be numbered.  It must return
+a format string in which the section title will be added."
+  :group 'org-export-e-texinfo
+  :type '(repeat
+	  (list (string :tag "Texinfo class")
+		(string :tag "Texinfo header")
+		(repeat :tag "Levels" :inline t
+			(choice
+			 (cons :tag "Heading"
+			       (string :tag "  numbered")
+			       (string :tag "unnumbered"))
+			 (function :tag "Hook computing sectioning"))))))
+
+
+;;;; Headline
+
+(defcustom org-e-texinfo-format-headline-function nil
+  "Function to format headline text.
+
+This function will be called with 5 arguments:
+TODO      the todo keyword (string or nil).
+TODO-TYPE the type of todo (symbol: `todo', `done', nil)
+PRIORITY  the priority of the headline (integer or nil)
+TEXT      the main headline text (string).
+TAGS      the tags as a list of strings (list of strings or nil).
+
+The function result will be used in the section format string.
+
+As an example, one could set the variable to the following, in
+order to reproduce the default set-up:
+
+\(defun org-e-texinfo-format-headline (todo todo-type priority text tags)
+  \"Default format function for an headline.\"
+  \(concat (when todo
+            \(format \"\\\\textbf{\\\\textsc{\\\\textsf{%s}}} \" todo))
+	  \(when priority
+            \(format \"\\\\framebox{\\\\#%c} \" priority))
+	  text
+	  \(when tags
+            \(format \"\\\\hfill{}\\\\textsc{%s}\"
+              \(mapconcat 'identity tags \":\"))))"
+  :group 'org-export-e-texinfo
+  :type 'function)
+
+
+;;;; Footnotes
+;;
+;; Footnotes are inserted directly
+
+;;;; Timestamps
+
+(defcustom org-e-texinfo-active-timestamp-format "@emph{%s}"
+  "A printf format string to be applied to active timestamps."
+  :group 'org-export-e-texinfo
+  :type 'string)
+
+(defcustom org-e-texinfo-inactive-timestamp-format "@emph{%s}"
+  "A printf format string to be applied to inactive timestamps."
+  :group 'org-export-e-texinfo
+  :type 'string)
+
+(defcustom org-e-texinfo-diary-timestamp-format "@emph{%s}"
+  "A printf format string to be applied to diary timestamps."
+  :group 'org-export-e-texinfo
+  :type 'string)
+
+;;;; Links
+
+(defcustom org-e-texinfo-link-with-unknown-path-format "@indicateurl{%s}"
+  "Format string for links with unknown path type."
+  :group 'org-export-e-texinfo
+  :type 'string)
+
+
+;;;; Tables
+
+(defcustom org-e-texinfo-tables-verbatim nil
+  "When non-nil, tables are exported verbatim."
+  :group 'org-export-e-texinfo
+  :type 'boolean)
+
+(defcustom org-e-texinfo-table-scientific-notation "%s\\,(%s)"
+  "Format string to display numbers in scientific notation.
+The format should have \"%s\" twice, for mantissa and exponent
+\(i.e. \"%s\\\\times10^{%s}\").
+
+When nil, no transformation is made."
+  :group 'org-export-e-texinfo
+  :type '(choice
+	  (string :tag "Format string")
+	  (const :tag "No formatting")))
+
+(defcustom org-e-texinfo-def-table-markup "@samp"
+  "Default setting for @table environments.")
+
+;;;; Text markup
+
+(defcustom org-e-texinfo-text-markup-alist '((bold . "@strong{%s}")
+					   (code . code)
+					   (italic . "@emph{%s}")
+					   (verbatim . verb)
+					   (comment . "@c %s"))
+  "Alist of Texinfo expressions to convert text markup.
+
+The key must be a symbol among `bold', `italic' and `comment'.
+The value is a formatting string to wrap fontified text with.
+
+Value can also be set to the following symbols: `verb' and
+`code'.  For the former, Org will use \"@verb\" to
+create a format string and select a delimiter character that
+isn't in the string.  For the latter, Org will use \"@code\"
+to typeset and try to protect special characters.
+
+If no association can be found for a given markup, text will be
+returned as-is."
+  :group 'org-export-e-texinfo
+  :type 'alist
+  :options '(bold code italic verbatim comment))
+
+
+;;;; Drawers
+
+(defcustom org-e-texinfo-format-drawer-function nil
+  "Function called to format a drawer in Texinfo code.
+
+The function must accept two parameters:
+  NAME      the drawer name, like \"LOGBOOK\"
+  CONTENTS  the contents of the drawer.
+
+The function should return the string to be exported.
+
+For example, the variable could be set to the following function
+in order to mimic default behaviour:
+
+\(defun org-e-texinfo-format-drawer-default \(name contents\)
+  \"Format a drawer element for Texinfo export.\"
+  contents\)"
+  :group 'org-export-e-texinfo
+  :type 'function)
+
+
+;;;; Inlinetasks
+
+(defcustom org-e-texinfo-format-inlinetask-function nil
+  "Function called to format an inlinetask in Texinfo code.
+
+The function must accept six parameters:
+  TODO      the todo keyword, as a string
+  TODO-TYPE the todo type, a symbol among `todo', `done' and nil.
+  PRIORITY  the inlinetask priority, as a string
+  NAME      the inlinetask name, as a string.
+  TAGS      the inlinetask tags, as a list of strings.
+  CONTENTS  the contents of the inlinetask, as a string.
+
+The function should return the string to be exported.
+
+For example, the variable could be set to the following function
+in order to mimic default behaviour:
+
+\(defun org-e-texinfo-format-inlinetask \(todo type priority name tags contents\)
+\"Format an inline task element for Texinfo export.\"
+  \(let ((full-title
+	 \(concat
+	  \(when todo
+            \(format \"@strong{%s} \" todo))
+	  \(when priority (format \"#%c \" priority))
+	  title
+	  \(when tags
+            \(format \":%s:\"
+                    \(mapconcat 'identity tags \":\")))))
+    \(format (concat \"@center %s\n\n\"
+		    \"%s\"
+                    \"\n\"))
+	    full-title contents))"
+  :group 'org-export-e-texinfo
+  :type 'function)
+
+
+;;;; Src blocks
+;;
+;; Src Blocks are example blocks, except for LISP
+
+;;;; Plain text
+
+(defcustom org-e-texinfo-quotes
+  '(("quotes"
+     ("\\(\\s-\\|[[(]\\|^\\)\"" . "``")
+     ("\\(\\S-\\)\"" . "''")
+     ("\\(\\s-\\|(\\|^\\)'" . "`")))
+  "Alist for quotes to use when converting english double-quotes.
+
+The CAR of each item in this alist is the language code.
+The CDR of each item in this alist is a list of three CONS:
+- the first CONS defines the opening quote;
+- the second CONS defines the closing quote;
+- the last CONS defines single quotes.
+
+For each item in a CONS, the first string is a regexp
+for allowed characters before/after the quote, the second
+string defines the replacement string for this quote."
+  :group 'org-export-e-texinfo
+  :type '(list
+	  (cons :tag "Opening quote"
+		(string :tag "Regexp for char before")
+		(string :tag "Replacement quote     "))
+	  (cons :tag "Closing quote"
+		(string :tag "Regexp for char after ")
+		(string :tag "Replacement quote     "))
+	  (cons :tag "Single quote"
+		(string :tag "Regexp for char before")
+		(string :tag "Replacement quote     "))))
+
+
+;;;; Compilation
+
+(defcustom org-e-texinfo-info-process
+  '("makeinfo %f")
+  "Commands to process a texinfo file to an INFO file.
+This is list of strings, each of them will be given to the shell
+as a command.  %f in the command will be replaced by the full
+file name, %b by the file base name \(i.e without extension) and
+%o by the base directory of the file."
+  :group 'org-export-texinfo
+  :type '(repeat :tag "Shell command sequence"
+		 (string :tag "Shell command")))
+
+
+;;; Internal Functions
+
+(defun org-e-texinfo-filter-section-blank-lines (headline back-end info)
+  "Filter controlling number of blank lines after a section."
+  (if (not (eq back-end 'e-texinfo)) headline
+    (let ((blanks (make-string 2 ?\n)))
+      (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline))))
+
+(defun org-e-texinfo--find-copying (info)
+  "Retrieve the headline identified by the property :copying:.
+
+INFO is the plist containing the export options and tree.  It is
+used to find and extract the single desired headline.  This
+cannot be treated as a standard headline since it must be
+inserted in a specific location."
+  (let (copying)
+    (org-element-map (plist-get info :parse-tree) 'headline
+		     (lambda (copy)
+		       (when (org-element-property :copying copy)
+			 (push copy copying))) info 't)
+    ;; Retrieve the single entry
+    (car copying)))
+
+(defun org-e-texinfo--find-verb-separator (s)
+  "Return a character not used in string S.
+This is used to choose a separator for constructs like \\verb."
+  (let ((ll "~,./?;':\"|!@#%^&-_=+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<>()[]{}"))
+    (loop for c across ll
+	  when (not (string-match (regexp-quote (char-to-string c)) s))
+	  return (char-to-string c))))
+
+(defun org-e-texinfo--make-option-string (options)
+  "Return a comma separated string of keywords and values.
+OPTIONS is an alist where the key is the options keyword as
+a string, and the value a list containing the keyword value, or
+nil."
+  (mapconcat (lambda (pair)
+	       (concat (first pair)
+		       (when (> (length (second pair)) 0)
+			 (concat "=" (second pair)))))
+	     options
+	     ","))
+
+(defun org-e-texinfo--quotation-marks (text info)
+  "Export quotation marks using ` and ' as the markers.
+TEXT is a string containing quotation marks to be replaced.  INFO
+is a plist used as a communication channel."
+  (mapc (lambda(l)
+	  (let ((start 0))
+	    (while (setq start (string-match (car l) text start))
+	      (let ((new-quote (concat (match-string 1 text) (cdr l))))
+		(setq text (replace-match new-quote  t t text))))))
+	(cdr org-e-texinfo-quotes))
+  text)
+
+(defun org-e-texinfo--text-markup (text markup)
+  "Format TEXT depending on MARKUP text markup.
+See `org-e-texinfo-text-markup-alist' for details."
+  (let ((fmt (cdr (assq markup org-e-texinfo-text-markup-alist))))
+    (cond
+     ;; No format string: Return raw text.
+     ((not fmt) text)
+     ((eq 'verb fmt)
+      (let ((separator (org-e-texinfo--find-verb-separator text)))
+	(concat "@verb{" separator text separator "}")))
+     ((eq 'code fmt)
+      (let ((start 0)
+	    (rtn "")
+	    char)
+	(while (string-match "[@{}]" text)
+	  (setq char (match-string 0 text))
+	  (if (> (match-beginning 0) 0)
+	      (setq rtn (concat rtn (substring text 0 (match-beginning 0)))))
+	  (setq text (substring text (1+ (match-beginning 0))))
+	  (setq char (concat "@" char)
+		rtn (concat rtn char)))
+	(setq text (concat rtn text)
+	      fmt "@code{%s}")
+	(format fmt text)))
+     ;; Else use format string.
+     (t (format fmt text)))))
+
+;;;; Menu creation
+
+(defun org-e-texinfo--build-menu (tree level info &optional detailed)
+  "Create the @menu/@end menu information from TREE at headline
+level LEVEL.
+
+TREE contains the parse-tree to work with, either of the entire
+document or of a specific parent headline.  LEVEL indicates what
+level of headlines to look at when generating the menu.  INFO is
+a plist containing contextual information.
+
+Detailed determines whether to build a single level of menu, or
+recurse into all children as well."
+  (let ((menu (org-e-texinfo--generate-menu-list tree level info))
+	output text-menu)
+    (cond
+     (detailed
+      ;; Looping is done within the menu generation.
+      (setq text-menu (org-e-texinfo--generate-detailed menu level info)))
+     (t
+      (setq text-menu (org-e-texinfo--generate-menu-items menu info))))
+    (when text-menu
+      (setq output (org-e-texinfo--format-menu text-menu))
+      (mapconcat 'identity output "\n"))))
+
+(defun org-e-texinfo--generate-detailed (menu level info)
+  "Generate a detailed listing of all subheadings within MENU starting at LEVEL.
+
+MENU is the parse-tree to work with.  LEVEL is the starting level
+for the menu headlines and from which recursion occurs.  INFO is
+a plist containing contextual information."
+  (let ((max-depth (plist-get info :headline-levels)))
+    (when (> max-depth level)
+      (loop for headline in menu append
+	    (let* ((title (org-e-texinfo--menu-headlines headline info))
+		   ;; Create list of menu entries for the next level
+		   (sublist (org-e-texinfo--generate-menu-list
+			     headline (1+ level) info))
+		   ;; Generate the menu items for that level.  If
+		   ;; there are none omit that heading completely,
+		   ;; otherwise join the title to it's related entries.
+		   (submenu (if (org-e-texinfo--generate-menu-items sublist info)
+				(append (list title)
+					(org-e-texinfo--generate-menu-items sublist info))
+			      'nil))
+		   ;; Start the process over the next level down.
+		   (recursion (org-e-texinfo--generate-detailed sublist (1+ level) info)))
+	      (setq recursion (append submenu recursion))
+	      recursion)))))
+
+(defun org-e-texinfo--generate-menu-list (tree level info)
+  "Generate the list of headlines that are within a given level
+of the tree for further formatting.
+
+TREE is the parse-tree containing the headlines.  LEVEL is the
+headline level to generate a list of.  INFO is a plist holding
+contextual information."
+  (let (seq)
+    (org-element-map
+     tree 'headline
+     (lambda (head)
+       (when (org-element-property :level head)
+	 (if (and (eq level (org-element-property :level head))
+		  ;; Do not take note of footnotes or copying headlines
+		  (not (org-element-property :copying head))
+		  (not (org-element-property :footnote-section-p head)))
+	     (push head seq)))))
+    ;; Return the list of headlines (reverse to have in actual order)
+    (reverse seq)))
+
+(defun org-e-texinfo--generate-menu-items (items info)
+  "Generate a list of headline information from the listing ITEMS.
+
+ITEMS is a list of the headlines to be converted into entries.
+INFO is a plist containing contextual information.
+
+Returns a list containing the following information from each
+headline: length, title, description.  This is used to format the
+menu using `org-e-texinfo--format-menu'."
+  (loop for headline in items collect
+	(let* ((title (org-export-data
+		       (org-element-property :title headline) info))
+	       (descr (org-export-data
+		       (org-element-property :description headline) info))
+	       (len (length title))
+	       (output (list len title descr)))
+	  output)))
+
+(defun org-e-texinfo--menu-headlines (headline info)
+  "Retrieve the title from HEADLINE.
+
+INFO is a plist holding contextual information.
+
+Return the headline as a list of (length title description) with
+length of -1 and nil description.  This is used in
+`org-e-texinfo--format-menu' to identify headlines as opposed to
+entries."
+  (let ((title (org-export-data
+		(org-element-property :title headline) info)))
+    (list -1 title 'nil)))
+
+(defun org-e-texinfo--format-menu (text-menu)
+  "Format the TEXT-MENU items to be properly printed in the menu.
+
+Each entry in the menu should be provided as (length title
+description).
+
+Headlines in the detailed menu are given length -1 to ensure they
+are never confused with other entries.  They also have no
+description.
+
+Other menu items are output as:
+    Title::     description
+
+With the spacing between :: and description based on the length
+of the longest menu entry."
+
+  (let* ((lengths (mapcar 'car text-menu))
+         (max-length (apply 'max lengths))
+	 output)
+    (setq output
+          (mapcar (lambda (name)
+                    (let* ((title (nth 1 name))
+                           (desc (nth 2 name))
+                           (length (nth 0 name)))
+                      (if (> length -1)
+                          (concat "* " title ":: "
+                                  (make-string
+				   (- (+ 3 max-length) length)
+                                               ?\s)
+                                  (if desc
+                                      (concat desc)))
+                        (concat "\n" title "\n"))))
+		  text-menu))
+    output))
+
+
+
+;;; Template
+
+(defun org-e-texinfo-template (contents info)
+  "Return complete document string after Texinfo conversion.
+CONTENTS is the transcoded contents string.  INFO is a plist
+holding export options."
+  (let* ((title (org-export-data (plist-get info :title) info))
+	 (info-filename (or (plist-get info :texinfo-filename)
+			    (file-name-nondirectory
+			     (org-export-output-file-name ".info"))))
+	 (author (org-export-data (plist-get info :author) info))
+	 (texinfo-header (plist-get info :texinfo-header))
+	 (subtitle (plist-get info :subtitle))
+	 (subauthor (plist-get info :subauthor))
+	 (class (plist-get info :texinfo-class))
+	 (header (nth 1 (assoc class org-e-texinfo-classes)))
+	 (copying (org-e-texinfo--find-copying info))
+	 (dircat (plist-get info :texinfo-dircat))
+	 (dirtitle (plist-get info :texinfo-dirtitle))
+	 (dirdesc (plist-get info :texinfo-dirdesc))
+	 ;; Spacing to align description (column 32 - 3 for `* ' and
+	 ;; `.' in text.
+	 (dirspacing (- 29 (length dirtitle))))
+    (concat
+     ;; Header
+     header "\n"
+     "@c %**start of header\n"
+     ;; Filename and Title
+     "@setfilename " info-filename "\n"
+     "@settitle " title "\n"
+     "\n\n"
+     "@c Version and Contact Info\n"
+     "@set AUTHOR " author "\n"
+
+     ;; Additional Header Options set by `#+TEXINFO_HEADER
+     (if texinfo-header
+	 (concat "\n"
+		 texinfo-header
+		 "\n"))
+     
+     "@c %**end of header\n"
+     "@finalout\n"
+     "\n\n"
+
+     ;; Copying
+     "@copying\n"
+     ;; Only export the content of the headline, do not need the
+     ;; initial headline.
+     (org-export-data (nth 2 copying) info)
+     "@end copying\n"
+     "\n\n"
+
+     ;; Info directory information
+     ;; Only supply if both title and category are provided
+     (if (and dircat dirtitle)
+	 (concat "@dircategory " dircat "\n"
+		 "@direntry\n"
+		 "* " dirtitle "."
+		 (make-string dirspacing ?\s)
+		 dirdesc "\n"
+		 "@end direntry\n"))
+     "\n\n"
+
+     ;; Title
+     "@titlepage\n"
+     "@title " title "\n\n"
+     (if subtitle
+	 (concat "@subtitle " subtitle "\n"))
+     "@author " author "\n"
+     (if subauthor
+	 (concat subauthor "\n"))
+     "\n"
+     "@c The following two commands start the copyright page.\n"
+     "@page\n"
+     "@vskip 0pt plus 1filll\n"
+     "@insertcopying\n"
+     "@end titlepage\n\n"
+     "@c Output the table of contents at the beginning.\n"
+     "@contents\n\n"
+
+     ;; Configure Top Node when not for Tex
+     "@ifnottex\n"
+     "@node Top\n"
+     "@top " title " Manual\n"
+     "@insertcopying\n"
+     "@end ifnottex\n\n"
+     
+     ;; Menu
+     "@menu\n"
+     (org-e-texinfo-make-menu info 'main)
+     "\n\n"
+     ;; Detailed Menu
+     "@detailmenu\n"
+     " --- The Detailed Node Listing ---\n"
+     (org-e-texinfo-make-menu info 'detailed)
+     "\n\n"
+     "@end detailmenu\n"
+     "@end menu\n"
+     "\n\n"
+     
+     ;; Document's body.
+     contents
+     "\n"
+     ;; Creator.
+     (let ((creator-info (plist-get info :with-creator)))
+       (cond
+	((not creator-info) "")
+	((eq creator-info 'comment)
+	 (format "@c %s\n" (plist-get info :creator)))
+	(t (concat (plist-get info :creator) "\n"))))
+     ;; Document end.
+     "\n@bye")))
+
+
+
+;;; Transcode Functions
+
+;;;; Babel Call
+;;
+;; Babel Calls are ignored.
+
+
+;;;; Bold
+
+(defun org-e-texinfo-bold (bold contents info)
+  "Transcode BOLD from Org to Texinfo.
+CONTENTS is the text with bold markup.  INFO is a plist holding
+contextual information."
+  (org-e-texinfo--text-markup contents 'bold))
+
+
+;;;; Center Block
+;;
+;; Center blocks are ignored
+
+
+;;;; Clock
+
+(defun org-e-texinfo-clock (clock contents info)
+  "Transcode a CLOCK element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (concat
+   "@noindent"
+   (format "@strong{%s} " org-clock-string)
+   (format org-e-texinfo-inactive-timestamp-format
+	   (concat (org-translate-time (org-element-property :value clock))
+		   (let ((time (org-element-property :time clock)))
+		     (and time (format " (%s)" time)))))
+   "@*"))
+
+
+;;;; Code
+
+(defun org-e-texinfo-code (code contents info)
+  "Transcode a CODE object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist used as a communication
+channel."
+  (org-e-texinfo--text-markup (org-element-property :value code) 'code))
+
+;;;; Comment
+
+(defun org-e-texinfo-comment (comment contents info)
+  "Transcode a COMMENT object from Org to Texinfo.
+CONTENTS is the text in the comment.  INFO is a plist holding
+contextual information."
+  (org-e-texinfo--text-markup (org-element-property :value comment) 'comment))
+
+;;;; Comment Block
+
+(defun org-e-texinfo-comment-block (comment-block contents info)
+  "Transcode a COMMENT-BLOCK object from Org to Texinfo.
+CONTENTS is the text within the block.  INFO is a plist holding
+contextual information."
+  (format "@ignore\n%s@end ignore" (org-element-property :value comment-block)))
+
+;;;; Drawer
+
+(defun org-e-texinfo-drawer (drawer contents info)
+  "Transcode a DRAWER element from Org to Texinfo.
+CONTENTS holds the contents of the block.  INFO is a plist
+holding contextual information."
+  (let* ((name (org-element-property :drawer-name drawer))
+	 (output (if (functionp org-e-texinfo-format-drawer-function)
+		     (funcall org-e-texinfo-format-drawer-function
+			      name contents)
+		   ;; If there's no user defined function: simply
+		   ;; display contents of the drawer.
+		   contents)))
+    output))
+
+
+;;;; Dynamic Block
+
+(defun org-e-texinfo-dynamic-block (dynamic-block contents info)
+  "Transcode a DYNAMIC-BLOCK element from Org to Texinfo.
+CONTENTS holds the contents of the block.  INFO is a plist
+holding contextual information.  See `org-export-data'."
+  contents)
+
+
+;;;; Entity
+
+(defun org-e-texinfo-entity (entity contents info)
+  "Transcode an ENTITY object from Org to Texinfo.
+CONTENTS are the definition itself.  INFO is a plist holding
+contextual information."
+  (let ((ent (org-element-property :latex entity)))
+    (if (org-element-property :latex-math-p entity) (format "@math{%s}" ent) ent)))
+
+
+;;;; Example Block
+
+(defun org-e-texinfo-example-block (example-block contents info)
+  "Transcode an EXAMPLE-BLOCK element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (format "@verbatim\n%s@end verbatim"
+	  (org-export-format-code-default example-block info)))
+
+
+;;;; Export Block
+
+(defun org-e-texinfo-export-block (export-block contents info)
+  "Transcode a EXPORT-BLOCK element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (when (string= (org-element-property :type export-block) "TEXINFO")
+    (org-remove-indentation (org-element-property :value export-block))))
+
+
+;;;; Export Snippet
+
+(defun org-e-texinfo-export-snippet (export-snippet contents info)
+  "Transcode a EXPORT-SNIPPET object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (when (eq (org-export-snippet-backend export-snippet) 'e-texinfo)
+    (org-element-property :value export-snippet)))
+
+
+;;;; Fixed Width
+
+(defun org-e-texinfo-fixed-width (fixed-width contents info)
+  "Transcode a FIXED-WIDTH element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (format "@example\n%s\n@end example"
+	  (org-remove-indentation
+	   (org-element-property :value fixed-width))))
+
+
+;;;; Footnote Definition
+;;
+;; Footnote Definitions are ignored.
+
+
+;;;; Footnote Reference
+;;
+
+(defun org-e-texinfo-footnote-reference (footnote contents info)
+  "Create a footnote reference for FOOTNOTE.
+
+FOOTNOTE is the footnote to define.  CONTENTS is nil.  INFO is a
+plist holding contextual information."
+  (let ((def (org-export-get-footnote-definition footnote info)))
+    (format "@footnote{%s}"
+	    (org-trim (org-export-data def info)))))
+
+;;;; Headline
+
+(defun org-e-texinfo-headline (headline contents info)
+  "Transcode an HEADLINE element from Org to Texinfo.
+CONTENTS holds the contents of the headline.  INFO is a plist
+holding contextual information."
+  (let* ((class (plist-get info :texinfo-class))
+	 (level (org-export-get-relative-level headline info))
+	 (numberedp (org-export-numbered-headline-p headline info))
+	 (class-sectionning (assoc class org-e-texinfo-classes))
+	 ;; Find the index type, if any
+	 (index (org-element-property :index headline))
+	 ;; Create node info, to insert it before section formatting.
+	 (node (format "@node %s\n"
+		       (org-export-data
+			(org-element-property :title headline) info)))
+	 ;; Menus must be generated with first child, otherwise they
+	 ;; will not nest properly
+	 (menu (let* ((first (org-export-first-sibling-p headline info))
+		      (parent (org-export-get-parent-headline headline))
+		      (title (org-export-data
+			      (org-element-property :title parent) info))
+		      heading listing
+		      (tree (plist-get info :parse-tree)))
+		 (if first
+		     (org-element-map
+		      (plist-get info :parse-tree) 'headline
+		      (lambda (ref)
+			(if (member title (org-element-property :title ref))
+			    (push ref heading)))
+		      info 't))
+		 (setq listing (org-e-texinfo--build-menu
+				(car heading) level info))
+	 	 (if listing
+	 	     (setq listing (format
+				    "\n@menu\n%s\n@end menu\n\n" listing))
+	 	   'nil)))
+	 ;; Section formatting will set two placeholders: one for the
+	 ;; title and the other for the contents.
+	 (section-fmt
+	  (let ((sec (if (and (symbolp (nth 2 class-sectionning))
+			      (fboundp (nth 2 class-sectionning)))
+			 (funcall (nth 2 class-sectionning) level numberedp)
+		       (nth (1+ level) class-sectionning))))
+	    (cond
+	     ;; No section available for that LEVEL.
+	     ((not sec) nil)
+	     ;; Section format directly returned by a function.
+	     ((stringp sec) sec)
+	     ;; (numbered-section . unnumbered-section)
+	     ((not (consp (cdr sec)))
+	      ;; If an index, always unnumbered
+	      (if index
+		  (concat menu node (cdr sec) "\n%s")
+		;; Otherwise number as needed.
+		(concat menu node
+			(funcall
+			 (if numberedp #'car #'cdr) sec) "\n%s"))))))
+	 (text (org-export-data
+		(org-element-property :title headline) info))
+	 (todo
+	  (and (plist-get info :with-todo-keywords)
+	       (let ((todo (org-element-property :todo-keyword headline)))
+		 (and todo (org-export-data todo info)))))
+	 (todo-type (and todo (org-element-property :todo-type headline)))
+	 (tags (and (plist-get info :with-tags)
+		    (org-export-get-tags headline info)))
+	 (priority (and (plist-get info :with-priority)
+			(org-element-property :priority headline)))
+	 ;; Create the headline text along with a no-tag version.  The
+	 ;; latter is required to remove tags from table of contents.
+	 (full-text (if (functionp org-e-texinfo-format-headline-function)
+			;; User-defined formatting function.
+			(funcall org-e-texinfo-format-headline-function
+				 todo todo-type priority text tags)
+		      ;; Default formatting.
+		      (concat
+		       (when todo
+			 (format "@strong{%s} " todo))
+		       (when priority (format "@emph{#%s} " priority))
+		       text
+		       (when tags
+			 (format ":%s:"
+				 (mapconcat 'identity tags ":"))))))
+	 (full-text-no-tag
+	  (if (functionp org-e-texinfo-format-headline-function)
+	      ;; User-defined formatting function.
+	      (funcall org-e-texinfo-format-headline-function
+		       todo todo-type priority text nil)
+	    ;; Default formatting.
+	    (concat
+	     (when todo (format "@strong{%s} " todo))
+	     (when priority (format "@emph{#%c} " priority))
+	     text)))
+	 (pre-blanks
+	  (make-string (org-element-property :pre-blank headline) 10)))
+    (cond
+     ;; Case 1: This is a footnote section: ignore it.
+     ((org-element-property :footnote-section-p headline) nil)
+     ;; Case 2: This is the `copying' section: ignore it
+     ;;         This is used elsewhere.
+     ((org-element-property :copying headline) nil)
+     ;; Case 3: An index.  If it matches one of the known indexes,
+     ;;         print it as such following the contents, otherwise
+     ;;         print the contents and leave the index up to the user.
+     (index
+      (format
+       section-fmt full-text
+       (concat pre-blanks contents "\n" 
+	       (if (member index '("cp" "fn" "ky" "pg" "tp" "vr"))
+		   (concat "@printindex " index)))))
+     ;; Case 4: This is a deep sub-tree: export it as a list item.
+     ;;         Also export as items headlines for which no section
+     ;;         format has been found.
+     ((or (not section-fmt) (org-export-low-level-p headline info))
+      ;; Build the real contents of the sub-tree.
+      (let ((low-level-body
+	     (concat
+	      ;; If the headline is the first sibling, start a list.
+	      (when (org-export-first-sibling-p headline info)
+		(format "@%s\n" (if numberedp 'enumerate 'itemize)))
+	      ;; Itemize headline
+	      "@item\n" full-text "\n" pre-blanks contents)))
+	;; If headline is not the last sibling simply return
+	;; LOW-LEVEL-BODY.  Otherwise, also close the list, before any
+	;; blank line.
+	(if (not (org-export-last-sibling-p headline info)) low-level-body
+	  (replace-regexp-in-string
+	   "[ \t\n]*\\'"
+	   (format "\n@end %s" (if numberedp 'enumerate 'itemize))
+	   low-level-body))))
+     ;; Case 5: Standard headline.  Export it as a section.
+     (t
+      (cond
+       ((not (and tags (eq (plist-get info :with-tags) 'not-in-toc)))
+	;; Regular section.  Use specified format string.
+	(format (replace-regexp-in-string "%]" "%%]" section-fmt) full-text
+		(concat pre-blanks contents)))
+       ((string-match "\\`@\\(.*?\\){" section-fmt)
+	;; If tags should be removed from table of contents, insert
+	;; title without tags as an alternative heading in sectioning
+	;; command.
+	(format (replace-match (concat (match-string 1 section-fmt) "[%s]")
+			       nil nil section-fmt 1)
+		;; Replace square brackets with parenthesis since
+		;; square brackets are not supported in optional
+		;; arguments.
+		(replace-regexp-in-string
+		 "\\[" "("
+		 (replace-regexp-in-string
+		  "\\]" ")"
+		  full-text-no-tag))
+		full-text
+		(concat pre-blanks contents)))
+       (t
+	;; Impossible to add an alternative heading.  Fallback to
+	;; regular sectioning format string.
+	(format (replace-regexp-in-string "%]" "%%]" section-fmt) full-text
+		(concat pre-blanks contents))))))))
+
+
+;;;; Horizontal Rule
+;;
+;; Horizontal rules are ignored
+
+;;;; Inline Babel Call
+;;
+;; Inline Babel Calls are ignored.
+
+
+;;;; Inline Src Block
+
+(defun org-e-texinfo-inline-src-block (inline-src-block contents info)
+  "Transcode an INLINE-SRC-BLOCK element from Org to Texinfo.
+CONTENTS holds the contents of the item.  INFO is a plist holding
+contextual information."
+  (let* ((code (org-element-property :value inline-src-block))
+	 (separator (org-e-texinfo--find-verb-separator code)))
+    (concat "@verb{" separator code separator "}")))
+
+
+;;;; Inlinetask
+
+(defun org-e-texinfo-inlinetask (inlinetask contents info)
+  "Transcode an INLINETASK element from Org to Texinfo.
+CONTENTS holds the contents of the block.  INFO is a plist
+holding contextual information."
+  (let ((title (org-export-data (org-element-property :title inlinetask) info))
+	(todo (and (plist-get info :with-todo-keywords)
+		   (let ((todo (org-element-property :todo-keyword inlinetask)))
+		     (and todo (org-export-data todo info)))))
+	(todo-type (org-element-property :todo-type inlinetask))
+	(tags (and (plist-get info :with-tags)
+		   (org-export-get-tags inlinetask info)))
+	(priority (and (plist-get info :with-priority)
+		       (org-element-property :priority inlinetask))))
+    ;; If `org-e-texinfo-format-inlinetask-function' is provided, call it
+    ;; with appropriate arguments.
+    (if (functionp org-e-texinfo-format-inlinetask-function)
+	(funcall org-e-texinfo-format-inlinetask-function
+		 todo todo-type priority title tags contents)
+      ;; Otherwise, use a default template.
+      (let ((full-title
+	     (concat
+	      (when todo (format "@strong{%s} " todo))
+	      (when priority (format "#%c " priority))
+	      title
+	      (when tags (format ":%s:"
+				 (mapconcat 'identity tags ":"))))))
+	(format (concat "@center %s\n\n"
+			"%s"
+			"\n")
+		full-title contents)))))
+
+
+;;;; Italic
+
+(defun org-e-texinfo-italic (italic contents info)
+  "Transcode ITALIC from Org to Texinfo.
+CONTENTS is the text with italic markup.  INFO is a plist holding
+contextual information."
+  (org-e-texinfo--text-markup contents 'italic))
+
+;;;; Item
+
+(defun org-e-texinfo-item (item contents info)
+  "Transcode an ITEM element from Org to Texinfo.
+CONTENTS holds the contents of the item.  INFO is a plist holding
+contextual information."
+  (let* ((tag (org-element-property :tag item))
+	 (desc (org-export-data tag info)))
+   (concat "\n@item " (if tag desc) "\n"
+	   (org-trim contents) "\n")))
+
+
+;;;; Keyword
+
+(defun org-e-texinfo-keyword (keyword contents info)
+  "Transcode a KEYWORD element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (let ((key (org-element-property :key keyword))
+	(value (org-element-property :value keyword)))
+    (cond
+     ((string= key "TEXINFO") value)
+     ((string= key "CINDEX") (format "@cindex %s" value))
+     ((string= key "FINDEX") (format "@findex %s" value))
+     ((string= key "KINDEX") (format "@kindex %s" value))
+     ((string= key "PINDEX") (format "@pindex %s" value))
+     ((string= key "TINDEX") (format "@tindex %s" value))
+     ((string= key "VINDEX") (format "@vindex %s" value))
+     )))
+
+
+;;;; Latex Environment
+;;
+;; Latex environments are ignored
+
+
+;;;; Latex Fragment
+;;
+;; Latex fragments are ignored.
+
+
+;;;; Line Break
+
+(defun org-e-texinfo-line-break (line-break contents info)
+  "Transcode a LINE-BREAK object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  "@*")
+
+
+;;;; Link
+
+(defun org-e-texinfo-link (link desc info)
+  "Transcode a LINK object from Org to Texinfo.
+
+DESC is the description part of the link, or the empty string.
+INFO is a plist holding contextual information.  See
+`org-export-data'."
+  (let* ((type (org-element-property :type link))
+	 (raw-path (org-element-property :path link))
+	 ;; Ensure DESC really exists, or set it to nil.
+	 (desc (and (not (string= desc "")) desc))
+	 (path (cond
+		((member type '("http" "https" "ftp"))
+		 (concat type ":" raw-path))
+		((string= type "file")
+		 (when (string-match "\\(.+\\)::.+" raw-path)
+		   (setq raw-path (match-string 1 raw-path)))
+		 (if (file-name-absolute-p raw-path)
+		     (concat "file://" (expand-file-name raw-path))
+		   (concat "file://" raw-path)))
+		(t raw-path)))
+	 (email (if (string= type "mailto")
+		    (let ((text (replace-regexp-in-string
+				 "@" "@@" raw-path)))
+		     (concat text (if desc (concat "," desc))))))
+	 protocol)
+    (cond
+     ;; Links pointing to an headline: Find destination and build
+     ;; appropriate referencing command.
+     ((member type '("custom-id" "id"))
+      (let ((destination (org-export-resolve-id-link link info)))
+	(case (org-element-type destination)
+	  ;; Id link points to an external file.
+	  (plain-text
+	   (if desc (format "@uref{file://%s,%s}" destination desc)
+	     (format "@uref{file://%s}" destination)))
+	  ;; LINK points to an headline.  Use the headline as the NODE target
+	  (headline
+	   (format "@ref{%s}"
+		   (org-export-data
+		    (org-element-property :title destination) info)))
+	  (otherwise
+	   (let ((path (org-export-solidify-link-text path)))
+	     (if (not desc) (format "@ref{%s}" path)
+	       (format "@ref{%s,,%s}" path desc)))))))
+     ((member type '("fuzzy"))
+      (let ((destination (org-export-resolve-fuzzy-link link info)))
+	(case (org-element-type destination)
+	  ;; Id link points to an external file.
+	  (plain-text
+	   (if desc (format "@uref{file://%s,%s}" destination desc)
+	     (format "@uref{file://%s}" destination)))
+	  ;; LINK points to an headline.  Use the headline as the NODE target
+	  (headline
+	   (format "@ref{%s}"
+		   (org-export-data
+		    (org-element-property :title destination) info)))
+	  (otherwise
+	   (let ((path (org-export-solidify-link-text path)))
+	     (if (not desc) (format "@ref{%s}" path)
+	       (format "@ref{%s,,%s}" path desc)))))))
+     ;; Special case for email addresses
+     (email
+      (format "@email{%s}" email))
+     ;; External link with a description part.
+     ((and path desc) (format "@uref{%s,%s}" path desc))
+     ;; External link without a description part.
+     (path (format "@uref{%s}" path))
+     ;; No path, only description.  Try to do something useful.
+     (t (format org-e-texinfo-link-with-unknown-path-format desc)))))
+
+
+;;;; Macro
+
+(defun org-e-texinfo-macro (macro contents info)
+  "Transcode a MACRO element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  ;; Use available tools.
+  (org-export-expand-macro macro info))
+
+
+;;;; Menu
+
+(defun org-e-texinfo-make-menu (info level)
+  "Create the menu for inclusion in the texifo document.
+
+INFO is the parsed buffer that contains the headlines.  LEVEL
+determines whether to make the main menu, or the detailed menu.
+
+This is only used for generating the primary menu.  In-Node menus
+are generated directly."
+  (let* ((parse (plist-get info :parse-tree))
+	 ;; Top determines level to build menu from, it finds the
+	 ;; level of the first headline in the export.
+	 (top (org-element-map
+	       parse 'headline
+	       (lambda (headline)
+		 (org-element-property :level headline)) info 't)))
+    (cond
+     ;; Generate the main menu
+     ((eq level 'main)
+      (org-e-texinfo--build-menu parse top info))
+     ;; Generate the detailed (recursive) menu
+     ((eq level 'detailed)
+      ;; Requires recursion
+      ;;(org-e-texinfo--build-detailed-menu parse top info)
+      (or (org-e-texinfo--build-menu parse top info 'detailed)
+	  "detailed"))
+     ;; Otherwise do nothing
+     (t))))
+
+
+;;;; Paragraph
+
+(defun org-e-texinfo-paragraph (paragraph contents info)
+  "Transcode a PARAGRAPH element from Org to Texinfo.
+CONTENTS is the contents of the paragraph, as a string.  INFO is
+the plist used as a communication channel."
+  contents)
+
+
+;;;; Plain List
+
+(defun org-e-texinfo-plain-list (plain-list contents info)
+  "Transcode a PLAIN-LIST element from Org to Texinfo.
+CONTENTS is the contents of the list.  INFO is a plist holding
+contextual information."
+  (let* ((attr (org-export-read-attribute :attr_texinfo plain-list))
+	 (indic (or (plist-get attr :indic)
+		    org-e-texinfo-def-table-markup))
+	 (type (org-element-property :type plain-list))
+	 (table-type (or (plist-get attr :table-type)
+			 "table"))
+	 ;; Ensure valid texinfo table type.
+	 (table-type (if (memq table-type '("table" "ftable" "vtable"))
+			 table-type
+		       "table"))
+	 (list-type (cond
+		     ((eq type 'ordered) "enumerate")
+		     ((eq type 'unordered) "itemize")
+		     ((eq type 'descriptive) table-type))))
+    (format "@%s%s\n@end %s"
+	    (if (eq type 'descriptive)
+		(concat list-type " " indic)
+	     list-type)
+	    contents
+	    list-type)))
+
+
+;;;; Plain Text
+
+(defun org-e-texinfo-plain-text (text info)
+  "Transcode a TEXT string from Org to Texinfo.
+TEXT is the string to transcode.  INFO is a plist holding
+contextual information."
+  ;; Protect @ { and }.
+  (while (string-match "\\([^\\]\\|^\\)\\([@{}]\\)" text)
+    (setq text
+	  (replace-match (format "\\%s" (match-string 2 text)) nil t text 2)))
+  ;; LaTeX into @LaTeX{} and TeX into @TeX{}
+  (let ((case-fold-search nil)
+	(start 0))
+    (while (string-match "\\(\\(?:La\\)?TeX\\)" text start)
+      (setq text (replace-match
+		  (format "@%s{}" (match-string 1 text)) nil t text)
+	    start (match-end 0))))
+  ;; Handle quotation marks
+  (setq text (org-e-texinfo--quotation-marks text info))
+  ;; Convert special strings.
+  (when (plist-get info :with-special-strings)
+    (while (string-match (regexp-quote "...") text)
+      (setq text (replace-match "@dots{}" nil t text))))
+  ;; Handle break preservation if required.
+  (when (plist-get info :preserve-breaks)
+    (setq text (replace-regexp-in-string "\\(\\\\\\\\\\)?[ \t]*\n" " @*\n"
+					 text)))
+  ;; Return value.
+  text)
+
+
+;;;; Planning
+
+(defun org-e-texinfo-planning (planning contents info)
+  "Transcode a PLANNING element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (concat
+   "@noindent"
+   (mapconcat
+    'identity
+    (delq nil
+	  (list
+	   (let ((closed (org-element-property :closed planning)))
+	     (when closed
+	       (concat
+		(format "@strong%s} " org-closed-string)
+		(format org-e-texinfo-inactive-timestamp-format
+			(org-translate-time closed)))))
+	   (let ((deadline (org-element-property :deadline planning)))
+	     (when deadline
+	       (concat
+		(format "@strong{%s} " org-deadline-string)
+		(format org-e-texinfo-active-timestamp-format
+			(org-translate-time deadline)))))
+	   (let ((scheduled (org-element-property :scheduled planning)))
+	     (when scheduled
+	       (concat
+		(format "@strong{%s} " org-scheduled-string)
+		(format org-e-texinfo-active-timestamp-format
+			(org-translate-time scheduled)))))))
+    " ")
+   "@*"))
+
+
+;;;; Property Drawer
+
+(defun org-e-texinfo-property-drawer (property-drawer contents info)
+  "Transcode a PROPERTY-DRAWER element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  ;; The property drawer isn't exported but we want separating blank
+  ;; lines nonetheless.
+  "")
+
+
+;;;; Quote Block
+
+(defun org-e-texinfo-quote-block (quote-block contents info)
+  "Transcode a QUOTE-BLOCK element from Org to Texinfo.
+CONTENTS holds the contents of the block.  INFO is a plist
+holding contextual information."
+  
+  (let* ((title (org-element-property :name quote-block))
+	 (start-quote (concat "@quotation"
+
+			      (if title
+				 (format " %s" title)))))
+    
+    (format "%s\n%s@end quotation" start-quote contents)))
+
+
+;;;; Quote Section
+
+(defun org-e-texinfo-quote-section (quote-section contents info)
+  "Transcode a QUOTE-SECTION element from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (let ((value (org-remove-indentation
+		(org-element-property :value quote-section))))
+    (when value (format "@verbatim\n%s@end verbatim" value))))
+
+
+;;;; Radio Target
+
+(defun org-e-texinfo-radio-target (radio-target text info)
+  "Transcode a RADIO-TARGET object from Org to Texinfo.
+TEXT is the text of the target.  INFO is a plist holding
+contextual information."
+  (format "@anchor{%s}%s"
+	  (org-export-solidify-link-text
+	   (org-element-property :value radio-target))
+	  text))
+
+
+;;;; Section
+
+(defun org-e-texinfo-section (section contents info)
+  "Transcode a SECTION element from Org to Texinfo.
+CONTENTS holds the contents of the section.  INFO is a plist
+holding contextual information."
+  contents)
+
+
+;;;; Special Block
+;;
+;; Are ignored at the moment
+
+;;;; Src Block
+
+(defun org-e-texinfo-src-block (src-block contents info)
+  "Transcode a SRC-BLOCK element from Org to Texinfo.
+CONTENTS holds the contents of the item.  INFO is a plist holding
+contextual information."
+  (let* ((lang (org-element-property :language src-block))
+	 (lisp-p (string-match-p "lisp" lang)))
+    (cond
+     ;; Case 1.  Lisp Block
+     (lisp-p
+      (format "@lisp\n%s\n@end lisp"
+	      (org-export-format-code-default src-block info)))
+     ;; Case 2.  Other blocks
+     (t
+      (format "@example\n%s\n@end example"
+	      (org-export-format-code-default src-block info))))))
+
+
+;;;; Statistics Cookie
+
+(defun org-e-texinfo-statistics-cookie (statistics-cookie contents info)
+  "Transcode a STATISTICS-COOKIE object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual information."
+  (org-element-property :value statistics-cookie))
+
+
+;;;; Strike-Through
+;;
+;; Strikethrough is ignored
+
+
+;;;; Subscript
+
+(defun org-e-texinfo-subscript (subscript contents info)
+  "Transcode a SUBSCRIPT object from Org to Texinfo.
+CONTENTS is the contents of the object.  INFO is a plist holding
+contextual information."
+  (format "@math{_%s}" contents))
+
+
+;;;; Superscript
+
+(defun org-e-texinfo-superscript (superscript contents info)
+  "Transcode a SUPERSCRIPT object from Org to Texinfo.
+CONTENTS is the contents of the object.  INFO is a plist holding
+contextual information."
+  (format "@math{^%s}" contents))
+
+
+;;;; Table
+;;
+;; `org-e-texinfo-table' is the entry point for table transcoding.  It
+;; takes care of tables with a "verbatim" attribute.  Otherwise, it
+;; delegates the job to either `org-e-texinfo-table--table.el-table' or
+;; `org-e-texinfo-table--org-table' functions, depending of the type of
+;; the table.
+;;
+;; `org-e-texinfo-table--align-string' is a subroutine used to build
+;; alignment string for Org tables.
+
+(defun org-e-texinfo-table (table contents info)
+  "Transcode a TABLE element from Org to Texinfo.
+CONTENTS is the contents of the table.  INFO is a plist holding
+contextual information."
+  (cond
+   ;; Case 1: verbatim table.
+   ((or org-e-texinfo-tables-verbatim
+	(let ((attr (mapconcat 'identity
+			       (org-element-property :attr_latex table)
+			       " ")))
+	  (and attr (string-match "\\<verbatim\\>" attr))))
+    (format "@verbatim \n%s\n@end verbatim"
+	    ;; Re-create table, without affiliated keywords.
+	    (org-trim
+	     (org-element-interpret-data
+	      `(table nil ,@(org-element-contents table))))))
+   ;; Case 2: table.el table.  Convert it using appropriate tools.
+   ((eq (org-element-property :type table) 'table.el)
+    (org-e-texinfo-table--table.el-table table contents info))
+   ;; Case 3: Standard table.
+   (t (org-e-texinfo-table--org-table table contents info))))
+
+(defun org-e-texinfo-table-column-widths (table info)
+  "Determine the largest table cell in each column to process alignment.
+
+TABLE is the table element to transcode.  INFO is a plist used as
+a communication channel."
+  (let* ((rows (org-element-map table 'table-row 'identity info))
+	 (collected (loop for row in rows collect
+			  (org-element-map
+			   row 'table-cell 'identity info)))
+	 (number-cells (length (car collected)))
+	 cells counts)
+    (loop for row in collected do
+	  (push (mapcar (lambda (ref)
+		     (let* ((start (org-element-property :contents-begin ref))
+			    (end (org-element-property :contents-end ref))
+			    (length (- end start)))
+		       length)) row) cells))
+    (setq cells (remove-if #'null cells))
+    (push (loop for count from 0 to (- number-cells 1) collect
+		   (loop for item in cells collect
+			 (nth count item))) counts)
+    (mapconcat '(lambda (size)
+		  (make-string size ?a)) (mapcar (lambda (ref)
+				   (apply 'max `,@ref)) (car counts))
+	       "} {")
+  ))
+
+(defun org-e-texinfo-table--org-table (table contents info)
+  "Return appropriate Texinfo code for an Org table.
+
+TABLE is the table type element to transcode.  CONTENTS is its
+contents, as a string.  INFO is a plist used as a communication
+channel.
+
+This function assumes TABLE has `org' as its `:type' attribute."
+  (let* ((attr (org-export-read-attribute :attr_texinfo table))
+	 (col-width (plist-get attr :columns))
+	 (columns (if col-width
+		      (format "@columnfractions %s"
+			      col-width)
+		    (format "{%s}"
+			    (org-e-texinfo-table-column-widths
+			     table info)))))
+    ;; Prepare the final format string for the table.
+    (cond
+     ;; Longtable.
+     ;; Others.
+     (t (concat
+	 (format "@multitable %s\n%s@end multitable"
+		 columns
+		 contents))))))
+
+(defun org-e-texinfo-table--table.el-table (table contents info)
+  "Returns nothing.
+
+Rather than return an invalid table, nothing is returned."
+  'nil)
+
+
+;;;; Table Cell
+
+(defun org-e-texinfo-table-cell (table-cell contents info)
+  "Transcode a TABLE-CELL element from Org to Texinfo.
+CONTENTS is the cell contents.  INFO is a plist used as
+a communication channel."
+  (concat (if (and contents
+		   org-e-texinfo-table-scientific-notation
+		   (string-match orgtbl-exp-regexp contents))
+	      ;; Use appropriate format string for scientific
+	      ;; notation.
+	      (format org-e-texinfo-table-scientific-notation
+		      (match-string 1 contents)
+		      (match-string 2 contents))
+	    contents)
+	  (when (org-export-get-next-element table-cell info) "\n@tab ")))
+
+
+;;;; Table Row
+
+(defun org-e-texinfo-table-row (table-row contents info)
+  "Transcode a TABLE-ROW element from Org to Texinfo.
+CONTENTS is the contents of the row.  INFO is a plist used as
+a communication channel."
+  ;; Rules are ignored since table separators are deduced from
+  ;; borders of the current row.
+  (when (eq (org-element-property :type table-row) 'standard) 
+    (concat "@item " contents "\n")))
+
+
+;;;; Target
+
+(defun org-e-texinfo-target (target contents info)
+  "Transcode a TARGET object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (format "@anchor{%s}"
+	  (org-export-solidify-link-text (org-element-property :value target))))
+
+
+;;;; Timestamp
+
+(defun org-e-texinfo-timestamp (timestamp contents info)
+  "Transcode a TIMESTAMP object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (let ((value (org-translate-time (org-element-property :value timestamp)))
+	(type (org-element-property :type timestamp)))
+    (cond ((memq type '(active active-range))
+	   (format org-e-texinfo-active-timestamp-format value))
+	  ((memq type '(inactive inactive-range))
+	   (format org-e-texinfo-inactive-timestamp-format value))
+	  (t (format org-e-texinfo-diary-timestamp-format value)))))
+
+
+;;;; Underline
+;;
+;; Underline is ignored
+
+
+;;;; Verbatim
+
+(defun org-e-texinfo-verbatim (verbatim contents info)
+  "Transcode a VERBATIM object from Org to Texinfo.
+CONTENTS is nil.  INFO is a plist used as a communication
+channel."
+  (org-e-texinfo--text-markup (org-element-property :value verbatim) 'verbatim))
+
+
+;;;; Verse Block
+
+(defun org-e-texinfo-verse-block (verse-block contents info)
+  "Transcode a VERSE-BLOCK element from Org to Texinfo.
+CONTENTS is verse block contents. INFO is a plist holding
+contextual information."
+  ;; In a verse environment, add a line break to each newline
+  ;; character and change each white space at beginning of a line
+  ;; into a space of 1 em.  Also change each blank line with
+  ;; a vertical space of 1 em.
+  (progn
+    (setq contents (replace-regexp-in-string
+		    "^ *\\\\\\\\$" "\\\\vspace*{1em}"
+		    (replace-regexp-in-string
+		     "\\(\\\\\\\\\\)?[ \t]*\n" " \\\\\\\\\n" contents)))
+    (while (string-match "^[ \t]+" contents)
+      (let ((new-str (format "\\hspace*{%dem}"
+			     (length (match-string 0 contents)))))
+	(setq contents (replace-match new-str nil t contents))))
+    (format "\\begin{verse}\n%s\\end{verse}" contents)))
+
+
+
+;;; Interactive functions
+
+(defun org-e-texinfo-export-to-texinfo
+  (&optional subtreep visible-only body-only ext-plist pub-dir)
+  "Export current buffer to a Texinfo file.
+
+If narrowing is active in the current buffer, only export its
+narrowed part.
+
+If a region is active, export that region.
+
+When optional argument SUBTREEP is non-nil, export the sub-tree
+at point, extracting information from the headline properties
+first.
+
+When optional argument VISIBLE-ONLY is non-nil, don't export
+contents of hidden elements.
+
+When optional argument BODY-ONLY is non-nil, only write code
+between \"\\begin{document}\" and \"\\end{document}\".
+
+EXT-PLIST, when provided, is a property list with external
+parameters overriding Org default settings, but still inferior to
+file-local settings.
+
+When optional argument PUB-DIR is set, use it as the publishing
+directory.
+
+Return output file's name."
+  (interactive)
+  (let ((outfile (org-export-output-file-name ".texi" subtreep pub-dir)))
+    (org-export-to-file
+     'e-texinfo outfile subtreep visible-only body-only ext-plist)))
+
+(defun org-e-texinfo-export-to-info
+  (&optional subtreep visible-only body-only ext-plist pub-dir)
+  "Export current buffer to Texinfo then process through to INFO.
+
+If narrowing is active in the current buffer, only export its
+narrowed part.
+
+If a region is active, export that region.
+
+When optional argument SUBTREEP is non-nil, export the sub-tree
+at point, extracting information from the headline properties
+first.
+
+When optional argument VISIBLE-ONLY is non-nil, don't export
+contents of hidden elements.
+
+When optional argument BODY-ONLY is non-nil, only write code
+between \"\\begin{document}\" and \"\\end{document}\".
+
+EXT-PLIST, when provided, is a property list with external
+parameters overriding Org default settings, but still inferior to
+file-local settings.
+
+When optional argument PUB-DIR is set, use it as the publishing
+directory.
+
+Return INFO file's name."
+  (interactive)
+  (org-e-texinfo-compile
+   (org-e-texinfo-export-to-texinfo
+    subtreep visible-only body-only ext-plist pub-dir)))
+
+(defun org-e-texinfo-compile (texifile)
+  "Compile a texinfo file.
+
+TEXIFILE is the name of the file being compiled.  Processing is
+done through the command specified in `org-e-texinfo-info-process'.
+
+Return INFO file name or an error if it couldn't be produced."
+  (let* ((wconfig (current-window-configuration))
+	 (texifile (file-truename texifile))
+	 (base (file-name-sans-extension texifile))
+	 errors)
+    (message (format "Processing Texinfo file %s ..." texifile))
+    (unwind-protect
+	(progn
+	  (cond
+	   ;; A function is provided: Apply it.
+	   ((functionp org-e-texinfo-info-process)
+	    (funcall org-e-texinfo-info-process (shell-quote-argument texifile)))
+	   ;; A list is provided: Replace %b, %f and %o with appropriate
+	   ;; values in each command before applying it.  Output is
+	   ;; redirected to "*Org INFO Texinfo Output*" buffer.
+	   ((consp org-e-texinfo-info-process)
+	    (let* ((out-dir (or (file-name-directory texifile) "./"))
+		   (outbuf (get-buffer-create "*Org Info Texinfo Output*")))
+	      (mapc
+	       (lambda (command)
+		 (shell-command
+		  (replace-regexp-in-string
+		   "%b" (shell-quote-argument base)
+		   (replace-regexp-in-string
+		    "%f" (shell-quote-argument texifile)
+		    (replace-regexp-in-string
+		     "%o" (shell-quote-argument out-dir) command t t) t t) t t)
+		  outbuf))
+	       org-e-texinfo-info-process)
+	      ;; Collect standard errors from output buffer.
+	      (setq errors (org-e-texinfo-collect-errors outbuf))))
+	   (t (error "No valid command to process to Info")))
+	  (let ((infofile (concat base ".info")))
+	    ;; Check for process failure.  Provide collected errors if
+	    ;; possible.
+	    (if (not (file-exists-p infofile))
+		(error (concat (format "INFO file %s wasn't produced" infofile)
+			       (when errors (concat ": " errors))))
+	      ;; Else remove log files, when specified, and signal end of
+	      ;; process to user, along with any error encountered.
+	      (message (concat "Process completed"
+			       (if (not errors) "."
+				 (concat " with errors: " errors)))))
+	    ;; Return output file name.
+	    infofile))
+      (set-window-configuration wconfig))))
+
+(defun org-e-texinfo-collect-errors (buffer)
+  "Collect some kind of errors from \"pdflatex\" command output.
+
+BUFFER is the buffer containing output.
+
+Return collected error types as a string, or nil if there was
+none."
+  (with-current-buffer buffer
+    (save-excursion
+      (goto-char (point-max))
+      ;; Find final "makeinfo" run.
+      (when (re-search-backward "^makeinfo (GNU texinfo)" nil t)
+	(let ((case-fold-search t)
+	      (errors ""))
+	  (when (save-excursion
+		  (re-search-forward "perhaps incorrect sectioning?" nil t))
+	    (setq errors (concat errors " [incorrect sectionnng]")))
+	  (when (save-excursion
+		  (re-search-forward "missing close brace" nil t))
+	    (setq errors (concat errors " [syntax error]")))
+	  (when (save-excursion
+		  (re-search-forward "Unknown command" nil t))
+	    (setq errors (concat errors " [undefined @command]")))
+	  (when (save-excursion
+		  (re-search-forward "No matching @end" nil t))
+	    (setq errors (concat errors " [block incomplete]")))
+	  (when (save-excursion
+		  (re-search-forward "requires a sectioning" nil t))
+	    (setq errors (concat errors " [invalid section command]")))
+	  (when (save-excursion
+		  (re-search-forward "[unexpected]" nil t))
+	    (setq errors (concat errors " [unexpected error]")))
+	  (and (org-string-nw-p errors) (org-trim errors)))))))
+
+
+(provide 'org-e-texinfo)
+;;; org-e-texinfo.el ends here