Color admonitions in generated PDF

259 views
Skip to first unread message

Jeff Larson

unread,
Oct 18, 2019, 3:59:22 PM10/18/19
to sphinx-users
Is it possible to set the background color of different admonitions in the PDF generated by Sphinx 3?

Using the StackOverlow answer here as a template: 
and motivates me to change the "notice" to "sphinxadmonition" changes the original:

Selection_037.png



to look like:

Selection_038.png


(Observe that "Note:" has disappeared and the "See Also: is unaffected.)

Explicitly, my conf.py currently contains:

latex_elements = {
     'preamble':
    r'''
    \definecolor{Admonition}{RGB}{221,233,239}

\makeatletter
  \newenvironment{admonitionbox}{
    \begin{lrbox}{\@tempboxa}\begin{minipage}{\columnwidth}
  }{
    \end{minipage}\end{lrbox}
    \colorbox{Admonition}{\usebox{\@tempboxa}}
  }

  \renewenvironment{sphinxadmonition}[2]{
    \begin{admonitionbox}
  }{
    \end{admonitionbox}
  }
\makeatother



Ideally, I could have different colors for different admonitions, but I understand that that may not be possible.

Thank you

jfbu

unread,
Oct 18, 2019, 6:17:42 PM10/18/19
to sphinx...@googlegroups.com
Le 18/10/2019 à 21:59, Jeff Larson a écrit :
> Is it possible to set the background color of different admonitions in the PDF generated by Sphinx 3?
>
> Using the StackOverlow answer here as a template:
> https://stackoverflow.com/questions/13530489/adding-background-color-of-notes-to-sphinx-generated-pdf-files/34766700
> and motivates me to change the "notice" to "sphinxadmonition" changes the original:
>
> Selection_037.png
>
>
>
> to look like:
>
> Selection_038.png
>
>
> (Observe that "Note:" has disappeared and the "See Also: is unaffected.)
>
> Explicitly, my conf.py currently contains:
>
> latex_elements = {
>      'preamble':
>     r'''
>     \definecolor{Admonition}{RGB}{221,233,239}
>
> \makeatletter
>   \newenvironment{admonitionbox}{
>     \begin{lrbox}{\@tempboxa}\begin{minipage}{\columnwidth}
>   }{
>     \end{minipage}\end{lrbox}
>     \colorbox{Admonition}{\usebox{\@tempboxa}}
>   }
>
>   \renewenvironment{sphinxadmonition}[2]{
>     \begin{admonitionbox}
>   }{
>     \end{admonitionbox}
>   }
> \makeatother
>
>
>
> Ideally, I could have different colors for different admonitions, but I understand that that may not be possible.
>
> Thank you

Hi, since Sphinx 1.5 one can customize each admonition type.

http://www.sphinx-doc.org/en/master/latex.html#environments

For example "note" uses this :

% may be renewenvironment'd by user for complete customization
\newenvironment{sphinxnote}[1]
{\begin{sphinxlightbox}\sphinxstrong{#1} }{\end{sphinxlightbox}}


It thus looks as if your customization could do something like this

latex_elements = {
'preamble': r"""
\newsavebox\mytempbox
\definecolor{sphinxnoteBgColor}{RGB}{221,233,239}
\renewenvironment{sphinxnote}[1]
{\begin{lrbox}{\mytempbox}\begin{minipage}{\columnwidth}%
\begin{sphinxlightbox}\sphinxstrong{#1} }
{\end{sphinxlightbox}\end{minipage}\end{lrbox}%
\colorbox{sphinxnoteBgColor}{\usebox{\mytempbox}}}
""",
}

Here we kept the "sphinxlightbox" thing whose aim is to insert
horizontal rules.

If you want to insert a background color and still allow pagebreak
then it is more appropriate to use the "sphinxheavybox". Simply
imitate the way things are set-up in sphinx.sty for warning, etc...

Notice that sphinxnoteBgColor will be applied automatically
(the heavybox uses a color named according to the admonition type)
but we need to define it this way, there is no interface in "sphinxsetup"

latex_elements = {
'preamble': r"""
\definecolor{sphinxnoteBgColor}{RGB}{221,233,239}
\renewenvironment{sphinxnote}[1]
{\begin{sphinxheavybox}\sphinxstrong{#1} }{\end{sphinxheavybox}}
""",

'sphinxsetup': 'noteborder=1.5pt',
}


I have also shown above how to modify the width of the frame using "sphinxsetup".

Unfortunately if you want only a border on top and bottom but not
on the side, this means an environment analogous to but not the
same as sphinxheavybox should be used. But this is arduous LaTeX coding.

So we can go back to your lrbox approach (dropping possibility of pagebreak)
and do something like this:

latex_elements = {
'preamble': r"""
\newsavebox\mytempbox

\definecolor{sphinxnoteBgColor}{RGB}{221,233,239}

\makeatletter
\renewenvironment{sphinxnote}[1]
{\begin{lrbox}{\mytempbox}\begin{minipage}{\columnwidth}%
\sphinxstrong{#1} }
{\end{minipage}\end{lrbox}%
\smallskip
\hrule height \spx@opt@noteborder\relax
\hbox{\colorbox{sphinxnoteBgColor}{\usebox{\mytempbox}}}%
\hrule height \spx@opt@noteborder\relax
\smallskip
}
\makeatother

""",
'sphinxsetup': 'noteborder=1.5pt',
}

This is a bit complicated LaTeX but less so than doing a variant of sphinxheavybox

Here we ignore the noteBorderColor parameter. Here is code
using it:

latex_elements = {
'preamble': r"""
\newsavebox\mytempbox
\definecolor{sphinxnoteBgColor}{RGB}{221,233,239}

\makeatletter
\renewenvironment{sphinxnote}[1]
{\begin{lrbox}{\mytempbox}\begin{minipage}{\columnwidth}%
\sphinxstrong{#1} }
{\end{minipage}\end{lrbox}%
\color{sphinxnoteBorderColor}
\hrule height \spx@opt@noteborder\relax
\normalcolor
\hbox{\colorbox{sphinxnoteBgColor}{\usebox{\mytempbox}}}%
\color{sphinxnoteBorderColor}
\hrule height \spx@opt@noteborder\relax
\normalcolor
}
\makeatother
""",

'sphinxsetup': 'noteborder=1.5pt, noteBorderColor={named}{magenta}',
}

For sphinxnoteBgColor, there is no "noteBgColor" key to 'sphinxsetup',
contrarily to "warningBgColor" etc..., because the "sphinxlightbox"
has no use of a background color. This interface should arguably be
added for cases such as this one.

In code above the handling of noteborder parameter is a bit complicated
because we must use the internal form.

Besides the above uses TeX primitives rather than LaTeX syntax,
but I don't know immediately how to do these effects in LaTeX
(and ultimately it all boils down to core TeX, so for the next
few years before LaTeX3 makes impossible using the TeX primitives
directly let's enjoy our current freedom).


(the color package does not seem to provide a \fcolorbox
for only horizontal borders, perhaps some other package
does, then you can use it in place of my code above)

Hope this helps,

Jean-François


jfbu

unread,
Oct 18, 2019, 6:25:05 PM10/18/19
to sphinx...@googlegroups.com
Le 19/10/2019 à 00:17, jfbu a écrit :
> Unfortunately if you want only a border on top and bottom but not
> on the side, this means an environment analogous to but not the
> same as sphinxheavybox should be used. But this is arduous LaTeX coding.

I just thought that we could simply use the sphinxheavybox with a zero
width border and add the top and bottom border by the method with \hrule
later in my post

If someone really needs this I can provide details

Jeff Larson

unread,
Oct 18, 2019, 6:27:50 PM10/18/19
to sphinx-users
This is outstanding for Note admonitions. Is the same thing possible for the seealso directive? 

jfbu

unread,
Oct 19, 2019, 5:41:14 AM10/19/19
to sphinx...@googlegroups.com
Le 19/10/2019 à 00:27, Jeff Larson a écrit :
> This is outstanding for Note admonitions. Is the same thing possible for the seealso directive?
> https://www.sphinx-doc.org/en/1.5/markup/para.html#directive-seealso

No, the styling by the LaTeX writer is minimal:

def visit_seealso(self, node):
# type: (nodes.Element) -> None
self.body.append('\n\n\\sphinxstrong{%s:}\n\n' % admonitionlabels['seealso'])

def depart_seealso(self, node):
# type: (nodes.Element) -> None
self.body.append("\n\n")

It does not create a LaTeX environment which can be customized,
the LaTeX file will contain only things like


-----start of example
Text before


\sphinxstrong{See also:}


This is contents of seealso directive



Text after
----end of example

and at time of
LaTeX compilation to PDF it is impossible from current mark-up to see where
the directive contents end.

Clearly, the LaTeX writer support for "seealso" directive is deficient.
I will create a ticket upstream.

As a possible work-around :

You can however use the following generic admonition mark-up

(although maybe you don't want that for HTML output and may need usage of
"only" directive which in the long run is too cumbersome)

.. admonition:: See also:

Un autre test

In the LaTeX file, this inherits the customization of the "note" admonition.
(which may be bad for you if you want another color for it).

This is due to LaTeX writer doing this

def visit_admonition(self, node):
# type: (nodes.Element) -> None
self.body.append('\n\\begin{sphinxadmonition}{note}')
self.no_latex_floats += 1

You see that a generic admonition directive falls back to "note" type.
Hence it appears impossible to customize it differently on LaTeX side
from the note admonition type (apart from hooking into the Python code)

And there is a difference:

for a "note" admonition, LaTeX mark-up ends up being like

\begin{sphinxadmonition}{note}{Note:}
This is a test
\end{sphinxadmonition}


for a generic admonition such as above, it ends up like this in LaTeX file

\begin{sphinxadmonition}{note}{See also:}

Un autre test
\end{sphinxadmonition}


And thus in PDF output, "See also:" will stand on its own line
in contrast to "Note:" which does not insert a linebreak after it.

These peculiarities are a legacy situation... I don't have good
explanation or rationale for it.




jfbu

unread,
Oct 19, 2019, 4:19:15 PM10/19/19
to sphinx...@googlegroups.com
Le 19/10/2019 à 00:27, Jeff Larson a écrit :
> This is outstanding for Note admonitions. Is the same thing possible for the seealso directive?
> https://www.sphinx-doc.org/en/1.5/markup/para.html#directive-seealso
>


Only now realizing I missed saying the obvious !

There is a famous LaTeX package tcolorbox which provides out of the box (sic) boxes (possibly breakable) with fancy decorations such as rounded corners etc...

Sphinx LaTeX does not use it, but if Sphinx LaTeX was written from scratch in 2019 it probably would.

So for example to customize looks of Note admonitions in PDF you can go something like this

latex_elements = {
'preamble': r"""
\usepackage{tcolorbox}
\definecolor{sphinxnoteBgColor}{RGB}{221,233,239}
\renewenvironment{sphinxnote}[1]
{\begin{tcolorbox}[colback=sphinxnoteBgColor,
colframe=red!75!black,
title=\sphinxstrong{#1}]}
{\end{tcolorbox}}
""",
}


The title defaults to "Note:" (and is localized to the language), but of course
you can use anything you like in place of "#1" above.

Don't forget the [1] which means the environment has a mandatory
argument (which you must grab but are free to use or not, it is the #1).

Anyway, you can get very nice colored boxes this way, up to the price
of a slightly slower PDF build time, and more disk resources and
necessity to have a complete TeX installation.

As I said in my very first reply, since Sphinx 1.5 you can
do the following above kind of things separately for each
admonition type.

But you should avoid redefining the "sphinxadmonition" environment
itself.

Side note: for the seealso directive I opened up a ticket
at the github repo and will make a PR to enhance the Sphinx
LaTeX with a sphinxseealso
environment.

Then you are free to redefine the environment
to use a tcolorbox as above and obtain spectacular
"see also" notices.

PS: the tcolorbox manual is 513 pages long... but I basically
copied pasted the first concrete example I found in it, and I
found it on page 12.

tcolorbox environments have zillions of options...

Best,

Jean-François

Reply all
Reply to author
Forward
0 new messages