Skip to content

Instantly share code, notes, and snippets.

@madebr
Created August 6, 2018 22:21
Show Gist options
  • Save madebr/3216e7ff23701caffc25a8925570c0e1 to your computer and use it in GitHub Desktop.
Save madebr/3216e7ff23701caffc25a8925570c0e1 to your computer and use it in GitHub Desktop.
OpenRW fonts kerning tool
#!/usr/bin/env python3
import sys
from pathlib import Path
from collections import namedtuple
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
curdir = Path(__file__).resolve().parent
pagerGlyphWidths = [
3, 3, 6, 8, 6, 10, 8, 3, 5, 5, 7, 0, 3, 7, 3, 0,
6, 4, 6, 6, 7, 6, 6, 6, 6, 6, 3, 0, 0, 0, 0, 6,
0, 6, 6, 6, 6, 6, 6, 6, 6, 3, 6, 6, 5, 8, 7, 6,
6, 7, 6, 6, 5, 6, 6, 8, 6, 7, 7, 0, 0, 0, 0, 0,
0, 6, 6, 6, 6, 6, 5, 6, 6, 3, 4, 6, 3, 9, 6, 6,
6, 6, 5, 6, 5, 6, 6, 8, 6, 6, 5, 0, 0, 0, 0, 0,
6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 5, 5, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 9, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 6,
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
pricedownGlyphWidths = [
11, 13, 30, 27, 20, 24, 22, 12, 14, 14, 0, 26, 9, 14, 9, 26,
20, 19, 20, 20, 22, 20, 20, 19, 20, 20, 13, 29, 24, 29, 24, 20,
27, 20, 20, 20, 20, 20, 17, 20, 20, 10, 20, 20, 15, 30, 20, 20,
20, 20, 20, 20, 22, 20, 22, 32, 20, 20, 19, 27, 20, 32, 23, 13,
27, 21, 21, 21, 21, 21, 18, 22, 21, 12, 20, 22, 17, 30, 22, 21,
21, 21, 21, 22, 21, 21, 21, 29, 19, 23, 21, 28, 25, 0, 0, 0,
20, 20, 20, 20, 30, 20, 20, 20, 20, 20, 10, 10, 10, 10, 21, 21,
21, 21, 20, 20, 20, 20, 21, 21, 21, 21, 21, 32, 23, 21, 21, 21,
21, 12, 12, 12, 12, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20,
13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]
arialGlyphWidths = [
27, 25, 55, 43, 47, 65, 53, 19, 29, 31, 21, 45, 23, 35, 27, 29,
47, 33, 45, 43, 49, 47, 47, 41, 47, 45, 25, 23, 53, 43, 53, 39,
61, 53, 51, 47, 49, 45, 43, 49, 53, 23, 41, 53, 45, 59, 53, 51,
47, 51, 49, 49, 45, 51, 49, 59, 59, 47, 51, 31, 27, 31, 29, 27,
19, 43, 45, 43, 43, 45, 27, 45, 43, 21, 33, 45, 23, 65, 43, 43,
47, 45, 33, 41, 29, 43, 41, 61, 51, 43, 43, 67, 53, 67, 67, 71,
53, 53, 53, 53, 65, 49, 45, 45, 45, 45, 23, 23, 23, 23, 51, 51,
51, 51, 51, 51, 51, 51, 51, 43, 43, 43, 43, 65, 43, 45, 45, 45,
45, 21, 21, 21, 21, 43, 43, 43, 43, 43, 43, 43, 43, 53, 43, 39,
25, 19, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 37, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
41,
]
FontMetaData = namedtuple('FontMetaData', ('texturePath', 'aspectratio', 'monoWidth', 'glyphWidths', 'binpath'))
pagerMetaData = FontMetaData(curdir / 'pager.png', 1, 11, pagerGlyphWidths, None)
pricedownMetaData = FontMetaData(curdir / 'pricedown.png', 1.25, None, pricedownGlyphWidths, None)
arialMetaData = FontMetaData(curdir / 'arial.png', 1.25, None, arialGlyphWidths, None)
fileMetaData = FontMetaData(curdir / 'file.png', None, None, None, curdir / 'file.bin')
class FontMapViewerWidget(QtWidgets.QWidget):
def __init__(self, metadata: FontMetaData):
QtWidgets.QWidget.__init__(self)
self.metadata = metadata
self.image = QtGui.QImage(str(metadata.texturePath))
self.s1 = 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG'
self.s2 = 'this is a string'
self.aspectratio = metadata.aspectratio
self.glyphWidths = metadata.glyphWidths
if metadata.binpath:
with metadata.binpath.open('rb') as f:
self.aspectratio = float(f.readline())
self.glyphWidths = [int(line) for line in f.readlines()]
self.glyphWidths += [0] * 200
if not self.glyphWidths:
raise ValueError('Need glyphWidths')
def numberColumns(self):
return 16
def rasterWidth(self):
return self.image.width() // self.numberColumns()
def rasterHeight(self):
return int(self.rasterWidth() * self.aspectratio)
def numberRows(self):
return self.image.height() // self.rasterHeight()
def toIndex(self, column, row):
return row * self.numberColumns() + column
def toCoord(self, index):
row = index // self.numberColumns()
column = index % self.numberColumns()
return column, row
def minimumSizeHint(self):
return QtCore.QSize(100, 100 * ((self.image.height() + 2 * self.rasterHeight()) / self.image.width()))
def paintEvent(self, event: QtGui.QPaintEvent):
painter = QtGui.QPainter(self)
painter.setPen(QtGui.QPen(Qt.darkGray, 3))
brush = QtGui.QBrush(Qt.SolidPattern)
brush.setColor(Qt.gray)
painter.fillRect(event.rect(), brush)
pen = QtGui.QPen()
pen.setWidth(0.5)
brush.setColor(Qt.red)
pen.setBrush(brush)
painter.setPen(pen)
scale = min(self.width() / self.image.width(), (self.height() * (self.numberRows() - 2) / self.numberRows()) / self.image.height())
painter.scale(scale, scale)
image_size = self.image.size()
srcRectF = QtCore.QRectF(0, 0, image_size.width(), image_size.height())
for i in range(self.numberRows()):
painter.drawLine(QtCore.QLineF(0, (i+1) * self.rasterHeight(), self.numberColumns() * self.rasterWidth(), (i+1) * self.rasterHeight()))
for i in range(self.numberColumns()):
painter.drawLine(QtCore.QLineF(i * self.rasterWidth(), 0, i * self.rasterWidth(), self.numberRows() * self.rasterHeight()))
brush.setColor(Qt.yellow)
pen.setBrush(brush)
painter.setPen(pen)
for i in range(self.numberRows()):
painter.drawLine(QtCore.QLineF(0, (i+0.77) * self.rasterHeight(), self.numberColumns() * self.rasterWidth(), (i+0.77) * self.rasterHeight()))
brush.setColor(Qt.green)
pen.setBrush(brush)
painter.setPen(pen)
for column in range(self.numberColumns()):
for row in range(self.numberRows()):
index = self.toIndex(column, row)
glyphWidth = self.glyphWidths[index]
x = column * self.rasterWidth() + glyphWidth
painter.drawLine(QtCore.QLineF(x, row * self.rasterHeight(),
x, (row+1) * self.rasterHeight()))
painter.drawImage(srcRectF, self.image)
def printStr(s, x, y):
for c in s:
ascii = ord(c)
index = ascii - 32
column, row = self.toCoord(index)
glyphWidth = self.glyphWidths[index]
srcRectF = QtCore.QRectF(column * self.rasterWidth(), row * self.rasterHeight(),
self.rasterWidth(), self.rasterHeight())
dstRectF = QtCore.QRectF(x, y,
self.rasterWidth(), self.rasterHeight())
painter.drawImage(dstRectF, self.image, srcRectF)
if self.metadata.monoWidth:
x += self.metadata.monoWidth
else:
x += glyphWidth
printStr(self.s1, 0, image_size.height())
printStr(self.s2, 0, image_size.height()+ self.rasterHeight())
@QtCore.pyqtSlot(str)
def textChanged(self, s: str):
self.s1 = s
self.update()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
widgets = []
widgets.append((FontMapViewerWidget(pagerMetaData), 'pager',))
widgets.append((FontMapViewerWidget(pricedownMetaData), 'pricedown',))
widgets.append((FontMapViewerWidget(arialMetaData), 'arial',))
try:
widgets.append((FontMapViewerWidget(fileMetaData), 'file',))
except ValueError:
print('No file widget')
layout = QtWidgets.QVBoxLayout()
l = QtWidgets.QLineEdit()
s = QtWidgets.QTabWidget()
for w, t in widgets:
s.addTab(w, t)
l.textChanged.connect(w.textChanged)
layout.addWidget(s)
layout.addWidget(l)
w = QtWidgets.QWidget()
w.setLayout(layout)
QtWidgets.QMainWindow.__init__(self)
self.setCentralWidget(w)
def main():
app = QtWidgets.QApplication(sys.argv)
mw = MainWindow()
mw.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment