emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [RFC] Rewrite radio tables
@ 2014-08-24 19:25 Nicolas Goaziou
  2014-08-24 20:18 ` Thorsten Jolitz
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Nicolas Goaziou @ 2014-08-24 19:25 UTC (permalink / raw)
  To: Org Mode List

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

Hello,

The following patch implements radio tables and `orgtbl-to-...'
functions using Org export engine. The implementation is probably not
totally backward compatible, though.

In particular, I suppressed :remove-nil-lines parameter, as I couldn't
make any sense out of it. If someone has a clear definition of its job,
and if that job is useful, I could implement it back.

Also, process does not dynamically bind `org-table-last-alignment' and
`org-table-last-column-widths' anymore. It should be possible to have
them back with some computations before starting the conversion, but,
unless they are vital, I'd rather not do it. `orgtbl-to-generic' is
tricky enough.

OTOH, I added :raw parameter, which allows to treat cells as strings and
not as Org syntax.

  #+ORGTBL: SEND ... :raw nil
  | *a* |

=>

  ...
  \textbf{a}
  ...

  #+ORGTBL: SEND ... :raw t
  | *a* |

=>

  ...
  *a*
  ...

I also added some specific parameters to translation functions,
e.g. :booktabs or :environment for `orgtbl-to-latex'. See docstring for
more information.

The patch comes with a test suite. However, it is no real use case.
Therefore, I'd appreciate some human testing before going further.

Feedback welcome.


Regards,

-- 
Nicolas Goaziou                                                0x80A93738

[-- Attachment #2: 0001-org-table-Use-ox.el-internally-for-radio-tables.patch --]
[-- Type: text/x-diff, Size: 60314 bytes --]

From ac278792f9b6472f02bdd14e5472428bde083ccf Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Sun, 24 Aug 2014 01:31:56 +0200
Subject: [PATCH] org-table: Use "ox.el" internally for radio tables

* lisp/org-table.el (org-table-clean-before-export, orgtbl-get-fmt,
  orgtbl-apply-fmt, orgtbl-eval-str, orgtbl-format-line,
  orgtbl-format-section): Remove functions.
(org-table-clean-did-remove-column, *orgtbl-table*, *orgtbl-rtn*,
  *orgtbl-hline*, *orgtbl-sep*, *orgtbl-default-fmt*, *orgtbl-fmt*,
  *orgtbl-efmt*, *orgtbl-lfmt*, *orgtbl-llfmt*, *orgtbl-lstart*,
  *orgtbl-llstart*, *orgtbl-lend*, *orgtbl-llend*): Remove variables.

(org-table-export, orgtbl-send-table): Apply function removal.  Do not
set `org-table-last-alignment' and `org-table-last-column-widths'
anymore.
(org-table-to-lisp, orgtbl-send-replace-tbl): Small refactoring.

(org-table--to-generic-table, org-table--to-generic-row,
org-table--to-generic-cell): New functions.
(orgtbl-to-generic): Rewrite function.  Handle :skip and :skipcols
parameters.
(orgtbl-to-latex, orgtbl-to-html, orgtbl-to-texinfo, orgtbl-to-orgtbl,
orgtbl-to-unicode): Use new `orgtbl-to-generic' features.

* testing/lisp/test-org-table.el (test-org-table/to-generic,
  test-org-table/to-latex, test-org-table/to-texinfo,
  test-org-table/to-html, test-org-table/to-unicode,
  test-org-table/send-region): New tests.
---
 lisp/org-table.el              | 1037 +++++++++++++++++++++-------------------
 testing/lisp/test-org-table.el |  351 ++++++++++++++
 2 files changed, 909 insertions(+), 479 deletions(-)

diff --git a/lisp/org-table.el b/lisp/org-table.el
index 06a1008..61be7d2 100644
--- a/lisp/org-table.el
+++ b/lisp/org-table.el
@@ -40,7 +40,9 @@
 
 (declare-function org-export-string-as "ox"
 		  (string backend &optional body-only ext-plist))
-(declare-function aa2u "ext:ascii-art-to-unicode" ())
+(declare-function org-export-create-backend "ox")
+(declare-function org-export-get-backend "ox" (name))
+
 (declare-function calc-eval "calc" (str &optional separator &rest args))
 
 (defvar orgtbl-mode) ; defined below
@@ -442,40 +444,6 @@ available parameters."
 			 (org-split-string (match-string 1 line)
 					   "[ \t]*|[ \t]*")))))))
 
-(defvar org-table-clean-did-remove-column nil) ; dynamically scoped
-(defun org-table-clean-before-export (lines &optional maybe-quoted)
-  "Check if the table has a marking column.
-If yes remove the column and the special lines."
-  (let ((special (if maybe-quoted
-		     "^[ \t]*| *\\\\?[\#!$*_^/ ] *|"
-		   "^[ \t]*| *[\#!$*_^/ ] *|"))
-	(ignore  (if maybe-quoted
-		     "^[ \t]*| *\\\\?[!$_^/] *|"
-		   "^[ \t]*| *[!$_^/] *|")))
-    (setq org-table-clean-did-remove-column
-	  (not (memq nil
-		     (mapcar
-		      (lambda (line)
-			(or (string-match org-table-hline-regexp line)
-			    (string-match special                line)))
-		      lines))))
-    (delq nil
-	  (mapcar
-	   (lambda (line)
-	     (cond
-	      ((or (org-table-colgroup-line-p line)  ;; colgroup info
-		   (org-table-cookie-line-p line)    ;; formatting cookies
-		   (and org-table-clean-did-remove-column
-			(string-match ignore line))) ;; non-exportable data
-	       nil)
-	      ((and org-table-clean-did-remove-column
-		    (or (string-match "^\\([ \t]*\\)|-+\\+" line)
-			(string-match "^\\([ \t]*\\)|[^|]*|" line)))
-	       ;; remove the first column
-	       (replace-match "\\1|" t nil line))
-	      (t line)))
-	   lines))))
-
 (defconst org-table-translate-regexp
   (concat "\\(" "@[-0-9I$]+" "\\|" "[a-zA-Z]\\{1,2\\}\\([0-9]+\\|&\\)" "\\)")
   "Match a reference that needs translation, for reference display.")
@@ -624,8 +592,6 @@ are found, lines will be split on whitespace into fields."
     (org-table-convert-region beg (+ (point) (- (point-max) pm)) arg)))
 
 
-(defvar org-table-last-alignment)
-(defvar org-table-last-column-widths)
 ;;;###autoload
 (defun org-table-export (&optional file format)
   "Export table to a file, with configurable format.
@@ -643,77 +609,61 @@ extension of the given file name, and finally on the variable
 `org-table-export-default-format'."
   (interactive)
   (unless (org-at-table-p) (user-error "No table at point"))
