Index: third_party/woff2/src/transform.cc |
diff --git a/third_party/woff2/src/transform.cc b/third_party/woff2/src/transform.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..44a47815408f7897bbefa79cd9a562f5f65252a7 |
--- /dev/null |
+++ b/third_party/woff2/src/transform.cc |
@@ -0,0 +1,270 @@ |
+// 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. |
+// |
+// Library for preprocessing fonts as part of the WOFF 2.0 conversion. |
+ |
+#include "./transform.h" |
+ |
+#include <complex> // for std::abs |
+ |
+#include "./buffer.h" |
+#include "./font.h" |
+#include "./glyph.h" |
+#include "./table_tags.h" |
+ |
+namespace woff2 { |
+ |
+namespace { |
+ |
+const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; |
+const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; |
+ |
+void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) { |
+ if (len == 0) return; |
+ size_t offset = out->size(); |
+ out->resize(offset + len); |
+ memcpy(&(*out)[offset], data, len); |
+} |
+ |
+void WriteBytes(std::vector<uint8_t>* out, const std::vector<uint8_t>& in) { |
+ for (int i = 0; i < in.size(); ++i) { |
+ out->push_back(in[i]); |
+ } |
+} |
+ |
+void WriteUShort(std::vector<uint8_t>* out, int value) { |
+ out->push_back(value >> 8); |
+ out->push_back(value & 255); |
+} |
+ |
+void WriteLong(std::vector<uint8_t>* out, int value) { |
+ out->push_back((value >> 24) & 255); |
+ out->push_back((value >> 16) & 255); |
+ out->push_back((value >> 8) & 255); |
+ out->push_back(value & 255); |
+} |
+ |
+void Write255UShort(std::vector<uint8_t>* out, int value) { |
+ if (value < 253) { |
+ out->push_back(value); |
+ } else if (value < 506) { |
+ out->push_back(255); |
+ out->push_back(value - 253); |
+ } else if (value < 762) { |
+ out->push_back(254); |
+ out->push_back(value - 506); |
+ } else { |
+ out->push_back(253); |
+ out->push_back(value >> 8); |
+ out->push_back(value & 0xff); |
+ } |
+} |
+ |
+// Glyf table preprocessing, based on |
+// GlyfEncoder.java |
+// but only the "sbbox" and "cbbox" options are supported. |
+class GlyfEncoder { |
+ public: |
+ explicit GlyfEncoder(int num_glyphs) |
+ : sbbox_(false), cbbox_(true), n_glyphs_(num_glyphs) { |
+ bbox_bitmap_.resize(((num_glyphs + 31) >> 5) << 2); |
+ } |
+ |
+ bool Encode(int glyph_id, const Glyph& glyph) { |
+ if (glyph.composite_data_size > 0) { |
+ WriteCompositeGlyph(glyph_id, glyph); |
+ } else if (glyph.contours.size() > 0) { |
+ WriteSimpleGlyph(glyph_id, glyph); |
+ } else { |
+ WriteUShort(&n_contour_stream_, 0); |
+ } |
+ return true; |
+ } |
+ |
+ void GetTransformedGlyfBytes(std::vector<uint8_t>* result) { |
+ WriteLong(result, 0); // version |
+ WriteUShort(result, n_glyphs_); |
+ WriteUShort(result, 0); // index_format, will be set later |
+ WriteLong(result, n_contour_stream_.size()); |
+ WriteLong(result, n_points_stream_.size()); |
+ WriteLong(result, flag_byte_stream_.size()); |
+ WriteLong(result, glyph_stream_.size()); |
+ WriteLong(result, composite_stream_.size()); |
+ WriteLong(result, bbox_bitmap_.size() + bbox_stream_.size()); |
+ WriteLong(result, instruction_stream_.size()); |
+ WriteBytes(result, n_contour_stream_); |
+ WriteBytes(result, n_points_stream_); |
+ WriteBytes(result, flag_byte_stream_); |
+ WriteBytes(result, glyph_stream_); |
+ WriteBytes(result, composite_stream_); |
+ WriteBytes(result, bbox_bitmap_); |
+ WriteBytes(result, bbox_stream_); |
+ WriteBytes(result, instruction_stream_); |
+ } |
+ |
+ private: |
+ void WriteInstructions(const Glyph& glyph) { |
+ Write255UShort(&glyph_stream_, glyph.instructions_size); |
+ WriteBytes(&instruction_stream_, |
+ glyph.instructions_data, glyph.instructions_size); |
+ } |
+ |
+ void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) { |
+ int num_contours = glyph.contours.size(); |
+ WriteUShort(&n_contour_stream_, num_contours); |
+ if (sbbox_) { |
+ WriteBbox(glyph_id, glyph); |
+ } |
+ // TODO: check that bbox matches, write bbox if not |
+ for (int i = 0; i < num_contours; i++) { |
+ Write255UShort(&n_points_stream_, glyph.contours[i].size()); |
+ } |
+ int lastX = 0; |
+ int lastY = 0; |
+ for (int i = 0; i < num_contours; i++) { |
+ int num_points = glyph.contours[i].size(); |
+ for (int j = 0; j < num_points; j++) { |
+ int x = glyph.contours[i][j].x; |
+ int y = glyph.contours[i][j].y; |
+ int dx = x - lastX; |
+ int dy = y - lastY; |
+ WriteTriplet(glyph.contours[i][j].on_curve, dx, dy); |
+ lastX = x; |
+ lastY = y; |
+ } |
+ } |
+ if (num_contours > 0) { |
+ WriteInstructions(glyph); |
+ } |
+ } |
+ |
+ void WriteCompositeGlyph(int glyph_id, const Glyph& glyph) { |
+ WriteUShort(&n_contour_stream_, -1); |
+ if (cbbox_) { |
+ WriteBbox(glyph_id, glyph); |
+ } |
+ WriteBytes(&composite_stream_, |
+ glyph.composite_data, |
+ glyph.composite_data_size); |
+ if (glyph.have_instructions) { |
+ WriteInstructions(glyph); |
+ } |
+ } |
+ |
+ void WriteBbox(int glyph_id, const Glyph& glyph) { |
+ bbox_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7); |
+ WriteUShort(&bbox_stream_, glyph.x_min); |
+ WriteUShort(&bbox_stream_, glyph.y_min); |
+ WriteUShort(&bbox_stream_, glyph.x_max); |
+ WriteUShort(&bbox_stream_, glyph.y_max); |
+ } |
+ |
+ void WriteTriplet(bool on_curve, int x, int y) { |
+ int abs_x = std::abs(x); |
+ int abs_y = std::abs(y); |
+ int on_curve_bit = on_curve ? 0 : 128; |
+ int x_sign_bit = (x < 0) ? 0 : 1; |
+ int y_sign_bit = (y < 0) ? 0 : 1; |
+ int xy_sign_bits = x_sign_bit + 2 * y_sign_bit; |
+ if (x == 0 && abs_y < 1280) { |
+ flag_byte_stream_.push_back(on_curve_bit + |
+ ((abs_y & 0xf00) >> 7) + y_sign_bit); |
+ glyph_stream_.push_back(abs_y & 0xff); |
+ } else if (y == 0 && abs_x < 1280) { |
+ flag_byte_stream_.push_back(on_curve_bit + 10 + |
+ ((abs_x & 0xf00) >> 7) + x_sign_bit); |
+ glyph_stream_.push_back(abs_x & 0xff); |
+ } else if (abs_x < 65 && abs_y < 65) { |
+ flag_byte_stream_.push_back(on_curve_bit + 20 + |
+ ((abs_x - 1) & 0x30) + |
+ (((abs_y - 1) & 0x30) >> 2) + |
+ xy_sign_bits); |
+ glyph_stream_.push_back((((abs_x - 1) & 0xf) << 4) | ((abs_y - 1) & 0xf)); |
+ } else if (abs_x < 769 && abs_y < 769) { |
+ flag_byte_stream_.push_back(on_curve_bit + 84 + |
+ 12 * (((abs_x - 1) & 0x300) >> 8) + |
+ (((abs_y - 1) & 0x300) >> 6) + xy_sign_bits); |
+ glyph_stream_.push_back((abs_x - 1) & 0xff); |
+ glyph_stream_.push_back((abs_y - 1) & 0xff); |
+ } else if (abs_x < 4096 && abs_y < 4096) { |
+ flag_byte_stream_.push_back(on_curve_bit + 120 + xy_sign_bits); |
+ glyph_stream_.push_back(abs_x >> 4); |
+ glyph_stream_.push_back(((abs_x & 0xf) << 4) | (abs_y >> 8)); |
+ glyph_stream_.push_back(abs_y & 0xff); |
+ } else { |
+ flag_byte_stream_.push_back(on_curve_bit + 124 + xy_sign_bits); |
+ glyph_stream_.push_back(abs_x >> 8); |
+ glyph_stream_.push_back(abs_x & 0xff); |
+ glyph_stream_.push_back(abs_y >> 8); |
+ glyph_stream_.push_back(abs_y & 0xff); |
+ } |
+ } |
+ |
+ std::vector<uint8_t> n_contour_stream_; |
+ std::vector<uint8_t> n_points_stream_; |
+ std::vector<uint8_t> flag_byte_stream_; |
+ std::vector<uint8_t> composite_stream_; |
+ std::vector<uint8_t> bbox_bitmap_; |
+ std::vector<uint8_t> bbox_stream_; |
+ std::vector<uint8_t> glyph_stream_; |
+ std::vector<uint8_t> instruction_stream_; |
+ bool sbbox_; |
+ bool cbbox_; |
+ int n_glyphs_; |
+}; |
+ |
+} // namespace |
+ |
+bool TransformGlyfAndLocaTables(Font* font) { |
+ // no transform for CFF |
+ if (font->FindTable(kCffTableTag) != NULL |
+ && font->FindTable(kGlyfTableTag) == NULL |
+ && font->FindTable(kLocaTableTag) == NULL) { |
+ return true; |
+ } |
+ Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080]; |
+ Font::Table* transformed_loca = &font->tables[kLocaTableTag ^ 0x80808080]; |
+ |
+ int num_glyphs = NumGlyphs(*font); |
+ GlyfEncoder encoder(num_glyphs); |
+ for (int i = 0; i < num_glyphs; ++i) { |
+ Glyph glyph; |
+ const uint8_t* glyph_data; |
+ size_t glyph_size; |
+ if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) || |
+ (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) { |
+ return FONT_COMPRESSION_FAILURE(); |
+ } |
+ encoder.Encode(i, glyph); |
+ } |
+ encoder.GetTransformedGlyfBytes(&transformed_glyf->buffer); |
+ |
+ const Font::Table* head_table = font->FindTable(kHeadTableTag); |
+ if (head_table == NULL || head_table->length < 52) { |
+ return FONT_COMPRESSION_FAILURE(); |
+ } |
+ transformed_glyf->buffer[7] = head_table->data[51]; // index_format |
+ |
+ transformed_glyf->tag = kGlyfTableTag ^ 0x80808080; |
+ transformed_glyf->length = transformed_glyf->buffer.size(); |
+ transformed_glyf->data = transformed_glyf->buffer.data(); |
+ |
+ transformed_loca->tag = kLocaTableTag ^ 0x80808080; |
+ transformed_loca->length = 0; |
+ transformed_loca->data = NULL; |
+ |
+ return true; |
+} |
+ |
+} // namespace woff2 |