Patch to align baseline of latex fragments and surrounding text

2021-10-10 Thread Matt Huszagh
Hello,

I've created a patch to align the baseline of latex image fragments to
the surrounding text. The patch consists of several parts. First, it
adds a customizable variable that can be set to a user supplied function
to compute the value of :ascent passed to `overlay-put'. It can also be
set to a symbol (e.g., 'center, which is the current setting and still
the default), or an integer (:ascent can take an integer
percentage). The patch also modifies `org--make-preview-overlay' to use
this new variable.

The other part of the patch is a new function that computes the correct
value for :ascent from an SVG file.

Unfortunately, this isn't a general-purpose solution to baseline text
alignment. It currently only works with SVG images and requires that
these SVG images encode the text baseline in the viewBox attribute (I've
explained in the function documentation how to achieve this).

I was not initially planning to include the function because it only
works in certain special cases (though if you setup your SVG image
generation as I've described in the documentation it should work
correctly for all of your LaTeX fragments). However, I ultimately
decided it was beneficial to include it for several reasons: (1) it
would not be obvious for many users how to write this function on their
own, (2) by including it, others can improve upon it, and (3) I tried to
minimize corner cases by leaving the default value as 'center and having
the function return 'center whenever it detects an incorrect :ascent
value.

Let me know what you think!

Best
Matt

>From 6c33fa1875cc169616d878b59054d50980f741f3 Mon Sep 17 00:00:00 2001
From: Matt Huszagh 
Date: Sun, 10 Oct 2021 15:46:05 -0700
Subject: [PATCH] org--make-preview-overlay: Add ability to set vertical
 alignment of latex fragments

* lisp/org.el (org--match-text-baseline-ascent): Add function that
computes the value of :ascent to match the baseline of the fragment to
the surrounding text.
(org-latex-fragment-overlay-ascent): Add custom variable that allows a
function to be used to compute the value of :ascent.
(org--make-preview-overlay): Incorporate the custom variable.
---
 lisp/org.el | 89 +++--
 1 file changed, 80 insertions(+), 9 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 405f0f0f9..dbc288a3c 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -320,6 +320,74 @@ identifier."
   :version "24.1"
   :group 'org-id)
 
