mrpiggi / svg

Handling SVG pictures in LaTeX documents using Inkscape, ImageMagick and/or Ghostscript
Other
65 stars 11 forks source link

includesvg incompatible with setkeys{Gin} #60

Closed ChrisJefferson closed 7 months ago

ChrisJefferson commented 7 months ago

The following latex file looks weirdly corrupted. The problem is the use of Gin I think?


\documentclass{article}
\usepackage{svg}

\usepackage{graphicx}
\setkeys{Gin}{height=3cm,keepaspectratio}

\begin{document}

\includesvg{bubbleup.svg}

\end{document}
ChrisJefferson commented 7 months ago

bubbleup

My svg

mrpiggi commented 7 months ago

The problem is the use of Gin I think?

Exactly. This is not part of the interface. I could implement it once I will find some time but this won't happen in the near future. And honestly, I am not keen to do this as this potentially increases the need for maintenance without any real benefit as there is a working interface.

Using global settings for graphics dimensions seems odd to me anyway. Nevertheless, you could achieve this by using \svgsetup accordingly (although for the particular image, height=3cm is way to small):

\documentclass{article}
\usepackage{svg}

\begin{document}

\svgsetup{height=3cm,keepaspectratio}
\includesvg{bubbleup}

\end{document}
ChrisJefferson commented 7 months ago

Hi, thank you for your quick reply.

If you don't mind, I'll explain the background of this problem a bit more.

I am using pandoc, a common program for transforming between various file formats. I was getting some broken output.

