diff --git a/src/glyf.cc b/src/glyf.cc index 8e908a3d..0ed9515e 100644 --- a/src/glyf.cc +++ b/src/glyf.cc @@ -18,6 +18,7 @@ namespace ots { bool OpenTypeGLYF::ParseFlagsForSimpleGlyph(Buffer &glyph, uint32_t num_flags, + std::vector& flags, uint32_t *flag_index, uint32_t *coordinates_length) { uint8_t flag = 0; @@ -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); @@ -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); } } @@ -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; } @@ -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 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; } @@ -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 { diff --git a/src/glyf.h b/src/glyf.h index 08c585df..05e846f1 100644 --- a/src/glyf.h +++ b/src/glyf.h @@ -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); @@ -36,9 +42,16 @@ class OpenTypeGLYF : public Table { bool ParseFlagsForSimpleGlyph(Buffer &glyph, uint32_t num_flags, + std::vector& 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); @@ -59,6 +72,8 @@ class OpenTypeGLYF : public Table { OpenTypeMAXP* maxp; std::vector > iov; + + std::vector fixed_bboxes; }; } // namespace ots