You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
11 KiB
380 lines
11 KiB
// 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
|
|
|