emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Orm Finnendahl <orm.finnendahl@selma.hfmdk-frankfurt.de>
To: Ihor Radchenko <yantar92@posteo.net>
Cc: Org mailing list <emacs-orgmode@gnu.org>
Subject: Re: multipage html output
Date: Wed, 24 Jul 2024 13:24:29 +0200	[thread overview]
Message-ID: <ZqDkbeUiFjZ94QIw@orm-t14s> (raw)
In-Reply-To: <87frrzdrbz.fsf@localhost>

Hi Ihor,

 thanks a lot for the patches and explanations. Your assessment makes
sense and I agree that we should stick to the current design as much
as possible. I will go ahead and adapt my ox-html.el code the way you
propose and use your patched ox.el for this. It shouldn't take too
much effort and I will get back as soon as I have results (or
questions ;-).

--
Orm

Am Mittwoch, den 24. Juli 2024 um 10:20:16 Uhr (+0000) schrieb Ihor Radchenko:
> Orm Finnendahl <orm.finnendahl@selma.hfmdk-frankfurt.de> writes:
> 
> > To recapitulate: In my code, org-export-as calls process-multipage in
> > the backend. This function:
> >
> > - collects and adds information necessary for org-multipage to do its
> >   job, splitting the document into different parts, etc. and
> >
> > - then calls org-export-data on the subtrees and exports each returned
> >   string to an individual file.
> >
> > - It finally issues a done string and executes a browser open/visit
> >   file or simply exits nil.
> 
> Currently, org-export-as does the following:
> 
> 1. Compute global export attributes, according to the selected export backend
> 2. Copy original buffer into working copy
> 3. Process and parse the copy, generating AST
> 4. Do the actual export
> 
> You plugged your multipage processing into (4), but what it actually
> does involves (3), (4), and also a new kind of post-processing.
> I do not think that it is a good design from the point of view of ox.el.
> I prefer to reuse or extend the existing mechanisms if at all possible -
> this makes new features less confusing for users and backend developers.
> 
> > - collects and adds information necessary for org-multipage to do its
> >   job, splitting the document into different parts, etc. and
> 
> What you describe here is more or less what :filter-parse-tree filters
> do - they can rearrange the parse tree before passing it to the
> transcoders. Why not reusing it for multipage export?
> 
> > - then calls org-export-data on the subtrees and exports each returned
> >   string to an individual file.
> 
> And you simply call `org-export-transcode-page' for this, followed by
> writing the returned string to file.
> 
> The first part can fit within `org-export-as', but writing to file is
> going a step beyond, duplicating what `org-export-to-file' does.
> 
> > - It finally issues a done string and executes a browser open/visit
> >   file or simply exits nil.
> 
> ... which again steps beyond `org-export-as' scope - post-processing is
> currently done as a part of `org-export-to-file'/`org-export-to-buffer'.
> 
> ----
> 
> Let me propose the following changes to ox.el:
> 
> 1. org-data will be transcoded using `org-export-transcode-org-data',
>    which can be overridden by setting org-data transcoders in the
>    individual backends.
> 
> 2. org-export-as will understand transcoded output to be a list of
>    strings and will transfer INFO plist as text property in the return
>    values
> 
> 3. org-export-to-file will make use of the text properties to retrieve
>    the file name to write.  This way, export backend itself can assign
>    the file names where each exporter string should go.
> 
> I believe that my changes should allow you to implement multipage export
> in the following way:
> 
> 1. You can use :filter-parse-tree in ox-html backend to replace the
>    original (org-data ...) AST with a list of
>    ((org-page ...) (org-page ...) ...) pseudo-elements and populate INFO
>    channel with auxiliary information you now compute in `org-html-process-multipage'
> 
> 2. You can define org-page transcoder to render individual pages as
> needed
> 
> 3. You can assign :output-file text property to the returned org-page
>    strings and use org-export-to-file to generate the multipage output
>    on disk
> 
> 4. You can handle opening exported files by augmenting POST-PROCESS
>    argument in `org-html-export-to-multipage-html' and calling
>    `org-export-file' instead of `org-export-as'.
> 
> The tentative patches (against Org mode main branch) implementing my
> changes are attached.
> 