Looking at pandoc, it uses the following latex template ( https://github.com/jgm/pandoc/blob/main/data/templates/default.latex ) , which contains the following (it's quite long, and the $ are handled before the code is given to latex). This template is used for all files generated, so I guess this Gin business is to handle all types of images.

$if(graphics)$
\usepackage{graphicx}
\makeatletter
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
\makeatother
% Scale images if necessary, so that they will not overflow the page
% margins by default, and it is still possible to overwrite the defaults
% using explicit options in \includegraphics[width, height, ...]{}
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
% Set default figure placement to htbp
\makeatletter
\def\fps@figure{htbp}
\makeatother
$endif$
$if(svg)$
\usepackage{svg}
$endif$

I realise it is not your job to fix this, but is there a suggested fix I can give to pandoc?

mrpiggi commented 7 months ago

I realise it is not your job to fix this, but is there a suggested fix I can give to pandoc?

I already saw the linked issue and had a brief look. That's why showed the way you could achieve the desired behavior with \svgsetup. So, regarding to the given snippet of the pandoc setup, it would just be \svgsetup{width=\maxwidth,height=\maxheight,keepaspectratio}.

An additional note: the conditional $if(svg)$ should probably be nested into the previous one as package svg loads graphicx anyway and with the current setup, the latter could be loaded without the desired setup.

ChrisJefferson commented 7 months ago

Thanks!

Unfortunately, that doesn't seem to work (everything just gets rendered on top of each other). I guess this might be because the \maxwidth and \maxheight are themselves full of references to \Gin? This is far beyond my latex ability!

mrpiggi commented 7 months ago

The pandoc setup should IMHO look like this

$if(graphics)$
\usepackage{graphicx}
\makeatletter
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
\makeatother
% Scale images if necessary, so that they will not overflow the page
% margins by default, and it is still possible to overwrite the defaults
% using explicit options in \includegraphics[width, height, ...]{}
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
% Set default figure placement to htbp
\makeatletter
\def\fps@figure{htbp}
\makeatother
$if(svg)$
\usepackage{svg}
\svgsetup{width=\maxwidth,height=\maxheight,keepaspectratio}
$endif$
$endif$

whereas it needs to be ensured, that $if(graphics)$ is set in case $if(svg)$ is required or the very first conditional would be something like $if(graphics) or (svg)$ (not quite sure if this is valid for pandoc). This change would have to be applied to the pandoc setup.

The problem here is, that pandoc relies on the internals of package graphicx/graphics by using \Gin@nat@width and \Gin@nat@height which are set, when \includegraphics is invoked. But package svg needs no know the desired size of the file to be included beforehand, which is why this approach fails as well.

So, there is currently no short-term workaround I can think of besides just explicitly specifying the desired size of the image via \includesvg[width=..., height=...]{<svg-file>}.

ChrisJefferson commented 7 months ago

Thank you very much for looking into this, we will see how pandoc wants to clean things up!

jgm commented 7 months ago

it needs to be ensured, that $if(graphics)$ is set in case $if(svg)$ is required

That is already ensured.

Question: why can't the svg package use the SVG size information encoded in the SVG's attributes

<svg width="597.89pt" height="1075pt" version="1.1" viewBox="0 0 597.89 1075.2" xmlns="http://www.w3.org/2000/svg">

when no [width=_,height=_] option is provided?

mrpiggi commented 7 months ago

Package svg is nothing but a wrapper for the (latex) export functionality provided by Inkscape. It does not touch the SVG file itself.

mrpiggi commented 7 months ago

Just to be clear, the native image size is used by default. It only fails with pandoc because of the provided settings via \setkeys{Gin}

jgm commented 7 months ago

OK, I see. The Gin business is supposed to scale images that extend beyond textwidth to textwidth, but somehow with the svg it scales it down to tiny.

I don't think removing the Gin stuff is a good option for us; it was added for a reason. Is there any other way to ensure that images that would overflow textwidth are resized appropriately?

mrpiggi commented 7 months ago

OK, I see. The Gin business is supposed to scale images that extend beyond textwidth to textwidth, but somehow with the svg it scales it down to tiny.

That's because \Gin@nat@width and \Gin@nat@height are set during the invocation of \includegraphics internally but package svg needs to know the desired output size beforehand. This relates to the format of *.pdf_tex files exported via Inkscape. In particular, those files are using lines like \includegraphics[width=\unitlength,...]{...} and in order to provide capability for scaling the resulting output, package svg has to adapt \unitlength accordingly.

I don't think removing the Gin stuff is a good option for us; it was added for a reason. Is there any other way to ensure that images that would overflow textwidth are resized appropriately?

Well, a similar but different approach would be, not to modify the default settings of package graphicx but to scale the output provided by both \includegraphics and \includesvg in a generic way. The sample provided below defines a command \pandocbounded{...} which can be used as a generic wrapper for any output content which should not extend both \textheight and \linewidth by:

  1. Store the desired output in a box
  2. Determine horizontal and vertical scaling factors regarding \textheight and \linewidth by evaluating the box size
  3. Choose the smaller factor
  4. Scale the content of the box if factor is smaller than 1, otherwise use the content as it is
\documentclass{article}
\usepackage{graphicx}
\usepackage{svg}

\makeatletter
\newsavebox\pandoc@box
\newcommand*\pandocbounded[1]{%
  \sbox\pandoc@box{#1}%
  % scaling factors for width and height
  \Gscale@div\@tempa\textheight{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}%
  \Gscale@div\@tempb\linewidth{\wd\pandoc@box}%
  % select the smaller of both
  \ifdim\@tempb\p@<\@tempa\p@
    \let\@tempa\@tempb
  \fi
  % scaling accordingly (\@tempa < 1)
  \ifdim\@tempa\p@<\p@
    \scalebox{\@tempa}{\usebox\pandoc@box}%
  % scaling not needed, use as it is
  \else
    \usebox{\pandoc@box}%
  \fi
}
\makeatother

\begin{document}
\pandocbounded{\includegraphics{<graphic-file>}}
\pandocbounded{\includesvg{<svg-file>}}
\end{document}

I am not familiar how pandoc is actually generating LaTeX source files. But \pandocbounded{...} should obviously only be used, if no setting regarding the graphic size is given. But this is something, you are probably aware of.

Don't hesitate to ask for more, if needed ;)

jgm commented 7 months ago

Thank you for this suggestion! Pandoc tries to emit fairly vanilla looking LaTeX, since it can also be used in "fragment" mode (where it doesn't produce a preamble). And I'd also prefer to avoid a change that breaks existing templates. So, I'm exploring an approach that uses the approach you sketched but redefines \includegraphics and \includesvg so they do this automatically. It is working, so far.

One question: why should \pandocbounded not be used if a setting for the graphic size is given? It seems to work fine in that case, in my tests. (Obviously, it prevents you from setting the size to greater than the text width...but I think that's okay.)

All that said, this approach is quite ugly compared to the Gin approach (in the sense that it involves a big chunk of code in the preamble). So, another option is for us just to tell people that if they run into this sort of problem they should specify an explicit size for the SVG (which is probably a good idea anyway).

mrpiggi commented 7 months ago

Pandoc tries to emit fairly vanilla looking LaTeX, since it can also be used in "fragment" mode (where it doesn't produce a preamble). And I'd also prefer to avoid a change that breaks existing templates. So, I'm exploring an approach that uses the approach you sketched but redefines \includegraphics and \includesvg so they do this automatically. It is working, so far.

IMHO, if you do patch \includegraphics and \includesvg (which is tedious, error-prone and can potentially lead to high maintenance costs) and the behavior changes whether pandoc is in charge, that's no vanilla LaTeX anymore but rather obfuscated code. With your current setup via \setkeys{Gin}{...} the output of \includegraphics[width=5cm,height=2cm]{example-image-A} in "fragment" mode is already different as keepaspectration=false is the default for graphicx.

This said, I am quite familiar with the burden of improving user interface or default settings but not to break anything out there.

One question: why should \pandocbounded not be used if a setting for the graphic size is given? It seems to work fine in that case, in my tests. (Obviously, it prevents you from setting the size to greater than the text width...but I think that's okay.)

That's exactly the use case I am talking about. If a user requests a width larger than 100%, for whatever reason, why should pandoc overrule this. In my eyes, scaling down images occasionally rather than giving a hint that user action is recommended, actually should have been opt-in in the first place. But once something is public...

All that said, this approach is quite ugly compared to the Gin approach (in the sense that it involves a big chunk of code in the preamble).

Well, if you measure the ugliness of code directly proportional to lines of code written then this is more beautiful:

\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}

in comparison to this:

\newsavebox\pandoc@box
\newcommand*\pandocbounded[1]{%
  \sbox\pandoc@box{#1}%
  \Gscale@div\@tempa{\textheight}{\dimexpr\ht\pandoc@box+\dp\pandoc@box\relax}%
  \Gscale@div\@tempb{\linewidth}{\wd\pandoc@box}%
  \ifdim\@tempb\p@<\@tempa\p@\let\@tempa\@tempb\fi% select the smaller of both
  \ifdim\@tempa\p@<\p@\scalebox{\@tempa}{\usebox\pandoc@box}\else\usebox{\pandoc@box}\fi
}

But considering your initial approach relies on both internals as well as implementation details and is obviously not working in any case, this should maybe get a second view. It actually only works because \Gin@nat@width and \Gin@nat@height are calculated by graphicx before parameters are evaluated. But if this is changed or those two internals are removed, your setup will break. As the development of l3backend-graphics is ongoing (hopefully with native support of SVG file at some point) it is not unlikely, that package graphicx is deprecated and/or \includegraphics is re-implemented.

So, another option is for us just to tell people that if they run into this sort of problem they should specify an explicit size for the SVG (which is probably a good idea anyway).

IMHO, the best solution would be to use the approach I came up with, extended by an user message to specify an image size, if down-scaling was necessary, ensuring, this does not happen unnoticed. Regarding the "fragment" mode, if dropping \pandocbounded is not that easy for pandoc in the first place, maybe just reprocessing the generated output and retaining just the content of \pandocbounded's argument could work?

If this would be tedious (e.g. because dealing nested braces in regex expressions is annoying), you could make your live easier with some TeX magic like:

\newcommand*\pandocbounded{}
\def\pandocbounded#1\pandocboundedstop{%
...
}

and then use it like this

\pandocbounded\includegraphics{<graphic-file>}\pandocboundedstop
\pandocbounded
\includesvg{<svg-file>}
\pandocboundedstop

which should be very easy to remove in a second stage in "fragmet" mode.

Hopefully, you don't get me wrong, I am really amazed about the capabilities of pandoc and really appreciate your efforts. I learned LaTeX the hard way multiple times and I just want to prevent you from making the same mistakes again I already made.

jgm commented 7 months ago

Thanks for these comments. I think you're probably right that this is the best approach, but breaking changes like this are always messy. I will ponder it.

I don't want "fragment" mode to give different output than "standalone" mode. But maybe it's not so bad if the fragment mode output includes \pandocbounded. We already use a command defined in the preamble to modify spacing in "tight" lists, and this would be similar to that.

mrpiggi commented 7 months ago

Fixed with https://github.com/jgm/pandoc/pull/9666