mgieseki / dvisvgm

A fast DVI, EPS, and PDF to SVG converter
https://dvisvgm.de
GNU General Public License v3.0
295 stars 28 forks source link

Support currentColor foreground #214

Closed tecosaur closed 1 year ago

tecosaur commented 1 year ago

SVGs support the wonderful color value currentColor. This is particularly useful in some situations, such as the LaTeX previews via dvisvgm functionality I am currently working on, where I want the images to dynamically adapt to theme changes.

https://user-images.githubusercontent.com/20903656/210933495-2ed1139e-896e-4a96-8861-ed0f3ff1f16e.mp4

For this use case, having a transparent background and currentColor foreground is ideal.

Currently, I achieve this result by taking the result of dvisvgm, finding the first color in the SVG file, and replacing all instances of that color with currentColor. This is an imperfect solution though (with issues like #212 complicating the process), and I imagine this isn't the only use case that would benefit from currentColor support.

With this in mind, it would be rather nice if dvisvgm supported a --currentcolor flag (or similar) to get an SVG where currentColor is used for the foreground OOTB.

mgieseki commented 1 year ago

Could you elaborate a bit on what you consider the foreground color? When converting a simple DVI file that doesn't change colors, the SVG doesn't contain any color attributes either. The fill color defaults to black then and there's no stroke color set, i.e. outlines are not drawn. In this case it's easy to add a fill="currentColor" to the g element enclosing the page content, for example.

However, if there are color values present in the SVG, these colors were explicitly set in the input file. A fill attribute in the enclosing group will only affect the elements with no direct or inherited color attributes. Is this sufficient? What about the explicitly colored elements? Are they supposed to keep their fill and/or stroke colors or do you want them all to be replaced so that multicolored graphics get monochrome?

At the moment, I'm not sure about the intended semantics of --currentcolor and how consistently they could be applied to arbitrary input files.

tecosaur commented 1 year ago

By the "foreground color" I mean the dominant color used for text, stroke, etc.

When using the result of blocks like this:

% Using preview.sty
\begin{preview} % p1
  \color{XYZ}
  ...content...
\end{preview}

\begin{preview} % p2
  ...more content...
\end{preview}

Looking at the generated SVGs, the first <g> element seems to have the fill set to the result of \color{XYZ} in all the cases I've seen, and so I'm currently using that as a heuristic for finding the foreground color. Then, I look for all instances of that color in the file and replace it with currentColor.

Here's a sample SVG from an earlier issue: org-tex-pojDTY-000000001, post-processing: org-tex-pojDTY-000000001

mgieseki commented 1 year ago

Hm, these are quite specific prerequisites. I can't see a reliable way to determine the "dominant color" because the DVI file can contain several subsequent and nested color specials that might affect none or different parts of the document. Only in case of simple input files it's easy to detect it. I guess it would be better to introduce a dedicated special to replace the currently active color with currentColor, e.g.

\special{dvisvgm:currentcolor}
tecosaur commented 1 year ago

That approach sounds potentially promising. I'm imagining that when \special{dvisvgm:currentcolor} is seen the current foreground color will be recorded and future instances of that colour will be replaced with currentColor, is this what you are thinking?

mgieseki commented 1 year ago

Yes, \special{dvisvgm:currentcolor} will simply replace the current active color by currentColor without affecting the color stack. As long as no further color special or PS/PDF color operator is processed, all fill and/or stroke attributes get value currentColor. I need to think about this in more detail, but I'm confident it will work.

tecosaur commented 1 year ago

It would be good to hear more on this being implemented :wink:. That sounds like it should work well to me, I've just got two questions:

Would the design you're thinking of work if hypothetically the colour was changed and a second \special{dvisvgm:currentcolor} was emitted?

If no colour is set (i.e. it's the default black), could just the --currentcolor flag be enough, or would \special{dvisvgm:currentcolor} be needed too?

mgieseki commented 1 year ago

I'm still playing around with several semantics of the special statement to find out the most plausible variant. Unfortunately, it's more complicated than I first thought. currentColor can't be a dedicated color value for several reasons but must be a global state that overrides the current color as long as it's active. \special{dvisvgm:currentcolor} would activate it and any following regular color change could deactivate it again. Of course, this mechanism may be used as often as necessary and at different locations of the TeX file. Maybe it's also useful to have something like \special{dvisvgm:currentcolor lock} and \special{dvisvgm:currentcolor unlock} to keep currentColor active until it's explicitly released, regardless any intermediate color changes.

I don't think option --currentcolor would be a really useful addition, as its functionality is too limited and specialized compared to the various sources of color changes and their nesting that would interfere with it.

Anyway, I'm currently preparing to move to a different city alongside my job and therefore don't have much time to work on dvisvgm. So please don't expect this to be implemented in the next few weeks.

mgieseki commented 1 year ago

I finally found some time to add a first implementation which is also part of the latest release 3.1.

tecosaur commented 6 months ago

It's been a while, but I just wanted to come back and say this has been working brilliantly. Thanks for adding this functionality!

mgieseki commented 6 months ago

That's great to hear. Thank you for the feedback!

karthink commented 6 months ago

Thank you for this! We're seeing significant improvements in Emacs' preview rendering experience, since we don't have to reopen each svg and replace colors manually.

|----------------------+---------------+---------------+-------------|
| Document with...     | dvisvgm 3.0.3 | dvisvgm 3.1.2 | Improvement |
|----------------------+---------------+---------------+-------------|
| 503 LaTeX fragments  |               |               |             |
| Rendering time (s)   | 1.67  ± 0.22  | 1.047 ± 0.20  |         37% |
|----------------------+---------------+---------------+-------------|
| 1200 LaTeX fragments |               |               |             |
| Rendering time (s)   | 4.60  ± 0.3   | 3.60   ± 0.1  |         22% |
|----------------------+---------------+---------------+-------------|