Greetings again.

Ihor Radchenko <yanta...@posteo.net> writes:

>>> Should be 9.8. We do not make breaking changes in bugfix releases.
>>> See https://orgmode.org/worg/org-maintenance.html#branches
>>
>> Changed it to 9.8, although I think we will have no breaking changes
>> after all your latest improvement ideas have been incorporated.
>
> We also avoid non-trivial changes on bugfix. (See the link.)

Yes chef.

>>> I just checked FSF records, and it does not look like you have an
>>> active copyright assignment. Could you please check?
>>
>> I have signed one a long time ago. I will check the situation at some
>> point.
>
> Note that I will need to confirm the assignment before I can commit
> your patch.

My records contain my assignment signed by an FSF person John Sullivan,
dated 2012-10-12. Should I email a copy of the document to you? The
document also contains what looks like an id number starting with "RT:",
but I do not know whether I am allowed to share it.

>> Not sure what happened there, weird end result of copy-pasting, amend
>> and the like. Fixed in new version.
>
> Thanks, but it does not yet follow all the commit message conventions.
> Please, (1) fill the commit message lines; (2) Use `quote', not `quote`;
> (3) use double space between sentences.

Fixed.

>>>> +(defun ob-ditaa--ensure-jar-file (file)
>>>> +  "Small internal helper function returning file if it exists and 
>>>> signalling error otherwise."
>>>
>>> Please shorten the first line of the docstring below 60 characters.
>>> See D.6 Tips for Documentation Strings section of Eliso manual.
>>
>> Done.
>
> Not like that. The first line should be a complete sentence.
>
>    • Format the documentation string so that it fits in an Emacs window
>      on an 80-column screen.  It is a good idea for most lines to be no
>      wider than 60 characters.  The first line should not be wider than
>      74 characters, or it will look bad in the output of ‘apropos’.
>
>    • The first line of the documentation string should consist of one or
>      two complete sentences that stand on their own as a summary.  ‘M-x
>      apropos’ displays just the first line, and if that line's contents
>      don't stand on their own, the result looks bad.  In particular,
>      start the first line with a capital letter and end it with a
>      period.

I was slacking because this is an internal function. Hopefully better
now.

>> * lisp/ob-ditaa.el (org-babel-default-header-args:ditaa) Include new
>> header arg `:encoding`. Add `graphics` to default value of
>> `:results`. Add `png` as default value of header arg `:file-ext`.
>
> There is no need to quote Org mode's keywords. Just Elisp symbols.

Fixed.

>> (org-ditaa-ensure-jar-file): Write a small helper function checking
>> existence of jar file.
>
> *ob-ditaa--ensure-jar-file

Fixed.

>> Character encoding of ditaa source code blocks was passed via Java,
>> while the encoding can be specified directly as a parameter to
>> ditaa. Character encoding is now passed directly to ditaa, with a
>> corresponding header argument `:encoding`.
>
> I am wondering if it makes sense to pass arbitrary encoding
> here. AFAIU, the encoding specifies input encoding and that's what Org
> mode controls, not user. In fact, we can even explicitly specific
> utf-8 in (with-temp-file in-file (insert body)) by let-binding
> `coding-system-for-write' around. I suspect that users will never use
> :encoding parameter and if they do, they will run into problems as
> they can't easily control the encoding ob-ditaa uses to write the
> file. (Is it even useful to have custom encoding?)

Good question, I have never used it.

The current version specifies encoding via :java in default header args,
so it is possible for the user to change it. I wanted to provide the
same functionality, but instead of java pass encoding directly to ditaa
so that it would be available regardless of whether ditaa is run via the
ditaa executable or java and jar.

Let me know if you want this functionality to be changed.

>> +*** =ob-ditaa=: output type control, ditaa executable, SVG output, and 
>> chararacter encoding
>> +
>> +Output file type is determined as specified in Babel documentation:
>> +the suffix of =:file= is the primary determinant, and =:file-ext=
>> +secondary.  Header argument =:eps= is supported for backwards
>> +compatibility.  Default output type is still PNG.
>> +
>> +In order to use an executable instead of a JAR file, you can set
>> +=org-ditaa-default-exec-mode= to ='ditaa=.  The location of the
>> +executable can be configured via =org-ditaa-exec=.
>> +
>> +SVG output can now be generated; note, however, that this requires a
>> +ditaa version of at least 0.11.0.
>> +
>> +Character encoding is passed directly to ditaa (earlier to Java) and
>> +can be controlled by new header argument =:encoding=.
>> +
>> +To align with other customizable variable names, which do not contain
>> +the word =babel=, variable =org-babel-ditaa-java-cmd= has been renamed
>> +to =org-ditaa-java-exec=.  The old variable =org-babel-ditaa-java-cmd=
>> +is still available as an obsolete alias.
>
> This needs to work. In particular, you need to split the announcement
> into new features/new options/removed or renamed functions and
> variable - headings under "Version 9.8 (not released yet)" in
> ORG-NEWS.

