Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for loading TrueType font files. #1960

Merged
merged 7 commits into from Aug 6, 2023
40 changes: 39 additions & 1 deletion pygments/formatters/img.py
Expand Up @@ -7,7 +7,6 @@
:copyright: Copyright 2006-2021 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

import os
import sys

Expand Down Expand Up @@ -68,6 +67,15 @@ def __init__(self, font_name, font_size=14):
self.font_size = font_size
self.fonts = {}
self.encoding = None
self.variable = False
if hasattr(font_name, 'read') or os.path.isfile(font_name):
font = ImageFont.truetype(font_name, self.font_size)
self.variable = True
for style in STYLES:
self.fonts[style] = font

return

if sys.platform.startswith('win'):
if not font_name:
self.font_name = DEFAULT_FONT_NAME_WIN
Expand Down Expand Up @@ -219,14 +227,42 @@ def get_font(self, bold, oblique):
Get the font based on bold and italic flags.
"""
if bold and oblique:
if self.variable:
return self.get_style('BOLDITALIC')

return self.fonts['BOLDITALIC']
elif bold:
if self.variable:
return self.get_style('BOLD')

return self.fonts['BOLD']
elif oblique:
if self.variable:
return self.get_style('ITALIC')

return self.fonts['ITALIC']
else:
if self.variable:
return self.get_style('NORMAL')

return self.fonts['NORMAL']

def get_style(self, style):
"""
Get the specified style of the font if it is a variable font.
If not found, return the normal font.
"""
font = self.fonts[style]
for style_name in STYLES[style]:
try:
font.set_variation_by_name(style_name)
return font
except ValueError:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it guarantee that no ValueError is raised? I'd guess some of the code could do that ... alternative would be to catch only OSError and pass in all other cases?

except OSError:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indicates an invalid font, right? Shouldn't there be a warning or an error?

return font

return font

class ImageFormatter(Formatter):
"""
Expand Down Expand Up @@ -254,6 +290,8 @@ class ImageFormatter(Formatter):
The font name to be used as the base font from which others, such as
bold and italic fonts will be generated. This really should be a
monospace font to look sane.
If a filename or a file-like object is specified, the user must
provide different styles of the font.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, a typo in the file name will result in font_name being understood as the name of a font and not a path, and the errors will read 'No usable fonts named ...' or something like that depending on the system. Can we make it explicit rather than implicit and use a separate font_path parameter?


Default: "Courier New" on Windows, "Menlo" on Mac OS, and
"DejaVu Sans Mono" on \\*nix
Expand Down