emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: gerard.vermeulen@posteo.net
To: Emacs orgmode <emacs-orgmode@gnu.org>,
	Ihor Radchenko <yantar92@posteo.net>
Subject: [BUG]: elusive vertical white-space errors in engraved source block export
Date: Thu, 15 Feb 2024 09:18:59 +0000	[thread overview]
Message-ID: <555680ecdba79265545c2b311a7eacf2@posteo.net> (raw)

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

Hi,

attached you'll find a pdf file showing the elusive vertical white-space 
errors
using the engraved source block export backend that I demonstrated 
yesterday
evening.

I also attached the org demonstration file and the mini-library that 
makes the
demonstration file work.

You can find also everything at https://github.com/gav451/org-exp-exp
(except for the pdf).

I checked that the errors are also present in a minimal "clean" 
environment
where the only installed packages are engrave-faces, auctex, and 
pdf-tools.

I have also seen these kind of errors on Gentoo systems, but I have no 
access
to those anymore.

I have a patch which is unacceptable because it limits the font size to
"normalsize" and it requires visible draw boxes around floating 
listings.
Nevertheless, I attach it to show where I got stuck.

Regards -- Gerard

[-- Attachment #2: any-backend.org --]
[-- Type: application/octet-stream, Size: 26890 bytes --]

#+title: Switch to any Org LaTeX source block export backend
#+author: Gerard Vermeulen
#+latex-class: article
#+latex_class_options: [11pt,a4paper,english,svgnames]
#+property: header-args:emacs-lisp :tangle any-backend.el
#+options: ^:{} date:nil toc:2 timestamp:nil
#+startup: showeverything
#+begin_src latex :noweb yes :results raw
  ,#+latex_header: <<lst:latex-header>>
#+end_src

* Introduction
:PROPERTIES:
:CUSTOM_ID: sec:introduction
:END:

This document demonstrates how to switch between the four different Org LaTeX
export source code block backends ~verbatim~, ~listings~, ~minted~ and
~engraved~.  In order to explore a bug in the ~engraved~ backend, the
sub-backends ~plain-engraved~, ~fixed-engraved~, and ~boxed-engraved~ replace
the ~engraved~ backend in the entry point [[ab-do-it]].

Sub-backend ~plain-engraved~ selects Org upstream, sub-backend ~fixed-engraved~
selects a bug work-around, and sub-backend ~boxed-engraved~ selects a bug
amplification which may lead to terrible ~pdf~ output.

Execution of the entry point listing [[ab-do-it]] assumes tangling of this file to
[[./any-backend.el]] and evaluation of [[./any-backend.el]] or installation of
[[./any-backend.el]] by means of ~package-install-from-buffer~.

* Minimal setup                                                    :noexport:
:PROPERTIES:
:CUSTOM_ID: sec:minimal-setup
:END:

Listing [[lst:insert-init]] uses ~ab-org-babel-file-as-block-body~ to insert the
contents of [[~/org-emacs/init.el][~/org-emacs/init.el]] into listing [[lst:insert-init-result]].

#+caption[Insert minimal dot-emacs]:
#+caption: Insert minimal dot-emacs.
#+header: :wrap "src emacs-lisp -n :eval never :tangle no"
#+name: lst:insert-init
#+header: :var filename="~/org-emacs/init.el"
#+begin_src emacs-lisp -n :eval no-export :exports both :tangle no
  (unless (or (featurep 'any-backend)
              (require 'any-backend nil 'noerror))
    (user-error "Evaluate or install `any-backend.el' (after tangling?)"))
  (ab-org-babel-file-as-block-body filename)
#+end_src

#+caption[Minimal dot-emacs]:
#+caption: Minimal dot-emacs.
#+name: lst:insert-init-result
#+RESULTS: lst:insert-init
#+begin_src emacs-lisp -n :eval never :tangle no
  ;; Almost everybody should edit the next line.
  (push (expand-file-name "~/VCS/org-mode/lisp") load-path)
  ;; Undo calling `org-ctags-enable'.
  (defun org-ctags-disable ()
    "Undo calling `org-ctags-enable'."
    (put 'org-mode 'find-tag-default-function nil)
    (setq org-ctags-enabled-p nil)
    (setq org-open-link-functions nil))
  (with-eval-after-load 'org-ctags
    (org-ctags-disable))
  ;; `tab'-related options affect `engrave-faces'.
  (setopt tab-always-indent 'complete
          tab-width 8
          tool-bar-mode nil)
  (when (eq system-type 'darwin)
    (setopt ns-alternate-modifier nil
            ns-command-modifier 'meta
            ns-right-command-modifier 'super))
  ;; Ensure to load all relevant `Org' libraries.
  (dolist (lib '(org ob-core ob-latex org-src ox-latex))
    (require lib))
  ;; Setting `org-confirm-babel-evaluate' to `nil' is DANGEROUS.
  (setopt org-adapt-indention nil
  	org-confirm-babel-evaluate nil
  	org-latex-compiler "lualatex"
          org-latex-prefer-user-labels t
          org-latex-src-block-backend 'engraved)
  ;; Install a minimum of other packages:
  ;; `engrave-faces' is absolutely necessary.
  ;; `auctex' and `pdf-tools' are nice to have.
  ;; First `pdf-tools' install may fail. FIX: exit Emacs to restart Emacs.
  (setopt package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
                             ("gnu-devel" . "https://elpa.gnu.org/devel/")
  			   ("melpa" . "https://melpa.org/packages/")
  			   ("nongnu" . "https://elpa.nongnu.org/nongnu/"))
  	package-pinned-packages '((auctex . "gnu")
  				  (engrave-faces . "gnu-devel")
  				  (pdf-tools . "melpa")))
  (dolist (pkg '(auctex engrave-faces pdf-tools))
    (unless (package-installed-p pkg)
      (package-install pkg)))
  ;; `pdf-tools' has clickable links out of the box, contrary to `docview'.
  ;; `pdf-loader-install' ensures compilation of the `epdfinfo.c' server.
  (when (fboundp 'pdf-loader-install)
    (pdf-loader-install))
#+end_src

* LaTeX preamble with page layout and float barriers
:PROPERTIES:
:CUSTOM_ID: sec:latex-preamble
:END:

The geometry in listing [[lst:latex-header]] makes that the line length in all
listings fits within the LaTeX length ~\textwidth~.  Listing [[lst:latex-header]]
also introduces float barriers to prevent floating figures and listings from
floating outside their ~[sub-][sub-]sections~.

#+caption[LaTeX preamble with page layout and float barriers]:
#+caption: LaTeX preamble with page layout and float barriers.
#+name: lst:latex-header
#+begin_src latex -n :exports code
  % BEGIN: lst:latex-header
  \usepackage{biblatex} % LaTeX compilation fails without this package.
  % PAGE LAYOUT: https://www.overleaf.com/learn/latex/Page_size_and_margins
  \usepackage{geometry}
  \usepackage{layout}
  \geometry{
    a4paper,
    total={480pt,680pt},
    top=60pt,
    left=60pt,
  }
  % FLOAT BARRIERS:
  % https://tex.stackexchange.com/questions/118662/use-placeins-for-subsections
  % Make section an implicit float barrier:
  \usepackage[section]{placeins}
  % Make subsection an implicit float barrier:
  \makeatletter
  \AtBeginDocument{%
    \expandafter\renewcommand\expandafter\subsection\expandafter{%
      \expandafter\@fb@secFB\subsection
    }%
  }
  \makeatother
  % Make subsubsection an implicit float barrier:
  \makeatletter
  \AtBeginDocument{%
    \expandafter\renewcommand\expandafter\subsubsection\expandafter{%
      \expandafter\@fb@secFB\subsubsection
    }%
  }
  % END: lst:latex-header
#+end_src

* Noweb templates
:PROPERTIES:
:CUSTOM_ID: sec:noweb-templates
:END:

Listing [[plain-engraved]], [[fixed-engraved]], [[boxed-engraved]], [[listings]], [[minted]], and
[[verbatim]] are ~noweb~ templates for listing [[ab-do-it]] and [[ab-do-it-result]].

#+caption: Noweb ~plain-engraved~ template.
#+name: plain-engraved
#+begin_src latex -n :exports code
  % Backend (PLAIN) ENGRAVED requires no packages.
#+end_src

#+caption: Noweb ~fixed-engraved~ template.
#+name: fixed-engraved
#+begin_src latex -n :exports code
  % Backend (FIXED) ENGRAVED requires no packages.
#+end_src

#+caption: Noweb ~boxed-engraved~ template.
#+name: boxed-engraved
#+begin_src latex -n :exports code
  % Backend (BOXED) ENGRAVED requires no packages.
#+end_src

#+caption: Noweb ~listings~ template.
#+name: listings
#+begin_src latex -n :exports code
  % Source block backend LISTINGS requires two packages.
  \usepackage{listings}
  \usepackage{color}
#+end_src

#+caption: Noweb ~minted~ template.
#+name: minted
#+begin_src latex -n :exports code
  % Backend MINTED requires one package.
  \usepackage{minted}
#+end_src

#+caption: Noweb ~verbatim~ template.
#+name: verbatim
#+begin_src latex -n :exports code
  % Backend VERBATIM requires no packages.
#+end_src

* Switch backend and explore an ~engraved~ export backend bug
:PROPERTIES:
:CUSTOM_ID: sec:switch-and-explore
:END:

Listing [[ab-header]], [[ab-file-as-body]], [[ab-filter]], [[ab-org-setup]], [[ab-setup]], and
[[defun-ab-do-it]], and [[ab-footer]] tangle into [[./any-backend.el]].

#+caption: Library file header.
#+name: ab-header
#+begin_src emacs-lisp -n :eval never
  ;;; any-backend.el --- demonstrate any block backend -*- lexical-binding:t -*-

  ;; Author: Gerard Vermeulen <gerard.vermeulen@posteo.net>
  ;; Version: 0.0.1

  ;; This program is distributed in the hope that it will be useful,
  ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  ;; GNU General Public License for more details.

  ;; You should have received a copy of the GNU General Public License
  ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

  ;;; Commentary:

  ;; This file is the tangled output of `any-backend.org' tailored for
  ;; use with `any-backend.el'.  Users should go to the "Export to
  ;; LaTeX" section of `any-backend.org' and execute the `ab-do-it'
  ;; source block after evaluation or installation of `any-backend.el'.

  ;;; Code:

  (require 'ox-latex)
#+end_src

#+caption[Copy file contents to an indented source block body]:
#+caption: Copy file contents to an indented source block body.
#+name: ab-file-as-body
#+begin_src emacs-lisp -n :results silent
  (defun ab-org-babel-file-as-block-body (filename)
    "Copy FILENAME contents to an indented Org Babel source block body."
    (when (file-readable-p filename)
      (let ((delete-trailing-whitespace t)
  	  (n (if org-src-preserve-indentation
  	         0 org-edit-src-content-indentation)))
        (with-temp-buffer
          (insert-file-contents filename)
          (string-rectangle (point-min) (point-max)
  		          (make-string n ?\s))
          (delete-trailing-whitespace (point-min) (point-max))
          (buffer-substring (point-min) (point-max))))))
#+end_src

#+caption[Define ~ab-org-latex-engrave-source-block-filter~]:
#+caption: Define a source block filter function.
#+name: ab-filter
#+begin_src emacs-lisp -n :results silent
  (defun ab-org-latex-engraved-source-block-filter (data _backend _info)
    "Replace \"Code\" with \"Breakable\" in non-floating DATA environments.

  Set `org-latex-engraved-preamble' to define a Breakable (non-floating)
  environment and an unbreakable Code (floating) environment."
    (unless (string-match "^\\\\DeclareTColorBox\\[\\]{Breakable}"
                          org-latex-engraved-preamble)
      (user-error
       "`org-latex-engraved-preamble' defines no `Breakable' environment"))
    (when (eq org-latex-src-block-backend 'engraved)
      ;; Transform only blocks matching at position 0.  Therefore, do
      ;; not transform blocks that are listing environments.
      (when (string-match "\\`\\\\begin{Code}\n" data)
        (setq data (replace-match "\\begin{Breakable}\n" t 'literal data))
        (if (string-match "^\\\\end{Code}\n" data)
            (setq data (replace-match "\\end{Breakable}\n" t 'literal data))
          (error "Match `^\\\\end{Code}' failure")))))
#+end_src

#+caption[Define ~ab-org-latex-setup~.]:
#+caption: Define ~ab-org-latex-setup~.
#+name: ab-org-setup
#+begin_src emacs-lisp -n :results silent
  (defun ab-org-latex-setup (how main)
    "Set `org-latex-src-block-backend' and `org-latex-toc-command'.
  HOW selects the sub-backend and MAIN selects which Org features to use."
    (cond
     ((memq how '(minted plain-engraved fixed-engraved boxed-engraved))
      (setq-local
       org-latex-toc-command
       "\\tableofcontents\\label{toc}\n\\listoflistings\n\\newpage\n"))
     ((eq how 'listings)
      (setq-local
       org-latex-toc-command
       "\\tableofcontents\\label{toc}\n\\lstlistoflistings\n\\newpage\n"))
     ((eq how 'verbatim)
      (setq-local
       org-latex-toc-command
       "\\tableofcontents\\label{toc}\n\\listoffigures\\newpage\n"))
     (t (user-error "Correct argument `%S' for `ab-setup'" how)))
    (if (memq how '(plain-engraved fixed-engraved boxed-engraved))
        (setq-local org-latex-src-block-backend 'engraved)
      (setq-local org-latex-src-block-backend how))
    (if main (setq-local org-latex-packages-alist nil)
      (setq-local org-latex-packages-alist
                  (or (and (eq how 'minted) '(("" "minted")))
                      (and (eq how 'listings) '(("" "listings")
                                                ("" "color")))))))
#+end_src

#+caption[Define ~ab-setup~]:
#+caption: Define ~ab-setup~.
#+caption: *Line 41* depends on ~how~ being ~fixed-engraved~ or ~boxed-engraved~.
#+name: ab-setup
#+begin_src emacs-lisp -n :results silent
  (defun ab-setup (how main)
    "Set `org-latex-src-block-backend', `org-latex-toc-command',
  `org-latex-engraved-preamble', and `org-export-filter-src-block-functions'.
  HOW selects the sub-backend and MAIN selects which Org features to use."
    (ab-org-latex-setup how main)
    (if (memq how '(verbatim listings minted plain-engraved))
        (progn (setq-local org-latex-engraved-preamble
                           (default-toplevel-value 'org-latex-engraved-preamble))
               (setq-local org-export-filter-src-block-functions nil))
      (setq-local org-latex-engraved-preamble
                  (format "\\usepackage{fvextra}
  [FVEXTRA-SETUP]
  %% Make code and line numbers normalsize. Make line numbers grey.
  \\renewcommand\\theFancyVerbLine{
    \\normalsize\\color{black!40!white}\\arabic{FancyVerbLine}}
  %% Do not rely on an eventual call to `engrave-faces-latex-gen-preamble'.
  \\usepackage{xcolor}
  \\providecolor{EfD}{HTML}{f7f7f7}
  \\providecolor{EFD}{HTML}{28292e}
  %% Define a breakable Code environment to prettily wrap the fontified code.
  \\usepackage[breakable,xparse]{tcolorbox}
  \\DeclareTColorBox[]{Breakable}{o}{
    colback=EfD, colframe=EFD, colupper=EFD,
    fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
    IfNoValueTF={#1}{
      boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=1.0pt
    }{
      boxsep=2.5pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1.5pt
    },
    left=2pt, right=2pt, top=1pt, bottom=0.5pt, breakable
  }
  %% Define an unbreakable Code environment to fontify code inside floats.
  \\DeclareTColorBox[]{Code}{o}{
    colback=EfD, colframe=EFD, colupper=EFD,
    fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
    IfNoValueTF={#1}{
      boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=1.0pt
    }{
      boxsep=2.5pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1pt
    },
    left=2pt, right=2pt, top=1pt, bottom=1pt, %s
  }
  [LISTINGS-SETUP]" (or (and (eq how 'fixed-engraved) "unbreakable")
                        (and (eq how 'boxed-engraved) "breakable")
                        (user-error "(ab-setup `%S') yells BOOM!" how))))
      (setq-local org-export-filter-src-block-functions
                  '(ab-org-latex-engraved-source-block-filter)))
    org-latex-src-block-backend)
#+end_src

#+caption[Define ~ab-select-how-and-export-to-latex~]:
#+caption: Define ~ab-select-how-and-export-to-latex~.
#+name: defun-ab-do-it
#+begin_src emacs-lisp -n :exports code :results silent
  ;;;###autoload
  (defun ab-select-how-and-export-to-latex (main)
    "Export Org buffer to LaTeX file after prompting how to export.
  In case MAIN is non-nil use org-9.7pre features, or in case MAIN is nil
  use org-9.16 features, to export to LaTeX.

  The `engraved' source block export backend may lead to defects in
  compiled `pdf' for floating listings where vertical line spacing may not
  be constant.  Option `plain-engraved' selects Org upstream, option
  `fixed-engraved' selects a bug work-around, and option `boxed-engraved'
  selects a bug amplification which may lead to terrible `pdf' output.
  The other choices are `verbatim', `listings', and `minted' to select the
  other backends."
    (interactive)
    (let ((how
           (intern-soft
            (completing-read
             "How: " '(plain-engraved fixed-engraved boxed-engraved
                                      verbatim listings minted)
             nil t)))
          (seconds 1))
      (ab-setup how main)
      (run-with-timer seconds nil #'org-latex-export-to-latex)
      (if main
          (format "#+latex_header: <<%s>>" how)
        (format "%% %s" how))))
#+end_src

#+caption[Library file footer]:
#+caption: Library file footer.
#+name: ab-footer
#+begin_src emacs-lisp -n :eval never
  (provide 'any-backend)

  ;;; any-backend.el ends here
#+end_src

* Export to LaTeX
:PROPERTIES:
:CUSTOM_ID: sec:any-backend-usage
:END:

Originally, I had written this document to explore ~noweb~, but that exposed a
bug fixed since ~2024-02-05~.  Execution of the listing [[ab-do-it]] source block
uses ~noweb~ in case of setting "~:var main='t~" in this source block and
execution of the listing [[ab-do-it]] source block does not use ~noweb~ in case of
setting "~:var main='nil~".  The latter setting has the effect of exporting the
body of listing [[ab-do-it-result]].

#+caption[Call ~ab-select-how-and-export-to-latex~]:
#+caption: Call ~ab-select-how-and-export-to-latex~.
#+name: ab-do-it
#+header: :var main='nil
#+header: :wrap "src latex -n :exports both :noweb yes :results raw"
#+begin_src emacs-lisp -n :exports both :results raw :eval no-export :tangle no
  (unless (or (featurep 'any-backend)
              (require 'any-backend nil 'noerror))
    (user-error "Evaluate or install `any-backend.el' (after tangling?)"))
  (ab-select-how-and-export-to-latex main)
#+end_src

#+caption[Run ~ab-select-how-and-export-to-latex~ result]:
#+caption: Run ~ab-select-how-and-export-to-latex~ result.
#+name: ab-do-it-result
#+RESULTS: ab-do-it
#+begin_src latex -n :exports both :noweb yes :results raw
% plain-engraved
#+end_src

#+RESULTS: ab-do-it-result
#+latex_header: % Backend (PLAIN) ENGRAVED requires no packages.

* Make never "broken" non-floating listing
:PROPERTIES:
:CUSTOM_ID: sec:make-non-floating-listing
:END:

#+caption[Insert ~any-backend.el~]:
#+caption: Insert ~any-backend.el~.
#+header: :wrap "src emacs-lisp -n :eval never :tangle no"
#+name: insert-non-float
#+header: :var filename="any-backend.el"
#+begin_src emacs-lisp -n :eval no-export :exports both :tangle no
  (unless (or (featurep 'any-backend)
              (require 'any-backend nil 'noerror))
    (user-error "Evaluate or install `any-backend.el' (after tangling?)"))
  (ab-org-babel-file-as-block-body filename)
#+end_src

** List never "broken" non-floating listing
:PROPERTIES:
:CUSTOM_ID: sec:list-non-floating-listing
:END:

The next non-floating listing occupies a space of more than two pages.
#+name: list-non-float-result
#+RESULTS: insert-non-float
#+begin_src emacs-lisp -n :eval never :tangle no
  ;;; any-backend.el --- demonstrate any block backend -*- lexical-binding:t -*-

  ;; Author: Gerard Vermeulen <gerard.vermeulen@posteo.net>
  ;; Version: 0.0.1

  ;; This program is distributed in the hope that it will be useful,
  ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  ;; GNU General Public License for more details.

  ;; You should have received a copy of the GNU General Public License
  ;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

  ;;; Commentary:

  ;; This file is the tangled output of `any-backend.org' tailored for
  ;; use with `any-backend.el'.  Users should go to the "Export to
  ;; LaTeX" section of `any-backend.org' and execute the `ab-do-it'
  ;; source block after evaluation or installation of `any-backend.el'.

  ;;; Code:

  (require 'ox-latex)

  (defun ab-org-babel-file-as-block-body (filename)
    "Copy FILENAME contents to an indented Org Babel source block body."
    (when (file-readable-p filename)
      (let ((delete-trailing-whitespace t)
  	  (n (if org-src-preserve-indentation
  	         0 org-edit-src-content-indentation)))
        (with-temp-buffer
          (insert-file-contents filename)
          (string-rectangle (point-min) (point-max)
  		          (make-string n ?\s))
          (delete-trailing-whitespace (point-min) (point-max))
          (buffer-substring (point-min) (point-max))))))

  (defun ab-org-latex-engraved-source-block-filter (data _backend _info)
    "Replace \"Code\" with \"Breakable\" in non-floating DATA environments.

  Set `org-latex-engraved-preamble' to define a Breakable (non-floating)
  environment and an unbreakable Code (floating) environment."
    (unless (string-match "^\\\\DeclareTColorBox\\[\\]{Breakable}"
                          org-latex-engraved-preamble)
      (user-error
       "`org-latex-engraved-preamble' defines no `Breakable' environment"))
    (when (eq org-latex-src-block-backend 'engraved)
      ;; Transform only blocks matching at position 0.  Therefore, do
      ;; not transform blocks that are listing environments.
      (when (string-match "\\`\\\\begin{Code}\n" data)
        (setq data (replace-match "\\begin{Breakable}\n" t 'literal data))
        (if (string-match "^\\\\end{Code}\n" data)
            (setq data (replace-match "\\end{Breakable}\n" t 'literal data))
          (error "Match `^\\\\end{Code}' failure")))))

  (defun ab-org-latex-setup (how main)
    "Set `org-latex-src-block-backend' and `org-latex-toc-command'.
  HOW selects the sub-backend and MAIN selects which Org features to use."
    (cond
     ((memq how '(minted plain-engraved fixed-engraved boxed-engraved))
      (setq-local
       org-latex-toc-command
       "\\tableofcontents\\label{toc}\n\\listoflistings\n\\newpage\n"))
     ((eq how 'listings)
      (setq-local
       org-latex-toc-command
       "\\tableofcontents\\label{toc}\n\\lstlistoflistings\n\\newpage\n"))
     ((eq how 'verbatim)
      (setq-local
       org-latex-toc-command
       "\\tableofcontents\\label{toc}\n\\listoffigures\\newpage\n"))
     (t (user-error "Correct argument `%S' for `ab-setup'" how)))
    (if (memq how '(plain-engraved fixed-engraved boxed-engraved))
        (setq-local org-latex-src-block-backend 'engraved)
      (setq-local org-latex-src-block-backend how))
    (if main (setq-local org-latex-packages-alist nil)
      (setq-local org-latex-packages-alist
                  (or (and (eq how 'minted) '(("" "minted")))
                      (and (eq how 'listings) '(("" "listings")
                                                ("" "color")))))))

  (defun ab-setup (how main)
    "Set `org-latex-src-block-backend', `org-latex-toc-command',
  `org-latex-engraved-preamble', and `org-export-filter-src-block-functions'.
  HOW selects the sub-backend and MAIN selects which Org features to use."
    (ab-org-latex-setup how main)
    (if (memq how '(verbatim listings minted plain-engraved))
        (progn (setq-local org-latex-engraved-preamble
                           (default-toplevel-value 'org-latex-engraved-preamble))
               (setq-local org-export-filter-src-block-functions nil))
      (setq-local org-latex-engraved-preamble
                  (format "\\usepackage{fvextra}
  [FVEXTRA-SETUP]
  %% Make code and line numbers normalsize. Make line numbers grey.
  \\renewcommand\\theFancyVerbLine{
    \\normalsize\\color{black!40!white}\\arabic{FancyVerbLine}}
  %% Do not rely on an eventual call to `engrave-faces-latex-gen-preamble'.
  \\usepackage{xcolor}
  \\providecolor{EfD}{HTML}{f7f7f7}
  \\providecolor{EFD}{HTML}{28292e}
  %% Define a breakable Code environment to prettily wrap the fontified code.
  \\usepackage[breakable,xparse]{tcolorbox}
  \\DeclareTColorBox[]{Breakable}{o}{
    colback=EfD, colframe=EFD, colupper=EFD,
    fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
    IfNoValueTF={#1}{
      boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=1.0pt
    }{
      boxsep=2.5pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1.5pt
    },
    left=2pt, right=2pt, top=1pt, bottom=0.5pt, breakable
  }
  %% Define an unbreakable Code environment to fontify code inside floats.
  \\DeclareTColorBox[]{Code}{o}{
    colback=EfD, colframe=EFD, colupper=EFD,
    fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
    IfNoValueTF={#1}{
      boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=1.0pt
    }{
      boxsep=2.5pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1pt
    },
    left=2pt, right=2pt, top=1pt, bottom=1pt, %s
  }
  [LISTINGS-SETUP]" (or (and (eq how 'fixed-engraved) "unbreakable")
                        (and (eq how 'boxed-engraved) "breakable")
                        (user-error "(ab-setup `%S') yells BOOM!" how))))
      (setq-local org-export-filter-src-block-functions
                  '(ab-org-latex-engraved-source-block-filter)))
    org-latex-src-block-backend)

  ;;;###autoload
  (defun ab-select-how-and-export-to-latex (main)
    "Export Org buffer to LaTeX file after prompting how to export.
  In case MAIN is non-nil use org-9.7pre features, or in case MAIN is nil
  use org-9.16 features, to export to LaTeX.

  The `engraved' source block export backend may lead to defects in
  compiled `pdf' for floating listings where vertical line spacing may not
  be constant.  Option `plain-engraved' selects Org upstream, option
  `fixed-engraved' selects a bug work-around, and option `boxed-engraved'
  selects a bug amplification which may lead to terrible `pdf' output.
  The other choices are `verbatim', `listings', and `minted' to select the
  other backends."
    (interactive)
    (let ((how
           (intern-soft
            (completing-read
             "How: " '(plain-engraved fixed-engraved boxed-engraved
                                      verbatim listings minted)
             nil t)))
          (seconds 1))
      (ab-setup how main)
      (run-with-timer seconds nil #'org-latex-export-to-latex)
      (if main
          (format "#+latex_header: <<%s>>" how)
        (format "%% %s" how))))

  (provide 'any-backend)

  ;;; any-backend.el ends here
#+end_src
The previous non-floating listing occupies a space of more than two pages.

* Conclusion: summary of constraints and limitations
:PROPERTIES:
:CUSTOM_ID: sec:conclusion
:END:

1. Placement of floating listings:
   - It is impossible to have more than three floating listings on a page,
     neither with ~minted~ nor with ~engraved~.
   - Float barriers help to control the placement of floating listing.
   - Mixing floating and non-floating listings in the same ~[sub-][sub-]section~
     gives visually weird results: floating listings may float between the parts
     of non-floating listings.
2. Table of contents:
   - the ~verbatim~ backend produces a "List of Figures" instead of a "List of
     Listings".
3. Sub-backend ~plain-engraved~ moving target defects:
   - Listing [[plain-engraved]] is in a botched state.
   - Listing [[ab-filter]] shows too much vertical spacing after line 13.
   - Listing [[ab-org-setup]] shows too much vertical spacing after line 13.
   - Listing [[ab-setup]] shows too much vertical spacing after line 13, 26, and 39.
   - Listing [[defun-ab-do-it]] shows too much vertical spacing after line 13.
4. Sub-backend ~boxed-engraved~ moving target defects:
   - Listing [[ab-org-setup]] shows too much vertical spacing after line 19.
   - Listing [[ab-setup]] shows too much vertical spacing after line 19, and 38.
   - Listing [[defun-ab-do-it]] shows too much vertical spacing after line 19.
*Fixing ~engraved~ requires:*
1. ~unbreakable~ floating listings.
2. ~\normalsize~ font size.
3. visible boxes.
Points (2) and (3) are unacceptable, IMO.

# Emacs looks for "Local variables:" after the last "newline-formfeed".
\f
# Local Variables:
# compile-command: "latexmk -interaction=nonstopmode -lualatex -pvc -shell-escape any-backend.tex"
# fill-column: 80
# org-edit-src-content-indentation: 2
# End:

[-- Attachment #3: any-backend.el --]
[-- Type: application/octet-stream, Size: 6886 bytes --]

;;; any-backend.el --- demonstrate any block backend -*- lexical-binding:t -*-

;; Author: Gerard Vermeulen <gerard.vermeulen@posteo.net>
;; Version: 0.0.1

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; This file is the tangled output of `any-backend.org' tailored for
;; use with `any-backend.el'.  Users should go to the "Export to
;; LaTeX" section of `any-backend.org' and execute the `ab-do-it'
;; source block after evaluation or installation of `any-backend.el'.

;;; Code:

(require 'ox-latex)

(defun ab-org-babel-file-as-block-body (filename)
  "Copy FILENAME contents to an indented Org Babel source block body."
  (when (file-readable-p filename)
    (let ((delete-trailing-whitespace t)
	  (n (if org-src-preserve-indentation
	         0 org-edit-src-content-indentation)))
      (with-temp-buffer
        (insert-file-contents filename)
        (string-rectangle (point-min) (point-max)
		          (make-string n ?\s))
        (delete-trailing-whitespace (point-min) (point-max))
        (buffer-substring (point-min) (point-max))))))

(defun ab-org-latex-engraved-source-block-filter (data _backend _info)
  "Replace \"Code\" with \"Breakable\" in non-floating DATA environments.

Set `org-latex-engraved-preamble' to define a Breakable (non-floating)
environment and an unbreakable Code (floating) environment."
  (unless (string-match "^\\\\DeclareTColorBox\\[\\]{Breakable}"
                        org-latex-engraved-preamble)
    (user-error
     "`org-latex-engraved-preamble' defines no `Breakable' environment"))
  (when (eq org-latex-src-block-backend 'engraved)
    ;; Transform only blocks matching at position 0.  Therefore, do
    ;; not transform blocks that are listing environments.
    (when (string-match "\\`\\\\begin{Code}\n" data)
      (setq data (replace-match "\\begin{Breakable}\n" t 'literal data))
      (if (string-match "^\\\\end{Code}\n" data)
          (setq data (replace-match "\\end{Breakable}\n" t 'literal data))
        (error "Match `^\\\\end{Code}' failure")))))

(defun ab-org-latex-setup (how main)
  "Set `org-latex-src-block-backend' and `org-latex-toc-command'.
HOW selects the sub-backend and MAIN selects which Org features to use."
  (cond
   ((memq how '(minted plain-engraved fixed-engraved boxed-engraved))
    (setq-local
     org-latex-toc-command
     "\\tableofcontents\\label{toc}\n\\listoflistings\n\\newpage\n"))
   ((eq how 'listings)
    (setq-local
     org-latex-toc-command
     "\\tableofcontents\\label{toc}\n\\lstlistoflistings\n\\newpage\n"))
   ((eq how 'verbatim)
    (setq-local
     org-latex-toc-command
     "\\tableofcontents\\label{toc}\n\\listoffigures\\newpage\n"))
   (t (user-error "Correct argument `%S' for `ab-setup'" how)))
  (if (memq how '(plain-engraved fixed-engraved boxed-engraved))
      (setq-local org-latex-src-block-backend 'engraved)
    (setq-local org-latex-src-block-backend how))
  (if main (setq-local org-latex-packages-alist nil)
    (setq-local org-latex-packages-alist
                (or (and (eq how 'minted) '(("" "minted")))
                    (and (eq how 'listings) '(("" "listings")
                                              ("" "color")))))))

(defun ab-setup (how main)
  "Set `org-latex-src-block-backend', `org-latex-toc-command',
`org-latex-engraved-preamble', and `org-export-filter-src-block-functions'.
HOW selects the sub-backend and MAIN selects which Org features to use."
  (ab-org-latex-setup how main)
  (if (memq how '(verbatim listings minted plain-engraved))
      (progn (setq-local org-latex-engraved-preamble
                         (default-toplevel-value 'org-latex-engraved-preamble))
             (setq-local org-export-filter-src-block-functions nil))
    (setq-local org-latex-engraved-preamble
                (format "\\usepackage{fvextra}
[FVEXTRA-SETUP]
%% Make code and line numbers normalsize. Make line numbers grey.
\\renewcommand\\theFancyVerbLine{
  \\normalsize\\color{black!40!white}\\arabic{FancyVerbLine}}
%% Do not rely on an eventual call to `engrave-faces-latex-gen-preamble'.
\\usepackage{xcolor}
\\providecolor{EfD}{HTML}{f7f7f7}
\\providecolor{EFD}{HTML}{28292e}
%% Define a breakable Code environment to prettily wrap the fontified code.
\\usepackage[breakable,xparse]{tcolorbox}
\\DeclareTColorBox[]{Breakable}{o}{
  colback=EfD, colframe=EFD, colupper=EFD,
  fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
  IfNoValueTF={#1}{
    boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=1.0pt
  }{
    boxsep=2.5pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1.5pt
  },
  left=2pt, right=2pt, top=1pt, bottom=0.5pt, breakable
}
%% Define an unbreakable Code environment to fontify code inside floats.
\\DeclareTColorBox[]{Code}{o}{
  colback=EfD, colframe=EFD, colupper=EFD,
  fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
  IfNoValueTF={#1}{
    boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=1.0pt
  }{
    boxsep=2.5pt, arc=0pt, outer arc=0pt, boxrule=0pt, leftrule=1pt
  },
  left=2pt, right=2pt, top=1pt, bottom=1pt, %s
}
[LISTINGS-SETUP]" (or (and (eq how 'fixed-engraved) "unbreakable")
                      (and (eq how 'boxed-engraved) "breakable")
                      (user-error "(ab-setup `%S') yells BOOM!" how))))
    (setq-local org-export-filter-src-block-functions
                '(ab-org-latex-engraved-source-block-filter)))
  org-latex-src-block-backend)

;;;###autoload
(defun ab-select-how-and-export-to-latex (main)
  "Export Org buffer to LaTeX file after prompting how to export.
In case MAIN is non-nil use org-9.7pre features, or in case MAIN is nil
use org-9.16 features, to export to LaTeX.

The `engraved' source block export backend may lead to defects in
compiled `pdf' for floating listings where vertical line spacing may not
be constant.  Option `plain-engraved' selects Org upstream, option
`fixed-engraved' selects a bug work-around, and option `boxed-engraved'
selects a bug amplification which may lead to terrible `pdf' output.
The other choices are `verbatim', `listings', and `minted' to select the
other backends."
  (interactive)
  (let ((how
         (intern-soft
          (completing-read
           "How: " '(plain-engraved fixed-engraved boxed-engraved
                                    verbatim listings minted)
           nil t)))
        (seconds 1))
    (ab-setup how main)
    (run-with-timer seconds nil #'org-latex-export-to-latex)
    (if main
        (format "#+latex_header: <<%s>>" how)
      (format "%% %s" how))))

(provide 'any-backend)

;;; any-backend.el ends here

[-- Attachment #4: any-backend.pdf --]
[-- Type: application/pdf, Size: 85057 bytes --]

[-- Attachment #5: 0001-Unacceptable-engraved-backend-fix.patch --]
[-- Type: application/octet-stream, Size: 3924 bytes --]

From 211db75966232616e43af55672edd78d114172e0 Mon Sep 17 00:00:00 2001
From: Gerard Vermeulen <gerard.vermeulen@posteo.net>
Date: Wed, 14 Feb 2024 18:41:42 +0100
Subject: [PATCH] Unacceptable engraved backend fix

This fixes the unbreakable/breakable for floating/non-floating
listings, but it also requires normalsize font size and visible boxes
around the listings.  This makes the patch unacceptable for general
use.
---
 lisp/ox-latex.el | 35 ++++++++++++++++++++---------------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index cfa2b8178..6bffe7f1c 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -1190,7 +1190,8 @@ will produce
 [FVEXTRA-SETUP]
 
 % Make line numbers smaller and grey.
-\\renewcommand\\theFancyVerbLine{\\footnotesize\\color{black!40!white}\\arabic{FancyVerbLine}}
+\\renewcommand\\theFancyVerbLine{
+  \\normalsize\\color{black!40!white}\\arabic{FancyVerbLine}}
 
 \\usepackage{xcolor}
 
@@ -1200,17 +1201,13 @@ will produce
 
 % Define a Code environment to prettily wrap the fontified code.
 \\usepackage[breakable,xparse]{tcolorbox}
-\\DeclareTColorBox[]{Code}{o}%
-{colback=EfD!98!EFD, colframe=EfD!95!EFD,
-  fontupper=\\footnotesize\\setlength{\\fboxsep}{0pt},
-  colupper=EFD,
-  IfNoValueTF={#1}%
-  {boxsep=2pt, arc=2.5pt, outer arc=2.5pt,
-    boxrule=0.5pt, left=2pt}%
-  {boxsep=2.5pt, arc=0pt, outer arc=0pt,
-    boxrule=0pt, leftrule=1.5pt, left=0.5pt},
-  right=2pt, top=1pt, bottom=0.5pt,
-  breakable}
+\\DeclareTColorBox[]{Code}{s}{
+  colback=EfD, colframe=EFD, colupper=EFD,
+  fontupper=\\normalsize\\setlength{\\fboxsep}{0pt},
+  boxsep=2pt, arc=2.5pt, outer arc=2.5pt, boxrule=0.5pt,
+  IfNoValueTF={#1}{unbreakable}{breakable},
+  left=2pt, right=2pt, top=1pt, bottom=0.5pt
+}
 
 [LISTINGS-SETUP]"
   "Preamble content injected when using engrave-faces-latex for source blocks.
@@ -3530,13 +3527,18 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
       (when (eq mathescape 'yes)
         (or engrave-faces-latex-mathescape t)))))
 
-(defun org-latex-src--engrave-code (content lang &optional theme options inline)
+(defun org-latex-src--engrave-code
+    (content lang &optional theme options inline caption)
   "Engrave CONTENT to LaTeX in a LANG-mode buffer, and give the result.
 When the THEME symbol is non-nil, that theme will be used.
 
 When INLINE is nil, a Verbatim environment wrapped in a Code
 environment will be used. When t, a Verb command will be used.
 
+When CAPTION is nil, make a breakable Code environment for non-floating
+listings. Otherwise, make an unbreakable Code environment for floating
+listings.
+
 When OPTIONS is provided, as either a string or list of key-value
 pairs accepted by `org-latex--make-option-string', it is passed
 to the Verbatim environment or Verb command."
@@ -3568,7 +3570,10 @@ to the Verbatim environment or Verb command."
              (engraved-wrapped
               (if inline
                   (concat "\\Verb" engraved-options "{" engraved-code "}")
-                (concat "\\begin{Code}\n\\begin{Verbatim}" engraved-options "\n"
+                ;; IME, only caption determines float or non-float.
+                (concat (if caption "\\begin{Code}\n\\begin{Verbatim}"
+                          "\\begin{Code}*\n\\begin{Verbatim}")
+                        engraved-options "\n"
                         engraved-code "\n\\end{Verbatim}\n\\end{Code}"))))
         (kill-buffer engraved-buffer)
         (if theme
@@ -3637,7 +3642,7 @@ and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
             (org-latex-src--engrave-code
              content lang
              (when engraved-theme (intern engraved-theme))
-             options))))
+             options nil caption))))
     (concat (car float-env) body (cdr float-env))))
 
 (cl-defun org-latex-src-block--listings
-- 
2.42.0


             reply	other threads:[~2024-02-15  9:20 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-15  9:18 gerard.vermeulen [this message]
2024-02-20  8:04 ` [BUG]: elusive vertical white-space errors in engraved source block export Ihor Radchenko

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=555680ecdba79265545c2b311a7eacf2@posteo.net \
    --to=gerard.vermeulen@posteo.net \
    --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).