Done.

>> +(defcustom org-ditaa-default-exec-mode 'jar
>> +  "Method to use for ditaa diagram generation when generating png or svg 
>> output.
>> +`jar' means to use java together with a JAR.
>> +The JAR must be set via `org-ditaa-jar-path'.
>> +
>> +`ditaa' means to use the ditaa executable.
>> +The executable can be configured via `org-ditaa-exec'."
>> +
>> +  :group 'org-babel
>> +  :package-version '(Org . "9.8")
>> +  :type 'symbol
>> +  :options '(ditaa jar))
>
> :options is not what you meant here. It is about widgets, not about
> variable values. You need custom :type '(choice ...). For example, see
> `org-num-face' (there are many examples in Org code).

Done (I hope). A boolean would have been sufficient here, but perhaps
specifying the choice like this is clearer.

>> +  (let* ((out-file (org-babel-graphical-output-file params))
>> +         (out-file-suffix (file-name-extension out-file))
>> +         (svg (string= out-file-suffix "svg"))
>> +         (pdf (string= out-file-suffix "pdf"))
>> +         (png (string= out-file-suffix "png"))
>
> Note that :pdf parameter also used to be there.

Correct, fixed.

>> +         (eps (or (string= out-file-suffix "eps")
>> +                  ;; backwards-compatibility of :eps header argument
>> +                  (and (not (or svg pdf png))
>> +                       (cdr (assq :eps params))))))
>
> This is not exactly backwards-compatible I think. :eps used to take
> precedence over file name. That said, looking at the code, it does not
> look like :eps worked reliably. Looks like it had to be used together
> with :pdf. But maybe a miss something.

The way it used to work looks weird to me as well and I don't want to
start figuring it out.

So, as you requested, :eps and :pdf take precedence in the new version
of the patch below. An error is signalled if both :eps and :pdf are
set. Even if it might have worked before, I don't think it should be
supported.

All the best,

Jarmo

>From cf2dbd5a9f26f753bc83a92536f7ebf2632a7cbf Mon Sep 17 00:00:00 2001
From: Jarmo Hurri <jarmo.hu...@iki.fi>
Date: Mon, 4 Aug 2025 16:08:02 +0300
Subject: [PATCH] ob-ditaa.el: output type control, ditaa executable, SVG
 output, and chararacter encoding

* lisp/ob-ditaa.el (org-babel-default-header-args:ditaa) Include new
header arg :encoding.  Add graphics to default value of :results.  Add
png as default value of header arg :file-ext.
(org-ditaa-default-exec-mode): Define new customizable variable for
controlling ditaa execution via jar or executable.
(org-ditaa-exec): Define new customizable variable for controlling
path to ditaa executable.
(org-ditaa-java-exec): Rename old customizable variable to conform to
ditaa variable naming (not containing word babel).  Provide an
obsolete alias for backwards-compatibility.
(org-ditaa--ensure-jar-file): Write a small helper function checking
existence of jar file.
(org-babel-execute:ditaa): Use standard
`org-babel-graphical-output-file' to determine output file and type.
Add support for ditaa executable.  Add support for SVG output.
Clarify code structure and local variable naming.  Provide
backwards-compatibility for :eps and :pdf header arguments.
* doc/org-manual.org (List of contributors): Remove reference to
non-existing location of ditaa.jar in org contrib, refer to ditaa
github page instead.
* etc/ORG-NEWS: Document new features, new options and renamed
variable.

There was a mismatch between what ob-ditaa expected and what some
operating systems provide.  In particular, ob-ditaa expected a JAR
executable via java -jar, while some operating systems provide a shell
script which executes the JAR in a more complicated manner.  Therefore
support for executing ditaa source code blocks directly via an
executable was added.

Newer versions of ditaa can generate SVG output, which was not
supported by ob-ditaa.  This is now fixed. Output type is deduced
automatically, and defaults to PNG.  Legacy output-type controlling
header arguments :eps and :pdf are still there for
backwards-compatibility, and override output type determination from
file type.

Character encoding of ditaa source code blocks was passed via Java,
while the encoding can be specified directly as a parameter to ditaa.
Character encoding is now passed directly to ditaa, with a
corresponding header argument :encoding.
---
 doc/org-manual.org |   3 +-
 etc/ORG-NEWS       |  26 +++++++
 lisp/ob-ditaa.el   | 177 +++++++++++++++++++++++++++++++--------------
 3 files changed, 152 insertions(+), 54 deletions(-)

diff --git a/doc/org-manual.org b/doc/org-manual.org
index e4be10a3b..0af834e04 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -23275,7 +23275,8 @@ community and the code.
   literal examples, and remote highlighting for referenced code lines.
 
 - Stathis Sideris wrote the =ditaa.jar= ASCII to PNG converter that is
-  now packaged into the [[https://git.sr.ht/~bzg/org-contrib][org-contrib]] repository.
+  available as a package in some operating systems or can be
+  downloaded from [[https://github.com/stathissideris/ditaa]].
 
 - Daniel Sinder came up with the idea of internal archiving by locking
   subtrees.
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 08d2bd938..5d25a05be 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -224,6 +224,15 @@ appropriate major mode is unavailable.
 
 When editing Dot source blocks, Org now uses Graphviz Dot mode, if installed.
 
+*** =ob-ditaa=: ditaa executable, and SVG output
+
+In order to use a ditaa executable instead of a JAR file, you can set
+=org-ditaa-default-exec-mode= to ='ditaa=.  The location of the
+executable can be configured via =org-ditaa-exec=.
+
+SVG output can now be generated; note, however, that this requires a
+ditaa version of at least 0.11.0.
+
 ** New and changed options
 
 # Changes dealing with changing default values of customizations,
@@ -391,6 +400,16 @@ symbol ~attach~ or a string -- directory name.  Now it can also be a
 function, which will be called with no arguments and its return value
 will be used as a directory to save the image to.
 
+*** =ob-ditaa=: output type control, and chararacter encoding
+
+Output file type is determined as specified in Babel documentation:
+the suffix of =:file= is the primary determinant, and =:file-ext=
+secondary.  Header arguments =:pdf= and =:eps= are supported for
+backwards compatibility.  Default output type is still PNG.
+
+Character encoding is passed directly to ditaa (earlier to Java) and
+can be controlled by new header argument =:encoding=.
+
 ** New functions and changes in function arguments
 
 # This also includes changes in function behavior from Elisp perspective.
@@ -525,6 +544,13 @@ capture ~:tree-type~ options]], the internal variable
 ~org-datetree-base-level~ has been removed, as well as the
 undocumented helper function ~org-datetree-insert-line~.
 
+*** =ob-ditaa=: new name for variable controlling java
+
+To align with other customizable variable names, which do not contain
+the word =babel=, variable =org-babel-ditaa-java-cmd= has been renamed
+to =org-ditaa-java-exec=.  The old variable =org-babel-ditaa-java-cmd=
+is still available as an obsolete alias.
+
 ** Miscellaneous
 *** ~org-capture~ target pointing to headline is now handled uniformly for =plain= entry type
 
diff --git a/lisp/ob-ditaa.el b/lisp/ob-ditaa.el
index 77eadd6c2..072b4c8df 100644
--- a/lisp/ob-ditaa.el
+++ b/lisp/ob-ditaa.el
@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 2009-2025 Free Software Foundation, Inc.
 
-;; Author: Eric Schulte
+;; Authors: Eric Schulte, Jarmo Hurri
 ;; Keywords: literate programming, reproducible research
 ;; URL: https://orgmode.org
 
@@ -25,15 +25,53 @@
 
 ;; Org-Babel support for evaluating ditaa source code.
 ;;
-;; This differs from most standard languages in that
+;; Source code blocks of type ditaa have some special features:
 ;;
-;; 1) there is no such thing as a "session" in ditaa
+;; - there is no such thing as a "session"
 ;;
-;; 2) we are generally only going to return results of type "file"
+;; - :export results is the default
 ;;
-;; 3) we are adding the "file" and "cmdline" header arguments
+;; - only results of type "file" are returned
+;;
+;; - there are no variables
+;;
+;; - three different variants of "ditaa" exist: a ditaa executable
+;;   (shell script), ditaa.jar Java archive and DitaaEPS.jar Java
+;;   archive; the third one is a fork generating eps output, and is
+;;   also a prerequisite for producing pdf output; ob-ditaa supports
+;;   all three of these; if ditaa.jar or DitaaEPS.jar is used, paths
+;;   to file(s) must be set; the following table summarizes which
+;;   variant is used in which case; column mode refers to
+;;   `org-ditaa-default-exec-mode'
+;;
+;;   | mode           | output   | command                                             |
+;;   |----------------+----------+-----------------------------------------------------|
+;;   | `ditaa'        | png, svg | `org-ditaa-exec'                                    |
+;;   | `jar'          | png, svg | `org-ditaa-java-exec' -jar `org-ditaa-jar-path'     |
+;;   | `ditaa', `jar' | eps      | `org-ditaa-java-exec' -jar `org-ditaa-eps-jar-path' |
+;;   | `ditaa', `jar' | pdf      | `org-ditaa-java-exec' -jar `org-ditaa-eps-jar-path' |
+;;
+;; - standard header argument "cmdline" controls command line parameters passed to ditaa
+;; - the following header arguments are added:
+;;   "encoding" : character encoding (default UTF-8)
+;;   "java" : additional parameters passed to java if ditaa run via a jar
 ;;
