Skip to content

Commit

Permalink
Check & fix bounding box of simple glyph records.
Browse files Browse the repository at this point in the history
Fixes #263.

(Actually, only a partial fix: this only deals with simple outline glyphs,
not composite glyphs. In principle we could do something with those as well,
but it seems like significantly more effort and probably less likely to arise
in practice.)
  • Loading branch information
jfkthame committed Aug 10, 2023
1 parent 84636f3 commit 51515e5
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 19 deletions.
136 changes: 118 additions & 18 deletions src/glyf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace ots {

bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
uint32_t num_flags,
std::vector<uint8_t>& flags,
uint32_t *flag_index,
uint32_t *coordinates_length) {
uint8_t flag = 0;
Expand Down Expand Up @@ -48,6 +49,8 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
"bit 6 must be set to zero for flag %d", flag, *flag_index);
}

flags[*flag_index] = flag & ~(1u << 3);

if (flag & (1u << 3)) { // repeat
if (*flag_index + 1 >= num_flags) {
return Error("Count too high (%d + 1 >= %d)", *flag_index, num_flags);
Expand All @@ -61,9 +64,12 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
}
delta += (delta * repeat);

*flag_index += repeat;
if (*flag_index >= num_flags) {
return Error("Count too high (%d >= %d)", *flag_index, num_flags);
if (*flag_index + repeat >= num_flags) {
return Error("Count too high (%d >= %d)", *flag_index + repeat, num_flags);
}

while (repeat--) {
flags[++*flag_index] = flag & ~(1u << 3);
}
}

Expand All @@ -80,28 +86,38 @@ bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph,
return true;
}

#define X_SHORT_VECTOR (1u << 1)
#define Y_SHORT_VECTOR (1u << 2)
#define X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR (1u << 4)
#define Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR (1u << 5)

bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,
int16_t num_contours) {
unsigned gid,
int16_t num_contours,
int16_t& xmin,
int16_t& ymin,
int16_t& xmax,
int16_t& ymax) {
// read the end-points array
uint16_t num_flags = 0;
for (int i = 0; i < num_contours; ++i) {
uint16_t tmp_index = 0;
if (!glyph.ReadU16(&tmp_index)) {
return Error("Can't read contour index %d", i);
return Error("Can't read contour index %d (glyph %u)", i, gid);
}
if (tmp_index == 0xffffu) {
return Error("Bad contour index %d", i);
return Error("Bad contour index %d (glyph %u)", i, gid);
}
// check if the indices are monotonically increasing
if (i && (tmp_index + 1 <= num_flags)) {
return Error("Decreasing contour index %d + 1 <= %d", tmp_index, num_flags);
return Error("Decreasing contour index %d + 1 <= %d (glyph %u)", tmp_index, num_flags, gid);
}
num_flags = tmp_index + 1;
}

if (this->maxp->version_1 &&
num_flags > this->maxp->max_points) {
Warning("Number of contour points exceeds maxp maxPoints, adjusting limit.");
Warning("Number of contour points exceeds maxp maxPoints, adjusting limit (glyph %u)", gid);
this->maxp->max_points = num_flags;
}

Expand All @@ -112,33 +128,117 @@ bool OpenTypeGLYF::ParseSimpleGlyph(Buffer &glyph,

if (this->maxp->version_1 &&
this->maxp->max_size_glyf_instructions < bytecode_length) {
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d",
this->maxp->max_size_glyf_instructions, bytecode_length);
Warning("Bytecode length is bigger than maxp.maxSizeOfInstructions %d: %d (glyph %u)",
this->maxp->max_size_glyf_instructions, bytecode_length, gid);
this->maxp->max_size_glyf_instructions = bytecode_length;
}

if (!glyph.Skip(bytecode_length)) {
return Error("Can't read bytecode of length %d", bytecode_length);
return Error("Can't read bytecode of length %d (glyph %u)", bytecode_length, gid);
}

uint32_t coordinates_length = 0;
std::vector<uint8_t> flags(num_flags);
for (uint32_t i = 0; i < num_flags; ++i) {
if (!ParseFlagsForSimpleGlyph(glyph, num_flags, &i, &coordinates_length)) {
return Error("Failed to parse glyph flags %d", i);
if (!ParseFlagsForSimpleGlyph(glyph, num_flags, flags, &i, &coordinates_length)) {
return Error("Failed to parse glyph flags %d (glyph %u)", i, gid);
}
}

if (!glyph.Skip(coordinates_length)) {
return Error("Glyph too short %d", glyph.length());
bool adjusted_bbox = false;
int16_t x = 0, y = 0;

// Read and check x-coords
for (uint32_t i = 0; i < num_flags; ++i) {
uint8_t flag = flags[i];
if (flag & X_SHORT_VECTOR) {
uint8_t dx;
if (!glyph.ReadU8(&dx)) {
return Error("Glyph too short %d (glyph %u)", glyph.length(), gid);
}
if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {
x += dx;
} else {
x -= dx;
}
} else if (flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {
// x remains unchanged
} else {
int16_t dx;
if (!glyph.ReadS16(&dx)) {
return Error("Glyph too short %d (glyph %u)", glyph.length(), gid);
}
x += dx;
}
if (x < xmin) {
xmin = x;
adjusted_bbox = true;
}
if (x > xmax) {
xmax = x;
adjusted_bbox = true;
}
}

// Read and check y-coords
for (uint32_t i = 0; i < num_flags; ++i) {
uint8_t flag = flags[i];
if (flag & Y_SHORT_VECTOR) {
uint8_t dy;
if (!glyph.ReadU8(&dy)) {
return Error("Glyph too short %d (glyph %u)", glyph.length(), gid);
}
if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) {
y += dy;
} else {
y -= dy;
}
} else if (flag & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) {
// x remains unchanged
} else {
int16_t dy;
if (!glyph.ReadS16(&dy)) {
return Error("Glyph too short %d (glyph %u)", glyph.length(), gid);
}
y += dy;
}
if (y < ymin) {
ymin = y;
adjusted_bbox = true;
}
if (y > ymax) {
ymax = y;
adjusted_bbox = true;
}
}

if (glyph.remaining() > 3) {
// We allow 0-3 bytes difference since gly_length is 4-bytes aligned,
// zero-padded length.
Warning("Extra bytes at end of the glyph: %d", glyph.remaining());
Warning("Extra bytes at end of the glyph: %d (glyph %u)", glyph.remaining(), gid);
}

this->iov.push_back(std::make_pair(glyph.buffer(), glyph.offset()));
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()));
}

return true;
}
Expand Down Expand Up @@ -297,7 +397,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, num_contours)) {
if (!ParseSimpleGlyph(glyph, i, num_contours, xmin, ymin, xmax, ymax)) {
return Error("Failed to parse glyph %d", i);
}
} else {
Expand Down
17 changes: 16 additions & 1 deletion src/glyf.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ class OpenTypeGLYF : public Table {
explicit OpenTypeGLYF(Font *font, uint32_t tag)
: Table(font, tag, tag), maxp(NULL) { }

~OpenTypeGLYF() {
for (auto* p : fixed_bboxes) {
delete[] p;
}
}

bool Parse(const uint8_t *data, size_t length);
bool Serialize(OTSStream *out);

Expand All @@ -36,9 +42,16 @@ class OpenTypeGLYF : public Table {

bool ParseFlagsForSimpleGlyph(Buffer &glyph,
uint32_t num_flags,
std::vector<uint8_t>& flags,
uint32_t *flag_index,
uint32_t *coordinates_length);
bool ParseSimpleGlyph(Buffer &glyph, int16_t num_contours);
bool ParseSimpleGlyph(Buffer &glyph,
unsigned gid,
int16_t num_contours,
int16_t& xmin,
int16_t& ymin,
int16_t& xmax,
int16_t& ymax);
bool ParseCompositeGlyph(
Buffer &glyph,
ComponentPointCount* component_point_count);
Expand All @@ -59,6 +72,8 @@ class OpenTypeGLYF : public Table {
OpenTypeMAXP* maxp;

std::vector<std::pair<const uint8_t*, size_t> > iov;

std::vector<uint8_t*> fixed_bboxes;
};

} // namespace ots
Expand Down

0 comments on commit 51515e5

Please sign in to comment.