// 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 // for std::abs #include "./ots.h" #include "./font.h" #include "./glyph.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* 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* out, const std::vector& in) { for (int i = 0; i < in.size(); ++i) { out->push_back(in[i]); } } void WriteUShort(std::vector* out, int value) { out->push_back(value >> 8); out->push_back(value & 255); } void WriteLong(std::vector* 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* 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* 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 n_contour_stream_; std::vector n_points_stream_; std::vector flag_byte_stream_; std::vector composite_stream_; std::vector bbox_bitmap_; std::vector bbox_stream_; std::vector glyph_stream_; std::vector instruction_stream_; bool sbbox_; bool cbbox_; int n_glyphs_; }; } // namespace bool TransformGlyfAndLocaTables(Font* font) { 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 OTS_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 OTS_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