-;; 4) there are no variables (at least for now)
+
+;;; Requirements:
+
+;; at least one of the following:
+;;
+;; ditaa (executable)
+;; - packaged in some distributions
+;; - configurable via `org-ditaa-exec'
+;;
+;; ditaa.jar | when exec mode is `jar'
+;; - `org-ditaa-jar-path' must point to this jar file
+;; - see https://github.com/stathissideris/ditaa
+;;
+;; DitaaEps.jar | when generating eps or pdf output
+;; - `org-ditaa-eps-jar-path' must point to this jar file
+;; - see https://sourceforge.net/projects/ditaa-addons/files/DitaaEps/
 
 ;;; Code:
 
@@ -44,11 +82,37 @@
 (require 'org-compat)
 
 (defvar org-babel-default-header-args:ditaa
-  '((:results . "file")
+  '((:results . "file graphics")
     (:exports . "results")
-    (:java . "-Dfile.encoding=UTF-8"))
+    (:encoding . "UTF-8")
+    (:file-ext . "png"))
   "Default arguments for evaluating a ditaa source block.")
 
+(defcustom org-ditaa-default-exec-mode 'jar
+  "Method to use for ditaa diagram generation when generating png or svg output.
+`jar' means to use java together with a JAR.
+The JAR must be set via `org-ditaa-jar-path'.
+
+`ditaa' means to use the ditaa executable.
+The executable can be configured via `org-ditaa-exec'."
+
+  :group 'org-babel
+  :package-version '(Org . "9.8")
+  :type '(choice (const :tag "Use java together with a JAR file." jar)
+                 (const :tag "Use ditaa executable." ditaa)))
+
+(defcustom org-ditaa-exec "ditaa"
+  "File name of the ditaa executable."
+  :group 'org-babel
+  :package-version '(Org . "9.8")
+  :type 'string)
+
+(define-obsolete-variable-alias 'org-babel-ditaa-java-cmd 'org-ditaa-java-exec "9.8")
+(defcustom org-ditaa-java-exec "java"
+  "Java executable to use when evaluating ditaa blocks using a JAR."
+  :group 'org-babel
+  :type 'string)
+
 (defcustom org-ditaa-jar-path (expand-file-name
 			       "ditaa.jar"
 			       (file-name-as-directory
@@ -58,64 +122,71 @@
 				  (expand-file-name
 				   "../contrib"
 				   (file-name-directory (org-find-library-dir "org")))))))
-  "Path to the ditaa jar executable."
-  :group 'org-babel
-  :type 'string)
-
-(defcustom org-babel-ditaa-java-cmd "java"
-  "Java executable to use when evaluating ditaa blocks."
+  "Path to the ditaa.jar file."
   :group 'org-babel
   :type 'string)
 
 (defcustom org-ditaa-eps-jar-path
   (expand-file-name "DitaaEps.jar" (file-name-directory org-ditaa-jar-path))
-  "Path to the DitaaEps.jar executable."
+  "Path to the DitaaEps.jar executable.
+Used when generating eps or pdf output."
   :group 'org-babel
   :version "24.4"
   :package-version '(Org . "8.0")
   :type 'string)
 
-(defcustom org-ditaa-jar-option "-jar"
-  "Option for the ditaa jar file.
-Do not leave leading or trailing spaces in this string."
-  :group 'org-babel
-  :version "24.1"
-  :type 'string)
+(defun ob-ditaa--ensure-jar-file (file)
+  "Check whether file exists.
+
+Return file if it exists, signal error otherwise. Small internal helper
+function used by ob-ditaa."
+  (if (file-exists-p file)
+      file
+    (error "(ob-ditaa) Could not find jar file %s" file)))
 
 (defun org-babel-execute:ditaa (body params)
-  "Execute BODY of Ditaa code with org-babel according to PARAMS.
+  "Execute BODY of ditaa code with org-babel according to PARAMS.
 This function is called by `org-babel-execute-src-block'."
-  (let* ((out-file (or (cdr (assq :file params))
-		       (error
-			"Ditaa code block requires :file header argument")))
-	 (cmdline (cdr (assq :cmdline params)))
-	 (java (cdr (assq :java params)))
-	 (in-file (org-babel-temp-file "ditaa-"))
-	 (eps (cdr (assq :eps params)))
-	 (eps-file (when eps
-		     (org-babel-process-file-name (concat in-file ".eps"))))
-	 (pdf-cmd (when (and (or (string= (file-name-extension out-file) "pdf")
-				 (cdr (assq :pdf params))))
-		    (concat
-		     "epstopdf"
-		     " " eps-file
-		     " -o=" (org-babel-process-file-name out-file))))
-	 (cmd (concat org-babel-ditaa-java-cmd
-		      " " java " " org-ditaa-jar-option " "
-		      (shell-quote-argument
-		       (expand-file-name
-			(if eps org-ditaa-eps-jar-path org-ditaa-jar-path)))
-		      " " cmdline
-		      " " (org-babel-process-file-name in-file)
-		      " " (if pdf-cmd
-			      eps-file
-			    (org-babel-process-file-name out-file)))))
-    (unless (file-exists-p org-ditaa-jar-path)
-      (error "Could not find ditaa.jar at %s" org-ditaa-jar-path))
-    (with-temp-file in-file (insert body))
-    (shell-command cmd)
-    (when pdf-cmd (shell-command pdf-cmd))
-    nil)) ;; signal that output has already been written to file
+  (let* ((out-file (org-babel-graphical-output-file params))
+         (out-file-suffix (file-name-extension out-file))
+         ;; backwards-compatibility of :eps and :pdf header arguments
+         ;; notice that these take precedence over file type (suffix)
+         (legacy-eps (cdr (assq :eps params))) 
+         (legacy-pdf (cdr (assq :pdf params))))
+    (when (and legacy-eps legacy-pdf)
+      (error "(ob-ditaa) Both :eps and :pdf legacy output types specified."))
+    (let* ((legacy-output-type (or legacy-eps legacy-pdf))
+           (eps (or legacy-eps (string= out-file-suffix "eps")))
+           (pdf (or legacy-pdf (string= out-file-suffix "pdf")))
+           (svg (and (not legacy-output-type) (string= out-file-suffix "svg")))
+           (png (and (not legacy-output-type)
+                     (or (string= out-file-suffix "png")
+                         (not (or svg pdf eps))))) ;; default output type is png
+           (ditaa-options (cdr (assq :cmdline params)))
+           (ditaa-encoding (cdr (assq :encoding params)))
+	   (java-options (cdr (assq :java params)))
+           (use-eps-jar (or eps pdf))
+           (exec-form (if (or (equal org-ditaa-default-exec-mode 'jar) use-eps-jar)
+                          (concat org-ditaa-java-exec
+                                  (when java-options (concat " " java-options))
+                                  " " "-jar" " "
+                                  (shell-quote-argument
+                                   (ob-ditaa--ensure-jar-file (if use-eps-jar org-ditaa-eps-jar-path
+                                                                org-ditaa-jar-path))))
+                        org-ditaa-exec))
+	   (in-file (org-babel-temp-file "ditaa-"))
+           (ditaa-out-file (org-babel-process-file-name (if pdf (concat in-file ".eps") out-file)))
+	   (cmd (concat exec-form
+                        (when ditaa-options (concat " " ditaa-options))
+                        (when svg (concat " " "--svg"))
+                        (when ditaa-encoding (concat " " "-e " ditaa-encoding))
+                        " " in-file " " ditaa-out-file)))
+      (with-temp-file in-file (insert body))
+      (shell-command cmd)
+      (when pdf
+        (shell-command (concat "epstopdf" " " ditaa-out-file " "
+		               "-o=" (org-babel-process-file-name out-file))))
+      nil))) ;; signal that output has already been written to file
 
 (defun org-babel-prep-session:ditaa (_session _params)
   "Return an error because ditaa does not support sessions."
-- 
2.50.1

Reply via email to