Skip to content

Commit e5f0ba1

Browse files
committed
LaTeX: implement support for custom background color
Some styles set the background_color to #ffffff, in such cases we ignore that because the default Sphinx PDF light gray is nicer. This also handles a "default" text color, but some testing shoud be done.
1 parent 3a48334 commit e5f0ba1

File tree

2 files changed

+73
-20
lines changed

2 files changed

+73
-20
lines changed

sphinx/highlighting.py

Lines changed: 60 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import re
56
from functools import partial
67
from hashlib import md5
78
from importlib import import_module
@@ -21,6 +22,7 @@
2122
guess_lexer,
2223
)
2324
from pygments.styles import get_style_by_name
25+
from pygments.token import Token
2426
from pygments.util import ClassNotFound
2527

2628
from sphinx.locale import __
@@ -247,22 +249,7 @@ def get_stylesheet(self, selectors: list[int] | str | None = None) -> str:
247249
return formatter.get_style_defs('.highlight') # type: ignore [no-untyped-call]
248250
else:
249251
if selectors:
250-
if isinstance(selectors, str):
251-
_tex_name = md5(selectors.encode()).hexdigest()[:6] # noqa: S324
252-
for d, l in [
253-
('0', 'G'),
254-
('1', 'H'),
255-
('2', 'I'),
256-
('3', 'J'),
257-
('4', 'K'),
258-
('5', 'L'),
259-
('6', 'M'),
260-
('7', 'N'),
261-
('8', 'O'),
262-
('9', 'P'),
263-
]:
264-
_tex_name = _tex_name.replace(d, l)
265-
else:
252+
if not isinstance(selectors, str):
266253
logger.error(
267254
__(
268255
'Encountered %s in selectors field; expected a string '
@@ -274,10 +261,67 @@ def get_stylesheet(self, selectors: list[int] | str | None = None) -> str:
274261
)
275262
# not using '' as we don't want \PYG being overwritten.
276263
_tex_name = 'INVALID'
264+
selectors = 'default' # TODO: make more informed choice?
265+
_tex_name = md5(selectors.encode()).hexdigest()[:6] # noqa: S324
266+
for d, l in [
267+
('0', 'G'),
268+
('1', 'H'),
269+
('2', 'I'),
270+
('3', 'J'),
271+
('4', 'K'),
272+
('5', 'L'),
273+
('6', 'M'),
274+
('7', 'N'),
275+
('8', 'O'),
276+
('9', 'P'),
277+
]:
278+
_tex_name = _tex_name.replace(d, l)
277279
stylesheet = self.formatter(
278280
style=selectors, commandprefix='PYG' + _tex_name
279281
).get_style_defs()
280282
sphinx_redefs = ''
283+
bc = self.get_style(selectors).background_color
284+
if bc is not None:
285+
bc = bc.lstrip('#').lower()
286+
# The xcolor LaTeX package requires 6 hexadecimal digits
287+
if len(bc) == 3:
288+
bc = bc[0] * 2 + bc[1] * 2 + bc[2] * 2
289+
# We intercept a purely white background, so that PDF will use Sphinx
290+
# light gray default, rather, or the user VerbatimColor global choice.
291+
# TODO: argue pros and cons.
292+
if bc != 'ffffff':
293+
sphinx_redefs = (
294+
'% background color for above style, "HTML" syntax\n'
295+
f'\\def\\sphinxPYG{_tex_name}bc{{{bc}}}\n'
296+
)
297+
# TODO: THIS MAY NOT BE THE RIGHT THING TO DO.
298+
# TODO: REMOVE NEXT COMMENTS.
299+
# I wanted to try with
300+
# solarized-light which will use #657b83 but my sample code-block
301+
# has no token not using a color so I could not confirm it does work.
302+
# (indeed solarized-light uses \textcolor everywhere in its stylesheet,
303+
# so I modified manually LaTeX output to confirm the whole thing
304+
# actually worked as expected).
305+
# I have not for lack of time searched for a pygments style defining
306+
# such a color and not using \textcolor everywhere.
307+
# The idea is to avoid invisible text on dark background which I believe
308+
# I have experienced in the past when using dark background via injection
309+
# of \sphinxsetup using raw:: latex directive.
310+
base_style = self.get_style(selectors).styles[Token]
311+
if base_style: # could look like 'italic #000 bg:#ffffff'
312+
match = re.match(
313+
r'#([0-9a-fA-F]{3,6})(?:\s+bg:#([0-9a-fA-F]{3,6}))?', base_style
314+
)
315+
if match is not None:
316+
tc = match.group(1)
317+
if len(tc) == 3:
318+
tc = tc[0] * 2 + tc[1] * 2 + tc[2] * 2
319+
sphinx_redefs += (
320+
'% text default color for above style, "HTML" syntax\n'
321+
f'\\def\\sphinxPYG{_tex_name}tc{{{tc}}}\n'
322+
)
323+
# TODO: what should we do for the color used to emphasize lines?
324+
# It is VerbatimHightlightColor.
281325
else:
282326
stylesheet = formatter.get_style_defs()
283327
sphinx_redefs = _LATEX_ADD_STYLES

sphinx/writers/latex.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2295,8 +2295,19 @@ def visit_literal_block(self, node: Element) -> None:
22952295
**highlight_args,
22962296
)
22972297
if _texstylename:
2298+
# There is no a priori "VerbatimTextColor" set, except is user employed
2299+
# the sphinxsetup with pre_TeXcolor. We could query the TeX boolean
2300+
# ifspx@opt@pre@withtextcolor but the @ letter is annoying here. So
2301+
# let's simply add a group level and not worry about testing if this
2302+
# or other things pre-exist so we don't have to reset.
22982303
self.body.append(
2299-
CR + f'\\def\\sphinxpygmentsstylename{{{_texstylename}}}'
2304+
f'{CR}\\begingroup\\def\\sphinxpygmentsstylename{{{_texstylename}}}%'
2305+
f'{CR}\\ifdefined\\sphinxPYG{_texstylename}bc'
2306+
f'{CR} \\sphinxsetup{{VerbatimColor={{HTML}}'
2307+
f'{{\\sphinxPYG{_texstylename}bc}}}}%{CR}\\fi'
2308+
f'{CR}\\ifdefined\\sphinxPYG{_texstylename}tc'
2309+
f'{CR} \\sphinxsetup{{pre_TeXcolor={{HTML}}'
2310+
f'{{\\sphinxPYG{_texstylename}tc}}}}%{CR}\\fi'
23002311
)
23012312
if self.in_footnote:
23022313
self.body.append(CR + r'\sphinxSetupCodeBlockInFootnote')
@@ -2314,8 +2325,6 @@ def visit_literal_block(self, node: Element) -> None:
23142325
# get consistent trailer
23152326
hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
23162327
if self.table and not self.in_footnote:
2317-
# TODO: probably add a % at end to avoid a space token if for a
2318-
# block with style-light option
23192328
hlcode += r'\end{sphinxVerbatimintable}'
23202329
else:
23212330
hlcode += r'\end{sphinxVerbatim}'
@@ -2327,7 +2336,7 @@ def visit_literal_block(self, node: Element) -> None:
23272336
if hllines:
23282337
self.body.append(r'\sphinxresetverbatimhllines' + CR)
23292338
if _texstylename:
2330-
self.body.append(r'\let\sphinxpygmentsstylename\undefined' + CR)
2339+
self.body.append(r'\endgroup' + CR)
23312340
raise nodes.SkipNode
23322341

23332342
def depart_literal_block(self, node: Element) -> None:

0 commit comments

Comments
 (0)