Changes
[emacs.git] / .emacs.d / elisp / org / ox-ascii.el
index 4a6696e..42495e2 100644 (file)
@@ -1,6 +1,6 @@
 ;;; ox-ascii.el --- ASCII Back-End for Org Export Engine
 
 ;;; ox-ascii.el --- ASCII Back-End for Org Export Engine
 
-;; Copyright (C) 2012-2014 Free Software Foundation, Inc.
+;; Copyright (C) 2012-2015 Free Software Foundation, Inc.
 
 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
 ;; Keywords: outlines, hypermedia, calendar, wp
 
 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
 ;; Keywords: outlines, hypermedia, calendar, wp
@@ -49,8 +49,6 @@
     (center-block . org-ascii-center-block)
     (clock . org-ascii-clock)
     (code . org-ascii-code)
     (center-block . org-ascii-center-block)
     (clock . org-ascii-clock)
     (code . org-ascii-code)
-    (comment . (lambda (&rest args) ""))
-    (comment-block . (lambda (&rest args) ""))
     (drawer . org-ascii-drawer)
     (dynamic-block . org-ascii-dynamic-block)
     (entity . org-ascii-entity)
     (drawer . org-ascii-drawer)
     (dynamic-block . org-ascii-dynamic-block)
     (entity . org-ascii-entity)
     (latex-fragment . org-ascii-latex-fragment)
     (line-break . org-ascii-line-break)
     (link . org-ascii-link)
     (latex-fragment . org-ascii-latex-fragment)
     (line-break . org-ascii-line-break)
     (link . org-ascii-link)
+    (node-property . org-ascii-node-property)
     (paragraph . org-ascii-paragraph)
     (plain-list . org-ascii-plain-list)
     (plain-text . org-ascii-plain-text)
     (planning . org-ascii-planning)
     (paragraph . org-ascii-paragraph)
     (plain-list . org-ascii-plain-list)
     (plain-text . org-ascii-plain-text)
     (planning . org-ascii-planning)
+    (property-drawer . org-ascii-property-drawer)
     (quote-block . org-ascii-quote-block)
     (quote-block . org-ascii-quote-block)
-    (quote-section . org-ascii-quote-section)
     (radio-target . org-ascii-radio-target)
     (section . org-ascii-section)
     (special-block . org-ascii-special-block)
     (radio-target . org-ascii-radio-target)
     (section . org-ascii-section)
     (special-block . org-ascii-special-block)
                   (:filter-parse-tree org-ascii-filter-paragraph-spacing
                                       org-ascii-filter-comment-spacing)
                   (:filter-section . org-ascii-filter-headline-blank-lines))
                   (:filter-parse-tree org-ascii-filter-paragraph-spacing
                                       org-ascii-filter-comment-spacing)
                   (:filter-section . org-ascii-filter-headline-blank-lines))
-  :options-alist '((:ascii-charset nil nil org-ascii-charset)))
+  :options-alist
+  '((:ascii-bullets nil nil org-ascii-bullets)
+    (:ascii-caption-above nil nil org-ascii-caption-above)
+    (:ascii-charset nil nil org-ascii-charset)
+    (:ascii-global-margin nil nil org-ascii-global-margin)
+    (:ascii-format-drawer-function nil nil org-ascii-format-drawer-function)
+    (:ascii-format-inlinetask-function
+     nil nil org-ascii-format-inlinetask-function)
+    (:ascii-headline-spacing nil nil org-ascii-headline-spacing)
+    (:ascii-indented-line-width nil nil org-ascii-indented-line-width)
+    (:ascii-inlinetask-width nil nil org-ascii-inlinetask-width)
+    (:ascii-inner-margin nil nil org-ascii-inner-margin)
+    (:ascii-links-to-notes nil nil org-ascii-links-to-notes)
+    (:ascii-list-margin nil nil org-ascii-list-margin)
+    (:ascii-paragraph-spacing nil nil org-ascii-paragraph-spacing)
+    (:ascii-quote-margin nil nil org-ascii-quote-margin)
+    (:ascii-table-keep-all-vertical-lines
+     nil nil org-ascii-table-keep-all-vertical-lines)
+    (:ascii-table-use-ascii-art nil nil org-ascii-table-use-ascii-art)
+    (:ascii-table-widen-columns nil nil org-ascii-table-widen-columns)
+    (:ascii-text-width nil nil org-ascii-text-width)
+    (:ascii-underline nil nil org-ascii-underline)
+    (:ascii-verbatim-format nil nil org-ascii-verbatim-format)))
 
 
 \f
 
 
 \f
@@ -162,6 +183,15 @@ This margin is applied on both sides of the text."
   :package-version '(Org . "8.0")
   :type 'integer)
 
   :package-version '(Org . "8.0")
   :type 'integer)
 
