6

I am currently using python pygments for my website to highlight some code and would like to adjust some of the colors used. So far I have tried the following approach:

from pygments.token import Keyword, Name, Comment, String, Error, \
     Number, Operator, Generic
from pygments.formatters import HtmlFormatter

class MyStyle(Style):
    styles = {
        Comment:                '#f00000',
        Keyword:                '#f00000',
        Name:                   '#f00000',
        Name.Function:          '#f00000',
        Name.Class:             '#f00000',
        String:                 '#f00000'
    }


code = 'print("Hello World")'
result = highlight(code, Python3Lexer(), HtmlFormatter(style=MyStyle))
print(result)

which prints out

<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Hello World&quot;</span><span class="p">)</span>
</pre></div>

The pygments.css that is generated alongside looks as following:

.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #8f5902; font-style: italic } /* Comment */
.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */
.highlight .g { color: #000000 } /* Generic */
.highlight .k { color: #204a87; font-weight: bold } /* Keyword */
.highlight .l { color: #000000 } /* Literal */
.highlight .n { color: #000000 } /* Name */
.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */
.highlight .x { color: #000000 } /* Other */
.highlight .p { color: #000000; font-weight: bold } /* Punctuation */
.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */
.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */
.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */
.highlight .gd { color: #a40000 } /* Generic.Deleted */
.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #ef2929 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #00A000 } /* Generic.Inserted */
.highlight .go { color: #000000; font-style: italic } /* Generic.Output */
.highlight .gp { color: #8f5902 } /* Generic.Prompt */
.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */
.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */
.highlight .ld { color: #000000 } /* Literal.Date */
.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */
.highlight .s { color: #4e9a06 } /* Literal.String */
.highlight .na { color: #c4a000 } /* Name.Attribute */
.highlight .nb { color: #204a87 } /* Name.Builtin */
.highlight .nc { color: #000000 } /* Name.Class */
.highlight .no { color: #000000 } /* Name.Constant */
.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #ce5c00 } /* Name.Entity */
.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #000000 } /* Name.Function */
.highlight .nl { color: #f57900 } /* Name.Label */
.highlight .nn { color: #000000 } /* Name.Namespace */
.highlight .nx { color: #000000 } /* Name.Other */
.highlight .py { color: #000000 } /* Name.Property */
.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #000000 } /* Name.Variable */
.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */
.highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */
.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */
.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */
.highlight .sc { color: #4e9a06 } /* Literal.String.Char */
.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */
.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */
.highlight .se { color: #4e9a06 } /* Literal.String.Escape */
.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */
.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */
.highlight .sx { color: #4e9a06 } /* Literal.String.Other */
.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */
.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */
.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */
.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #000000 } /* Name.Function.Magic */
.highlight .vc { color: #000000 } /* Name.Variable.Class */
.highlight .vg { color: #000000 } /* Name.Variable.Global */
.highlight .vi { color: #000000 } /* Name.Variable.Instance */
.highlight .vm { color: #000000 } /* Name.Variable.Magic */
.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */

Unfortunately none of the style elements are adjusted this way. Is there an easier way to change some of the colors used, e.g. for the "nb" class?

Yes
  • 339
  • 3
  • 19

1 Answers1

8

TL;DR:

You need 2 steps to use your custom style:

  1. define and create: extend the Style class ✔️
  2. apply and integrate: decide between 3 HTMLFormatter options ❌️

You missed the last.


First of all, to reproduce your code, following imports need to be added:

from pygments import highlight
from pygments.style import Style
from pygments.lexers import Python3Lexer

Defining Custom Styles

Your own style was created correctly and follows the official tutorial "Creating Own Styles". Your MyStyle class (inherited from base class Style) assigns a red color to several language-elements.

Applying Custom Styles

To use the custom style you passed it as argument style = MyStyle() to the formatter constructor: HtmlFormatter(style=MyStyle()).

Unfortunately, this does not apply the style. It must be integrated in some way with the existing default style that pygments uses.

See: How can I customize the output from pygments?

To integrate and apply a custom style you can basically decide between 3 options:

  1. export an external CSS-file by specifying a path
  2. embed the integrated CSS (default merged with your style) as <style> into the HTML
  3. inline your styles directly to the rendered/highlighted code's HTML-elements, like <span style="color: #f00000">print</span>

The pygments API reference for HTMLFormatter compares two of them:

With the full option, a complete HTML 4 document is output, including the style definitions inside a tag, or in a separate file if the cssfile option is given.

The 3 options will be explained in following sections.

Export a separate CSS-file

formatter = HtmlFormatter(style=MyStyle, full=True, cssfile=`my_pygments.css`)

Writes the full CSS stylesheet to specified file. From the docs:

If the full option is true and this option is given, it must be the name of an external file. If the filename does not include an absolute path, the file’s path will be assumed to be relative to the main output file’s path, if the latter can be found. The stylesheet is then written to this file instead of the HTML file. New in Pygments 0.6.

Embed the CSS into HTML

formatter = HtmlFormatter(style=MyStyle(), full=True)

From the docs:

Tells the formatter to output a “full” document, i.e. a complete self-contained document (default: False). Above I highlighted in bold the benefit of using the resulting HTML isolated, as single-file, without any dependencies, like CSS-files: "self-contained"

Inline the style to each code's HTML-element

formatter = HtmlFormatter(style=MyStyle())
formatter.noclasses = True

Here the existing CSS-classes (like nb) that pygment uses are not touched. Instead you defined custom styles are directly applied to HTML-elements like style="color: #f00000".

So, your given HTML output (with style-classes nb, p, s2):

<div class="highlight">
  <pre>
    <span></span><span class="nb">print</span>
    <span class="p">(</span><span class="s2">&quot;Hello World&quot;</span><span class="p">)</span>
  </pre>
</div>

will become this (no style-classes):

<div class="highlight" style="background: #ffffff">
  <pre style="line-height: 125%">
    <span></span><span style="color: #f00000">print</span>
    (<span style="color: #f00000">&quot;Hello World&quot;</span>)
  </pre>
</div>

Solution

I added the missing imports, extracted the formatter variable and passed enabled the self-contained HTML by passing option full = True, so you can test the CSS to be as expected.

from pygments import highlight
from pygments.style import Style
from pygments.lexers import Python3Lexer
from pygments.token import Keyword, Name, Comment, String, Error, \
     Number, Operator, Generic
from pygments.formatters import HtmlFormatter

# step 1: define custom style
class MyStyle(Style):
    styles = {
        Comment:                '#f00000',
        Keyword:                '#f00000',
        Name:                   '#f00000',
        Name.Function:          '#f00000',
        Name.Class:             '#f00000',
        String:                 '#f00000'
    }

# step 2: apply custom style
formatter = HtmlFormatter(style=MyStyle, full=True)  # embed Style inside HTML (self-contained, no external CSS-file
# formatter.noclasses = True  # inline style to each element directly


code = 'print("Hello World")'
result = highlight(code, Python3Lexer(), formatter)
print(result)

Will print following HTML (including CSS embeded by <style>):

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<!--
generated by Pygments <https://pygments.org/>
Copyright 2006-2019 by the Pygments team.
Licensed under the BSD license, see LICENSE for details.
-->
<html>
<head>
  <title></title>
  <meta http-equiv="content-type" content="text/html; charset=None">
  <style type="text/css">
/*
generated by Pygments <https://pygments.org/>
Copyright 2006-2019 by the Pygments team.
Licensed under the BSD license, see LICENSE for details.
*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
pre { line-height: 125%; }
body .hll { background-color: #ffffcc }
body  { background: #ffffff; }
body .c { color: #f00000 } /* Comment */
body .k { color: #f00000 } /* Keyword */
body .n { color: #f00000 } /* Name */
body .ch { color: #f00000 } /* Comment.Hashbang */
body .cm { color: #f00000 } /* Comment.Multiline */
body .cp { color: #f00000 } /* Comment.Preproc */
body .cpf { color: #f00000 } /* Comment.PreprocFile */
body .c1 { color: #f00000 } /* Comment.Single */
body .cs { color: #f00000 } /* Comment.Special */
body .kc { color: #f00000 } /* Keyword.Constant */
body .kd { color: #f00000 } /* Keyword.Declaration */
body .kn { color: #f00000 } /* Keyword.Namespace */
body .kp { color: #f00000 } /* Keyword.Pseudo */
body .kr { color: #f00000 } /* Keyword.Reserved */
body .kt { color: #f00000 } /* Keyword.Type */
body .s { color: #f00000 } /* Literal.String */
body .na { color: #f00000 } /* Name.Attribute */
body .nb { color: #f00000 } /* Name.Builtin */
body .nc { color: #f00000 } /* Name.Class */
body .no { color: #f00000 } /* Name.Constant */
body .nd { color: #f00000 } /* Name.Decorator */
body .ni { color: #f00000 } /* Name.Entity */
body .ne { color: #f00000 } /* Name.Exception */
body .nf { color: #f00000 } /* Name.Function */
body .nl { color: #f00000 } /* Name.Label */
body .nn { color: #f00000 } /* Name.Namespace */
body .nx { color: #f00000 } /* Name.Other */
body .py { color: #f00000 } /* Name.Property */
body .nt { color: #f00000 } /* Name.Tag */
body .nv { color: #f00000 } /* Name.Variable */
body .sa { color: #f00000 } /* Literal.String.Affix */
body .sb { color: #f00000 } /* Literal.String.Backtick */
body .sc { color: #f00000 } /* Literal.String.Char */
body .dl { color: #f00000 } /* Literal.String.Delimiter */
body .sd { color: #f00000 } /* Literal.String.Doc */
body .s2 { color: #f00000 } /* Literal.String.Double */
body .se { color: #f00000 } /* Literal.String.Escape */
body .sh { color: #f00000 } /* Literal.String.Heredoc */
body .si { color: #f00000 } /* Literal.String.Interpol */
body .sx { color: #f00000 } /* Literal.String.Other */
body .sr { color: #f00000 } /* Literal.String.Regex */
body .s1 { color: #f00000 } /* Literal.String.Single */
body .ss { color: #f00000 } /* Literal.String.Symbol */
body .bp { color: #f00000 } /* Name.Builtin.Pseudo */
body .fm { color: #f00000 } /* Name.Function.Magic */
body .vc { color: #f00000 } /* Name.Variable.Class */
body .vg { color: #f00000 } /* Name.Variable.Global */
body .vi { color: #f00000 } /* Name.Variable.Instance */
body .vm { color: #f00000 } /* Name.Variable.Magic */

  </style>
</head>
<body>
<h2></h2>

<div class="highlight"><pre><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Hello World&quot;</span><span class="p">)</span>
</pre></div>
</body>
</html>
hc_dev
  • 8,389
  • 1
  • 26
  • 38
  • Sorry, the answer got long and in-depth as I wanted to explain the different options for __applying custom CSS styles__. Hope it reads well, explains understandabel and helps. – hc_dev Aug 07 '21 at 08:30