-  (org-table-align) ;; make sure we have everything we need
-  (let* ((beg (org-table-begin))
-	 (end (org-table-end))
-	 (txt (buffer-substring-no-properties beg end))
-	 (file (or file (org-entry-get beg "TABLE_EXPORT_FILE" t)))
-	 (formats '("orgtbl-to-tsv" "orgtbl-to-csv"
-		    "orgtbl-to-latex" "orgtbl-to-html"
-		    "orgtbl-to-generic" "orgtbl-to-texinfo"
-		    "orgtbl-to-orgtbl"))
-	 (format (or format
-		     (org-entry-get beg "TABLE_EXPORT_FORMAT" t)))
-	 buf deffmt-readable fileext)
+  (org-table-align)	       ; Make sure we have everything we need.
+  (let ((file (or file (org-entry-get (point) "TABLE_EXPORT_FILE" t))))
     (unless file
       (setq file (read-file-name "Export table to: "))
       (unless (or (not (file-exists-p file))
 		  (y-or-n-p (format "Overwrite file %s? " file)))
 	(user-error "File not written")))
-    (if (file-directory-p file)
-	(user-error "This is a directory path, not a file"))
-    (if (and (buffer-file-name)
-	     (equal (file-truename file)
-		    (file-truename (buffer-file-name))))
-	(user-error "Please specify a file name that is different from current"))
-    (setq fileext (concat (file-name-extension file) "$"))
-    (unless format
-      (setq deffmt-readable
-	    (or (car (delq nil (mapcar (lambda(f) (if (string-match fileext f) f)) formats)))
-		org-table-export-default-format))
-      (while (string-match "\t" deffmt-readable)
-	(setq deffmt-readable (replace-match "\\t" t t deffmt-readable)))
-      (while (string-match "\n" deffmt-readable)
-	(setq deffmt-readable (replace-match "\\n" t t deffmt-readable)))
-      (setq format (org-completing-read "Format: " formats nil nil deffmt-readable)))
-    (if (string-match "\\([^ \t\r\n]+\\)\\( +.*\\)?" format)
-	(let* ((transform (intern (match-string 1 format)))
-	       (params (if (match-end 2)
-			   (read (concat "(" (match-string 2 format) ")"))))
-	       (skip (plist-get params :skip))
-	       (skipcols (plist-get params :skipcols))
-	       (lines (nthcdr (or skip 0) (org-split-string txt "[ \t]*\n[ \t]*")))
-	       (lines (org-table-clean-before-export lines))
-	       (i0 (if org-table-clean-did-remove-column 2 1))
-	       (table (mapcar
-		       (lambda (x)
-			 (if (string-match org-table-hline-regexp x)
-			     'hline
-			   (org-remove-by-index
-			    (org-split-string (org-trim x) "\\s-*|\\s-*")
-			    skipcols i0)))
-		       lines))
-	       (fun (if (= i0 2) 'cdr 'identity))
-	       (org-table-last-alignment
-		(org-remove-by-index (funcall fun org-table-last-alignment)
-				     skipcols i0))
-	       (org-table-last-column-widths
-		(org-remove-by-index (funcall fun org-table-last-column-widths)
-				     skipcols i0)))
-
-	  (unless (fboundp transform)
-	    (user-error "No such transformation function %s" transform))
-	  (setq txt (funcall transform table params))
-
-	  (with-current-buffer (find-file-noselect file)
-	    (setq buf (current-buffer))
-	    (erase-buffer)
-	    (fundamental-mode)
-	    (insert txt "\n")
-	    (save-buffer))
-	  (kill-buffer buf)
-	  (message "Export done."))
-      (user-error "TABLE_EXPORT_FORMAT invalid"))))
+    (when (file-directory-p file)
+      (user-error "This is a directory path, not a file"))
+    (when (and (buffer-file-name (buffer-base-buffer))
+	       (file-equal-p
+		(file-truename file)
+		(file-truename (buffer-file-name (buffer-base-buffer)))))
+      (user-error "Please specify a file name that is different from current"))
+    (let ((fileext (concat (file-name-extension file) "$"))
+	  (format (or format (org-entry-get (point) "TABLE_EXPORT_FORMAT" t))))
+      (unless format
+	(let* ((formats '("orgtbl-to-tsv" "orgtbl-to-csv" "orgtbl-to-latex"
+			  "orgtbl-to-html" "orgtbl-to-generic"
+			  "orgtbl-to-texinfo" "orgtbl-to-orgtbl"
+			  "orgtbl-to-unicode"))
+	       (deffmt-readable
+		 (replace-regexp-in-string
+		  "\t" "\\t"
+		  (replace-regexp-in-string
+		   "\n" "\\n"
+		   (or (car (delq nil
+				  (mapcar
+				   (lambda (f)
+				     (and (org-string-match-p fileext f) f))
+				   formats)))
+		       org-table-export-default-format)
+		   t t) t t)))
+	  (setq format
+		(org-completing-read
+		 "Format: " formats nil nil deffmt-readable))))
+      (if (string-match "\\([^ \t\r\n]+\\)\\( +.*\\)?" format)
+	  (let ((transform (intern (match-string 1 format)))
+		(params (and (match-end 2)
+			     (read (concat "(" (match-string 2 format) ")"))))
+		(table (org-table-to-lisp
+			(buffer-substring-no-properties
+			 (org-table-begin) (org-table-end)))))
+	    (unless (fboundp transform)
+	      (user-error "No such transformation function %s" transform))
+	    (let (buf)
+	      (with-current-buffer (find-file-noselect file)
+		(setq buf (current-buffer))
+		(erase-buffer)
+		(fundamental-mode)
+		(insert (funcall transform table params) "\n")
+		(save-buffer))
+	      (kill-buffer buf))
+	    (message "Export done."))
+	(user-error "TABLE_EXPORT_FORMAT invalid")))))
 
 (defvar org-table-aligned-begin-marker (make-marker)
   "Marker at the beginning of the table last aligned.
@@ -4499,15 +4449,12 @@ a radio table."
     (unless (re-search-forward
 	     (concat "BEGIN +RECEIVE +ORGTBL +" name "\\([ \t]\\|$\\)") nil t)
       (user-error "Don't know where to insert translated table"))
-    (goto-char (match-beginning 0))
-    (beginning-of-line 2)
-    (save-excursion
-      (let ((beg (point)))
-	(unless (re-search-forward
-		 (concat "END +RECEIVE +ORGTBL +" name) nil t)
-	  (user-error "Cannot find end of insertion region"))
-	(beginning-of-line 1)
-	(delete-region beg (point))))
+    (let ((beg (line-beginning-position 2)))
+      (unless (re-search-forward
+	       (concat "END +RECEIVE +ORGTBL +" name) nil t)
+	(user-error "Cannot find end of insertion region"))
+      (beginning-of-line)
+      (delete-region beg (point)))
     (insert txt "\n")))
 
 ;;;###autoload
@@ -4516,76 +4463,43 @@ a radio table."
 The structure will be a list.  Each item is either the symbol `hline'
 for a horizontal separator line, or a list of field values as strings.
 The table is taken from the parameter TXT, or from the buffer at point."
-  (unless txt
-    (unless (org-at-table-p)
-      (user-error "No table at point")))
-  (let* ((txt (or txt
-		  (buffer-substring-no-properties (org-table-begin)
-						  (org-table-end))))
-	 (lines (org-split-string txt "[ \t]*\n[ \t]*")))
-
-    (mapcar
-     (lambda (x)
-       (if (string-match org-table-hline-regexp x)
-	   'hline
-	 (org-split-string (org-trim x) "\\s-*|\\s-*")))
-     lines)))
+  (unless (or txt (org-at-table-p)) (user-error "No table at point"))
+  (let ((txt (or txt
+		 (buffer-substring-no-properties (org-table-begin)
+						 (org-table-end)))))
+    (mapcar (lambda (x)
+	      (if (string-match org-table-hline-regexp x) 'hline
+		(org-split-string (org-trim x) "\\s-*|\\s-*")))
+	    (org-split-string txt "[ \t]*\n[ \t]*"))))
 
 (defun orgtbl-send-table (&optional maybe)
-  "Send a transformed version of this table to the receiver position.
-With argument MAYBE, fail quietly if no transformation is defined for
-this table."
+  "Send a transformed version of table at point to the receiver position.
+With argument MAYBE, fail quietly if no transformation is defined
+for this table."
   (interactive)
   (catch 'exit
     (unless (org-at-table-p) (user-error "Not at a table"))
     ;; when non-interactive, we assume align has just happened.
     (when (org-called-interactively-p 'any) (org-table-align))
     (let ((dests (orgtbl-gather-send-defs))
-	  (txt (buffer-substring-no-properties (org-table-begin)
-					       (org-table-end)))
+	  (table (org-table-to-lisp
+		  (buffer-substring-no-properties (org-table-begin)
+						  (org-table-end))))
 	  (ntbl 0))
-      (unless dests (if maybe (throw 'exit nil)
-		      (user-error "Don't know how to transform this table")))
+      (unless dests
+	(if maybe (throw 'exit nil)
+	  (user-error "Don't know how to transform this table")))
       (dolist (dest dests)
-	(let* ((name (plist-get dest :name))
-	       (transform (plist-get dest :transform))
-	       (params (plist-get dest :params))
-	       (skip (plist-get params :skip))
-	       (skipcols (plist-get params :skipcols))
-	       (no-escape (plist-get params :no-escape))
-	       beg
-	       (lines (org-table-clean-before-export
-		       (nthcdr (or skip 0)
-			       (org-split-string txt "[ \t]*\n[ \t]*"))))
-	       (i0 (if org-table-clean-did-remove-column 2 1))
-	       (lines (if no-escape lines
-			(mapcar (lambda(l) (replace-regexp-in-string
-					    "\\([&%#_^]\\)" "\\\\\\1{}" l)) lines)))
-	       (table (mapcar
-		       (lambda (x)
-			 (if (string-match org-table-hline-regexp x)
-			     'hline
-			   (org-remove-by-index
-			    (org-split-string (org-trim x) "\\s-*|\\s-*")
-			    skipcols i0)))
-		       lines))
-	       (fun (if (= i0 2) 'cdr 'identity))
-	       (org-table-last-alignment
-		(org-remove-by-index (funcall fun org-table-last-alignment)
-				     skipcols i0))
-	       (org-table-last-column-widths
-		(org-remove-by-index (funcall fun org-table-last-column-widths)
-				     skipcols i0))
-	       (txt (if (fboundp transform)
-			(funcall transform table params)
-		      (user-error "No such transformation function %s" transform))))
-	  (orgtbl-send-replace-tbl name txt))
-	(setq ntbl (1+ ntbl)))
+	(let ((name (plist-get dest :name))
+	      (transform (plist-get dest :transform))
+	      (params (plist-get dest :params)))
+	  (unless (fboundp transform)
+	    (user-error "No such transformation function %s" transform))
+	  (orgtbl-send-replace-tbl name (funcall transform table params)))
+	(incf ntbl))
       (message "Table converted and installed at %d receiver location%s"
 	       ntbl (if (> ntbl 1) "s" ""))
-      (if (> ntbl 0)
-	  ntbl
-	nil))))
+      (and (> ntbl 0) ntbl))))
 
 (defun org-remove-by-index (list indices &optional i0)
   "Remove the elements in LIST with indices in INDICES.
@@ -4635,330 +4549,489 @@ First element has index 0, or I0 if given."
     (insert txt)
     (goto-char pos)))
 
-;; Dynamically bound input and output for table formatting.
-(defvar *orgtbl-table* nil
-  "Carries the current table through formatting routines.")
-(defvar *orgtbl-rtn* nil
-  "Formatting routines push the output lines here.")
-;; Formatting parameters for the current table section.
-(defvar *orgtbl-hline* nil "Text used for horizontal lines.")
-(defvar *orgtbl-sep* nil "Text used as a column separator.")
-(defvar *orgtbl-default-fmt* nil "Default format for each entry.")
-(defvar *orgtbl-fmt* nil "Format for each entry.")
-(defvar *orgtbl-efmt* nil "Format for numbers.")
-(defvar *orgtbl-lfmt* nil "Format for an entire line, overrides fmt.")
-(defvar *orgtbl-llfmt* nil "Specializes lfmt for the last row.")
-(defvar *orgtbl-lstart* nil "Text starting a row.")
-(defvar *orgtbl-llstart* nil "Specializes lstart for the last row.")
-(defvar *orgtbl-lend* nil "Text ending a row.")
-(defvar *orgtbl-llend* nil "Specializes lend for the last row.")
-
-(defsubst orgtbl-get-fmt (fmt i)
-  "Retrieve the format from FMT corresponding to the Ith column."
-  (if (and (not (functionp fmt)) (consp fmt))
-      (plist-get fmt i)
-    fmt))
-
-(defsubst orgtbl-apply-fmt (fmt &rest args)
-  "Apply format FMT to arguments ARGS.
-When FMT is nil, return the first argument from ARGS."
-  (cond ((functionp fmt) (apply fmt args))
-	(fmt (apply 'format fmt args))
-	(args (car args))
-	(t args)))
-
-(defsubst orgtbl-eval-str (str)
-  "If STR is a function, evaluate it with no arguments."
-  (if (functionp str)
-      (funcall str)
-    str))
-
-(defun orgtbl-format-line (line)
-  "Format LINE as a table row."
-  (if (eq line 'hline) (if *orgtbl-hline* (push *orgtbl-hline* *orgtbl-rtn*))
-    (let* ((i 0)
-	   (line
-	    (mapcar
-	     (lambda (f)
-	       (setq i (1+ i))
-	       (let* ((efmt (orgtbl-get-fmt *orgtbl-efmt* i))
-		      (f (if (and efmt (string-match orgtbl-exp-regexp f))
-			     (orgtbl-apply-fmt efmt (match-string 1 f)
-					       (match-string 2 f))
-			   f)))
-		 (orgtbl-apply-fmt (or (orgtbl-get-fmt *orgtbl-fmt* i)
-				       *orgtbl-default-fmt*)
-				   f)))
-	     line)))
-      (push (if *orgtbl-lfmt*
-		(apply #'orgtbl-apply-fmt *orgtbl-lfmt* line)
-	      (concat (orgtbl-eval-str *orgtbl-lstart*)
-		      (mapconcat 'identity line *orgtbl-sep*)
-		      (orgtbl-eval-str *orgtbl-lend*)))
-	    *orgtbl-rtn*))))
-
-(defun orgtbl-format-section (section-stopper)
-  "Format lines until the first occurrence of SECTION-STOPPER."
-  (let (prevline)
-    (progn
-      (while (not (eq (car *orgtbl-table*) section-stopper))
-	(if prevline (orgtbl-format-line prevline))
-	(setq prevline (pop *orgtbl-table*)))
-      (if prevline (let ((*orgtbl-lstart* *orgtbl-llstart*)
-			 (*orgtbl-lend* *orgtbl-llend*)
-			 (*orgtbl-lfmt* *orgtbl-llfmt*))
-		     (orgtbl-format-line prevline))))))
-
 ;;;###autoload
-(defun orgtbl-to-generic (table params &optional backend)
+(defun orgtbl-to-generic (table params)
   "Convert the orgtbl-mode TABLE to some other format.
+
 This generic routine can be used for many standard cases.
-TABLE is a list, each entry either the symbol `hline' for a horizontal
-separator line, or a list of fields for that line.
-PARAMS is a property list of parameters that can influence the conversion.
-A third optional argument BACKEND can be used to convert the content of
-the cells using a specific export back-end.
 