+(defun org--match-text-baseline-ascent (imagefile imagetype)
+  "Set :ascent to match the text baseline of an image to the surrounding text.
+IMAGEFILE is the path of the image file and IMAGETYPE is the
+image file type.
+
+This function currently only works for SVG images (defaulting to
+'center otherwise).  It also requires that the SVG's viewbox is
+correctly set according to the text baseline position.
+Specifically, it computes
+
+ascent = 100 * -min-y / height
+
+where min-y and height are taken from the viewbox.  If this
+computation returns a non-sensical value (below 0 or above 100),
+it will return 'center instead.
+
+It's possible to include text baseline information in the viewbox
+by using \\documentclass[preview]{standalone} in the input LaTeX
+file, compiling first to DVI and then converting this to an SVG
+image with dvisvgm.
+
+For example,
+
+(setq my-dvisvgm
+  '(my-dvisvgm :programs (\"latex\" \"dvisvgm\")
+   :description \"dvi > svg\"
+   :message \"you need to install latex and dvisvgm.\"
+   :use-xcolor t
+   :image-input-type \"dvi\"
+   :image-output-type \"svg\"
+   :latex-compiler (\"latex -output-directory=%o %f\")
+   :image-converter (\"dvisvgm --no-fonts --exact-bbox -c %S -o %O %f\")))
+
+(add-to-list 'org-preview-latex-process-alist my-dvisvgm)
+(setq org-preview-latex-default-process 'my-dvisvgm)
+
+(setq org-format-latex-header
+  \"\\documentclass[preview]{standalone}
+  [PACKAGES]
+  [DEFAULT-PACKAGES]\")"
+  (if (eq imagetype 'svg)
+  (let* ((viewbox (split-string
+   (xml-get-attribute (car (xml-parse-file imagefile)) 'viewBox)))
+ (min-y (string-to-number (nth 1 viewbox)))
+ (height (string-to-number (nth 3 viewbox)))
+ (ascent (round (* -100 (/ min-y height)
+(if (or (< ascent 0) (> ascent 100))
+'center
+  ascent))
+'center))
+
+(defcustom org-latex-fragment-overlay-ascent 'center
+  "Value of :ascent used for LaTeX image fragments.
+Org uses `overlay-put' to overlay LaTeX image fragments on inline
+math.  `overlay-put' takes an :ascent parameter that can specify
+the vertical offset of this image fragment.  See the 'Image
+Descriptors' section of the elisp manual for more information.
+
+If this variable is a symbol or integer it will be passed
+directly to :ascent.  It can also be a function that takes the
+image file as an argument.  The functi

Re: Patch to align baseline of latex fragments and surrounding text

2021-10-10 Thread Matt Huszagh
Matt Huszagh  writes:

> I've created a patch to align the baseline of latex image fragments to
> the surrounding text. The patch consists of several parts. First, it
> adds a customizable variable that can be set to a user supplied function
> to compute the value of :ascent passed to `overlay-put'. It can also be
> set to a symbol (e.g., 'center, which is the current setting and still
> the default), or an integer (:ascent can take an integer
> percentage). The patch also modifies `org--make-preview-overlay' to use
> this new variable.
>
> The other part of the patch is a new function that computes the correct
> value for :ascent from an SVG file.
>
> Unfortunately, this isn't a general-purpose solution to baseline text
> alignment. It currently only works with SVG images and requires that
> these SVG images encode the text baseline in the viewBox attribute (I've
> explained in the function documentation how to achieve this).
>
> I was not initially planning to include the function because it only
> works in certain special cases (though if you setup your SVG image
> generation as I've described in the documentation it should work
> correctly for all of your LaTeX fragments). However, I ultimately
> decided it was beneficial to include it for several reasons: (1) it
> would not be obvious for many users how to write this function on their
> own, (2) by including it, others can improve upon it, and (3) I tried to
> minimize corner cases by leaving the default value as 'center and having
> the function return 'center whenever it detects an incorrect :ascent
> value.

I should also mention that I've tested this for different image scaling
factors (`:image-size-adjust' in `org-preview-latex-process-alist') and
it works regardless of the scale factor. That is, the baseline will be
correctly aligned irrespective of the scale factor you set (though
obviously the text median
(https://en.wikipedia.org/wiki/Baseline_(typography))) will only match
the surrounding text for the correct scaling factor.

Matt



Re: Patch to align baseline of latex fragments and surrounding text

2021-12-02 Thread Matt Huszagh
Timothy, looping you in on this.

I feel that maybe it would be useful to attach screenshots to show the
improvement from this patch? Anyway, I've attached two images: one with
the correct baseline alignment to surrounding text and the other with
the current, incorrect, baseline alignment.

I think a lot of people would like this functionality. It looks much
better than the current behavior.

Anyway, feedback appreciated.



Re: Patch to align baseline of latex fragments and surrounding text

2021-12-09 Thread Sébastien Miquel

Hi,

Matt Huszagh writes:

I feel that maybe it would be useful to attach screenshots to show the
improvement from this patch? Anyway, I've attached two images: one with
the correct baseline alignment to surrounding text and the other with
the current, incorrect, baseline alignment.

I think a lot of people would like this functionality. It looks much
better than the current behavior.

This looks great indeed but I've failed to reproduce in my
environment.

I couldn't get ~org--match-text-baseline-ascent~ to compute the
ascent : the ~xml-get-attribute~ call returns
 : ("-16.945024" "12.153473" "16.148855" "8.064997")
which gives an ascent < -100, and the code then defaults to 'center.

The options described in your =my-dvisvgm= seem outdated, you can
check the latest default value of =dvisvgm= : =use-xcolor= is
deprecated and a =:image-size-adjust= property is provided for the
images to be sized properly. Are the arguments =--no-fonts= and
=--exact-bbox= necessary ?

If there are no drawbacks, perhaps this behaviour should be the
default. Otherwise, it should at least be easier to toggle.

Can something similar be done with =dvipng= ?

Regards,

--
Sébastien Miquel




Re: Patch to align baseline of latex fragments and surrounding text

2021-12-09 Thread Matt Huszagh
Sébastien Miquel  writes:

> This looks great indeed but I've failed to reproduce in my
> environment.

Thanks for testing this Sébastien.

> I couldn't get ~org--match-text-baseline-ascent~ to compute the
> ascent : the ~xml-get-attribute~ call returns
>   : ("-16.945024" "12.153473" "16.148855" "8.064997")
> which gives an ascent < -100, and the code then defaults to 'center.

I'd need to know more about your setup for generating latex
fragments. Did you follow all the directions in
org--match-text-baseline-ascent? How is your org-format-latex-header
set? In particular, are you using \documentclass[preview]{standalone}?
If you can provide me with the TeX file used to generate the fragment,
as well as the SVG file you get as a result, that would be helpful too.

> The options described in your =my-dvisvgm= seem outdated, you can
> check the latest default value of =dvisvgm= : =use-xcolor= is
> deprecated and a =:image-size-adjust= property is provided for the
> images to be sized properly. Are the arguments =--no-fonts= and
> =--exact-bbox= necessary ?

Hm, I'm not actually sure why I put use-xcolor in there, but it isn't
necessary. I've removed it in the updated patch (attached).

:image-size-adjust isn't necessary, I just mentioned it to point out
that baseline alignment works regardless of the size of a latex fragment
(I have another open PR that allows setting the size of fragments in a
context dependent way, which can be used for instance, to keep fragments
size-aligned to the surrounding text). I expect using the scale
parameter in org-format-latex-options will work similarly, but I'll
investigate.

--no-fonts is the same as -n. The --exact-bbox flag is necessary to
avoid cropping in certain cases (see
https://github.com/mgieseki/dvisvgm/issues/8). You're free to use
--bbox=min, but your glyphs may be cut off in places and this may affect
the baseline location too, though I haven't tested it.

> If there are no drawbacks, perhaps this behaviour should be the
> default. Otherwise, it should at least be easier to toggle.

I didn't attempt to make this the default because it requires a specific
setup, which is also different from the current default setup in other
respects. Most importantly, it requires using the standalone document
class, though I believe article is used at the moment.

> Can something similar be done with =dvipng= ?

Unfortunately I don't think so. This code isn't doing anything fancy; it
has no way of computing the text baseline from some arbitrary image file
displaying text. Instead, it relies on some other tool providing this
information inside the image file. In this case, standalone encodes this
information in the viewbox of the SVG file.

Thanks
Matt

>From 2640b24d2cce8e2b61101125c348db35800570ff Mon Sep 17 00:00:00 2001
From: Matt Huszagh 
Date: Sun, 10 Oct 2021 15:46:05 -0700
Subject: [PATCH] org--make-preview-overlay: Add ability to set vertical
 alignment of latex fragments

* lisp/org.el (org--match-text-baseline-ascent): Add function that
computes the value of :ascent to match the baseline of the fragment to
the surrounding text.
(org-latex-fragment-overlay-ascent): Add custom variable that allows a
function to be used to compute the value of :ascent.
(org--make-preview-overlay): Incorporate the custom variable.
---
 lisp/org.el | 88 +++--
 1 file changed, 79 insertions(+), 9 deletions(-)

diff --git a/lisp/org.el b/lisp/org.el
index 405f0f0f9..91a4d0bf5 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -320,6 +320,73 @@ identifier."
   :version "24.1"
   :group 'org-id)
 
+(defun org--match-text-baseline-ascent (imagefile imagetype)
+  "Set :ascent to match the text baseline of an image to the surrounding text.
+IMAGEFILE is the path of the image file and IMAGETYPE is the
+image file type.
+
+This function currently only works for SVG images (defaulting to
+'center otherwise).  It also requires that the SVG's viewbox is
+correctly set according to the text baseline position.
+Specifically, it computes
+
+ascent = 100 * -min-y / height
+
+where min-y and height are taken from the viewbox.  If this
+computation returns a non-sensical value (below 0 or above 100),
+it will return 'center instead.
+
+It's possible to include text baseline information in the viewbox
+by using \\documentclass[preview]{standalone} in the input LaTeX
+file, compiling first to DVI and then converting this to an SVG
+image with dvisvgm.
+
+For example,
+
+(setq my-dvisvgm
+  '(my-dvisvgm :programs (\"latex\" \"dvisvgm\")
+   :description \"dvi > svg\"
+   :message \"you need to install latex and dvisvgm.\"
+   :image-input-type \"dvi\"
+   :image-output-type \"svg\"
+   :latex-compiler (\"latex -output-directory=%o %f\")
+   :image-converter (\"dvisvgm --no-fonts --exact-bbox -c %S -o %O %f\")))
+
+(add-to-list 'org-preview-latex-process-alist my-dvisvgm)

Re: Patch to align baseline of latex fragments and surrounding text

2021-12-10 Thread Sébastien Miquel

Matt Huszagh writes:

I couldn't get ~org--match-text-baseline-ascent~ to compute the
ascent : the ~xml-get-attribute~ call returns
   : ("-16.945024" "12.153473" "16.148855" "8.064997")
which gives an ascent < -100, and the code then defaults to 'center.

I'd need to know more about your setup for generating latex
fragments. Did you follow all the directions in
org--match-text-baseline-ascent? How is your org-format-latex-header
set? In particular, are you using \documentclass[preview]{standalone}?
If you can provide me with the TeX file used to generate the fragment,
as well as the SVG file you get as a result, that would be helpful too.

My mistake. For some reason, I had thought that
=\documentclass[preview]{standalone}= was used by default for LaTeX
previews. Setting it as described in your patch, it now works
properly, even with the default value of =dvisvgm=.


If there are no drawbacks, perhaps this behaviour should be the
default. Otherwise, it should at least be easier to toggle.

I didn't attempt to make this the default because it requires a specific
setup, which is also different from the current default setup in other
respects. Most importantly, it requires using the standalone document
class, though I believe article is used at the moment.


To make this behavior easier to toggle, you could
 1. Change the default value of =org-format-latex-header=. The
    =standalone= class makes sense, but I don't know if that might
    break things.
 2. Specify the =:latex-header= of the default =dvisvgm= option. Same
    caveat applies.
 3. Add a =dvisvgm-with-ascent= option to the default value of
    =org-preview-latex-process-alist=.

Instead of the new variable =org-latex-fragment-overlay-ascent=,
perhaps the function used to compute the ascent could be provided as
another property, such as =:ascent=, added to the relevant options in
=org-preview-latex-process-alist=. It seems to make more sense since
it only applies to svg output, and it makes it easier to have this
behavior as default. It would require =org--make-preview-overlay= to
take the ascent as an additional argument.

Please note that I am not a maintainer, these are just a few thoughts.
I do hope your work can be applied and that LaTeX fragments can be
properly aligned by default.

You should add [PATCH] to the subject of your mail, so that it gets
listed at https://updates.orgmode.org/ and not forgotten. A maintainer
will reply eventually, but it might take up to a few months.

Regards,

--
Sébastien Miquel