> From 540c8ef21c26df79cf48f58afb4e88130985e2f7 Mon Sep 17 00:00:00 2001
> Message-ID: <540c8ef21c26df79cf48f58afb4e88130985e2f7.1721815865.git.yantar92@posteo.net>
> From: Ihor Radchenko <yantar92@posteo.net>
> Date: Wed, 24 Jul 2024 11:40:57 +0200
> Subject: [PATCH 1/3] ox: Factor out org-data transcoding into dedicated
>  overrideable transcoder
> 
> * lisp/ox.el (org-export-transcode-org-data): New function serving as
> the default transcoder for org-data export.
> (org-export-transcoder): Use `org-export-transcode-org-data' when no
> org-data transcoder is defined.
> (org-export-as): Rely upon org-data transcoder to do its job.
> ---
>  lisp/ox.el | 55 +++++++++++++++++++++++++++++-------------------------
>  1 file changed, 30 insertions(+), 25 deletions(-)
> 
> diff --git a/lisp/ox.el b/lisp/ox.el
> index fbd9bb0df..bdee71082 100644
> --- a/lisp/ox.el
> +++ b/lisp/ox.el
> @@ -1883,9 +1883,11 @@ (defun org-export-transcoder (blob info)
>  INFO is a plist containing export directives."
>    (let ((type (org-element-type blob)))
>      ;; Return contents only for complete parse trees.
> -    (if (eq type 'org-data) (lambda (_datum contents _info) contents)
> -      (let ((transcoder (cdr (assq type (plist-get info :translate-alist)))))
> -	(and (functionp transcoder) transcoder)))))
> +    (let ((transcoder (cdr (assq type (plist-get info :translate-alist)))))
> +      (cond
> +       ((functionp transcoder) transcoder)
> +       ;; Use default org-data transcoder unless specified.
> +       ((eq type 'org-data) #'org-export-transcode-org-data)))))
>  
>  (defun org-export--keep-spaces (data info)
>    "Non-nil, when post-blank spaces after removing DATA should be preserved.
> @@ -3004,31 +3006,34 @@ (defun org-export-as
>                         backend info subtreep visible-only ext-plist))
>  	   ;; Eventually transcode TREE.  Wrap the resulting string into
>  	   ;; a template.
> -	   (let* ((body (org-element-normalize-string
> -		         (or (org-export-data (plist-get info :parse-tree) info)
> -                             "")))
> -		  (inner-template (cdr (assq 'inner-template
> -					     (plist-get info :translate-alist))))
> -		  (full-body (org-export-filter-apply-functions
> -			      (plist-get info :filter-body)
> -			      (if (not (functionp inner-template)) body
> -			        (funcall inner-template body info))
> -			      info))
> -		  (template (cdr (assq 'template
> -				       (plist-get info :translate-alist))))
> -                  (output
> -                   (if (or (not (functionp template)) body-only) full-body
> -	             (funcall template full-body info))))
> +	   (let ((output
> +                  (or (org-export-data (plist-get info :parse-tree) info)
> +                      "")))
>               ;; Call citation export finalizer.
>               (when (plist-get info :with-cite-processors)
>                 (setq output (org-cite-finalize-export output info)))
> -	     ;; Remove all text properties since they cannot be
> -	     ;; retrieved from an external process.  Finally call
> -	     ;; final-output filter and return result.
> -	     (org-no-properties
> -	      (org-export-filter-apply-functions
> -	       (plist-get info :filter-final-output)
> -	       output info)))))))))
> +             (let ((filters (plist-get info :filter-final-output)))
> +               ;; Remove all text properties since they cannot be
> +	       ;; retrieved from an external process.  Finally call
> +	       ;; final-output filter and return result.
> +               (org-no-properties
> +                (org-export-filter-apply-functions filters output info))))))))))
> +
> +(defun org-export-transcode-org-data (_ body info)
> +  "Transcode `org-data' node with BODY.  Return transcoded string.
> +INFO is the communication channel plist."
> +  (let* ((inner-template (cdr (assq 'inner-template
> +				    (plist-get info :translate-alist))))
> +	 (full-body (org-export-filter-apply-functions
> +		     (plist-get info :filter-body)
> +		     (if (not (functionp inner-template)) body
> +		       (funcall inner-template body info))
> +		     info))
> +	 (template (cdr (assq 'template
> +			      (plist-get info :translate-alist))))
> +         (body-only (memq 'body-only (plist-get info :export-options))))
> +    (if (or (not (functionp template)) body-only) full-body
> +      (funcall template full-body info))))
>  
>  (defun org-export--annotate-info (backend info &optional subtreep visible-only ext-plist)
>    "Annotate the INFO plist according to the BACKEND.
> -- 
> 2.45.2
> 

> From 1b0b331f92abc1ca7e04f71fe7ff60da57c719b8 Mon Sep 17 00:00:00 2001
> Message-ID: <1b0b331f92abc1ca7e04f71fe7ff60da57c719b8.1721815865.git.yantar92@posteo.net>
> In-Reply-To: <540c8ef21c26df79cf48f58afb4e88130985e2f7.1721815865.git.yantar92@posteo.net>
> References: <540c8ef21c26df79cf48f58afb4e88130985e2f7.1721815865.git.yantar92@posteo.net>
> From: Ihor Radchenko <yantar92@posteo.net>
> Date: Wed, 24 Jul 2024 11:51:21 +0200
> Subject: [PATCH 2/3] org-export-as: Allow the return value to be a list of
>  strings; add INFO
> 
> * lisp/ox.el (org-export-as): Allow the transcoders to return list of
> strings and return it.  When returning a string, put INFO plist as
> text property.  Do not remove text properties assigned by the
> transcoders.
> (org-export-data): Document that list of strings may be returned.
> ---
>  lisp/ox.el | 28 ++++++++++++++++++++--------
>  1 file changed, 20 insertions(+), 8 deletions(-)
> 
> diff --git a/lisp/ox.el b/lisp/ox.el
> index bdee71082..a76b3b353 100644
> --- a/lisp/ox.el
> +++ b/lisp/ox.el
> @@ -1930,7 +1930,7 @@ (defun org-export-data (data info)
>  
>  The `:filter-parse-tree' filters are not applied.
>  
> -Return a string."
> +Return a string or a list of strings."
>    (or (gethash data (plist-get info :exported-data))
>        ;; Handle broken links according to
>        ;; `org-export-with-broken-links'.
> @@ -2969,7 +2969,9 @@ (defun org-export-as
>  with external parameters overriding Org default settings, but
>  still inferior to file-local settings.
>  
> -Return code as a string."
> +Return code as a string or a list of strings.
> +The returned strings will have their `org-export-info' property set to
> +export information channel."
>    (when (symbolp backend) (setq backend (org-export-get-backend backend)))
>    (org-export-barf-if-invalid-backend backend)
>    (org-fold-core-ignore-modifications
> @@ -3009,15 +3011,25 @@ (defun org-export-as
>  	   (let ((output
>                    (or (org-export-data (plist-get info :parse-tree) info)
>                        "")))
> +             (setq output (ensure-list output))
>               ;; Call citation export finalizer.
>               (when (plist-get info :with-cite-processors)
> -               (setq output (org-cite-finalize-export output info)))
> +               (setq output
> +                     (mapcar
> +                      (lambda (o) (org-cite-finalize-export o info))
> +                      output)))
>               (let ((filters (plist-get info :filter-final-output)))
> -               ;; Remove all text properties since they cannot be
> -	       ;; retrieved from an external process.  Finally call
> -	       ;; final-output filter and return result.
> -               (org-no-properties
> -                (org-export-filter-apply-functions filters output info))))))))))
> +               ;; Call final-output filter and return result.
> +               (setq output
> +                     (mapcar
> +                      (lambda (o) (org-export-filter-apply-functions filters o info))
> +                      output)))
> +             ;; Apply org-export-info property.
> +             (setq output
> +                   (mapcar
> +                    (lambda (o) (org-add-props o nil 'org-export-info info))
> +                    output))
> +             (if (length= output 1) (car output) output))))))))
>  
>  (defun org-export-transcode-org-data (_ body info)
>    "Transcode `org-data' node with BODY.  Return transcoded string.
> -- 
> 2.45.2
> 

> From 6fa2efadd229a667fba1b18aecc9d1ead5f284ac Mon Sep 17 00:00:00 2001
> Message-ID: <6fa2efadd229a667fba1b18aecc9d1ead5f284ac.1721815865.git.yantar92@posteo.net>
> In-Reply-To: <540c8ef21c26df79cf48f58afb4e88130985e2f7.1721815865.git.yantar92@posteo.net>
> References: <540c8ef21c26df79cf48f58afb4e88130985e2f7.1721815865.git.yantar92@posteo.net>
> From: Ihor Radchenko <yantar92@posteo.net>
> Date: Wed, 24 Jul 2024 12:09:36 +0200
> Subject: [PATCH 3/3] org-export-to-file: Derive file name to write from export
>  output
> 
> * lisp/ox.el (org-export--write-output): New helper function
> performing writing an export output or a list of outputs to file.  It
> derives the file name from :output-file property in the output string
> or INFO plist stored in the output string.
> (org-export-to-file): Handle export output being a list of strings.
> Use `org-export--write-output'.
> ---
>  lisp/ox.el | 61 ++++++++++++++++++++++++++++++++++--------------------
>  1 file changed, 38 insertions(+), 23 deletions(-)
> 
> diff --git a/lisp/ox.el b/lisp/ox.el
> index a76b3b353..d78c04998 100644
> --- a/lisp/ox.el
> +++ b/lisp/ox.el
> @@ -6830,6 +6830,31 @@   (defun org-latex-export-as-latex
>  	(switch-to-buffer-other-window buffer))
>        buffer)))
>  
> +(defun org-export--write-output (output encoding)
> +  "Write OUTPUT to file with ENCODING.
> +OUTPUT may be a string or a list of strings.
> +The target file is retrieved from :output-file OUTPUT property or
> +:output-file property in plist stored in `org-export-info' property of
> +each string.
> +
> +Return the file name or a list of file names."
> +  (if (listp output) (mapcar #'org-export--write-output output)
> +    (let ((file (or
> +                 (get-text-property 0 :output-file output)
> +                 (plist-get
> +                  (get-text-property 0 'org-export-info output)
> +                  :output-file))))
> +      (with-temp-buffer
> +        (insert output)
> +        ;; Ensure final newline.  This is what was done
> +        ;; historically, when we used `write-file'.
> +        ;; Note that adding a newline is only safe for
> +        ;; non-binary data.
> +        (unless (bolp) (insert "\n"))
> +        (let ((coding-system-for-write encoding))
> +	  (write-region nil nil file))
> +        file))))
> +
>  ;;;###autoload
>  (defun org-export-to-file
>      (backend file &optional async subtreep visible-only body-only ext-plist
> @@ -6878,33 +6903,23 @@   (defun org-latex-export-to-latex
>  	    `(let ((output
>  		    (org-export-as
>  		     ',backend ,subtreep ,visible-only ,body-only
> -		     ',ext-plist)))
> -	       (with-temp-buffer
> -		 (insert output)
> -                 ;; Ensure final newline.  This is what was done
> -                 ;; historically, when we used `write-file'.
> -                 ;; Note that adding a newline is only safe for
> -                 ;; non-binary data.
> -                 (unless (bolp) (insert "\n"))
> -		 (let ((coding-system-for-write ',encoding))
> -		   (write-region nil nil ,file)))
> -	       (or (ignore-errors (funcall ',post-process ,file)) ,file)))
> +		     ',ext-plist))
> +                   file)
> +               (setq file (org-export--write-output output ',encoding))
> +               (let ((post (lambda (f) (or (ignore-errors (funcall ',post-process f)) f))))
> +                 (if (listp file) (mapcar post file) (funcall post file)))))
>          (let ((output (org-export-as
> -                       backend subtreep visible-only body-only ext-plist)))
> -          (with-temp-buffer
> -            (insert output)
> -            ;; Ensure final newline.  This is what was done
> -            ;; historically, when we used `write-file'.
> -            ;; Note that adding a newline is only safe for
> -            ;; non-binary data.
> -            (unless (bolp) (insert "\n"))
> -            (let ((coding-system-for-write encoding))
> -	      (write-region nil nil file)))
> +                       backend subtreep visible-only body-only ext-plist))
> +              file)
> +          (setq file (org-export--write-output output encoding))
>            (when (and (org-export--copy-to-kill-ring-p) (org-string-nw-p output))
>              (org-kill-new output))
>            ;; Get proper return value.
> -          (or (and (functionp post-process) (funcall post-process file))
> -	      file))))))
> +          (let ((post (lambda (f)
> +                        (or (and (functionp post-process)
> +                                 (funcall post-process f))
> +	                    f))))
> +            (if (listp file) (mapcar post file) (funcall post file))))))))
>  
>  (defun org-export-output-file-name (extension &optional subtreep pub-dir)
>    "Return output file's name according to buffer specifications.
> -- 
> 2.45.2
> 

> 
> -- 
> Ihor Radchenko // yantar92,
> Org mode contributor,
> Learn more about Org mode at <https://orgmode.org/>.
> Support Org development at <https://liberapay.com/org-mode>,
> or support my work at <https://liberapay.com/yantar92>



  reply	other threads:[~2024-07-24 11:25 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-03  9:44 multipage html output Orm Finnendahl
2024-07-03 10:33 ` Dr. Arne Babenhauserheide
2024-07-03 10:58 ` Christian Moe
2024-07-03 11:05   ` Ihor Radchenko
2024-07-03 14:34     ` Christian Moe
2024-07-04  9:50     ` Orm Finnendahl
2024-07-04 11:41       ` Ihor Radchenko
2024-07-04 13:33         ` Orm Finnendahl
2024-07-04 16:20           ` Ihor Radchenko
2024-07-07 19:33             ` Orm Finnendahl
2024-07-08 15:29               ` Ihor Radchenko
2024-07-08 19:12                 ` Orm Finnendahl
2024-07-09 17:55                   ` Ihor Radchenko
2024-07-10 18:03                     ` Orm Finnendahl
2024-07-10 18:53                       ` Ihor Radchenko
2024-07-07 20:50             ` Orm Finnendahl
2024-07-08 15:05               ` Ihor Radchenko
2024-07-08 15:41                 ` Orm Finnendahl
2024-07-08 15:56                   ` Ihor Radchenko
2024-07-08 19:18                     ` Orm Finnendahl
2024-07-09 18:08                       ` Ihor Radchenko
2024-07-10 19:37                         ` Orm Finnendahl
2024-07-11 12:35                           ` Ihor Radchenko
2024-07-13  7:44                             ` Orm Finnendahl
2024-07-13 10:13                               ` Ihor Radchenko
2024-07-13 11:01                                 ` Orm Finnendahl
2024-07-23  8:56                                 ` Orm Finnendahl
2024-07-23 10:24                                   ` Ihor Radchenko
2024-07-23 11:35                                     ` Orm Finnendahl
2024-07-23 12:52                                       ` Ihor Radchenko
2024-07-23 14:56                                         ` Orm Finnendahl
     [not found]                                         ` <Zp_EhDDxxYRWKFPL@orm-t14s>
     [not found]                                           ` <874j8g2lvq.fsf@localhost>
2024-07-23 15:36                                             ` Orm Finnendahl
2024-07-23 14:13                                       ` Ihor Radchenko
     [not found]                                         ` <Zp_b2lL2SzDswa-w@orm-t14s>
2024-07-23 17:10                                           ` Ihor Radchenko
2024-07-23 20:35                                             ` Orm Finnendahl
2024-07-24 10:20                                               ` Ihor Radchenko
2024-07-24 11:24                                                 ` Orm Finnendahl [this message]
2024-07-25  9:49                                                 ` Orm Finnendahl
2024-07-25  9:57                                                   ` Ihor Radchenko
2024-07-25  9:57                                                 ` Orm Finnendahl
2024-07-25 10:04                                                   ` Ihor Radchenko
2024-07-25 14:59                                                     ` Orm Finnendahl
2024-07-27 19:24                                                     ` Orm Finnendahl
2024-07-27 19:39                                                       ` Ihor Radchenko
2024-08-05 16:52                                                         ` Orm Finnendahl
2024-08-05 18:22                                                           ` Ihor Radchenko
2024-08-06  7:19                                                             ` Orm Finnendahl
2024-08-06 18:47                                                             ` Orm Finnendahl
2024-08-06 20:04                                                               ` Orm Finnendahl
2024-08-10 12:32                                                               ` Ihor Radchenko
2024-08-11 10:54                                                                 ` Orm Finnendahl
2024-08-11 13:47                                                                   ` Ihor Radchenko
2024-08-11 14:44                                                                     ` Orm Finnendahl
2024-08-12  8:35                                                                       ` Orm Finnendahl
2024-08-12 17:10                                                                         ` Ihor Radchenko
2024-08-12 18:58                                                                           ` Orm Finnendahl
2024-08-17  7:21                                                                             ` Rudolf Adamkovič
2024-08-17 14:05                                                                             ` Ihor Radchenko
2024-08-19 16:31                                                                               ` Orm Finnendahl
2024-08-22 12:27                                                                                 ` Ihor Radchenko
2024-07-26  8:22                                                 ` Orm Finnendahl
2024-07-27 13:01                                                   ` Ihor Radchenko
2024-07-27 14:25                                                     ` Orm Finnendahl
2024-07-23 14:19                                       ` Ihor Radchenko
2024-07-23 15:13                                         ` Orm Finnendahl
2024-07-23 16:20                                           ` Ihor Radchenko
2024-07-23 17:02                                             ` Orm Finnendahl
2024-07-23 17:13                                               ` Ihor Radchenko
2024-07-23 19:00                                                 ` Orm Finnendahl
2024-07-03 21:11 ` Rudolf Adamkovič
  -- strict thread matches above, loose matches on Subject: below --
2024-07-06  5:47 Pedro Andres Aranda Gutierrez
2024-07-06  9:04 ` Orm Finnendahl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZqDkbeUiFjZ94QIw@orm-t14s \
    --to=orm.finnendahl@selma.hfmdk-frankfurt.de \
    --cc=emacs-orgmode@gnu.org \
    --cc=yantar92@posteo.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).