-For the generic converter, some parameters are obligatory: you need to
-specify either :lfmt, or all of (:lstart :lend :sep).
+TABLE is a list, each entry either the symbol `hline' for
+a horizontal separator line, or a list of fields for that
+line.  PARAMS is a property list of parameters that can
+influence the conversion.
 
 Valid parameters are:
 
-:splice     When set to t, return only table body lines, don't wrap
-            them into :tstart and :tend.  Default is nil.  When :splice
-            is non-nil, this also means that the exporter should not look
-            for and interpret header and footer sections.
+:backend
+
+  Export back-end used as a basis to transcode elements of the
+  table, when no specific parameter applies to it.  It is also
+  used to translate cells contents.  You can prevent this by
+  setting :raw property to a non-nil value.
+
+:splice
+
+  When non-nil, return only table body lines (i.e, skip header
+  section).  Also don't wrap them into :tstart and :tend.
+  Default is nil.
+
+:skip
+
+  When set to an integer N, skip the first N lines of the table.
+  Horizontal separation lines do count for this parameter!
+
+:skipcols
 
-:hline      String to be inserted on horizontal separation lines.
-            May be nil to ignore hlines.
+  List of columns that should be skipped.  If the table has
+  a column with calculation marks, that column is automatically
+  discarded as well.  Please note that the translator function
+  sees the table after the removal of these columns, the function
+  never knows that there have been additional columns.
 
-:sep        Separator between two fields
-:remove-nil-lines Do not include lines that evaluate to nil.
+:hline
+
+  String to be inserted on horizontal separation lines. May
+  be nil to ignore hlines.
+
+:sep
+
+  Separator between two fields, as a string.
 
 Each in the following group may be either a string or a function
 of no arguments returning a string:
 
-:tstart     String to start the table.  Ignored when :splice is t.
-:tend       String to end the table.  Ignored when :splice is t.
-:lstart     String to start a new table line.
-:llstart    String to start the last table line, defaults to :lstart.
-:lend       String to end a table line
-:llend      String to end the last table line, defaults to :lend.
-
-Each in the following group may be a string, a function of one
-argument (the field or line) returning a string, or a plist
-mapping columns to either of the above:
-
-:lfmt       Format for entire line, with enough %s to capture all fields.
-            If this is present, :lstart, :lend, and :sep are ignored.
-:llfmt      Format for the entire last line, defaults to :lfmt.
-:fmt        A format to be used to wrap the field, should contain
-            %s for the original field value.  For example, to wrap
-            everything in dollars, you could use :fmt \"$%s$\".
-            This may also be a property list with column numbers and
-            formats.  For example :fmt (2 \"$%s$\" 4 \"%s%%\")
-:hlstart :hllstart :hlend :hllend :hlsep :hlfmt :hllfmt :hfmt
-            Same as above, specific for the header lines in the table.
-            All lines before the first hline are treated as header.
-            If any of these is not present, the data line value is used.
+:tstart
+
+  String to start the table.  Ignored when :splice is t.
+
+:tend
+
+  String to end the table.  Ignored when :splice is t.
+
+:lstart
+
+  String to start a new table line.
+
+:llstart
+
+  String to start the last table line, defaults to :lstart.
+
+:lend
+
+  String to end a table line.
+
+:llend
+
+  String to end the last table line, defaults to :lend.
+
+Each in the following group may be a string or a function of
+several arguments (one for each cell in row) returning a string:
+
+:lfmt
+
+  Format for entire line, with enough %s to capture all fields.
+  If this is present, :lstart, :lend, and :sep are ignored.
+
+:llfmt
+
+  Format for the entire last line, defaults to :lfmt.
+
+:fmt
+
+  A format to be used to wrap the field, should contain %s for
+  the original field value.  For example, to wrap everything in
+  dollars, you could use :fmt \"$%s$\".  This may also be
+  a property list with column numbers and format strings, or
+  functions, e.g.,
+
+    \(:fmt (2 \"$%s$\" 4 (lambda (c) (format \"$%s$\" c))))
+
+:hlstart :hllstart :hlend :hllend :hsep :hlfmt :hllfmt :hfmt
+
+ Same as above, specific for the header lines in the table.
+ All lines before the first hline are treated as header.  If
+ any of these is not present, the data line value is used.
 
 This may be either a string or a function of two arguments:
 
-:efmt       Use this format to print numbers with exponentials.
-            The format should have %s twice for inserting mantissa
-            and exponent, for example \"%s\\\\times10^{%s}\".  This
-            may also be a property list with column numbers and
-            formats.  :fmt will still be applied after :efmt.
-
-In addition to this, the parameters :skip and :skipcols are always handled
-directly by `orgtbl-send-table'.  See manual."
-  (let* ((splicep (plist-get params :splice))
-	 (hline (plist-get params :hline))
-	 (skipheadrule (plist-get params :skipheadrule))
-	 (remove-nil-linesp (plist-get params :remove-nil-lines))
-	 (remove-newlines (plist-get params :remove-newlines))
-	 (*orgtbl-hline* hline)
-	 (*orgtbl-table* table)
-	 (*orgtbl-sep* (plist-get params :sep))
-	 (*orgtbl-efmt* (plist-get params :efmt))
-	 (*orgtbl-lstart* (plist-get params :lstart))
-	 (*orgtbl-llstart* (or (plist-get params :llstart) *orgtbl-lstart*))
-	 (*orgtbl-lend* (plist-get params :lend))
-	 (*orgtbl-llend* (or (plist-get params :llend) *orgtbl-lend*))
-	 (*orgtbl-lfmt* (plist-get params :lfmt))
-	 (*orgtbl-llfmt* (or (plist-get params :llfmt) *orgtbl-lfmt*))
-	 (*orgtbl-fmt* (plist-get params :fmt))
-	 *orgtbl-rtn*)
-    ;; Convert cells content to backend BACKEND
-    (when backend
-      (setq *orgtbl-table*
-	    (mapcar
-	     (lambda(r)
-	       (if (listp r)
-		   (mapcar
-		    (lambda (c)
-		      (org-trim (org-export-string-as c backend t '(:with-tables t))))
-		    r)
-		 r))
-	     *orgtbl-table*)))
-    ;; Put header
-    (unless splicep
-      (when (plist-member params :tstart)
-	(let ((tstart (orgtbl-eval-str (plist-get params :tstart))))
-	  (if tstart (push tstart *orgtbl-rtn*)))))
-    ;; If we have a heading, format it and handle the trailing hline.
-    (if (and (not splicep)
-	     (or (consp (car *orgtbl-table*))
-		 (consp (nth 1 *orgtbl-table*)))
-	     (memq 'hline (cdr *orgtbl-table*)))
-	(progn
-	  (when (eq 'hline (car *orgtbl-table*))
-	    ;; There is a hline before the first data line
-	    (and hline (push hline *orgtbl-rtn*))
-	    (pop *orgtbl-table*))
-	  (let* ((*orgtbl-lstart* (or (plist-get params :hlstart)
-				      *orgtbl-lstart*))
-		 (*orgtbl-llstart* (or (plist-get params :hllstart)
-				       *orgtbl-llstart*))
-		 (*orgtbl-lend* (or (plist-get params :hlend) *orgtbl-lend*))
-		 (*orgtbl-llend* (or (plist-get params :hllend)
-				     (plist-get params :hlend) *orgtbl-llend*))
-		 (*orgtbl-lfmt* (or (plist-get params :hlfmt) *orgtbl-lfmt*))
-		 (*orgtbl-llfmt* (or (plist-get params :hllfmt)
-				     (plist-get params :hlfmt) *orgtbl-llfmt*))
-		 (*orgtbl-sep* (or (plist-get params :hlsep) *orgtbl-sep*))
-		 (*orgtbl-fmt* (or (plist-get params :hfmt) *orgtbl-fmt*)))
-	    (orgtbl-format-section 'hline))
-	  (if (and hline (not skipheadrule)) (push hline *orgtbl-rtn*))
-	  (pop *orgtbl-table*)))
-    ;; Now format the main section.
-    (orgtbl-format-section nil)
-    (unless splicep
-      (when (plist-member params :tend)
-	(let ((tend (orgtbl-eval-str (plist-get params :tend))))
-	  (if tend (push tend *orgtbl-rtn*)))))
-    (mapconcat (if remove-newlines
-		   (lambda (tend)
-		     (replace-regexp-in-string "[\n\r\t\f]" "\\\\n" tend))
-		 'identity)
-	       (nreverse (if remove-nil-linesp
-			     (remq nil *orgtbl-rtn*)
-			   *orgtbl-rtn*)) "\n")))
+:efmt
+
+  Use this format to print numbers with exponential.  The format
+  should have %s twice for inserting mantissa and exponent, for
+  example \"%s\\\\times10^{%s}\".  This may also be a property
+  list with column numbers and format strings or functions.
+  :fmt will still be applied after :efmt."
+  (let ((backend (plist-get params :backend)))
+    (when (and backend (symbolp backend) (not (org-export-get-backend backend)))
+      (user-error "Unknown :backend value"))
+    (when (or (not backend) (plist-get params :raw)) (require 'ox-org))
+    (org-trim
+     (org-export-string-as
+      ;; Return TABLE as Org syntax.  Tolerate non-string cells.
+      (with-output-to-string
+	(dolist (e table)
+	  (cond ((eq e 'hline) (princ "|--\n"))
+		((consp e)
+		 (princ "| ") (dolist (c e) (princ c) (princ " |"))
+		 (princ "\n")))))
+      ;; Build a custom back-end according to PARAMS.  Before defining
+      ;; a translator, check if there is anything to do.  When there
+      ;; isn't, let BACKEND handle the element.
+      (org-export-create-backend
+       :parent (or backend 'org)
+       :filters
+       '((:filter-parse-tree
+	  ;; Handle :skip parameter.
+	  (lambda (tree backend info)
+	    (let ((skip (plist-get info :skip)))
+	      (when skip
+		(unless (wholenump skip) (user-error "Wrong :skip value"))
+		(let ((n 0))
+		  (org-element-map tree 'table-row
+		    (lambda (row)
+		      (if (>= n skip) t
+			(org-element-extract-element row)
+			(incf n)
+			nil))
+		    info t))
+		tree)))
+	  ;; Handle :skipcols parameter.
+	  (lambda (tree backend info)
+	    (let ((skipcols (plist-get info :skipcols)))
+	      (when skipcols
+		(unless (consp skipcols) (user-error "Wrong :skipcols value"))
+		(org-element-map tree 'table
+		  (lambda (table)
+		    (let ((specialp
+			   (org-export-table-has-special-column-p table)))
+		      (dolist (row (org-element-contents table))
+			(when (eq (org-element-property :type row) 'standard)
+			  (let ((c 1))
+			    (dolist (cell (nthcdr (if specialp 1 0)
+						  (org-element-contents row)))
+			      (when (memq c skipcols)
+				(org-element-extract-element cell))
+			      (incf c)))))))
+		  info)
+		tree)))))
+       :transcoders
+       `((table . ,(org-table--to-generic-table params))
+	 (table-row . ,(org-table--to-generic-row params))
+	 (table-cell . ,(org-table--to-generic-cell params))
+	 ;; Section.  Return contents to avoid garbage around table.
+	 (section . (lambda (s c i) c))))
+      'body-only (org-combine-plists params '(:with-tables t))))))
+
+(defun org-table--to-generic-table (params)
+  "Return custom table transcoder according to PARAMS.
+PARAMS is a plist.  See `orgtbl-to-generic' for more information.
+Return nil if no transcoder is needed."
+  (let ((backend (plist-get params :backend))
+	(splice (plist-get params :splice))
+	(tstart (plist-get params :tstart))
+	(tend (plist-get params :tend)))
+    `(lambda (table contents info)
+       (concat ,@(cond ((or splice (not tstart)) nil)
+		       ((functionp tstart) `((funcall ',tstart) "\n"))
+		       ((stringp tstart) `(,tstart "\n"))
+		       (t (user-error "Wrong :tstart value")))
+	       ,(if (and backend (not (or tstart tend splice)))
+		    `(org-export-with-backend ',backend table contents info)
+		  'contents)
+	       ,(cond ((or splice (not tend)) nil)
+		      ((functionp tend) `(funcall ',tend))
+		      ((stringp tend) tend)
+		      (t (user-error "Wrong :tend value")))))))
+
+(defun org-table--to-generic-row (params)
+  "Return custom table row transcoder according to PARAMS.
+PARAMS is a plist.  See `orgtbl-to-generic' for more
+information."
+  (let* ((backend (plist-get params :backend))
+	 (lstart (plist-get params :lstart))
+	 (llstart (or (plist-get params :llstart) lstart))
+	 (hlstart (or (plist-get params :hlstart) lstart))
+	 (hllstart (or (plist-get params :hllstart) hlstart))
+	 (lend (plist-get params :lend))
+	 (llend (or (plist-get params :llend) lend))
+	 (hlend (or (plist-get params :hlend) lend))
+	 (hllend (or (plist-get params :hllend) hlend))
+	 (lfmt (plist-get params :lfmt))
+	 (llfmt (or (plist-get params :llfmt) lfmt))
+	 (hlfmt (or (plist-get params :hlfmt) lfmt))
+	 (hllfmt (or (plist-get params :hllfmt) hlfmt))
+	 (splice (plist-get params :splice)))
+    `(lambda (row contents info)
+       (if (eq (org-element-property :type row) 'rule)
+	   ,(cond ((plist-member params :hline) (plist-get params :hline))
+		  (backend `(org-export-with-backend ',backend row info)))
+	 (let ((headerp
+		(and (org-export-table-has-header-p
+		      (org-element-property :parent row) info)
+		     (= (org-export-table-row-group row info) 1)))
+	       (lastp (not (org-export-get-next-element row info)))
+	       (last-header-p (org-export-table-row-ends-header-p row info)))
+	   (when (and contents ,(or (not splice) '(not headerp)))
+	     ;; Check if we can apply `:lfmt', `:llfmt', `:hlfmt', or
+	     ;; `:hllfmt' to CONTENTS.  Otherwise, fallback on
+	     ;; `:lstart', `:lend' and their relatives.
+	     ,(let ((use
+		     (lambda (v p)
+		       `(apply
+			 ,@(cond
+			    ((functionp v) `(',v))
+			    ((stringp v) `(#'format ,v))
+			    (t (user-error "Wrong %s value" p)))
+			 (org-element-map row 'table-cell
+			   (lambda (cell)
+			     ;; Use `org-export-data-with-backend'
+			     ;; instead of `org-export-data' to avoid
+			     ;; cached values, which
+			     ;; ignore :orgtbl-ignore-sep parameter.
+			     (org-export-data-with-backend
+			      cell
+			      (plist-get info :back-end)
+			      (org-combine-plists
+			       info '(:orgtbl-ignore-sep t))))
+			   info)))))
+		`(cond
+		  ,(and hllfmt `(last-header-p ,(funcall use hllfmt ":hllfmt")))
+		  ,(and hlfmt `(headerp ,(funcall use hlfmt ":hlfmt")))
+		  ,(and llfmt `(lastp ,(funcall use llfmt ":llfmt")))
+		  (t
+		   ,(if lfmt (funcall use lfmt ":lfmt")
+		      (let ((use
+			     (lambda (v p)
+			       (cond ((null v) nil)
+				     ((functionp v) `(funcall ',v))
+				     ((stringp v) v)
+				     (t (user-error "Wrong %s value" p))))))
+			`(concat
+			  (cond
+			   ,(and (or hllstart hllend)
+				 `(last-header-p
+				   (concat ,(funcall use hllstart ":hllstart")
+					   contents
+					   ,(funcall use hllend ":hllend"))))
+			   ,(and (or hlstart hlend)
+				 `(headerp
+				   (concat ,(funcall use hlstart ":hlstart")
+					   contents
+					   ,(funcall use hlend ":hlend"))))
+			   ,(and (or llstart llend)
+				 `(lastp
+				   (concat ,(funcall use llstart ":llstart")
+					   contents
+					   ,(funcall use llend ":llend"))))
+			   (t
+			    ,(cond
+			      ((or lstart lend)
+			       `(concat ,(funcall use lstart ":lstart")
+					contents
+					,(funcall use lend ":lend")))
+			      (backend
+			       `(org-export-with-backend
+				 ',backend row contents info))
+			      (t 'contents))))))))))))))))
+
+(defun org-table--to-generic-cell (params)
+  "Return custom table cell transcoder according to PARAMS.
+PARAMS is a plist.  See `orgtbl-to-generic' for more
+information."
+  (let* ((backend (plist-get params :backend))
+	 (efmt (plist-get params :efmt))
+	 (fmt (plist-get params :fmt))
+	 (hfmt (or (plist-get params :hfmt) fmt))
+	 (sep (plist-get params :sep))
+	 (hsep (or (plist-get params :hsep) sep)))
+    `(lambda (cell contents info)
+       (let ((column (1+ (cdr (org-export-table-cell-address cell info))))
+	     (headerp (= (org-export-table-row-group
+			  (org-export-get-parent-element cell) info)
+			 1))
+	     (lastp (not (org-export-get-next-element cell info))))
+	 ;; Make sure that contents are exported as Org data when :raw
+	 ;; parameter is non-nil.
+	 ,(when (and backend (plist-get params :raw))
+	    `(setq contents
+		   (org-export-data-with-backend
+		    (org-element-contents cell) 'org info)))
+	 (when contents
+	   ;; Check if we can apply `:efmt' on CONTENTS.  If `:efmt'
+	   ;; binds columns to format strings or functions, first
+	   ;; get the right one.
+	   ,(when efmt
+	      `(when (string-match orgtbl-exp-regexp contents)
+		 (let ((mantissa (match-string 1 contents))
+		       (exponent (match-string 2 contents)))
+		   (setq contents
+			 ,(cond
+			   ((stringp efmt)
+			    `(format ,efmt mantissa exponent))
+			   ((functionp efmt)
+			    `(funcall #',efmt mantissa exponent))
+			   ((consp efmt)
+			    `(let ((efmt (cadr (memq column ',efmt))))
+			       (cond
+				((null efmt) contents)
+				((stringp efmt)
+				 (format efmt mantissa exponent))
+				((functionp efmt)
+				 (funcall efmt mantissa exponent))
+				(t (user-error "Wrong :efmt value")))))
+			   (t (user-error "Wrong :efmt value")))))))
+	   ;; Check if we can apply FMT (or HFMT) on CONTENTS.  If
+	   ;; FMT binds columns to format strings, first get the
+	   ;; right one.
+	   ,(when hfmt
+	      (let ((value
+		     (lambda (v p)
+		       (cond
+			((null v) 'contents)
+			((functionp v) `(funcall #',v contents))
+			((stringp v) `(format ,v contents))
+			((consp v)
+			 `(let ((fmt (cadr (memq column ',v))))
+			    (cond
+			     ((null fmt) contents)
+			     ((stringp fmt) (format fmt contents))
+			     ((functionp fmt) (funcall fmt contents))
+			     (t (user-error "Wrong %s value" p)))))
+			(t (user-error "Wrong %s value" p))))))
+		`(setq contents
+		       (if headerp ,(funcall value hfmt ":hfmt")
+			 ,(funcall value fmt ":fmt"))))))
+	 ;; If a separator is provided, use it instead of BACKEND's.
+	 ;; Separators are ignored when LFMT (or equivalent) is
+	 ;; provided.
+	 (if (and ,hsep
+		  (not lastp)
+		  (not (plist-get info :orgtbl-ignore-sep)))
+	     (concat contents (or (and headerp ,hsep) ,sep))
+	   ,(if (not backend) 'contents
+	      `(org-export-with-backend ',backend cell contents info)))))))
 
 ;;;###autoload
 (defun orgtbl-to-tsv (table params)
   "Convert the orgtbl-mode table to TAB separated material."
   (orgtbl-to-generic table (org-combine-plists '(:sep "\t") params)))
+
 ;;;###autoload
 (defun orgtbl-to-csv (table params)
   "Convert the orgtbl-mode table to CSV material.
 This does take care of the proper quoting of fields with comma or quotes."
-  (orgtbl-to-generic table (org-combine-plists
-			    '(:sep "," :fmt org-quote-csv-field)
-			    params)))
+  (orgtbl-to-generic table
+		     (org-combine-plists '(:sep "," :fmt org-quote-csv-field)
+					 params)))
 
 ;;;###autoload
 (defun orgtbl-to-latex (table params)
   "Convert the orgtbl-mode TABLE to LaTeX.
-TABLE is a list, each entry either the symbol `hline' for a horizontal
-separator line, or a list of fields for that line.
-PARAMS is a property list of parameters that can influence the conversion.
-Supports all parameters from `orgtbl-to-generic'.  Most important for
-LaTeX are:
-
-:splice    When set to t, return only table body lines, don't wrap
-           them into a tabular environment.  Default is nil.
-
-:fmt       A format to be used to wrap the field, should contain %s for the
-           original field value.  For example, to wrap everything in dollars,
-           use :fmt \"$%s$\".  This may also be a property list with column
-           numbers and formats.  For example :fmt (2 \"$%s$\" 4 \"%s%%\")
-           The format may also be a function that formats its one argument.
-
-:efmt      Format for transforming numbers with exponentials.  The format
-           should have %s twice for inserting mantissa and exponent, for
-           example \"%s\\\\times10^{%s}\".  LaTeX default is \"%s\\\\,(%s)\".
-           This may also be a property list with column numbers and formats.
-           The format may also be a function that formats its two arguments.
-
-:llend     If you find too much space below the last line of a table,
-           pass a value of \"\" for :llend to suppress the final \\\\.
 
-The general parameters :skip and :skipcols have already been applied when
-this function is called."
-  (let* ((alignment (mapconcat (lambda (x) (if x "r" "l"))
-			       org-table-last-alignment ""))
-	 (params2
-	  (list
-	   :tstart (concat "\\begin{tabular}{" alignment "}")
-	   :tend "\\end{tabular}"
-	   :lstart "" :lend " \\\\" :sep " & "
-	   :efmt "%s\\,(%s)" :hline "\\hline")))
-    (require 'ox-latex)
-    (orgtbl-to-generic table (org-combine-plists params2 params) 'latex)))
+TABLE is a list, each entry either the symbol `hline' for
+a horizontal separator line, or a list of fields for that line.
+PARAMS is a property list of parameters that can influence the
+conversion.  All parameters from `orgtbl-to-generic' are
+supported.  Additionally, it is also possible to use the
+following parameters:
+
+:booktabs
+
+  When non-nil, use formal \"booktabs\" style.
+
+:environment
+
+  Specify environment to use, as a string.  If you use
+  \"longtable\", you may also want to specify :language property,
+  as a string, to get proper continuation strings.
+
+The general parameters :skip and :skipcols have already been
+applied when this function is called."
+  (require 'ox-latex)
+  (orgtbl-to-generic
+   table
+   (org-combine-plists
+    ;; Provide sane default values.
+    (list :backend 'latex
+	  :latex-default-table-mode 'table
+	  :latex-tables-centered nil
+	  :latex-tables-booktabs (plist-get params :booktabs)
+	  :latex-table-scientific-notation (plist-get params :efmt)
+	  :latex-default-table-environment
+	  (or (plist-get params :environment) "tabular"))
+    params
+    '(:efmt nil))))
 
 ;;;###autoload
 (defun orgtbl-to-html (table params)
   "Convert the orgtbl-mode TABLE to HTML.
-TABLE is a list, each entry either the symbol `hline' for a horizontal
-separator line, or a list of fields for that line.
-PARAMS is a property list of parameters that can influence the conversion.
-Currently this function recognizes the following parameters:
 
-:splice    When set to t, return only table body lines, don't wrap
-           them into a <table> environment.  Default is nil.
+TABLE is a list, each entry either the symbol `hline' for
+a horizontal separator line, or a list of fields for that line.
+PARAMS is a property list of parameters that can influence the
+conversion.  All parameters from `orgtbl-to-generic' are
+supported.  Additionally, it is also possible to use the
+following parameter:
 
-The general parameters :skip and :skipcols have already been applied when
-this function is called.  The function does *not* use `orgtbl-to-generic',
-so you cannot specify parameters for it."
+:attributes
+
+  Attributes and values, as a plist, which will be used in
+  <table> tag.
+
+The general parameters :skip and :skipcols have already been
+applied when this function is called."
   (require 'ox-html)
-  (let ((output (org-export-string-as
-		 (orgtbl-to-orgtbl table nil) 'html t '(:with-tables t))))
-    (if (not (plist-get params :splice)) output
-      (org-trim
-       (replace-regexp-in-string
-	"\\`<table .*>\n" ""
-	(replace-regexp-in-string "</table>\n*\\'" "" output))))))
+  (orgtbl-to-generic
+   table
+   (org-combine-plists
+    ;; Provide sane default values.
+    (list :backend 'html
+	  :html-table-data-tags '("<td%s>" . "</td>")
+	  :html-table-use-header-tags-for-first-column nil
+	  :html-table-align-individual-fields t
+	  :html-table-row-tags '("<tr>" . "</tr>")
+	  :html-table-attributes
+	  (if (plist-member params :attributes)
+	      (plist-get params :attributes)
+	    '(:border "2" :cellspacing "0" :cellpadding "6" :rules "groups"
+		      :frame "hsides")))
+    params)))
 
 ;;;###autoload
 (defun orgtbl-to-texinfo (table params)
-  "Convert the orgtbl-mode TABLE to TeXInfo.
-TABLE is a list, each entry either the symbol `hline' for a horizontal
-separator line, or a list of fields for that line.
-PARAMS is a property list of parameters that can influence the conversion.
-Supports all parameters from `orgtbl-to-generic'.  Most important for
-TeXInfo are:
-
-:splice nil/t      When set to t, return only table body lines, don't wrap
-                   them into a multitable environment.  Default is nil.
-
-:fmt fmt           A format to be used to wrap the field, should contain
-                   %s for the original field value.  For example, to wrap
-                   everything in @kbd{}, you could use :fmt \"@kbd{%s}\".
-                   This may also be a property list with column numbers and
-                   formats.  For example :fmt (2 \"@kbd{%s}\" 4 \"@code{%s}\").
-                   Each format also may be a function that formats its one
-                   argument.
-
-:cf \"f1 f2..\"    The column fractions for the table.  By default these
-                   are computed automatically from the width of the columns
-                   under org-mode.
+  "Convert the orgtbl-mode TABLE to Texinfo.
+
+TABLE is a list, each entry either the symbol `hline' for
+a horizontal separator line, or a list of fields for that line.
+PARAMS is a property list of parameters that can influence the
+conversion.  All parameters from `orgtbl-to-generic' are
+supported.  Additionally, it is also possible to use the
+following parameter:
+
+:columns
+
+  Column widths, as a string.  When providing column fractions,
+  \"@columnfractions\" command can be omitted.
 
 The general parameters :skip and :skipcols have already been applied when
 this function is called."
-  (let* ((total (float (apply '+ org-table-last-column-widths)))
-	 (colfrac (or (plist-get params :cf)
-		      (mapconcat
-		       (lambda (x) (format "%.3f" (/ (float x) total)))
-		       org-table-last-column-widths " ")))
-	 (params2
-	  (list
-	   :tstart (concat "@multitable @columnfractions " colfrac)
-	   :tend "@end multitable"
-	   :lstart "@item " :lend "" :sep " @tab "
-	   :hlstart "@headitem ")))
-    (require 'ox-texinfo)
-    (orgtbl-to-generic table (org-combine-plists params2 params) 'texinfo)))
+  (require 'ox-texinfo)
+  (let ((output
+	 (orgtbl-to-generic
+	  table
+	  (org-combine-plists
+	   (list :backend 'texinfo
+		 :texinfo-tables-verbatim nil
+		 :latex-table-scientific-notation (plist-get params :efmt))
+	   params
+	   (list :efmt nil))))
+	(columns (let ((w (plist-get params :columns)))
+		   (cond ((not w) nil)
+			 ((org-string-match-p "{\\|@columnfractions " w) w)
+			 (t (concat "@columnfractions " w))))))
+    (if (not columns) output
+      (replace-regexp-in-string
+       "@multitable \\(.*\\)" columns output t nil 1))))
 
 ;;;###autoload
 (defun orgtbl-to-orgtbl (table params)
@@ -4967,21 +5040,8 @@ Useful when slicing one table into many.  The :hline, :sep,
 :lstart, and :lend provide orgtbl framing.  The default nil :tstart
 and :tend suppress strings without splicing; they can be set to
 provide ORGTBL directives for the generated table."
-  (let* ((params2
-	  (list
-	   :remove-newlines t
-	   :tstart nil :tend nil
-	   :hline "|---"
-	   :sep " | "
-	   :lstart "| "
-	   :lend " |"))
-	 (params (org-combine-plists params2 params)))
-    (with-temp-buffer
-      (insert (orgtbl-to-generic table params))
-      (goto-char (point-min))
-      (while (re-search-forward org-table-hline-regexp nil t)
-	(org-table-align))
-      (buffer-substring 1 (buffer-size)))))
+  (require 'ox-org)
+  (orgtbl-to-generic table (org-combine-plists (list :backend 'org))))
 
 (defun orgtbl-to-table.el (table params)
   "Convert the orgtbl-mode TABLE into a table.el table."
@@ -4994,19 +5054,38 @@ provide ORGTBL directives for the generated table."
 
 (defun orgtbl-to-unicode (table params)
   "Convert the orgtbl-mode TABLE into a table with unicode characters.
-You need the ascii-art-to-unicode.el package for this.  You can download
-it here: http://gnuvola.org/software/j/aa2u/ascii-art-to-unicode.el."
-  (with-temp-buffer
-    (insert (orgtbl-to-table.el table params))
-    (goto-char (point-min))
-    (if (or (featurep 'ascii-art-to-unicode)
-	    (require 'ascii-art-to-unicode nil t))
-	(aa2u)
-      (unless (delq nil (mapcar (lambda (l) (string-match "aa2u" (car l))) org-stored-links))
-	(push '("http://gnuvola.org/software/j/aa2u/ascii-art-to-unicode.el"
-		"Link to ascii-art-to-unicode.el") org-stored-links))
-      (user-error "Please download ascii-art-to-unicode.el (use C-c C-l to insert the link to it)"))
-    (buffer-string)))
+
+
+TABLE is a list, each entry either the symbol `hline' for
+a horizontal separator line, or a list of fields for that line.
+PARAMS is a property list of parameters that can influence the
+conversion.  All parameters from `orgtbl-to-generic' are
+supported.  Additionally, it is also possible to use the
+following parameters:
+
+:ascii-art
+
+  When non-nil, use \"ascii-art-to-unicode\" package to translate
+  the table.  You can download it here:
+  http://gnuvola.org/software/j/aa2u/ascii-art-to-unicode.el.
+
+:narrow
+
+  When non-nil, narrow columns width than provided width cookie,
+  using \"=>\" as an ellipsis, just like in an Org mode buffer.
+
+The general parameters :skip and :skipcols have already been
+applied when this function is called."
+  (require 'ox-ascii)
+  (orgtbl-to-generic
+   table
+   (org-combine-plists
+    (list :backend 'ascii
+	  :ascii-charset 'utf-8
+	  :ascii-table-keep-all-vertical-lines (plist-get params :)
+	  :ascii-table-widen-columns (not (plist-get params :narrow))
+	  :ascii-table-use-ascii-art (plist-get params :ascii-art))
+    params)))
 
 (defun org-table-get-remote-range (name-or-id form)
   "Get a field value or a list of values in a range from table at ID.
diff --git a/testing/lisp/test-org-table.el b/testing/lisp/test-org-table.el
index 40101be..d99d735 100644
--- a/testing/lisp/test-org-table.el
+++ b/testing/lisp/test-org-table.el
@@ -1168,6 +1168,357 @@ See also `test-org-table/copy-field'."
       (should (string= got
 		       expect)))))
 
+;;; Radio Tables
+
+(ert-deftest test-org-table/to-generic ()
+  "Test `orgtbl-to-generic' specifications."
+  ;; Test :splice parameter.
+  (should
+   (equal "b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:splice t))))
+  (should
+   (equal "a\nb"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n| b |") '(:splice t))))
+  ;; Test :hline parameter.
+  (should
+   (equal "a\nb"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:hline nil))))
+  (should
+   (equal "a\n~\nb"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:hline "~"))))
+  ;; Test :sep parameter.
+  (should
+   (equal "a!b\nc!d"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:sep "!"))))
+  ;; Test :hsep parameter.
+  (should
+   (equal "a!b\nc?d"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:sep "?" :hsep "!"))))
+  ;; Test :tstart parameter.
+  (should
+   (equal "<begin>\na"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tstart "<begin>"))))
+  (should
+   (equal "<begin>\na"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:tstart (lambda () "<begin>")))))
+  (should
+   (equal "a"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:tstart "<begin>" :splice t))))
+  ;; Test :tend parameter.
+  (should
+   (equal "a\n<end>"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |") '(:tend "<end>"))))
+  (should
+   (equal "a\n<end>"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:tend (lambda () "<end>")))))
+  (should
+   (equal "a"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:tend "<end>" :splice t))))
+  ;; Test :lstart parameter.
+  (should
+   (equal "> a"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a |") '(:lstart "> "))))
+  (should
+   (equal "> a"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:lstart (lambda () "> ")))))
+  ;; Test :llstart parameter.
+  (should
+   (equal "> a\n>> b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:lstart "> " :llstart ">> "))))
+  ;; Test :hlstart parameter.
+  (should
+   (equal "!> a\n> b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:lstart "> " :hlstart "!> "))))
+  ;; Test :hllstart parameter.
+  (should
+   (equal "!> a\n!!> b\n> c"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n| b |\n|---|\n| c |")
+			     '(:lstart "> " :hlstart "!> " :hllstart "!!> "))))
+  ;; Test :lend parameter.
+  (should
+   (equal "a <"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |") '(:lend " <"))))
+  ;; Test :llend parameter.
+  (should
+   (equal "a <\nb <<"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:lend " <" :llend " <<"))))
+  ;; Test :hlend parameter.
+  (should
+   (equal "a <!\nb <"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:lend " <" :hlend " <!"))))
+  ;; Test :hllend parameter.
+  (should
+   (equal "a <!\nb <!!\nc <"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n| b |\n|---|\n| c |")
+			     '(:lend " <" :hlend " <!" :hllend " <!!"))))
+  ;; Test :lfmt parameter.
+  (should
+   (equal "a!b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:lfmt "%s!%s"))))
+  (should
+   (equal "a+b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:lfmt (lambda (c1 c2) (concat c1 "+" c2))))))
+  (should
+   (equal "a!b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:lfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
+  ;; Test :llfmt parameter.
+  (should
+   (equal "a!b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:llfmt "%s!%s"))))
+  (should
+   (equal "a!b\nc+d"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n| c | d |")
+	   '(:lfmt "%s!%s" :llfmt (lambda (c1 c2) (concat c1 "+" c2))))))
+  (should
+   (equal "a!b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:llfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
+  ;; Test :hlfmt parameter.
+  (should
+   (equal "a!b\ncd"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:hlfmt "%s!%s"))))
+  (should
+   (equal "a+b\ncd"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:hlfmt (lambda (c1 c2) (concat c1 "+" c2))))))
+  (should
+   (equal "a!b\n>c d<"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:hlfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
+  ;; Test :hllfmt parameter.
+  (should
+   (equal "a!b\ncd"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:hllfmt "%s!%s"))))
+  (should
+   (equal "a+b\ncd"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:hllfmt (lambda (c1 c2) (concat c1 "+" c2))))))
+  (should
+   (equal "a!b\n>c d<"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |")
+	   '(:hllfmt "%s!%s" :lstart ">" :lend "<" :sep " "))))
+  ;; Test :fmt parameter.
+  (should
+   (equal ">a<\n>b<"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:fmt ">%s<"))))
+  (should
+   (equal ">a<b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:fmt (1 ">%s<" 2 (lambda (c) c))))))
+  (should
+   (equal "a b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:fmt (2 " %s")))))
+  (should
+   (equal ">a<"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:fmt (lambda (c) (format ">%s<" c))))))
+  ;; Test :hfmt parameter.
+  (should
+   (equal ">a<\nb"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |\n|---|\n| b |")
+			     '(:hfmt ">%s<"))))
+  (should
+   (equal ">a<b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:hfmt (1 ">%s<" 2 identity)))))
+  (should
+   (equal "a b"
+	  (orgtbl-to-generic (org-table-to-lisp "| a | b |")
+			     '(:hfmt (2 " %s")))))
+  (should
+   (equal ">a<"
+	  (orgtbl-to-generic (org-table-to-lisp "| a |")
+			     '(:hfmt (lambda (c) (format ">%s<" c))))))
+  ;; Test :efmt parameter.
+  (should
+   (equal "2x10^3"
+	  (orgtbl-to-generic (org-table-to-lisp "| 2e3 |")
+			     '(:efmt "%sx10^%s"))))
+  (should
+   (equal "2x10^3"
+	  (orgtbl-to-generic (org-table-to-lisp "| 2e3 |")
+			     '(:efmt (lambda (m e) (concat m "x10^" e))))))
+  (should
+   (equal "2x10^3"
+	  (orgtbl-to-generic (org-table-to-lisp "| 2e3 |")
+			     '(:efmt (1 "%sx10^%s")))))
+  (should
+   (equal "2x10^3"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| 2e3 |")
+	   '(:efmt (1 (lambda (m e) (format "%sx10^%s" m e)))))))
+  (should
+   (equal "2e3"
+	  (orgtbl-to-generic (org-table-to-lisp "| 2e3 |") '(:efmt nil))))
+  ;; Test :skip parameter.
+  (should
+   (equal "cd"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| \ | <c> |\n| a | b |\n|---+---|\n| c | d |")
+	   '(:skip 2))))
+  ;; Test :skipcols parameter.
+  (should
+   (equal "a\nc"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp "| a | b |\n| c | d |") '(:skipcols (2)))))
+  (should
+   (equal "a\nc"
+	  (orgtbl-to-generic
+	   (org-table-to-lisp
+	    "| / | <c> | <c> |\n| # | a | b |\n|---+---+---|\n|   | c | d |")
+	   '(:skipcols (2)))))
+  ;; Test :raw parameter.
+  (when (featurep 'ox-latex)
+    (should
+     (org-string-match-p
+      "/a/"
+      (orgtbl-to-generic (org-table-to-lisp "| /a/ | b |")
+			 '(:backend latex :raw t))))))
+
+(ert-deftest test-org-table/to-latex ()
+  "Test `orgtbl-to-latex' specifications."
+  (should
+   (equal "\\begin{tabular}{l}\na\\\\\n\\end{tabular}"
+	  (orgtbl-to-latex (org-table-to-lisp "| a |") nil)))
+  ;; Test :environment parameter.
+  (should
+   (equal "\\begin{tabularx}{l}\na\\\\\n\\end{tabularx}"
+	  (orgtbl-to-latex (org-table-to-lisp "| a |")
+			   '(:environment "tabularx"))))
+  ;; Test :booktabs parameter.
+  (should
+   (org-string-match-p
+    "\\toprule" (orgtbl-to-latex (org-table-to-lisp "| a |") '(:booktabs t)))))
+
+(ert-deftest test-org-table/to-html ()
+  "Test `orgtbl-to-html' specifications."
+  (should
+   (equal (orgtbl-to-html (org-table-to-lisp "| a |") nil)
+	  "<table border=\"2\" cellspacing=\"0\" cellpadding=\"6\" rules=\"groups\" frame=\"hsides\">
+
+
+<colgroup>
+<col  class=\"left\" />
+</colgroup>
+<tbody>
+<tr>
+<td class=\"left\">a</td>
+</tr>
+</tbody>
+</table>"))
+  ;; Test :attributes parameter.
+  (should
+   (org-string-match-p
+    "<table>"
+    (orgtbl-to-html (org-table-to-lisp "| a |") '(:attributes nil))))
+  (should
+   (org-string-match-p
+    "<table border=\"2\">"
+    (orgtbl-to-html (org-table-to-lisp "| a |") '(:attributes (:border "2"))))))
+
+(ert-deftest test-org-table/to-texinfo ()
+  "Test `orgtbl-to-texinfo' specifications."
+  (should
+   (equal "@multitable {a}\n@item a\n@end multitable"
+	  (orgtbl-to-texinfo (org-table-to-lisp "| a |") nil)))
+  ;; Test :columns parameter.
+  (should
+   (equal "@multitable @columnfractions .4 .6\n@item a\n@tab b\n@end multitable"
+	  (orgtbl-to-texinfo (org-table-to-lisp "| a | b |")
+			     '(:columns ".4 .6"))))
+  (should
+   (equal "@multitable @columnfractions .4 .6\n@item a\n@tab b\n@end multitable"
+	  (orgtbl-to-texinfo (org-table-to-lisp "| a | b |")
+			     '(:columns "@columnfractions .4 .6"))))
+  (should
+   (equal "@multitable {xxx} {xx}\n@item a\n@tab b\n@end multitable"
+	  (orgtbl-to-texinfo (org-table-to-lisp "| a | b |")
+			     '(:columns "{xxx} {xx}")))))
+
+(ert-deftest test-org-table/to-orgtbl ()
+  "Test `orgtbl-to-orgtbl' specifications."
+  (should
+   (equal "| a | b |\n|---+---|\n| c | d |"
+	  (orgtbl-to-orgtbl
+	   (org-table-to-lisp "| a | b |\n|---+---|\n| c | d |") nil))))
+
+(ert-deftest test-org-table/to-unicode ()
+  "Test `orgtbl-to-unicode' specifications."
+  (should
+   (equal "━━━\n a \n━━━"
+	  (orgtbl-to-unicode (org-table-to-lisp "| a |") nil)))
+  ;; Test :narrow parameter.
+  (should
+   (equal "━━━━\n => \n━━━━"
+	  (orgtbl-to-unicode (org-table-to-lisp "| <2> |\n| xxx |")
+			     '(:narrow t)))))
+
+(ert-deftest test-org-table/send-region ()
+  "Test `orgtbl-send-table' specifications."
+  ;; Error when not at a table.
+  (should-error
+   (org-test-with-temp-text "Paragraph"
+     (orgtbl-send-table)))
+  ;; Error when destination is missing.
+  (should-error
+   (org-test-with-temp-text "#+ORGTBL: SEND\n<point>| a |"
+     (orgtbl-send-table)))
+  ;; Error when transformation function is not specified.
+  (should-error
+   (org-test-with-temp-text "
+# BEGIN RECEIVE ORGTBL table
+# END RECEIVE ORGTBL table
+#+ORGTBL: SEND table
+<point>| a |"
+     (orgtbl-send-table)))
+  ;; Standard test.
+  (should
+   (equal "| a |\n|---|\n| b |\n"
+	  (org-test-with-temp-text "
+# BEGIN RECEIVE ORGTBL table
+# END RECEIVE ORGTBL table
+#+ORGTBL: SEND table orgtbl-to-orgtbl :hlines nil
+<point>| a |\n|---|\n| b |"
+	    (orgtbl-send-table)
+	    (goto-char (point-min))
+	    (buffer-substring-no-properties
+	     (search-forward "# BEGIN RECEIVE ORGTBL table\n")
+	     (progn (search-forward "# END RECEIVE ORGTBL table")
+		    (match-beginning 0)))))))
+
+
 (provide 'test-org-table)
 
 ;;; test-org-table.el ends here
-- 
2.1.0


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

* Re: [RFC] Rewrite radio tables
  2014-08-24 19:25 [RFC] Rewrite radio tables Nicolas Goaziou
@ 2014-08-24 20:18 ` Thorsten Jolitz
  2014-08-24 20:51   ` Thorsten Jolitz
  2014-08-25 16:08 ` Nick Dokos
  2014-08-27 14:50 ` AW
  2 siblings, 1 reply; 9+ messages in thread
From: Thorsten Jolitz @ 2014-08-24 20:18 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

Hello,

> The following patch implements radio tables and `orgtbl-to-...'
> functions using Org export engine. The implementation is probably not
> totally backward compatible, though.
  
funny, I just wrote `org-dp-create-table' (in
https://github.com/tj64/org-dp) a few hours ago, obviously it is not
competing with this rather complex framework of Org table creation, but
it does its job and might be useful for anybody just looking for a
simple way to create a table programmatically.

Usage is:

#+BEGIN_SRC emacs-lisp :results raw
(org-dp-create-table
 (list
   '("col1" "col2" "col3")
   'hline
   '("a" "b" "c")
   '(1 2 3)
   'hline
   '(x y z)))
#+END_SRC

#+results:
| col1 | col2 | col3 |
|------+------+------|
| a    | b    | c    |
| 1    | 2    | 3    |
|------+------+------|
| x    | y    | z    |


implementation is:

#+BEGIN_SRC emacs-lisp
(defun org-dp-create-table (row-lst)
  "Create table of type 'org'.
ROW-LST is an alist of lists with elements that are contents of a
single row's table cells, e.g.

 (list
   (list \"col1\" \"col2\" \"col3\")
   'hline
   (list 1 2 3)
   (list 'x 'y 'z))

for a table with 3 columns and 4 rows, including one horizontal
line."
  ;; table
  (org-dp-create 
   'table
   (mapconcat
    (lambda (--row-cells)
      (if (eq --row-cells 'hline)
	  ;; rule rows
	  (org-dp-create 'table-row nil nil nil :type 'rule)
	;; standard rows
	(org-dp-create 'table-row
		       (mapconcat
			(lambda (--cell-cont)
			  ;; cells
			  (org-dp-create 'table-cell
					 (format "%s" --cell-cont)))
			--row-cells "")
		       nil nil :type 'standard)))
    row-lst "")
   nil nil :type 'org))
#+END_SRC

-- 
cheers,
Thorsten

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

* Re: [RFC] Rewrite radio tables
  2014-08-24 20:18 ` Thorsten Jolitz
@ 2014-08-24 20:51   ` Thorsten Jolitz
  0 siblings, 0 replies; 9+ messages in thread
From: Thorsten Jolitz @ 2014-08-24 20:51 UTC (permalink / raw)
  To: emacs-orgmode

Thorsten Jolitz <tjolitz@gmail.com> writes:

> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>
> Hello,
>
>> The following patch implements radio tables and `orgtbl-to-...'
>> functions using Org export engine. The implementation is probably not
>> totally backward compatible, though.

Btw, great job - thats quite a complex part of Org-mode, looks like a
lot of work ...

> funny, I just wrote `org-dp-create-table' (in
> https://github.com/tj64/org-dp) a few hours ago, obviously it is not
> competing with this rather complex framework of Org table creation, but
> it does its job and might be useful for anybody just looking for a
> simple way to create a table programmatically.

Just figured out that I can make the function more generic with a few
changes, now it handles table-formulas and table.el tables too, so FYI: 

,----[ C-h f org-dp-create-table RET ]
| org-dp-create-table is a Lisp function in `org-dp-lib.el'.
| 
| (org-dp-create-table ROW-LST &optional TBLFM TABLE-EL-P)
| 
| Create table with content ROW-LST.
| ROW-LST is an alist of lists with elements that are contents of a
| single row's table cells, e.g.
| 
|  (list
|    (list "col1" "col2" "col3")
|    'hline
|    (list 1 2 3)
|    (list 'x 'y 'z))
| 
| for a table with 3 columns and 4 rows, including one horizontal
| line.
| 
| TBLFM, if given, should be a list containing a table formula as
| string. If TABLE-EL-P is non-nil, the table type will be
| 'table.el, otherwise its is 'org by default.
`----

usage:

#+BEGIN_SRC emacs-lisp :results raw
  (org-dp-create-table
   (list
    ;; rows
    (list "col1" "col2" "col3")
    'hline
    (list "a" "b" "c")
    (list 1 2 3)
    'hline
    (list 'x 'y 'z))
   ;; table-formula 
   (list "x=y")
   ;; table type
   ; t)
#+END_SRC

#+results:
| col1 | col2 | col3 |
|------+------+------|
| a    | b    | c    |
| 1    | 2    | 3    |
|------+------+------|
| x    | y    | z    |
#+TBLFM: x=y


-- 
cheers,
Thorsten

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

* Re: [RFC] Rewrite radio tables
  2014-08-24 19:25 [RFC] Rewrite radio tables Nicolas Goaziou
  2014-08-24 20:18 ` Thorsten Jolitz
@ 2014-08-25 16:08 ` Nick Dokos
  2014-08-25 16:11   ` Nick Dokos
  2014-08-25 19:20   ` Nicolas Goaziou
  2014-08-27 14:50 ` AW
  2 siblings, 2 replies; 9+ messages in thread
From: Nick Dokos @ 2014-08-25 16:08 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:


> The following patch implements radio tables and `orgtbl-to-...'
> functions using Org export engine. The implementation is probably not
> totally backward compatible, though.
>

Thanks for doing this!

> ...
> Feedback welcome.
>

I tried it with one of Thorsten Grotte's examples:

--8<---------------cut here---------------start------------->8---
\documentclass{article}

\begin{document}

I tried to export this table to latex:

\begin{comment}
  
#+TBLNAME: Test
#+ORGTBL: SEND Test orgtbl-to-latex :skip 1 :splice t
| C |    A |    B |
|---+------+------|
|   |    6 |    2 |
|   |    4 |    9 |
|---+------+------|
| _ | suma | sumb |
|   |   10 |   11 |
#+TBLFM: $suma=vsum(@I..@II)::$sumb=vsum(@I..@II)
\end{comment}


and got the following result:

% BEGIN RECEIVE ORGTBL Test
10 & 11\\
% END RECEIVE ORGTBL Test

\end{document}
--8<---------------cut here---------------end--------------->8---

It works fine without the :skip argument, but with it, it seems to
skip to the second hline.

Thanks,
Nick

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

* Re: [RFC] Rewrite radio tables
  2014-08-25 16:08 ` Nick Dokos
@ 2014-08-25 16:11   ` Nick Dokos
  2014-08-25 19:20   ` Nicolas Goaziou
  1 sibling, 0 replies; 9+ messages in thread
From: Nick Dokos @ 2014-08-25 16:11 UTC (permalink / raw)
  To: emacs-orgmode

Nick Dokos <ndokos@gmail.com> writes:

> I tried it with one of Thorsten Grotte's examples:
>

That should be "Thorsten Grothe". Apologies for the
misspelled name.

Thanks,
Nick

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

* Re: [RFC] Rewrite radio tables
  2014-08-25 16:08 ` Nick Dokos
  2014-08-25 16:11   ` Nick Dokos
@ 2014-08-25 19:20   ` Nicolas Goaziou
  2014-09-13 13:23     ` Nicolas Goaziou
  1 sibling, 1 reply; 9+ messages in thread
From: Nicolas Goaziou @ 2014-08-25 19:20 UTC (permalink / raw)
  To: Nick Dokos; +Cc: emacs-orgmode

Hello,

Nick Dokos <ndokos@gmail.com> writes:

> I tried it with one of Thorsten Grotte's examples:

Thanks for testing it out.

> \documentclass{article}
>
> \begin{document}
>
> I tried to export this table to latex:
>
> \begin{comment}
>   
> #+TBLNAME: Test
> #+ORGTBL: SEND Test orgtbl-to-latex :skip 1 :splice t
> | C |    A |    B |
> |---+------+------|
> |   |    6 |    2 |
> |   |    4 |    9 |
> |---+------+------|
> | _ | suma | sumb |
> |   |   10 |   11 |
> #+TBLFM: $suma=vsum(@I..@II)::$sumb=vsum(@I..@II)
> \end{comment}
>
>
> and got the following result:
>
> % BEGIN RECEIVE ORGTBL Test
> 10 & 11\\
> % END RECEIVE ORGTBL Test
>
> \end{document}
>
> It works fine without the :skip argument, but with it, it seems to
> skip to the second hline.

Actually it looks fine to me. :skip is applied first, so table becomes

  |---+------+------|
  |   |    6 |    2 |
  |   |    4 |    9 |
  |---+------+------|
  | _ | suma | sumb |
  |   |   10 |   11 |

Then :splice is applied, so header (first rowgroup) is dropped

  | _ | suma | sumb |
  |   |   10 |   11 |

Eventually, this table is exported.

The current implementation gives

  % BEGIN RECEIVE ORGTBL Test
  \hline
  6 & 2 \\
  4 & 9 \\
  \hline
  10 & 11 \\
  % END RECEIVE ORGTBL Test

This doesn't match the specs, though. According to the (current)
docstring

  :splice  When set to t, return only table body lines, don't wrap
           them into :tstart and :tend.  Default is nil.  When :splice
           is non-nil, this also means that the exporter should not look
           for and interpret header and footer sections.

So it should ignore table's header, but it isn't the case, as
demonstrated in the following example. The same table with only :splice
t as parameter gives

  % BEGIN RECEIVE ORGTBL Test
  C & A & B \\
  \hline
   & 6 & 2 \\
   & 4 & 9 \\
  \hline
  $\backslash$\(_\) & suma & sumb \\
   & 10 & 11 \\
  % END RECEIVE ORGTBL Test

I'm surprised no one noticed it. This may be a sign that this feature
should be removed (i.e., :splice should not skip header).

WDYT?


Regards,

-- 
Nicolas Goaziou

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

* Re: [RFC] Rewrite radio tables
  2014-08-24 19:25 [RFC] Rewrite radio tables Nicolas Goaziou
  2014-08-24 20:18 ` Thorsten Jolitz
  2014-08-25 16:08 ` Nick Dokos
@ 2014-08-27 14:50 ` AW
  2014-08-27 23:33   ` Nicolas Goaziou
  2 siblings, 1 reply; 9+ messages in thread
From: AW @ 2014-08-27 14:50 UTC (permalink / raw)
  To: emacs-orgmode; +Cc: Nicolas Goaziou

Am Sonntag, 24. August 2014, 21:25:57 schrieb Nicolas Goaziou:
> Hello,
> 
> The following patch implements radio tables and `orgtbl-to-...'
> functions using Org export engine. The implementation is probably not
> totally backward compatible, though.

Hello,

I'm really interested in improvments of orgtbl and exporting to LaTeX, but as 
a user I have no idea what to do with the patchfile. 

If I can patch against a file of orgmode 8.2.7, please drop a line.

However, @Nicolas, thank you very much for time and effort.

Regards,

Alexander

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

* Re: [RFC] Rewrite radio tables
  2014-08-27 14:50 ` AW
@ 2014-08-27 23:33   ` Nicolas Goaziou
  0 siblings, 0 replies; 9+ messages in thread
From: Nicolas Goaziou @ 2014-08-27 23:33 UTC (permalink / raw)
  To: AW; +Cc: emacs-orgmode

Hello,

AW <alexander.willand@t-online.de> writes:

> I'm really interested in improvments of orgtbl and exporting to LaTeX, but as 
> a user I have no idea what to do with the patchfile. 
>
> If I can patch against a file of orgmode 8.2.7, please drop a line.

This patch is against master. Though, you can try applying it on maint.

Save the patch in your local org-mode repository. Possibly create a new
branch on top of maint. Then from dired, with point on the file:

  ! git am RET

The patch should be installed as the head of your current branch (unless
there are conflicts).


Regards,

-- 
Nicolas Goaziou

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

* Re: [RFC] Rewrite radio tables
  2014-08-25 19:20   ` Nicolas Goaziou
@ 2014-09-13 13:23     ` Nicolas Goaziou
  0 siblings, 0 replies; 9+ messages in thread
From: Nicolas Goaziou @ 2014-09-13 13:23 UTC (permalink / raw)
  To: Nick Dokos; +Cc: emacs-orgmode

Correcting myself,

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> This doesn't match the specs, though. According to the (current)
> docstring
>
>   :splice  When set to t, return only table body lines, don't wrap
>            them into :tstart and :tend.  Default is nil.  When :splice
>            is non-nil, this also means that the exporter should not look
>            for and interpret header and footer sections.

Actually, I misinterpreted the specs. Here, header and footer do not
refer to table's header and footer, but to generated code around the
table.  IOW, :splice is not expected to remove table's header.

I pushed the rewrite on master with this problem solved.

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

end of thread, other threads:[~2014-09-13 13:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-24 19:25 [RFC] Rewrite radio tables Nicolas Goaziou
2014-08-24 20:18 ` Thorsten Jolitz
2014-08-24 20:51   ` Thorsten Jolitz
2014-08-25 16:08 ` Nick Dokos
2014-08-25 16:11   ` Nick Dokos
2014-08-25 19:20   ` Nicolas Goaziou
2014-09-13 13:23     ` Nicolas Goaziou
2014-08-27 14:50 ` AW
2014-08-27 23:33   ` Nicolas Goaziou

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

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

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