Skip to content

Commit

Permalink
Don't apply bounding-box fixup to "tricky" CJK fonts
Browse files Browse the repository at this point in the history
It breaks their rendering on macOS, because the system no longer recognizes and special-cases them
after we touch (fix) the glyf data.

Fixes #265.
  • Loading branch information
jfkthame committed Aug 30, 2023
1 parent 6ba665a commit c9b38cf
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 28 deletions.
59 changes: 35 additions & 24 deletions src/glyf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "head.h"
#include "loca.h"
#include "maxp.h"
#include "name.h"

// glyf - Glyph Data
// http://www.microsoft.com/typography/otspec/glyf.htm
Expand Down Expand Up @@ -94,10 +95,11 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
unsigned gid,
int16_t num_contours,
int16_t& xmin,
int16_t& ymin,
int16_t& xmax,
int16_t& ymax) {
int16_t xmin,
int16_t ymin,
int16_t xmax,
int16_t ymax,
bool is_tricky_font) {
// read the end-points array
uint16_t num_flags = 0;
for (int i = 0; i < num_contours; ++i) {
Expand Down Expand Up @@ -219,27 +221,32 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
}

if (adjusted_bbox) {
Warning("Glyph bbox was incorrect; adjusting (glyph %u)", gid);
// copy the numberOfContours field
this->iov.push_back(std::make_pair(glyph.buffer(), 2));
// output a fixed-up version of the bounding box
uint8_t* fixed_bbox = new uint8_t[8];
fixed_bboxes.push_back(fixed_bbox);
xmin = ots_htons(xmin);
std::memcpy(fixed_bbox, &xmin, 2);
ymin = ots_htons(ymin);
std::memcpy(fixed_bbox + 2, &ymin, 2);
xmax = ots_htons(xmax);
std::memcpy(fixed_bbox + 4, &xmax, 2);
ymax = ots_htons(ymax);
std::memcpy(fixed_bbox + 6, &ymax, 2);
this->iov.push_back(std::make_pair(fixed_bbox, 8));
// copy the remainder of the glyph data
this->iov.push_back(std::make_pair(glyph.buffer() + 10, glyph.offset() - 10));
} else {
this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
if (is_tricky_font) {
Warning("Glyph bbox was incorrect; NOT adjusting tricky font (glyph %u)", gid);
} else {
Warning("Glyph bbox was incorrect; adjusting (glyph %u)", gid);
// copy the numberOfContours field
this->iov.push_back(std::make_pair(glyph.buffer(), 2));
// output a fixed-up version of the bounding box
uint8_t* fixed_bbox = new uint8_t[8];
fixed_bboxes.push_back(fixed_bbox);
xmin = ots_htons(xmin);
std::memcpy(fixed_bbox, &xmin, 2);
ymin = ots_htons(ymin);
std::memcpy(fixed_bbox + 2, &ymin, 2);
xmax = ots_htons(xmax);
std::memcpy(fixed_bbox + 4, &xmax, 2);
ymax = ots_htons(ymax);
std::memcpy(fixed_bbox + 6, &ymax, 2);
this->iov.push_back(std::make_pair(fixed_bbox, 8));
// copy the remainder of the glyph data
this->iov.push_back(std::make_pair(glyph.buffer() + 10, glyph.offset() - 10));
return true;
}
}

this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));

return true;
}

Expand Down Expand Up @@ -342,6 +349,10 @@ bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) {
return Error("Missing maxp or loca or head table needed by glyf table");
}

OpenTypeNAME *name = static_cast<OpenTypeNAME*>(
GetFont()->GetTypedTable(OTS_TAG_NAME));
bool is_tricky = name->IsTrickyFont();

this->maxp = maxp;

const unsigned num_glyphs = maxp->num_glyphs;
Expand Down Expand Up @@ -397,7 +408,7 @@ bool OpenTypeGLYF::Parse(const uint8_t *data, size_t length) {
// does we will simply ignore it.
glyph.set_offset(0);
} else if (num_contours > 0) {
if (!ParseSimpleGlyph(glyph, i, num_contours, xmin, ymin, xmax, ymax)) {
if (!ParseSimpleGlyph(glyph, i, num_contours, xmin, ymin, xmax, ymax, is_tricky)) {
return Error("Failed to parse glyph %d", i);
}
} else {
Expand Down
9 changes: 5 additions & 4 deletions src/glyf.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ class OpenTypeGLYF : public Table {
bool ParseSimpleGlyph(Buffer &glyph,
unsigned gid,
int16_t num_contours,
int16_t& xmin,
int16_t& ymin,
int16_t& xmax,
int16_t& ymax);
int16_t xmin,
int16_t ymin,
int16_t xmax,
int16_t ymax,
bool is_tricky_font);
bool ParseCompositeGlyph(
Buffer &glyph,
ComponentPointCount* component_point_count);
Expand Down
40 changes: 40 additions & 0 deletions src/name.cc
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,44 @@ bool OpenTypeNAME::IsValidNameId(uint16_t nameID, bool addIfMissing) {
return this->name_ids.count(nameID);
}

// List of font names considered "tricky" (dependent on applying original TrueType instructions) by FreeType, see
// https://gitlab.freedesktop.org/freetype/freetype/-/blob/2d9fce53d4ce89f36075168282fcdd7289e082f9/src/truetype/ttobjs.c#L170-241
static const char* tricky_font_names[] = {
"cpop",
"DFGirl-W6-WIN-BF",
"DFGothic-EB",
"DFGyoSho-Lt",
"DFHei",
"DFHSGothic-W5",
"DFHSMincho-W3",
"DFHSMincho-W7",
"DFKaiSho-SB",
"DFKaiShu",
"DFKai-SB",
"DFMing",
"DLC",
"HuaTianKaiTi?",
"HuaTianSongTi?",
"Ming(for ISO10646)",
"MingLiU",
"MingMedium",
"PMingLiU",
"MingLi43"
};

bool OpenTypeNAME::IsTrickyFont() const {
for (const auto& name : this->names) {
const uint16_t id = name.name_id;
if (id != 1) {
continue;
}
for (const auto* p : tricky_font_names) {
if (name.text.find(p) != std::string::npos) {
return true;
}
}
}
return false;
}

} // namespace
1 change: 1 addition & 0 deletions src/name.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class OpenTypeNAME : public Table {
bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);
bool IsValidNameId(uint16_t nameID, bool addIfMissing = false);
bool IsTrickyFont() const;

private:
std::vector<NameRecord> names;
Expand Down

0 comments on commit c9b38cf

Please sign in to comment.