I am handing off font data from one application to another, and unfortunately the first application is only able to give me "postscript" font names and nothing else. What I need is to find the filename on disk of the postscript font name. For example I have the postscript name "ZoomlaYingXing-A024" which is called "ZhuLang Semi-Cursive Script Chinese Font.otf" on my hard drive.
The second application is written in PyQt4 and ideally I could use some of the tools in Qt to handle this. For example I can construct a QFont from the font name, and query QFont.exactMatch() to make sure that I get THAT font - and from there I could create a QFontInfo object, however the only useful data I seem to be able to get out of a QFontInfo object is rawName() which just gives me the postscript name again...
From reading around it looks like I could search the registry but I have never worked with the registry and the examples I found were written in C++ which I'm not sure how to translate.
For info this only needs to work on Windows.
You could use fontTools and matplotlib.
If you need any more information about how to decode the font postscript, see the answer I posted here: How to get the font name (title) from a font file in python
from matplotlib import font_manager
from fontTools import ttLib
def isFontMatchPostscriptName(fontPath: str, font: ttLib.TTFont):
postScriptName = font['name'].getDebugName(6)
if postScriptName == "Cambria":
print("The font is located in: ", fontPath)
fontsPath = font_manager.findSystemFonts()
for fontPath in fontsPath:
with open(fontPath, 'rb') as fontFile:
fontType = fontFile.read(4)
if fontType == (b'ttcf'):
# TTC File.
ttCollection = ttLib.ttCollection.TTCollection(fontPath)
for font in ttCollection.fonts:
isFontMatchPostscriptName(fontPath, font)
else:
# It will be any TrueType or OpenType file except TTC file
isFontMatchPostscriptName(fontPath, ttLib.TTFont(fontPath))
Related
Is there a way to know if a font is a TrueType or OpenType font?
I specify that the extension of the font does not matter to me, so ttx.guessFileType(fontPath) is not a good solution for me.
OpenType vs TrueType?
First, we need to define what we mean by "OpenType" and "TrueType" fonts. The OpenType font format was developed as mostly a superset of the TrueType format, and nowadays most fonts with .otf and .ttf extensions are in fact OpenType fonts.
Is the font OpenType format?
Since OpenType is a superset of TrueType, you can check whether an .otf or .ttf font is OpenType like this:
if fontPath.endswith('.otf') or fontPath.endswith('.ttf'):
fontFormat = 'OpenType'
Are the glyph outlines TrueType (quadratic) or OpenType/CFF (cubic)?
The file extensions .otf and .ttf are theoretically interchangeable, so you're correct to avoid relying on the extension. But most of the time, OpenType fonts with an .otf extension contain glyph outlines drawn with cubic beziers and stored in a CFF or CFF2 table, whereas OpenType fonts with a .ttf extension contain glyph outlines drawn with quadratic beziers and stored in a glyf table.
So if you're unsure about the file extension, you can simply check whether the font contains a glyf table.
from fontTools.ttLib.ttFont import TTFont
font = TTFont("font.ttf")
if 'glyf' in font:
outlineFormat = "TrueType"
elif 'CFF ' in font or 'CFF2' in font:
outlineFormat = "OpenType/CFF"
else:
outlineFormat = "Unknown/Invalid"
Side note: Normally, if a font contains TrueType outlines, the first four bytes of the font will also be coded as '\x00\x01\x00\x00', and if the font contains OpenType/CFF outlines, the first our bytes will be coded as 'OTTO'. In fontTools you can check this via the TTFont.sfntVersion property.
You can check the file signature:
#!/usr/bin/python3
o = open('font', 'rb')
magic = o.read(5)
if magic.startswith(b'OTTO'):
print('OpenType')
elif magic.startswith(b'\x00\x01\x00\x00\x00'):
print('TrueType')
else:
print('other')
Source
I want to create a .ttf font with python and fontforge which also contains emojis.
my glyphs are .svg files which have a resolution of 512x512 pixels.
I already tried the Following:
import fontforge
blank = fontforge.font()
blank.save("blank.sfd")
font = fontforge.open("blank.sfd")
glyph = font.createMappedChar("\U0000263A")
glyph.importOutlines("263A.svg")
font.generate("font.ttf")
However, I get the following error:
Traceback (most recent call last):
File "/home/adrian/dev/font/glyphs/test.py", line 5, in <module>
glyph = font.createMappedChar("\U0000263A")
ValueError: Glyph name, ☺️, not in current encoding
I looked into thebblank.sfd and there is a line: "Encoding: ISO8859-1".
I tried to replace the " ISO8859-1" with "UTF-8" or "Univode" but then I get the following error:
Internal Error: SFD file specifies too few slots for its encoding.
How can I solve that?
I tried to use the emoji glyph for letter "A" for first. then font get created and it works.
However, it is just a black circle and face, eyes, smile, blush,... aren't recognizable because it is black instead of Yellow, orange, white, pink,...
In the blank.sfd file is something written about layers:
Layer: 0 0 "Back" 1
Layer: 1 0 "Fore" 0
I guess that I have to change something with the layers to make the colors to take effect but I am not sure and if that's the case I don't know how and how I can assign the layers with elements in the. svg glyph.
I use the program Inkscape to create the glyps, by the way.
How can I solve these 2 things?
I have stumbled across this wanting a simple character added to my terminal, here's a mix on the question that solves it for me.
For making your own Emoji, you might find this rather difficult with Font Forge in its current state. Just try loading a font like NotoColorEmoji.ttf into FontForge's app and you'll see an error. There is a github issue for it:
https://github.com/fontforge/fontforge/issues/677
https://github.com/fontforge/fontforge/issues/2044
But if the emoji you're trying to make is a single color glyphs like: https://github.com/powerline/powerline, you're in luck.
This script is close to yours but works well:
import fontforge
blank = fontforge.font()
blank.encoding = "UnicodeBMP"
glyph = blank.createChar(int("F10F", 16))
glyph.importOutlines("logo.svg")
blank.generate("myfont.ttf")
Feel free to change the unicode to any sequence in the private use area.
Then copy it to your font directory (if linux)
sudo cp myfont.ttf /usr/share/fonts/TTF/
In a new terminal or reloading display you can: echo -e "\uf10f"
I made a custom handwriting font and exported it to a .ttf file. The service I used, Caligriphr, allows for alternate glyphs for each character. When I type with the font in notepad, alternate glyphs display correctly. However, when I write text onto an image using PIL with the custom font, only one glyph is used
for each character. Below is my code to write on the image:
body = ""
with open('body.txt') as fin:
ls = fin.readlines()
for l in ls:
words = l.split(' ')
for word in words:
double = random.randint(1,2) == 2
if(double):
body += ' ' + word
else:
body += ' ' + word
image = Image.open('graph.jpg').convert("RGBA")
text = Image.new('RGBA', image.size, (255,255,255,0))
font = ImageFont.FreeTypeFont('Graphite.ttf', 100)
d = ImageDraw.Draw(text)
d.text(xy=offset, text=body, fill = (26,29,32, 230), font=font)
tilt = random.random() * 2
slt = text.rotate(tilt, expand=1)
sx, sy = slt.size
image.paste(slt, (0,0, sx, sy), slt)
image.save('sample.png')
Edit: showing code for how body string is constructed
Any help would be appreciated.
Alternate Glyphs Displayed in Notepad
PIL output not utilizing alternate glyphs
This may depend on whether you're using the original PIL library, or Pillow.
Getting contextual alternate glyphs in the drawn output requires that the rendering engine draw the string as an entire string (not character by character) and, while doing so, process certain data in the font that performs glyph substitutions from the default glyphs.
This data could be OpenType Layout tables in OpenType fonts, AAT tables in Apple TrueType fonts, or Graphite tables in Graphite fonts, depending on what the platform/library supports. Since you mention you got alternates in Notepad, that indicates that the font has OpenType Layout data.
Reading the Pillow ImageFont documentation, it doesn't give any indication as to whether it supports any of these font formats. However, looking at the Pillow project in Github, I see that [winbuild\config.py] pulls in Harfbuzz, and that would provide support for OpenType Layout data. So, it seems that Pillow ought to draw using the contextual alternate glyphs, though I don't know if anything is needed to trigger it. (In Notepad, it happens by default, but that's not true everywhere.)
If using Pillow, you might need to explicitly enable the Raqm layout engine when loading the font: "Raqm - A library for complex text layout" http://host-oman.github.io/libraqm/raqm-Raqm.html
This engine can be specified using the ImageFont.truetype() function:
font = ImageFont.truetype('Graphite.ttf', 100, layout_engine=ImageFont.LAYOUT_RAQM)
Docs: https://pillow.readthedocs.io/en/stable/reference/ImageFont.html#PIL.ImageFont.truetype
Testing if Raqm is availble on your system:
>>> from PIL import features
>>> features.check('raqm')
True
If not available, on my Ubuntu 20 it installs via
$ sudo apt install libraqm0
Edit: It looks like you don't even have to specify the layout_engine parameter at all. On my system alternate glyphs are used as soon as I install the Raqm library, even without changing the code.
I have an issue with writing text to an image under Python and PIL -
I'm able to write text to a png file, though not bold text. Could anyone provide an example of how to achieve this?
I thought the easiest solution may be was use a bold-variant of a text, but I'm unable to see anything in the Windows/font folder that supplies this - does this mean font types have a 'bold attribute' that is T/F?:
Code I'm using:
import PIL
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
# font = ImageFont.truetype("Arial-Bold.ttf",14)
font = ImageFont.truetype("Arial.ttf",14)
img=Image.new("RGBA", (500,250),(255,255,255))
draw = ImageDraw.Draw(img)
draw.text((0, 0),"This is a test",(0,0,0),font=font)
draw = ImageDraw.Draw(img)
img.save("a_test.png")
A simple way to do it:
font = ImageFont.load_default().font
Also you can do a google search for 'verdana.ttf' and download it put it in the same directory as the python file:
Then add it like this:
font = ImageFont.truetype("Verdana.ttf",14)
You aren't looking at actual font files in the control panel (explorer magically turns into the font viewer control panel when in the Windows/fonts folder as well), they are grouped by family for your convenience. Double click the family to see the fonts in the family:
Then right-click and choose properties to find the file name:
I am generating SVG image in python (pure, no external libs yet). I want to know what will be a text element size, before I place it properly. Any good idea how to make it? I checked pysvg library but I saw nothing like getTextSize()
This can be be pretty complicated. To start with, you'll have to familiarize yourself with chapter on text of the SVG specification. Assuming you want to get the width of plain text elements, and not textpath elements, at a minimum you'd have to:
Parse the font selection properties, spacing properties and read the xml:space attibute, as well as the writing-mode property (can also be top-bottom instead of just left-to-right and right-to-left).
Based on the above, open the correct font, and read the glyph data and extract the widths and heights of the glyphs in your text string. Alone finding the font can be a big task, seeing the multiple places where font files can hide.
(optionally) Look through the string for possible ligatures (depending on the language), and replace them with the correct glyph if it exists in the font.
Add the widths for all the characters and spaces, the latter depending on the spacing properties and (optionally) possible kerning pairs.
A possible solution would be to use the pango library. You can find python bindings for it in py-gtk. Unfortunately, except from some examples, the documentation for the python bindings is pretty scarce. But it would take care of the details of font loading and determining the extents of a Layout.
Another way is to study the SVG renderer in your browser. But e.g. the support for SVG text in Firefox is limited.
Also instructive is to study how TeX does it, especially the concept (pdf) of boxes (for letters) and glue (for spacing).
I had this exact same problem, but I had a variable width font. I solved it by taking the text element (correct font and content) I wanted, wrote it to a svg file, and I used Inkscape installed on my PC to render the drawing to a temporary png file. I then read back the dimensions of the png file (extracted from the header), removed the temp svg and png files and used the result to place the text where I wanted and elements around it.
I found that rendering to a drawing, using a DPI of 90 seemed to give me the exact numbers I needed, or the native numbers used in svgwrite as a whole. -D is the flag to use so that only the drawable element, i.e. the text, is rendered.
os.cmd(/cygdrive/c/Program\ Files\ \(x86\)/Inkscape/inkscape.exe -f work_temp.svg -e work_temp.png -d 90 -D)
I used these functions to extract the png numbers, found at this link, note mine is corrected slightly for python3 (still working in python2)
def is_png(data):
return (data[:8] == b'\x89PNG\r\n\x1a\n'and (data[12:16] == b'IHDR'))
def get_image_info(data):
if is_png(data):
w, h = struct.unpack('>LL', data[16:24])
width = int(w)
height = int(h)
else:
raise Exception('not a png image')
return width, height
if __name__ == '__main__':
with open('foo.png', 'rb') as f:
data = f.read()
print is_png(data)
print get_image_info(data)
It's clunky, but it worked