Skip to content

Commit

Permalink
Merge pull request #264 from jfkthame/glyf-bbox
Browse files Browse the repository at this point in the history
Check & fix bounding box of simple glyph records.
  • Loading branch information
khaledhosny authored Aug 16, 2023
2 parents 84636f3 + 51515e5 commit 6ba665a
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 6ba665a

Please sign in to comment.