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 new QA HTML Test tool for comparing a font family. #548

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Lib/gftools/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
font_is_italic,
partition,
get_encoded_glyphs,
get_encoded_glyphs_from_fonts,
)


Expand Down Expand Up @@ -496,7 +497,8 @@ def __init__(
self.sample_text = " ".join(font_sample_text(self.ttFonts[0]))
# TODO to collect unencoded glyphs, we need to make a better version
# of hbinput
self.glyphs = get_encoded_glyphs(self.ttFonts[0])
self.glyphs = get_encoded_glyphs_from_fonts(self.ttFonts)
self.encoded_glyphs = {css_class: get_encoded_glyphs(self.ttFonts[i]) for i, css_class in enumerate(self.css_font_classes)}

def partition(self):
with tempfile.TemporaryDirectory() as tmp_out:
Expand Down Expand Up @@ -553,7 +555,7 @@ def __init__(
self.too_big_for_browserstack = len(self.css_font_classes_before) > 4

self.sample_text = " ".join(font_sample_text(self.ttFonts_before[0]))
self.glyphs = get_encoded_glyphs(self.ttFonts_before[0])
self.glyphs = get_encoded_glyphs_fonts(self.ttFonts_before)

def _match_css_font_classes(self):
"""Match css font classes by full names for static fonts and
Expand Down
85 changes: 85 additions & 0 deletions Lib/gftools/templates/family.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{% extends "_base.html" %}
{% block title %}Glyphs{% endblock %}
{% block style %}
{{ super() }}
.cell{
position: relative;
z-index: -1;
float: left;
background-color: #fcfcfc;
display: block;
text-align: center;
padding: 5pt;
margin: 5pt;
width: {{ pt_size | int * 1.5 }}px;
line-height: {{ pt_size | int * 1.5}}px;
}
.box-title-family{
width: {{ pt_size }}pt;
float: left;
font-size: 8pt;
font-weight: 700;
padding-top: 5px;
margin-bottom: 10pt;
display: block;
}
.wrapper_box{
display: flex;
padding: 5pt;
margin: 5pt;
width: 24px;
top: 240px;
}
.wrapper_title{
display: flex;
padding: 5pt;
margin: 5pt;
width: 24px;
/* position: fixed; */
top: 200px;
}
.wrapper_content{
display: block;
}
.title_rotated {
transform: rotate(-90deg);
white-space:nowrap;
display:block;
}
.error{
background-color: #D0342C;
color: #fcfcfc;
}

{% endblock %}

{% block content_name %}
<div style="height: 200px">
<b>Font Family Overview</b>
</div>
{% endblock %}

{% block content %}
<div class="wrapper_content">
<div class="wrapper_title">
{% for font_class in css_font_classes or css_font_classes_before or css_font_classes_after %}
<div class="{{ font_class.selector }} box cell title_rotated">{{ font_class.selector }}</div>
{% endfor %}
</div>
<div class="wrapper_box">
{% for font_class in css_font_classes or css_font_classes_before or css_font_classes_after %}
<div class="box">
<span class="{{ font_class.selector }}" style="font-size: {{ pt_size }}pt">
{% for glyph in glyphs %}
{% if glyph in encoded_glyphs[font_class] %}
<div class="cell" title="{{ glyph }}">{{ glyph }}</div>
{% else %}
<div class="cell error" title="{{ glyph }}">{{ glyph }}</div>
{% endif %}
{% endfor %}
</span>
</div>
{% endfor %}
<div>
</div>
{% endblock %}
81 changes: 81 additions & 0 deletions Lib/gftools/tests/test_html_family.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import pytest
from glob import glob
from fontTools.ttLib import TTFont
import tempfile
import os
from gftools.html import *
import re
from copy import deepcopy


TEST_DATA = os.path.join("data", "test")


@pytest.fixture
def SimpleTemplate():
class SimpleTemplate(HtmlTemplater):
def __init__(self, out, template_dir):
super().__init__(out=out, template_dir=template_dir)
return SimpleTemplate


@pytest.fixture
def static_fonts():
return [f for f in glob(os.path.join("data", "test", "mavenpro", "*.ttf"))]


@pytest.fixture
def static_ttfonts():
return [TTFont(f) for f in glob(os.path.join("data", "test", "mavenpro", "*.ttf"))]


@pytest.fixture
def var_font():
return os.path.join(TEST_DATA, "Inconsolata[wdth,wght].ttf")


@pytest.fixture
def var_ttfont():
return TTFont(os.path.join(TEST_DATA, "Inconsolata[wdth,wght].ttf"))


@pytest.fixture
def var_font2():
return os.path.join(TEST_DATA, "MavenPro[wght].ttf")


def _string_to_file(string, dst):
with open(dst, "w") as doc:
doc.write(string)
return dst


def _file_to_string(fp):
with open(fp) as f:
return f.read()

def get_fonts(path):
font_suffixes = {'ttf', 'otf'}
fonts = []
for filename in os.listdir(path):
full_path = os.path.join(path, filename)
suffix = filename.split('.')[-1]

if suffix.lower() in font_suffixes:
fonts.append(TTFont(full_path))
return fonts

def test_html_family():
base_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))
from gftools.utils import mkdir, get_sorted_font_indices
font_path = os.path.join(base_path, "data", "test", "mavenpro")
static_ttfonts = get_fonts(font_path)
sorted_indices = get_sorted_font_indices(static_ttfonts)

out = os.path.join(base_path, TEST_DATA, "mavenpro", "browser_previews")
mkdir(out)
html = HtmlProof(
out=out,
fonts=[static_ttfonts[i].reader.file.name for i in sorted_indices]
)
html.build_pages(["family.html"], pt_size=16)
39 changes: 36 additions & 3 deletions Lib/gftools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,12 @@ def get_encoded_glyphs(ttFont):
"""Collect all encoded glyphs"""
return list(map(chr, ttFont.getBestCmap().keys()))

def get_encoded_glyphs_from_fonts(ttFonts):
"""Collect all encoded glyphs from a list of fonts"""
encoded_glyphs = set()
for ttFont in ttFonts:
encoded_glyphs.update(set(get_encoded_glyphs(ttFont)))
return sorted(encoded_glyphs)

def get_unencoded_glyphs(font):
""" Check if font has unencoded glyphs """
Expand Down Expand Up @@ -442,9 +448,13 @@ def has_mac_names(ttfont):


def font_is_italic(ttfont):
"""Check if the font has the word "Italic" in its stylename."""
stylename = ttfont["name"].getName(2, 3, 1, 0x409).toUnicode()
return True if "Italic" in stylename else False
try:
from fontbakery.profiles.shared_conditions import is_italic
return is_italic(ttfont)
except:
"""Check if the font has the word "Italic" in its stylename."""
stylename = ttfont["name"].getName(2, 3, 1, 0x409).toUnicode()
return True if "Italic" in stylename else False


def font_sample_text(ttFont):
Expand Down Expand Up @@ -513,3 +523,26 @@ def read_proto(fp, schema):
data = text_format.Parse(f.read(), schema)
return data

def get_sorted_font_indices(font_objs):
"""
Returns a tuple with indices sorted by
font family, italic, width and weight values.
"""
font_dict= dict()
for i, font_obj in enumerate(font_objs):
if "OS/2" not in font_obj or "name" not in font_obj:
# fonts must include the font tables OS/2 and name
# for figuring out a valid sorting.
continue

OS2 = font_obj["OS/2"]
name = font_obj["name"]

fam_name = name.getBestFamilyName()
wght_value = OS2.usWeightClass
wdth_value = OS2.usWidthClass
ital_value = font_is_italic(font_obj)

font_dict[(fam_name, ital_value, wdth_value, wght_value, i)] = i

return tuple([v for k, v in sorted(font_dict.items())])