| Index: third_party/woff2/src/glyph.cc
|
| diff --git a/third_party/woff2/src/glyph.cc b/third_party/woff2/src/glyph.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4cef0d9be828f357a221fa47a5cec86b31254fbf
|
| --- /dev/null
|
| +++ b/third_party/woff2/src/glyph.cc
|
| @@ -0,0 +1,380 @@
|
| +// Copyright 2013 Google Inc. All Rights Reserved.
|
| +//
|
| +// Licensed under the Apache License, Version 2.0 (the "License");
|
| +// you may not use this file except in compliance with the License.
|
| +// You may obtain a copy of the License at
|
| +//
|
| +// http://www.apache.org/licenses/LICENSE-2.0
|
| +//
|
| +// Unless required by applicable law or agreed to in writing, software
|
| +// distributed under the License is distributed on an "AS IS" BASIS,
|
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +// See the License for the specific language governing permissions and
|
| +// limitations under the License.
|
| +//
|
| +// Glyph manipulation
|
| +
|
| +#include "./glyph.h"
|
| +
|
| +#include <stdlib.h>
|
| +#include <limits>
|
| +#include "./buffer.h"
|
| +#include "./store_bytes.h"
|
| +
|
| +namespace woff2 {
|
| +
|
| +static const int32_t kFLAG_ONCURVE = 1;
|
| +static const int32_t kFLAG_XSHORT = 1 << 1;
|
| +static const int32_t kFLAG_YSHORT = 1 << 2;
|
| +static const int32_t kFLAG_REPEAT = 1 << 3;
|
| +static const int32_t kFLAG_XREPEATSIGN = 1 << 4;
|
| +static const int32_t kFLAG_YREPEATSIGN = 1 << 5;
|
| +static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
|
| +static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3;
|
| +static const int32_t kFLAG_MORE_COMPONENTS = 1 << 5;
|
| +static const int32_t kFLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
|
| +static const int32_t kFLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
| +static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
|
| +
|
| +bool ReadCompositeGlyphData(Buffer* buffer, Glyph* glyph) {
|
| + glyph->have_instructions = false;
|
| + glyph->composite_data = buffer->buffer() + buffer->offset();
|
| + size_t start_offset = buffer->offset();
|
| + uint16_t flags = kFLAG_MORE_COMPONENTS;
|
| + while (flags & kFLAG_MORE_COMPONENTS) {
|
| + if (!buffer->ReadU16(&flags)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + glyph->have_instructions |= (flags & kFLAG_WE_HAVE_INSTRUCTIONS) != 0;
|
| + size_t arg_size = 2; // glyph index
|
| + if (flags & kFLAG_ARG_1_AND_2_ARE_WORDS) {
|
| + arg_size += 4;
|
| + } else {
|
| + arg_size += 2;
|
| + }
|
| + if (flags & kFLAG_WE_HAVE_A_SCALE) {
|
| + arg_size += 2;
|
| + } else if (flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
|
| + arg_size += 4;
|
| + } else if (flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) {
|
| + arg_size += 8;
|
| + }
|
| + if (!buffer->Skip(arg_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + }
|
| + if (buffer->offset() - start_offset > std::numeric_limits<uint32_t>::max()) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + glyph->composite_data_size = buffer->offset() - start_offset;
|
| + return true;
|
| +}
|
| +
|
| +bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) {
|
| + Buffer buffer(data, len);
|
| +
|
| + int16_t num_contours;
|
| + if (!buffer.ReadS16(&num_contours)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| +
|
| + if (num_contours == 0) {
|
| + // Empty glyph.
|
| + return true;
|
| + }
|
| +
|
| + // Read the bounding box.
|
| + if (!buffer.ReadS16(&glyph->x_min) ||
|
| + !buffer.ReadS16(&glyph->y_min) ||
|
| + !buffer.ReadS16(&glyph->x_max) ||
|
| + !buffer.ReadS16(&glyph->y_max)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| +
|
| + if (num_contours > 0) {
|
| + // Simple glyph.
|
| + glyph->contours.resize(num_contours);
|
| +
|
| + // Read the number of points per contour.
|
| + uint16_t last_point_index = 0;
|
| + for (int i = 0; i < num_contours; ++i) {
|
| + uint16_t point_index;
|
| + if (!buffer.ReadU16(&point_index)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + uint16_t num_points = point_index - last_point_index + (i == 0 ? 1 : 0);
|
| + glyph->contours[i].resize(num_points);
|
| + last_point_index = point_index;
|
| + }
|
| +
|
| + // Read the instructions.
|
| + if (!buffer.ReadU16(&glyph->instructions_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + glyph->instructions_data = data + buffer.offset();
|
| + if (!buffer.Skip(glyph->instructions_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| +
|
| + // Read the run-length coded flags.
|
| + std::vector<std::vector<uint8_t> > flags(num_contours);
|
| + uint8_t flag = 0;
|
| + uint8_t flag_repeat = 0;
|
| + for (int i = 0; i < num_contours; ++i) {
|
| + flags[i].resize(glyph->contours[i].size());
|
| + for (int j = 0; j < glyph->contours[i].size(); ++j) {
|
| + if (flag_repeat == 0) {
|
| + if (!buffer.ReadU8(&flag)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + if (flag & kFLAG_REPEAT) {
|
| + if (!buffer.ReadU8(&flag_repeat)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + }
|
| + } else {
|
| + flag_repeat--;
|
| + }
|
| + flags[i][j] = flag;
|
| + glyph->contours[i][j].on_curve = flag & kFLAG_ONCURVE;
|
| + }
|
| + }
|
| +
|
| + // Read the x coordinates.
|
| + int prev_x = 0;
|
| + for (int i = 0; i < num_contours; ++i) {
|
| + for (int j = 0; j < glyph->contours[i].size(); ++j) {
|
| + uint8_t flag = flags[i][j];
|
| + if (flag & kFLAG_XSHORT) {
|
| + // single byte x-delta coord value
|
| + uint8_t x_delta;
|
| + if (!buffer.ReadU8(&x_delta)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + int sign = (flag & kFLAG_XREPEATSIGN) ? 1 : -1;
|
| + glyph->contours[i][j].x = prev_x + sign * x_delta;
|
| + } else {
|
| + // double byte x-delta coord value
|
| + int16_t x_delta = 0;
|
| + if (!(flag & kFLAG_XREPEATSIGN)) {
|
| + if (!buffer.ReadS16(&x_delta)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + }
|
| + glyph->contours[i][j].x = prev_x + x_delta;
|
| + }
|
| + prev_x = glyph->contours[i][j].x;
|
| + }
|
| + }
|
| +
|
| + // Read the y coordinates.
|
| + int prev_y = 0;
|
| + for (int i = 0; i < num_contours; ++i) {
|
| + for (int j = 0; j < glyph->contours[i].size(); ++j) {
|
| + uint8_t flag = flags[i][j];
|
| + if (flag & kFLAG_YSHORT) {
|
| + // single byte y-delta coord value
|
| + uint8_t y_delta;
|
| + if (!buffer.ReadU8(&y_delta)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + int sign = (flag & kFLAG_YREPEATSIGN) ? 1 : -1;
|
| + glyph->contours[i][j].y = prev_y + sign * y_delta;
|
| + } else {
|
| + // double byte y-delta coord value
|
| + int16_t y_delta = 0;
|
| + if (!(flag & kFLAG_YREPEATSIGN)) {
|
| + if (!buffer.ReadS16(&y_delta)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + }
|
| + glyph->contours[i][j].y = prev_y + y_delta;
|
| + }
|
| + prev_y = glyph->contours[i][j].y;
|
| + }
|
| + }
|
| + } else if (num_contours == -1) {
|
| + // Composite glyph.
|
| + if (!ReadCompositeGlyphData(&buffer, glyph)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + // Read the instructions.
|
| + if (glyph->have_instructions) {
|
| + if (!buffer.ReadU16(&glyph->instructions_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + glyph->instructions_data = data + buffer.offset();
|
| + if (!buffer.Skip(glyph->instructions_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + } else {
|
| + glyph->instructions_size = 0;
|
| + }
|
| + } else {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +void StoreBbox(const Glyph& glyph, size_t* offset, uint8_t* dst) {
|
| + Store16(glyph.x_min, offset, dst);
|
| + Store16(glyph.y_min, offset, dst);
|
| + Store16(glyph.x_max, offset, dst);
|
| + Store16(glyph.y_max, offset, dst);
|
| +}
|
| +
|
| +void StoreInstructions(const Glyph& glyph, size_t* offset, uint8_t* dst) {
|
| + Store16(glyph.instructions_size, offset, dst);
|
| + StoreBytes(glyph.instructions_data, glyph.instructions_size, offset, dst);
|
| +}
|
| +
|
| +bool StoreEndPtsOfContours(const Glyph& glyph, size_t* offset, uint8_t* dst) {
|
| + int end_point = -1;
|
| + for (const auto& contour : glyph.contours) {
|
| + end_point += contour.size();
|
| + if (contour.size() > std::numeric_limits<uint16_t>::max() ||
|
| + end_point > std::numeric_limits<uint16_t>::max()) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + Store16(end_point, offset, dst);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool StorePoints(const Glyph& glyph, size_t* offset,
|
| + uint8_t* dst, size_t dst_size) {
|
| + int last_flag = -1;
|
| + int repeat_count = 0;
|
| + int last_x = 0;
|
| + int last_y = 0;
|
| + size_t x_bytes = 0;
|
| + size_t y_bytes = 0;
|
| +
|
| + // Store the flags and calculate the total size of the x and y coordinates.
|
| + for (const auto& contour : glyph.contours) {
|
| + for (const auto& point : contour) {
|
| + int flag = point.on_curve ? kFLAG_ONCURVE : 0;
|
| + int dx = point.x - last_x;
|
| + int dy = point.y - last_y;
|
| + if (dx == 0) {
|
| + flag |= kFLAG_XREPEATSIGN;
|
| + } else if (dx > -256 && dx < 256) {
|
| + flag |= kFLAG_XSHORT | (dx > 0 ? kFLAG_XREPEATSIGN : 0);
|
| + x_bytes += 1;
|
| + } else {
|
| + x_bytes += 2;
|
| + }
|
| + if (dy == 0) {
|
| + flag |= kFLAG_YREPEATSIGN;
|
| + } else if (dy > -256 && dy < 256) {
|
| + flag |= kFLAG_YSHORT | (dy > 0 ? kFLAG_YREPEATSIGN : 0);
|
| + y_bytes += 1;
|
| + } else {
|
| + y_bytes += 2;
|
| + }
|
| + if (flag == last_flag && repeat_count != 255) {
|
| + dst[*offset - 1] |= kFLAG_REPEAT;
|
| + repeat_count++;
|
| + } else {
|
| + if (repeat_count != 0) {
|
| + if (*offset >= dst_size) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + dst[(*offset)++] = repeat_count;
|
| + }
|
| + if (*offset >= dst_size) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + dst[(*offset)++] = flag;
|
| + repeat_count = 0;
|
| + }
|
| + last_x = point.x;
|
| + last_y = point.y;
|
| + last_flag = flag;
|
| + }
|
| + }
|
| + if (repeat_count != 0) {
|
| + if (*offset >= dst_size) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + dst[(*offset)++] = repeat_count;
|
| + }
|
| +
|
| + if (*offset + x_bytes + y_bytes > dst_size) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| +
|
| + // Store the x and y coordinates.
|
| + size_t x_offset = *offset;
|
| + size_t y_offset = *offset + x_bytes;
|
| + last_x = 0;
|
| + last_y = 0;
|
| + for (const auto& contour : glyph.contours) {
|
| + for (const auto& point : contour) {
|
| + int dx = point.x - last_x;
|
| + int dy = point.y - last_y;
|
| + if (dx == 0) {
|
| + // pass
|
| + } else if (dx > -256 && dx < 256) {
|
| + dst[x_offset++] = std::abs(dx);
|
| + } else {
|
| + Store16(dx, &x_offset, dst);
|
| + }
|
| + if (dy == 0) {
|
| + // pass
|
| + } else if (dy > -256 && dy < 256) {
|
| + dst[y_offset++] = std::abs(dy);
|
| + } else {
|
| + Store16(dy, &y_offset, dst);
|
| + }
|
| + last_x += dx;
|
| + last_y += dy;
|
| + }
|
| + }
|
| + *offset = y_offset;
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size) {
|
| + size_t offset = 0;
|
| + if (glyph.composite_data_size > 0) {
|
| + // Composite glyph.
|
| + if (*dst_size < ((10ULL + glyph.composite_data_size) +
|
| + ((glyph.have_instructions ? 2ULL : 0) +
|
| + glyph.instructions_size))) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + Store16(-1, &offset, dst);
|
| + StoreBbox(glyph, &offset, dst);
|
| + StoreBytes(glyph.composite_data, glyph.composite_data_size, &offset, dst);
|
| + if (glyph.have_instructions) {
|
| + StoreInstructions(glyph, &offset, dst);
|
| + }
|
| + } else if (glyph.contours.size() > 0) {
|
| + // Simple glyph.
|
| + if (glyph.contours.size() > std::numeric_limits<int16_t>::max()) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + if (*dst_size < ((12ULL + 2 * glyph.contours.size()) +
|
| + glyph.instructions_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + Store16(glyph.contours.size(), &offset, dst);
|
| + StoreBbox(glyph, &offset, dst);
|
| + if (!StoreEndPtsOfContours(glyph, &offset, dst)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + StoreInstructions(glyph, &offset, dst);
|
| + if (!StorePoints(glyph, &offset, dst, *dst_size)) {
|
| + return FONT_COMPRESSION_FAILURE();
|
| + }
|
| + }
|
| + *dst_size = offset;
|
| + return true;
|
| +}
|
| +
|
| +} // namespace woff2
|
|
|