+(defcustom org-ascii-list-margin 0
+  "Width of margin used for plain lists, in characters.
+This margin applies to top level list only, not to its
+sub-lists."
+  :group 'org-export-ascii
+  :version "25.1"
+  :package-version '(Org . "8.3")
+  :type 'integer)
+
 (defcustom org-ascii-inlinetask-width 30
   "Width of inline tasks, in number of characters.
 This number ignores any margin."
 (defcustom org-ascii-inlinetask-width 30
   "Width of inline tasks, in number of characters.
 This number ignores any margin."
@@ -384,14 +414,18 @@ nil to ignore the inline task."
 
 ;; Internal functions fall into three categories.
 
 
 ;; Internal functions fall into three categories.
 
-;; The first one is about text formatting.  The core function is
-;; `org-ascii--current-text-width', which determines the current
-;; text width allowed to a given element.  In other words, it helps
-;; keeping each line width within maximum text width defined in
-;; `org-ascii-text-width'.  Once this information is known,
-;; `org-ascii--fill-string', `org-ascii--justify-string',
-;; `org-ascii--box-string' and `org-ascii--indent-string' can
-;; operate on a given output string.
+;; The first one is about text formatting.  The core functions are
+;; `org-ascii--current-text-width' and
+;; `org-ascii--current-justification', which determine, respectively,
+;; the current text width allowed to a given element and its expected
+;; justification.  Once this information is known,
+;; `org-ascii--fill-string', `org-ascii--justify-lines',
+;; `org-ascii--justify-element' `org-ascii--box-string' and
+;; `org-ascii--indent-string' can operate on a given output string.
+;; In particular, justification happens at the regular (i.e.,
+;; non-greater) element level, which means that when the exporting
+;; process reaches a container (e.g., a center block) content are
+;; already justified.
 
 ;; The second category contains functions handling elements listings,
 ;; triggered by "#+TOC:" keyword.  As such, `org-ascii--build-toc'
 
 ;; The second category contains functions handling elements listings,
 ;; triggered by "#+TOC:" keyword.  As such, `org-ascii--build-toc'
@@ -420,23 +454,24 @@ a communication channel.
 Optional argument JUSTIFY can specify any type of justification
 among `left', `center', `right' or `full'.  A nil value is
 equivalent to `left'.  For a justification that doesn't also fill
 Optional argument JUSTIFY can specify any type of justification
 among `left', `center', `right' or `full'.  A nil value is
 equivalent to `left'.  For a justification that doesn't also fill
-string, see `org-ascii--justify-string'.
+string, see `org-ascii--justify-lines' and
+`org-ascii--justify-block'.
 
 Return nil if S isn't a string."
 
 Return nil if S isn't a string."
-  ;; Don't fill paragraph when break should be preserved.
-  (cond ((not (stringp s)) nil)
-       ((plist-get info :preserve-breaks) s)
-       (t (let ((double-space-p sentence-end-double-space))
-            (with-temp-buffer
-              (let ((fill-column text-width)
-                    (use-hard-newlines t)
-                    (sentence-end-double-space double-space-p))
-                (insert s)
-                (fill-region (point-min) (point-max) justify))
-              (buffer-string))))))
-
-(defun org-ascii--justify-string (s text-width how)
-  "Justify string S.
+  (when (stringp s)
+    (let ((double-space-p sentence-end-double-space))
+      (with-temp-buffer
+       (let ((fill-column text-width)
+             (use-hard-newlines t)
+             (sentence-end-double-space double-space-p))
+         (insert (if (plist-get info :preserve-breaks)
+                     (replace-regexp-in-string "\n" hard-newline s)
+                   s))
+         (fill-region (point-min) (point-max) justify))
+       (buffer-string)))))
+
+(defun org-ascii--justify-lines (s text-width how)
+  "Justify all lines in string S.
 TEXT-WIDTH is an integer specifying maximum length of a line.
 HOW determines the type of justification: it can be `left',
 `right', `full' or `center'."
 TEXT-WIDTH is an integer specifying maximum length of a line.
 HOW determines the type of justification: it can be `left',
 `right', `full' or `center'."
@@ -452,6 +487,48 @@ HOW determines the type of justification: it can be `left',
        (forward-line)))
     (buffer-string)))
 
        (forward-line)))
     (buffer-string)))
 
+(defun org-ascii--justify-element (contents element info)
+  "Justify CONTENTS of ELEMENT.
+INFO is a plist used as a communication channel.  Justification
+is done according to the type of element.  More accurately,
+paragraphs are filled and other elements are justified as blocks,
+that is according to the widest non blank line in CONTENTS."
+  (if (not (org-string-nw-p contents)) contents
+    (let ((text-width (org-ascii--current-text-width element info))
+         (how (org-ascii--current-justification element)))
+      (cond
+       ((eq (org-element-type element) 'paragraph)
+       ;; Paragraphs are treated specially as they need to be filled.
+       (org-ascii--fill-string contents text-width info how))
+       ((eq how 'left) contents)
+       (t (with-temp-buffer
+           (insert contents)
+           (goto-char (point-min))
+           (catch 'exit
+             (let ((max-width 0))
+               ;; Compute maximum width.  Bail out if it is greater
+               ;; than page width, since no justification is
+               ;; possible.
+               (save-excursion
+                 (while (not (eobp))
+                   (unless (org-looking-at-p "[ \t]*$")
+                     (end-of-line)
+                     (let ((column (current-column)))
+                       (cond
+                        ((>= column text-width) (throw 'exit contents))
+                        ((> column max-width) (setq max-width column)))))
+                   (forward-line)))
+               ;; Justify every line according to TEXT-WIDTH and
+               ;; MAX-WIDTH.
+               (let ((offset (/ (- text-width max-width)
+                                (if (eq how 'right) 1 2))))
+                 (if (zerop offset) (throw 'exit contents)
+                   (while (not (eobp))
+                     (unless (org-looking-at-p "[ \t]*$")
+                       (org-indent-to-column offset))
+                     (forward-line)))))
+             (buffer-string))))))))
+
 (defun org-ascii--indent-string (s width)
   "Indent string S by WIDTH white spaces.
 Empty lines are not indented."
 (defun org-ascii--indent-string (s width)
   "Indent string S by WIDTH white spaces.
 Empty lines are not indented."
@@ -463,7 +540,7 @@ Empty lines are not indented."
   "Return string S with a partial box to its left.
 INFO is a plist used as a communication channel."
   (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
   "Return string S with a partial box to its left.
 INFO is a plist used as a communication channel."
   (let ((utf8p (eq (plist-get info :ascii-charset) 'utf-8)))
-    (format (if utf8p "â\95­â\94\80â\94\80â\94\80â\94\80\n%s\nâ\95°────" ",----\n%s\n`----")
+    (format (if utf8p "â\94\8câ\94\80â\94\80â\94\80â\94\80\n%s\nâ\94\94────" ",----\n%s\n`----")
            (replace-regexp-in-string
             "^" (if utf8p "│ " "| ")
             ;; Remove last newline character.
            (replace-regexp-in-string
             "^" (if utf8p "│ " "| ")
             ;; Remove last newline character.
@@ -474,24 +551,25 @@ INFO is a plist used as a communication channel."
 INFO is a plist used as a communication channel."
   (case (org-element-type element)
     ;; Elements with an absolute width: `headline' and `inlinetask'.
 INFO is a plist used as a communication channel."
   (case (org-element-type element)
     ;; Elements with an absolute width: `headline' and `inlinetask'.
-    (inlinetask org-ascii-inlinetask-width)
+    (inlinetask (plist-get info :ascii-inlinetask-width))
     (headline
     (headline
-     (- org-ascii-text-width
+     (- (plist-get info :ascii-text-width)
        (let ((low-level-rank (org-export-low-level-p element info)))
        (let ((low-level-rank (org-export-low-level-p element info)))
-         (if low-level-rank (* low-level-rank 2) org-ascii-global-margin))))
+         (if low-level-rank (* low-level-rank 2)
+           (plist-get info :ascii-global-margin)))))
     ;; Elements with a relative width: store maximum text width in
     ;; TOTAL-WIDTH.
     (otherwise
     ;; Elements with a relative width: store maximum text width in
     ;; TOTAL-WIDTH.
     (otherwise
-     (let* ((genealogy (cons element (org-export-get-genealogy element)))
+     (let* ((genealogy (org-element-lineage element nil t))
            ;; Total width is determined by the presence, or not, of an
            ;; inline task among ELEMENT parents.
            (total-width
             (if (loop for parent in genealogy
                       thereis (eq (org-element-type parent) 'inlinetask))
            ;; Total width is determined by the presence, or not, of an
            ;; inline task among ELEMENT parents.
            (total-width
             (if (loop for parent in genealogy
                       thereis (eq (org-element-type parent) 'inlinetask))
-                org-ascii-inlinetask-width
+                (plist-get info :ascii-inlinetask-width)
               ;; No inlinetask: Remove global margin from text width.
               ;; No inlinetask: Remove global margin from text width.
-              (- org-ascii-text-width
-                 org-ascii-global-margin
+              (- (plist-get info :ascii-text-width)
+                 (plist-get info :ascii-global-margin)
                  (let ((parent (org-export-get-parent-headline element)))
                    ;; Inner margin doesn't apply to text before first
                    ;; headline.
                  (let ((parent (org-export-get-parent-headline element)))
                    ;; Inner margin doesn't apply to text before first
                    ;; headline.
@@ -502,41 +580,66 @@ INFO is a plist used as a communication channel."
                        ;; low level headlines, since they've got their
                        ;; own indentation mechanism.
                        (if low-level-rank (* low-level-rank 2)
                        ;; low level headlines, since they've got their
                        ;; own indentation mechanism.
                        (if low-level-rank (* low-level-rank 2)
-                         org-ascii-inner-margin))))))))
+                         (plist-get info :ascii-inner-margin)))))))))
        (- total-width
        (- total-width
-         ;; Each `quote-block', `quote-section' and `verse-block' above
-         ;; narrows text width by twice the standard margin size.
+         ;; Each `quote-block' and `verse-block' above narrows text
+         ;; width by twice the standard margin size.
          (+ (* (loop for parent in genealogy
                      when (memq (org-element-type parent)
          (+ (* (loop for parent in genealogy
                      when (memq (org-element-type parent)
-                                '(quote-block quote-section verse-block))
+                                '(quote-block verse-block))
                      count parent)
                      count parent)
-               2 org-ascii-quote-margin)
+               2 (plist-get info :ascii-quote-margin))
+            ;; Apply list margin once per "top-level" plain-list
+            ;; containing current line
+            (* (let ((count 0))
+                 (dolist (e genealogy count)
+                   (and (eq (org-element-type e) 'plain-list)
+                        (not (eq (org-element-type (org-export-get-parent e))
+                                 'item))
+                        (incf count))))
+               (plist-get info :ascii-list-margin))
             ;; Text width within a plain-list is restricted by
             ;; indentation of current item.  If that's the case,
             ;; compute it with the help of `:structure' property from
             ;; parent item, if any.
             ;; Text width within a plain-list is restricted by
             ;; indentation of current item.  If that's the case,
             ;; compute it with the help of `:structure' property from
             ;; parent item, if any.
-            (let ((parent-item
+            (let ((item
                    (if (eq (org-element-type element) 'item) element
                      (loop for parent in genealogy
                            when (eq (org-element-type parent) 'item)
                            return parent))))
                    (if (eq (org-element-type element) 'item) element
                      (loop for parent in genealogy
                            when (eq (org-element-type parent) 'item)
                            return parent))))
-              (if (not parent-item) 0
+              (if (not item) 0
                 ;; Compute indentation offset of the current item,
                 ;; that is the sum of the difference between its
                 ;; indentation and the indentation of the top item in
                 ;; the list and current item bullet's length.  Also
                 ;; remove checkbox length, and tag length (for
                 ;; description lists) or bullet length.
                 ;; Compute indentation offset of the current item,
                 ;; that is the sum of the difference between its
                 ;; indentation and the indentation of the top item in
                 ;; the list and current item bullet's length.  Also
                 ;; remove checkbox length, and tag length (for
                 ;; description lists) or bullet length.
-                (let ((struct (org-element-property :structure parent-item))
-                      (beg-item (org-element-property :begin parent-item)))
+                (let ((struct (org-element-property :structure item))
+                      (beg-item (org-element-property :begin item)))
                   (+ (- (org-list-get-ind beg-item struct)
                         (org-list-get-ind
                          (org-list-get-top-point struct) struct))
                   (+ (- (org-list-get-ind beg-item struct)
                         (org-list-get-ind
                          (org-list-get-top-point struct) struct))
-                     (string-width (or (org-ascii--checkbox parent-item info)
+                     (string-width (or (org-ascii--checkbox item info)
                                        ""))
                      (string-width
                                        ""))
                      (string-width
-                      (or (org-list-get-tag beg-item struct)
-                          (org-list-get-bullet beg-item struct)))))))))))))
+                      (let ((tag (org-element-property :tag item)))
+                        (if tag (org-export-data tag info)
+                          (org-element-property :bullet item))))))))))))))
+
+(defun org-ascii--current-justification (element)
+  "Return expected justification for ELEMENT's contents.
+Return value is a symbol among `left', `center', `right' and
+`full'."
+  (let (justification)
+    (while (and (not justification)
+               (setq element (org-element-property :parent element)))
+      (case (org-element-type element)
+       (center-block (setq justification 'center))
+       (special-block
+        (let ((name (org-element-property :type element)))
+          (cond ((string= name "JUSTIFYRIGHT") (setq justification 'right))
+                ((string= name "JUSTIFYLEFT") (setq justification 'left)))))))
+    (or justification 'left)))
 
 (defun org-ascii--build-title
   (element info text-width &optional underline notags toc)
 
 (defun org-ascii--build-title
   (element info text-width &optional underline notags toc)
@@ -601,7 +704,7 @@ possible.  It doesn't apply to `inlinetask' elements."
        (let ((under-char
              (nth (1- (org-export-get-relative-level element info))
                   (cdr (assq (plist-get info :ascii-charset)
        (let ((under-char
              (nth (1- (org-export-get-relative-level element info))
                   (cdr (assq (plist-get info :ascii-charset)
-                             org-ascii-underline)))))
+                             (plist-get info :ascii-underline))))))
         (and under-char
              (concat "\n"
                      (make-string (/ (string-width first-part)
         (and under-char
              (concat "\n"
                      (make-string (/ (string-width first-part)
@@ -640,7 +743,7 @@ caption keyword."
                 (org-export-data caption info))
         (org-ascii--current-text-width element info) info)))))
 
                 (org-export-data caption info))
         (org-ascii--current-text-width element info) info)))))
 
-(defun org-ascii--build-toc (info &optional n keyword)
+(defun org-ascii--build-toc (info &optional n keyword local)
   "Return a table of contents.
 
 INFO is a plist used as a communication channel.
   "Return a table of contents.
 
 INFO is a plist used as a communication channel.
@@ -649,28 +752,34 @@ Optional argument N, when non-nil, is an integer specifying the
 depth of the table.
 
 Optional argument KEYWORD specifies the TOC keyword, if any, from
 depth of the table.
 
 Optional argument KEYWORD specifies the TOC keyword, if any, from
-which the table of contents generation has been initiated."
-  (let ((title (org-ascii--translate "Table of Contents" info)))
-    (concat
-     title "\n"
-     (make-string (string-width title)
-                 (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
-     "\n\n"
-     (let ((text-width
-           (if keyword (org-ascii--current-text-width keyword info)
-             (- org-ascii-text-width org-ascii-global-margin))))
-       (mapconcat
-       (lambda (headline)
-         (let* ((level (org-export-get-relative-level headline info))
-                (indent (* (1- level) 3)))
-           (concat
-            (unless (zerop indent) (concat (make-string (1- indent) ?.) " "))
-            (org-ascii--build-title
-             headline info (- text-width indent) nil
-             (or (not (plist-get info :with-tags))
-                 (eq (plist-get info :with-tags) 'not-in-toc))
-             'toc))))
-       (org-export-collect-headlines info n) "\n")))))
+which the table of contents generation has been initiated.
+
+When optional argument LOCAL is non-nil, build a table of
+contents according to the current headline."
+  (concat
+   (unless local
+     (let ((title (org-ascii--translate "Table of Contents" info)))
+       (concat title "\n"
+              (make-string
+               (string-width title)
+               (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))
+              "\n\n")))
+   (let ((text-width
+         (if keyword (org-ascii--current-text-width keyword info)
+           (- (plist-get info :ascii-text-width)
+              (plist-get info :ascii-global-margin)))))
+     (mapconcat
+      (lambda (headline)
+       (let* ((level (org-export-get-relative-level headline info))
+              (indent (* (1- level) 3)))
+         (concat
+          (unless (zerop indent) (concat (make-string (1- indent) ?.) " "))
+          (org-ascii--build-title
+           headline info (- text-width indent) nil
+           (or (not (plist-get info :with-tags))
+               (eq (plist-get info :with-tags) 'not-in-toc))
+           'toc))))
+      (org-export-collect-headlines info n (and local keyword)) "\n"))))
 
 (defun org-ascii--list-listings (keyword info)
   "Return a list of listings.
 
 (defun org-ascii--list-listings (keyword info)
   "Return a list of listings.
@@ -685,7 +794,8 @@ generation.  INFO is a plist used as a communication channel."
      "\n\n"
      (let ((text-width
            (if keyword (org-ascii--current-text-width keyword info)
      "\n\n"
      (let ((text-width
            (if keyword (org-ascii--current-text-width keyword info)
-             (- org-ascii-text-width org-ascii-global-margin)))
+             (- (plist-get info :ascii-text-width)
+                (plist-get info :ascii-global-margin))))
           ;; Use a counter instead of retrieving ordinal of each
           ;; src-block.
           (count 0))
           ;; Use a counter instead of retrieving ordinal of each
           ;; src-block.
           (count 0))
@@ -724,7 +834,8 @@ generation.  INFO is a plist used as a communication channel."
      "\n\n"
      (let ((text-width
            (if keyword (org-ascii--current-text-width keyword info)
      "\n\n"
      (let ((text-width
            (if keyword (org-ascii--current-text-width keyword info)
-             (- org-ascii-text-width org-ascii-global-margin)))
+             (- (plist-get info :ascii-text-width)
+                (plist-get info :ascii-global-margin))))
           ;; Use a counter instead of retrieving ordinal of each
           ;; src-block.
           (count 0))
           ;; Use a counter instead of retrieving ordinal of each
           ;; src-block.
           (count 0))
@@ -812,13 +923,22 @@ channel."
              (if (not dest) (org-ascii--translate "Unknown reference" info)
                (format
                 (org-ascii--translate "See section %s" info)
              (if (not dest) (org-ascii--translate "Unknown reference" info)
                (format
                 (org-ascii--translate "See section %s" info)
-                (mapconcat 'number-to-string
-                           (org-export-get-headline-number dest info) "."))))
+                (if (org-export-numbered-headline-p dest info)
+                    (mapconcat #'number-to-string
+                               (org-export-get-headline-number dest info) ".")
+                  (org-export-data (org-element-property :title dest) info)))))
             width info) "\n\n")))
        ;; Do not add a link that cannot be resolved and doesn't have
        ;; any description: destination is already visible in the
        ;; paragraph.
        ((not (org-element-contents link)) nil)
             width info) "\n\n")))
        ;; Do not add a link that cannot be resolved and doesn't have
        ;; any description: destination is already visible in the
        ;; paragraph.
        ((not (org-element-contents link)) nil)
+       ;; Do not add a link already handled by custom export
+       ;; functions.
+       ((let ((protocol (nth 2 (assoc type org-link-protocols)))
+              (path (org-element-property :path link)))
+          (and (functionp protocol)
+               (funcall protocol (org-link-unescape path) anchor 'ascii)))
+        nil)
        (t
         (concat
          (org-ascii--fill-string
        (t
         (concat
          (org-ascii--fill-string
@@ -843,11 +963,13 @@ INFO is a plist used as a communication channel."
 (defun org-ascii-template--document-title (info)
   "Return document title, as a string.
 INFO is a plist used as a communication channel."
 (defun org-ascii-template--document-title (info)
   "Return document title, as a string.
 INFO is a plist used as a communication channel."
-  (let* ((text-width org-ascii-text-width)
+  (let* ((text-width (plist-get info :ascii-text-width))
         ;; Links in the title will not be resolved later, so we make
         ;; sure their path is located right after them.
         ;; Links in the title will not be resolved later, so we make
         ;; sure their path is located right after them.
-        (org-ascii-links-to-notes nil)
-        (title (org-export-data (plist-get info :title) info))
+        (info (org-combine-plists info '(:ascii-links-to-notes nil)))
+        (title (if (plist-get info :with-title)
+                   (org-export-data (plist-get info :title) info)
+                 ""))
         (author (and (plist-get info :with-author)
                      (let ((auth (plist-get info :author)))
                        (and auth (org-export-data auth info)))))
         (author (and (plist-get info :with-author)
                      (let ((auth (plist-get info :author)))
                        (and auth (org-export-data auth info)))))
@@ -878,7 +1000,7 @@ INFO is a plist used as a communication channel."
           date "\n\n\n"))
         ((org-string-nw-p date)
          (concat
           date "\n\n\n"))
         ((org-string-nw-p date)
          (concat
-          (org-ascii--justify-string date text-width 'right)
+          (org-ascii--justify-lines date text-width 'right)
           "\n\n\n"))
         ((and (org-string-nw-p author) (org-string-nw-p email))
          (concat author "\n" email "\n\n\n"))
           "\n\n\n"))
         ((and (org-string-nw-p author) (org-string-nw-p email))
          (concat author "\n" email "\n\n\n"))
@@ -899,17 +1021,15 @@ INFO is a plist used as a communication channel."
                            (string-width (or email "")))
                       2)
                    text-width) (if utf8p ?━ ?_))))
                            (string-width (or email "")))
                       2)
                    text-width) (if utf8p ?━ ?_))))
-       (org-ascii--justify-string
+       (org-ascii--justify-lines
         (concat line "\n"
                 (unless utf8p "\n")
                 (upcase formatted-title)
                 (cond
                  ((and (org-string-nw-p author) (org-string-nw-p email))
         (concat line "\n"
                 (unless utf8p "\n")
                 (upcase formatted-title)
                 (cond
                  ((and (org-string-nw-p author) (org-string-nw-p email))
-                  (concat (if utf8p "\n\n\n" "\n\n") author "\n" email))
-                 ((org-string-nw-p author)
-                  (concat (if utf8p "\n\n\n" "\n\n") author))
-                 ((org-string-nw-p email)
-                  (concat (if utf8p "\n\n\n" "\n\n") email)))
+                  (concat "\n\n" author "\n" email))
+                 ((org-string-nw-p author) (concat "\n\n" author))
+                 ((org-string-nw-p email) (concat "\n\n" email)))
                 "\n" line
                 (when (org-string-nw-p date) (concat "\n\n\n" date))
                 "\n\n\n") text-width 'center)))))
                 "\n" line
                 (when (org-string-nw-p date) (concat "\n\n\n" date))
                 "\n\n\n") text-width 'center)))))
@@ -919,81 +1039,82 @@ INFO is a plist used as a communication channel."
 CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
   (org-element-normalize-string
 CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
   (org-element-normalize-string
-   (org-ascii--indent-string
-    (concat
-     ;; 1. Document's body.
-     contents
-     ;; 2. Footnote definitions.
-     (let ((definitions (org-export-collect-footnote-definitions
-                        (plist-get info :parse-tree) info))
-          ;; Insert full links right inside the footnote definition
-          ;; as they have no chance to be inserted later.
-          (org-ascii-links-to-notes nil))
-       (when definitions
-        (concat
-         "\n\n\n"
-         (let ((title (org-ascii--translate "Footnotes" info)))
-           (concat
-            title "\n"
-            (make-string
-             (string-width title)
-             (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))))
-         "\n\n"
-         (let ((text-width (- org-ascii-text-width org-ascii-global-margin)))
-           (mapconcat
-            (lambda (ref)
-              (let ((id (format "[%s] " (car ref))))
-                ;; Distinguish between inline definitions and
-                ;; full-fledged definitions.
-                (org-trim
-                 (let ((def (nth 2 ref)))
-                   (if (eq (org-element-type def) 'org-data)
-                       ;; Full-fledged definition: footnote ID is
-                       ;; inserted inside the first parsed paragraph
-                       ;; (FIRST), if any, to be sure filling will
-                       ;; take it into consideration.
-                       (let ((first (car (org-element-contents def))))
-                         (if (not (eq (org-element-type first) 'paragraph))
-                             (concat id "\n" (org-export-data def info))
-                           (push id (nthcdr 2 first))
-                           (org-export-data def info)))
-                     ;; Fill paragraph once footnote ID is inserted
-                     ;; in order to have a correct length for first
-                     ;; line.
-                     (org-ascii--fill-string
-                      (concat id (org-export-data def info))
-                      text-width info))))))
-            definitions "\n\n"))))))
-    org-ascii-global-margin)))
+   (let ((global-margin (plist-get info :ascii-global-margin)))
+     (org-ascii--indent-string
+      (concat
+       ;; 1. Document's body.
+       contents
+       ;; 2. Footnote definitions.
+       (let ((definitions (org-export-collect-footnote-definitions info))
+            ;; Insert full links right inside the footnote definition
+            ;; as they have no chance to be inserted later.
+            (info (org-combine-plists info '(:ascii-links-to-notes nil))))
+        (when definitions
+          (concat
+           "\n\n\n"
+           (let ((title (org-ascii--translate "Footnotes" info)))
+             (concat
+              title "\n"
+              (make-string
+               (string-width title)
+               (if (eq (plist-get info :ascii-charset) 'utf-8) ?─ ?_))))
+           "\n\n"
+           (let ((text-width (- (plist-get info :ascii-text-width)
+                                global-margin)))
+             (mapconcat
+              (lambda (ref)
+                (let ((id (format "[%s] " (car ref))))
+                  ;; Distinguish between inline definitions and
+                  ;; full-fledged definitions.
+                  (org-trim
+                   (let ((def (nth 2 ref)))
+                     (if (eq (org-element-type def) 'org-data)
+                         ;; Full-fledged definition: footnote ID is
+                         ;; inserted inside the first parsed
+                         ;; paragraph (FIRST), if any, to be sure
+                         ;; filling will take it into consideration.
+                         (let ((first (car (org-element-contents def))))
+                           (if (not (eq (org-element-type first) 'paragraph))
+                               (concat id "\n" (org-export-data def info))
+                             (push id (nthcdr 2 first))
+                             (org-export-data def info)))
+                       ;; Fill paragraph once footnote ID is inserted
+                       ;; in order to have a correct length for first
+                       ;; line.
+                       (org-ascii--fill-string
+                        (concat id (org-export-data def info))
+                        text-width info))))))
+              definitions "\n\n"))))))
+      global-margin))))
 
 (defun org-ascii-template (contents info)
   "Return complete document string after ASCII conversion.
 CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
 
 (defun org-ascii-template (contents info)
   "Return complete document string after ASCII conversion.
 CONTENTS is the transcoded contents string.  INFO is a plist
 holding export options."
-  (concat
-   ;; 1. Build title block.
-   (org-ascii--indent-string
-    (concat (org-ascii-template--document-title info)
-           ;; 2. Table of contents.
-           (let ((depth (plist-get info :with-toc)))
-             (when depth
-               (concat
-                (org-ascii--build-toc info (and (wholenump depth) depth))
-                "\n\n\n"))))
-    org-ascii-global-margin)
-   ;; 3. Document's body.
-   contents
-   ;; 4. Creator.  Ignore `comment' value as there are no comments in
-   ;;    ASCII.  Justify it to the bottom right.
-   (org-ascii--indent-string
-    (let ((creator-info (plist-get info :with-creator))
-         (text-width (- org-ascii-text-width org-ascii-global-margin)))
-      (unless (or (not creator-info) (eq creator-info 'comment))
-       (concat
-        "\n\n\n"
-        (org-ascii--fill-string
-         (plist-get info :creator) text-width info 'right))))
-    org-ascii-global-margin)))
+  (let ((global-margin (plist-get info :ascii-global-margin)))
+    (concat
+     ;; Build title block.
+     (org-ascii--indent-string
+      (concat (org-ascii-template--document-title info)
+             ;; 2. Table of contents.
+             (let ((depth (plist-get info :with-toc)))
+               (when depth
+                 (concat
+                  (org-ascii--build-toc info (and (wholenump depth) depth))
+                  "\n\n\n"))))
+      global-margin)
+     ;; Document's body.
+     contents
+     ;; Creator.  Justify it to the bottom right.
+     (and (plist-get info :with-creator)
+         (org-ascii--indent-string
+          (let ((text-width
+                 (- (plist-get info :ascii-text-width) global-margin)))
+            (concat
+             "\n\n\n"
+             (org-ascii--fill-string
+              (plist-get info :creator) text-width info 'right)))
+          global-margin)))))
 
 (defun org-ascii--translate (s info)
   "Translate string S according to specified language and charset.
 
 (defun org-ascii--translate (s info)
   "Translate string S according to specified language and charset.
@@ -1020,8 +1141,9 @@ contextual information."
   "Transcode a CENTER-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
   "Transcode a CENTER-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
-  (org-ascii--justify-string
-   contents (org-ascii--current-text-width center-block info) 'center))
+  ;; Center has already been taken care of at a lower level, so
+  ;; there's nothing left to do.
+  contents)
 
 
 ;;;; Clock
 
 
 ;;;; Clock
@@ -1030,16 +1152,16 @@ holding contextual information."
   "Transcode a CLOCK object from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
   "Transcode a CLOCK object from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
-  (concat org-clock-string " "
-         (org-translate-time
-          (org-element-property :raw-value
-                                (org-element-property :value clock)))
-         (let ((time (org-element-property :duration clock)))
-           (and time
-                (concat " => "
-                        (apply 'format
-                               "%2s:%02s"
-                               (org-split-string time ":")))))))
+  (org-ascii--justify-element
+   (concat org-clock-string " "
+          (org-timestamp-translate (org-element-property :value clock))
+          (let ((time (org-element-property :duration clock)))
+            (and time
+                 (concat " => "
+                         (apply 'format
+                                "%2s:%02s"
+                                (org-split-string time ":"))))))
+   clock info))
 
 
 ;;;; Code
 
 
 ;;;; Code
@@ -1048,7 +1170,8 @@ information."
   "Return a CODE object from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
   "Return a CODE object from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
-  (format org-ascii-verbatim-format (org-element-property :value code)))
+  (format (plist-get info :ascii-verbatim-format)
+         (org-element-property :value code)))
 
 
 ;;;; Drawer
 
 
 ;;;; Drawer
@@ -1059,7 +1182,8 @@ CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
   (let ((name (org-element-property :drawer-name drawer))
        (width (org-ascii--current-text-width drawer info)))
 holding contextual information."
   (let ((name (org-element-property :drawer-name drawer))
        (width (org-ascii--current-text-width drawer info)))
-    (funcall org-ascii-format-drawer-function name contents width)))
+    (funcall (plist-get info :ascii-format-drawer-function)
+            name contents width)))
 
 
 ;;;; Dynamic Block
 
 
 ;;;; Dynamic Block
@@ -1087,8 +1211,10 @@ contextual information."
 (defun org-ascii-example-block (example-block contents info)
   "Transcode a EXAMPLE-BLOCK element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
 (defun org-ascii-example-block (example-block contents info)
   "Transcode a EXAMPLE-BLOCK element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
-  (org-ascii--box-string
-   (org-export-format-code-default example-block info) info))
+  (org-ascii--justify-element
+   (org-ascii--box-string
+    (org-export-format-code-default example-block info) info)
+   example-block info))
 
 
 ;;;; Export Snippet
 
 
 ;;;; Export Snippet
@@ -1106,7 +1232,8 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
   "Transcode a EXPORT-BLOCK element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
   (when (string= (org-element-property :type export-block) "ASCII")
   "Transcode a EXPORT-BLOCK element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
   (when (string= (org-element-property :type export-block) "ASCII")
-    (org-remove-indentation (org-element-property :value export-block))))
+    (org-ascii--justify-element
+     (org-element-property :value export-block) export-block info)))
 
 
 ;;;; Fixed Width
 
 
 ;;;; Fixed Width
@@ -1114,9 +1241,11 @@ CONTENTS is nil.  INFO is a plist holding contextual information."
 (defun org-ascii-fixed-width (fixed-width contents info)
   "Transcode a FIXED-WIDTH element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
 (defun org-ascii-fixed-width (fixed-width contents info)
   "Transcode a FIXED-WIDTH element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
-  (org-ascii--box-string
-   (org-remove-indentation
-    (org-element-property :value fixed-width)) info))
+  (org-ascii--justify-element
+   (org-ascii--box-string
+    (org-remove-indentation
+     (org-element-property :value fixed-width)) info)
+   fixed-width info))
 
 
 ;;;; Footnote Definition
 
 
 ;;;; Footnote Definition
@@ -1149,8 +1278,9 @@ holding contextual information."
           ;; original buffer's spacing.
           (pre-blanks
            (make-string
           ;; original buffer's spacing.
           (pre-blanks
            (make-string
-            (if org-ascii-headline-spacing (car org-ascii-headline-spacing)
-              (org-element-property :pre-blank headline)) ?\n))
+            (or (car (plist-get info :ascii-headline-spacing))
+                (org-element-property :pre-blank headline))
+            ?\n))
           ;; Even if HEADLINE has no section, there might be some
           ;; links in its title that we shouldn't forget to describe.
           (links
           ;; Even if HEADLINE has no section, there might be some
           ;; links in its title that we shouldn't forget to describe.
           (links
@@ -1164,7 +1294,7 @@ holding contextual information."
          (concat
           ;; Bullet.
           (let ((bullets (cdr (assq (plist-get info :ascii-charset)
          (concat
           ;; Bullet.
           (let ((bullets (cdr (assq (plist-get info :ascii-charset)
-                                    org-ascii-bullets))))
+                                    (plist-get info :ascii-bullets)))))
             (char-to-string
              (nth (mod (1- low-level-rank) (length bullets)) bullets)))
           " "
             (char-to-string
              (nth (mod (1- low-level-rank) (length bullets)) bullets)))
           " "
@@ -1192,7 +1322,7 @@ information."
   (let ((text-width (org-ascii--current-text-width horizontal-rule info))
        (spec-width
         (org-export-read-attribute :attr_ascii horizontal-rule :width)))
   (let ((text-width (org-ascii--current-text-width horizontal-rule info))
        (spec-width
         (org-export-read-attribute :attr_ascii horizontal-rule :width)))
-    (org-ascii--justify-string
+    (org-ascii--justify-lines
      (make-string (if (and spec-width (string-match "^[0-9]+$" spec-width))
                      (string-to-number spec-width)
                    text-width)
      (make-string (if (and spec-width (string-match "^[0-9]+$" spec-width))
                      (string-to-number spec-width)
                    text-width)
@@ -1206,7 +1336,7 @@ information."
   "Transcode an INLINE-SRC-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
   "Transcode an INLINE-SRC-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
-  (format org-ascii-verbatim-format
+  (format (plist-get info :ascii-verbatim-format)
          (org-element-property :value inline-src-block)))
 
 
          (org-element-property :value inline-src-block)))
 
 
@@ -1218,7 +1348,7 @@ contextual information."
 See `org-ascii-format-inlinetask-function' for a description
 of the parameters."
   (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
 See `org-ascii-format-inlinetask-function' for a description
 of the parameters."
   (let* ((utf8p (eq (plist-get info :ascii-charset) 'utf-8))
-        (width (or width org-ascii-inlinetask-width)))
+        (width (or width (plist-get info :ascii-inlinetask-width))))
     (org-ascii--indent-string
      (concat
       ;; Top line, with an additional blank line if not in UTF-8.
     (org-ascii--indent-string
      (concat
       ;; Top line, with an additional blank line if not in UTF-8.
@@ -1236,9 +1366,9 @@ of the parameters."
       ;; Bottom line.
       (make-string width (if utf8p ?━ ?_)))
      ;; Flush the inlinetask to the right.
       ;; Bottom line.
       (make-string width (if utf8p ?━ ?_)))
      ;; Flush the inlinetask to the right.
-     (- org-ascii-text-width org-ascii-global-margin
+     (- (plist-get info :ascii-text-width) (plist-get info :ascii-global-margin)
        (if (not (org-export-get-parent-headline inlinetask)) 0
        (if (not (org-export-get-parent-headline inlinetask)) 0
-         org-ascii-inner-margin)
+         (plist-get info :ascii-inner-margin))
        (org-ascii--current-text-width inlinetask info)))))
 
 (defun org-ascii-inlinetask (inlinetask contents info)
        (org-ascii--current-text-width inlinetask info)))))
 
 (defun org-ascii-inlinetask (inlinetask contents info)
@@ -1246,7 +1376,7 @@ of the parameters."
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
   (let ((width (org-ascii--current-text-width inlinetask info)))
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
   (let ((width (org-ascii--current-text-width inlinetask info)))
-    (funcall org-ascii-format-inlinetask-function
+    (funcall (plist-get info :ascii-format-inlinetask-function)
             ;; todo.
             (and (plist-get info :with-todo-keywords)
                  (let ((todo (org-element-property
             ;; todo.
             (and (plist-get info :with-todo-keywords)
                  (let ((todo (org-element-property
@@ -1334,20 +1464,21 @@ information."
   (let ((key (org-element-property :key keyword))
        (value (org-element-property :value keyword)))
     (cond
   (let ((key (org-element-property :key keyword))
        (value (org-element-property :value keyword)))
     (cond
-     ((string= key "ASCII") value)
+     ((string= key "ASCII") (org-ascii--justify-element value keyword info))
      ((string= key "TOC")
      ((string= key "TOC")
-      (let ((value (downcase value)))
-       (cond
-        ((string-match "\\<headlines\\>" value)
-         (let ((depth (or (and (string-match "[0-9]+" value)
-                               (string-to-number (match-string 0 value)))
-                          (plist-get info :with-toc))))
-           (org-ascii--build-toc
-            info (and (wholenump depth) depth) keyword)))
-        ((string= "tables" value)
-         (org-ascii--list-tables keyword info))
-        ((string= "listings" value)
-         (org-ascii--list-listings keyword info))))))))
+      (org-ascii--justify-element
+       (let ((case-fold-search t))
+        (cond
+         ((org-string-match-p "\\<headlines\\>" value)
+          (let ((depth (and (string-match "\\<[0-9]+\\>" value)
+                            (string-to-number (match-string 0 value))))
+                (localp (org-string-match-p "\\<local\\>" value)))
+            (org-ascii--build-toc info depth keyword localp)))
+         ((org-string-match-p "\\<tables\\>" value)
+          (org-ascii--list-tables keyword info))
+         ((org-string-match-p "\\<listings\\>" value)
+          (org-ascii--list-listings keyword info))))
+       keyword info)))))
 
 
 ;;;; Latex Environment
 
 
 ;;;; Latex Environment
@@ -1357,7 +1488,9 @@ information."
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
   (when (plist-get info :with-latex)
 CONTENTS is nil.  INFO is a plist holding contextual
 information."
   (when (plist-get info :with-latex)
-    (org-remove-indentation (org-element-property :value latex-environment))))
+    (org-ascii--justify-element
+     (org-remove-indentation (org-element-property :value latex-environment))
+     latex-environment info)))
 
 
 ;;;; Latex Fragment
 
 
 ;;;; Latex Fragment
@@ -1385,9 +1518,9 @@ CONTENTS is nil.  INFO is a plist holding contextual
 
 DESC is the description part of the link, or the empty string.
 INFO is a plist holding contextual information."
 
 DESC is the description part of the link, or the empty string.
 INFO is a plist holding contextual information."
-  (let ((raw-link (org-element-property :raw-link link))
-       (type (org-element-property :type link)))
+  (let ((type (org-element-property :type link)))
     (cond
     (cond
+     ((org-export-custom-protocol-maybe link desc 'ascii))
      ((string= type "coderef")
       (let ((ref (org-element-property :path link)))
        (format (org-export-get-coderef-format ref desc)
      ((string= type "coderef")
       (let ((ref (org-element-property :path link)))
        (format (org-export-get-coderef-format ref desc)
@@ -1404,14 +1537,33 @@ INFO is a plist holding contextual information."
            (let ((number
                   (org-export-get-ordinal
                    destination info nil 'org-ascii--has-caption-p)))
            (let ((number
                   (org-export-get-ordinal
                    destination info nil 'org-ascii--has-caption-p)))
-             (when number
-               (if (atom number) (number-to-string number)
-                 (mapconcat 'number-to-string number "."))))))))
+             (if number
+                 (if (atom number) (number-to-string number)
+                   (mapconcat #'number-to-string number "."))
+               ;; Unnumbered headline.
+               (when (eq 'headline (org-element-type destination))
+                 (format "[%s]"
+                         (org-export-data
+                          (org-element-property :title destination)
+                          info)))))))))
      (t
      (t
-      (if (not (org-string-nw-p desc)) (format "[%s]" raw-link)
-       (concat
-        (format "[%s]" desc)
-        (unless org-ascii-links-to-notes (format " (%s)" raw-link))))))))
+      (let ((raw-link (org-element-property :raw-link link)))
+       (if (not (org-string-nw-p desc)) (format "[%s]" raw-link)
+         (concat (format "[%s]" desc)
+                 (and (not (plist-get info :ascii-links-to-notes))
+                      (format " (%s)" raw-link)))))))))
+
+
+;;;; Node Properties
+
+(defun org-ascii-node-property (node-property contents info)
+  "Transcode a NODE-PROPERTY element from Org to ASCII.
+CONTENTS is nil.  INFO is a plist holding contextual
+information."
+  (format "%s:%s"
+          (org-element-property :key node-property)
+          (let ((value (org-element-property :value node-property)))
+            (if value (concat " " value) ""))))
 
 
 ;;;; Paragraph
 
 
 ;;;; Paragraph
@@ -1420,16 +1572,17 @@ INFO is a plist holding contextual information."
   "Transcode a PARAGRAPH element from Org to ASCII.
 CONTENTS is the contents of the paragraph, as a string.  INFO is
 the plist used as a communication channel."
   "Transcode a PARAGRAPH element from Org to ASCII.
 CONTENTS is the contents of the paragraph, as a string.  INFO is
 the plist used as a communication channel."
-  (org-ascii--fill-string
-   (if (not (wholenump org-ascii-indented-line-width)) contents
-     (concat
-      ;; Do not indent first paragraph in a section.
-      (unless (and (not (org-export-get-previous-element paragraph info))
-                  (eq (org-element-type (org-export-get-parent paragraph))
-                      'section))
-       (make-string org-ascii-indented-line-width ?\s))
-      (replace-regexp-in-string "\\`[ \t]+" "" contents)))
-   (org-ascii--current-text-width paragraph info) info))
+  (org-ascii--justify-element
+   (let ((indented-line-width (plist-get info :ascii-indented-line-width)))
+     (if (not (wholenump indented-line-width)) contents
+       (concat
+       ;; Do not indent first paragraph in a section.
+       (unless (and (not (org-export-get-previous-element paragraph info))
+                    (eq (org-element-type (org-export-get-parent paragraph))
+                        'section))
+         (make-string indented-line-width ?\s))
+       (replace-regexp-in-string "\\`[ \t]+" "" contents))))
+   paragraph info))
 
 
 ;;;; Plain List
 
 
 ;;;; Plain List
@@ -1438,7 +1591,11 @@ the plist used as a communication channel."
   "Transcode a PLAIN-LIST element from Org to ASCII.
 CONTENTS is the contents of the list.  INFO is a plist holding
 contextual information."
   "Transcode a PLAIN-LIST element from Org to ASCII.
 CONTENTS is the contents of the list.  INFO is a plist holding
 contextual information."
-  contents)
+  (let ((margin (plist-get info :ascii-list-margin)))
+    (if (or (< margin 1)
+           (eq (org-element-type (org-export-get-parent plain-list)) 'item))
+       contents
+      (org-ascii--indent-string contents margin))))
 
 
 ;;;; Plain Text
 
 
 ;;;; Plain Text
@@ -1466,25 +1623,34 @@ INFO is a plist used as a communication channel."
   "Transcode a PLANNING element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist used as a communication
 channel."
   "Transcode a PLANNING element from Org to ASCII.
 CONTENTS is nil.  INFO is a plist used as a communication
 channel."
-  (mapconcat
-   'identity
-   (delq nil
-        (list (let ((closed (org-element-property :closed planning)))
-                (when closed
-                  (concat org-closed-string " "
-                          (org-translate-time
-                           (org-element-property :raw-value closed)))))
-              (let ((deadline (org-element-property :deadline planning)))
-                (when deadline
-                  (concat org-deadline-string " "
-                          (org-translate-time
-                           (org-element-property :raw-value deadline)))))
-              (let ((scheduled (org-element-property :scheduled planning)))
-                (when scheduled
-                  (concat org-scheduled-string " "
-                          (org-translate-time
-                           (org-element-property :raw-value scheduled)))))))
-   " "))
+  (org-ascii--justify-element
+   (mapconcat
+    #'identity
+    (delq nil
+         (list (let ((closed (org-element-property :closed planning)))
+                 (when closed
+                   (concat org-closed-string " "
+                           (org-timestamp-translate closed))))
+               (let ((deadline (org-element-property :deadline planning)))
+                 (when deadline
+                   (concat org-deadline-string " "
+                           (org-timestamp-translate deadline))))
+               (let ((scheduled (org-element-property :scheduled planning)))
+                 (when scheduled
+                   (concat org-scheduled-string " "
+                           (org-timestamp-translate scheduled))))))
+    " ")
+   planning info))
+
+
+;;;; Property Drawer
+
+(defun org-ascii-property-drawer (property-drawer contents info)
+  "Transcode a PROPERTY-DRAWER element from Org to ASCII.
+CONTENTS holds the contents of the drawer.  INFO is a plist
+holding contextual information."
+  (and (org-string-nw-p contents)
+       (org-ascii--justify-element contents property-drawer info)))
 
 
 ;;;; Quote Block
 
 
 ;;;; Quote Block
@@ -1493,26 +1659,7 @@ channel."
   "Transcode a QUOTE-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
   "Transcode a QUOTE-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
-  (org-ascii--indent-string contents org-ascii-quote-margin))
-
-
-;;;; Quote Section
-
-(defun org-ascii-quote-section (quote-section contents info)
-  "Transcode a QUOTE-SECTION element from Org to ASCII.
-CONTENTS is nil.  INFO is a plist holding contextual information."
-  (let ((width (org-ascii--current-text-width quote-section info))
-       (value
-        (org-export-data
-         (org-remove-indentation (org-element-property :value quote-section))
-         info)))
-    (org-ascii--indent-string
-     value
-     (+ org-ascii-quote-margin
-       ;; Don't apply inner margin if parent headline is low level.
-       (let ((headline (org-export-get-parent-headline quote-section)))
-         (if (org-export-low-level-p headline info) 0
-           org-ascii-inner-margin))))))
+  (org-ascii--indent-string contents (plist-get info :ascii-quote-margin)))
 
 
 ;;;; Radio Target
 
 
 ;;;; Radio Target
@@ -1533,7 +1680,7 @@ contextual information."
   (org-ascii--indent-string
    (concat
     contents
   (org-ascii--indent-string
    (concat
     contents
-    (when org-ascii-links-to-notes
+    (when (plist-get info :ascii-links-to-notes)
       ;; Add list of links at the end of SECTION.
       (let ((links (org-ascii--describe-links
                    (org-ascii--unique-links section info)
       ;; Add list of links at the end of SECTION.
       (let ((links (org-ascii--describe-links
                    (org-ascii--unique-links section info)
@@ -1543,7 +1690,7 @@ contextual information."
    ;; Do not apply inner margin if parent headline is low level.
    (let ((headline (org-export-get-parent-headline section)))
      (if (or (not headline) (org-export-low-level-p headline info)) 0
    ;; Do not apply inner margin if parent headline is low level.
    (let ((headline (org-export-get-parent-headline section)))
      (if (or (not headline) (org-export-low-level-p headline info)) 0
-       org-ascii-inner-margin))))
+       (plist-get info :ascii-inner-margin)))))
 
 
 ;;;; Special Block
 
 
 ;;;; Special Block
@@ -1552,6 +1699,9 @@ contextual information."
   "Transcode a SPECIAL-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
   "Transcode a SPECIAL-BLOCK element from Org to ASCII.
 CONTENTS holds the contents of the block.  INFO is a plist
 holding contextual information."
+  ;; "JUSTIFYLEFT" and "JUSTFYRIGHT" have already been taken care of
+  ;; at a lower level.  There is no other special block type to
+  ;; handle.
   contents)
 
 
   contents)
 
 
@@ -1562,13 +1712,15 @@ holding contextual information."
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
   (let ((caption (org-ascii--build-caption src-block info))
 CONTENTS holds the contents of the item.  INFO is a plist holding
 contextual information."
   (let ((caption (org-ascii--build-caption src-block info))
+       (caption-above-p (plist-get info :ascii-caption-above))
        (code (org-export-format-code-default src-block info)))
     (if (equal code "") ""
        (code (org-export-format-code-default src-block info)))
     (if (equal code "") ""
-      (concat
-       (when (and caption org-ascii-caption-above) (concat caption "\n"))
-       (org-ascii--box-string code info)
-       (when (and caption (not org-ascii-caption-above))
-        (concat "\n" caption))))))
+      (org-ascii--justify-element
+       (concat
+       (and caption caption-above-p (concat caption "\n"))
+       (org-ascii--box-string code info)
+       (and caption (not caption-above-p) (concat "\n" caption)))
+       src-block info))))
 
 
 ;;;; Statistics Cookie
 
 
 ;;;; Statistics Cookie
@@ -1616,26 +1768,29 @@ holding contextual information."
   "Transcode a TABLE element from Org to ASCII.
 CONTENTS is the contents of the table.  INFO is a plist holding
 contextual information."
   "Transcode a TABLE element from Org to ASCII.
 CONTENTS is the contents of the table.  INFO is a plist holding
 contextual information."
-  (let ((caption (org-ascii--build-caption table info)))
-    (concat
-     ;; Possibly add a caption string above.
-     (when (and caption org-ascii-caption-above) (concat caption "\n"))
-     ;; Insert table.  Note: "table.el" tables are left unmodified.
-     (cond ((eq (org-element-property :type table) 'org) contents)
-          ((and org-ascii-table-use-ascii-art
-                (eq (plist-get info :ascii-charset) 'utf-8)
-                (require 'ascii-art-to-unicode nil t))
-           (with-temp-buffer
-             (insert (org-remove-indentation
-                      (org-element-property :value table)))
-             (goto-char (point-min))
-             (aa2u)
-             (goto-char (point-max))
-             (skip-chars-backward " \r\t\n")
-             (buffer-substring (point-min) (point))))
-          (t (org-remove-indentation (org-element-property :value table))))
-     ;; Possible add a caption string below.
-     (and (not org-ascii-caption-above) caption))))
+  (let ((caption (org-ascii--build-caption table info))
+       (caption-above-p (plist-get info :ascii-caption-above)))
+    (org-ascii--justify-element
+     (concat
+      ;; Possibly add a caption string above.
+      (and caption caption-above-p (concat caption "\n"))
+      ;; Insert table.  Note: "table.el" tables are left unmodified.
+      (cond ((eq (org-element-property :type table) 'org) contents)
+           ((and (plist-get info :ascii-table-use-ascii-art)
+                 (eq (plist-get info :ascii-charset) 'utf-8)
+                 (require 'ascii-art-to-unicode nil t))
+            (with-temp-buffer
+              (insert (org-remove-indentation
+                       (org-element-property :value table)))
+              (goto-char (point-min))
+              (aa2u)
+              (goto-char (point-max))
+              (skip-chars-backward " \r\t\n")
+              (buffer-substring (point-min) (point))))
+           (t (org-remove-indentation (org-element-property :value table))))
+      ;; Possible add a caption string below.
+      (and (not caption-above-p) caption))
+     table info)))
 
 
 ;;;; Table Cell
 
 
 ;;;; Table Cell
@@ -1661,12 +1816,13 @@ are ignored."
                               (plist-put info :ascii-table-cell-width-cache
                                          (make-hash-table :test 'equal)))
                         :ascii-table-cell-width-cache)))
                               (plist-put info :ascii-table-cell-width-cache
                                          (make-hash-table :test 'equal)))
                         :ascii-table-cell-width-cache)))
-        (key (cons table col)))
+        (key (cons table col))
+        (widenp (plist-get info :ascii-table-widen-columns)))
     (or (gethash key cache)
        (puthash
         key
         (let ((cookie-width (org-export-table-cell-width table-cell info)))
     (or (gethash key cache)
        (puthash
         key
         (let ((cookie-width (org-export-table-cell-width table-cell info)))
-          (or (and (not org-ascii-table-widen-columns) cookie-width)
+          (or (and (not widenp) cookie-width)
               (let ((contents-width
                      (let ((max-width 0))
                        (org-element-map table 'table-row
               (let ((contents-width
                      (let ((max-width 0))
                        (org-element-map table 'table-row
@@ -1681,8 +1837,7 @@ are ignored."
                          info)
                        max-width)))
                 (cond ((not cookie-width) contents-width)
                          info)
                        max-width)))
                 (cond ((not cookie-width) contents-width)
-                      (org-ascii-table-widen-columns
-                       (max cookie-width contents-width))
+                      (widenp (max cookie-width contents-width))
                       (t cookie-width)))))
         cache))))
 
                       (t cookie-width)))))
         cache))))
 
@@ -1696,14 +1851,14 @@ a communication channel."
   ;; each cell in the column.
   (let ((width (org-ascii--table-cell-width table-cell info)))
     ;; When contents are too large, truncate them.
   ;; each cell in the column.
   (let ((width (org-ascii--table-cell-width table-cell info)))
     ;; When contents are too large, truncate them.
-    (unless (or org-ascii-table-widen-columns
+    (unless (or (plist-get info :ascii-table-widen-columns)
                (<= (string-width (or contents "")) width))
       (setq contents (concat (substring contents 0 (- width 2)) "=>")))
     ;; Align contents correctly within the cell.
     (let* ((indent-tabs-mode nil)
           (data
            (when contents
                (<= (string-width (or contents "")) width))
       (setq contents (concat (substring contents 0 (- width 2)) "=>")))
     ;; Align contents correctly within the cell.
     (let* ((indent-tabs-mode nil)
           (data
            (when contents
-             (org-ascii--justify-string
+             (org-ascii--justify-lines
               contents width
               (org-export-table-cell-alignment table-cell info)))))
       (setq contents
               contents width
               (org-export-table-cell-alignment table-cell info)))))
       (setq contents
@@ -1790,7 +1945,7 @@ holding contextual information."
 (defun org-ascii-verbatim (verbatim contents info)
   "Return a VERBATIM object from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
 (defun org-ascii-verbatim (verbatim contents info)
   "Return a VERBATIM object from Org to ASCII.
 CONTENTS is nil.  INFO is a plist holding contextual information."
-  (format org-ascii-verbatim-format
+  (format (plist-get info :ascii-verbatim-format)
          (org-element-property :value verbatim)))
 
 
          (org-element-property :value verbatim)))
 
 
@@ -1802,8 +1957,8 @@ CONTENTS is verse block contents.  INFO is a plist holding
 contextual information."
   (let ((verse-width (org-ascii--current-text-width verse-block info)))
     (org-ascii--indent-string
 contextual information."
   (let ((verse-width (org-ascii--current-text-width verse-block info)))
     (org-ascii--indent-string
-     (org-ascii--justify-string contents verse-width 'left)
-     org-ascii-quote-margin)))
+     (org-ascii--justify-element contents verse-block info)
+     (plist-get info :ascii-quote-margin))))
 
 
 \f
 
 
 \f
@@ -1818,9 +1973,10 @@ plist containing the communication channel.
 
 This function only applies to `ascii' back-end.  See
 `org-ascii-headline-spacing' for information."
 
 This function only applies to `ascii' back-end.  See
 `org-ascii-headline-spacing' for information."
-  (if (not org-ascii-headline-spacing) headline
-    (let ((blanks (make-string (1+ (cdr org-ascii-headline-spacing)) ?\n)))
-      (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline))))
+  (let ((headline-spacing (plist-get info :ascii-headline-spacing)))
+    (if (not headline-spacing) headline
+      (let ((blanks (make-string (1+ (cdr headline-spacing)) ?\n)))
+       (replace-regexp-in-string "\n\\(?:\n[ \t]*\\)*\\'" blanks headline)))))
 
 (defun org-ascii-filter-paragraph-spacing (tree back-end info)
   "Filter controlling number of blank lines between paragraphs.
 
 (defun org-ascii-filter-paragraph-spacing (tree back-end info)
   "Filter controlling number of blank lines between paragraphs.
@@ -1830,13 +1986,13 @@ back-end used for export.  INFO is a plist used as
 a communication channel.
 
 See `org-ascii-paragraph-spacing' for information."
 a communication channel.
 
 See `org-ascii-paragraph-spacing' for information."
-  (when (wholenump org-ascii-paragraph-spacing)
-    (org-element-map tree 'paragraph
-      (lambda (p)
-       (when (eq (org-element-type (org-export-get-next-element p info))
-                 'paragraph)
-         (org-element-put-property
-          p :post-blank org-ascii-paragraph-spacing)))))
+  (let ((paragraph-spacing (plist-get info :ascii-paragraph-spacing)))
+    (when (wholenump paragraph-spacing)
+      (org-element-map tree 'paragraph
+       (lambda (p)
+         (when (eq (org-element-type (org-export-get-next-element p info))
+                   'paragraph)
+           (org-element-put-property p :post-blank paragraph-spacing))))))
   tree)
 
 (defun org-ascii-filter-comment-spacing (tree backend info)
   tree)
 
 (defun org-ascii-filter-comment-spacing (tree backend info)