Home | History | Annotate | Download | only in subtly
      1 /*
      2  * Copyright 2011 Google Inc. All Rights Reserved.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "subtly/font_assembler.h"
     18 
     19 #include <stdio.h>
     20 
     21 #include <set>
     22 #include <map>
     23 
     24 #include "sfntly/tag.h"
     25 #include "sfntly/font.h"
     26 #include "sfntly/font_factory.h"
     27 #include "sfntly/table/core/cmap_table.h"
     28 #include "sfntly/table/truetype/loca_table.h"
     29 #include "sfntly/table/truetype/glyph_table.h"
     30 #include "sfntly/table/core/maximum_profile_table.h"
     31 #include "sfntly/port/type.h"
     32 #include "sfntly/port/refcount.h"
     33 #include "subtly/font_info.h"
     34 
     35 namespace subtly {
     36 using namespace sfntly;
     37 
     38 FontAssembler::FontAssembler(FontInfo* font_info,
     39                              IntegerSet* table_blacklist)
     40     : table_blacklist_(table_blacklist) {
     41   font_info_ = font_info;
     42   Initialize();
     43 }
     44 
     45 FontAssembler::FontAssembler(FontInfo* font_info)
     46     : table_blacklist_(NULL) {
     47   font_info_ = font_info;
     48   Initialize();
     49 }
     50 
     51 void FontAssembler::Initialize() {
     52   font_factory_.Attach(sfntly::FontFactory::GetInstance());
     53   font_builder_.Attach(font_factory_->NewFontBuilder());
     54 }
     55 
     56 CALLER_ATTACH Font* FontAssembler::Assemble() {
     57   // Assemble tables we can subset.
     58   if (!AssembleCMapTable() || !AssembleGlyphAndLocaTables()) {
     59     return NULL;
     60   }
     61   // For all other tables, either include them unmodified or don't at all.
     62   const TableMap* common_table_map =
     63       font_info_->GetTableMap(font_info_->fonts()->begin()->first);
     64   for (TableMap::const_iterator it = common_table_map->begin(),
     65            e = common_table_map->end(); it != e; ++it) {
     66     if (table_blacklist_
     67         && table_blacklist_->find(it->first) != table_blacklist_->end()) {
     68       continue;
     69     }
     70     font_builder_->NewTableBuilder(it->first, it->second->ReadFontData());
     71   }
     72   return font_builder_->Build();
     73 }
     74 
     75 bool FontAssembler::AssembleCMapTable() {
     76   // Creating the new CMapTable and the new format 4 CMap
     77   Ptr<CMapTable::Builder> cmap_table_builder =
     78       down_cast<CMapTable::Builder*>
     79       (font_builder_->NewTableBuilder(Tag::cmap));
     80   if (!cmap_table_builder)
     81     return false;
     82   Ptr<CMapTable::CMapFormat4::Builder> cmap_builder =
     83       down_cast<CMapTable::CMapFormat4::Builder*>
     84       (cmap_table_builder->NewCMapBuilder(CMapFormat::kFormat4,
     85                                           CMapTable::WINDOWS_BMP));
     86   if (!cmap_builder)
     87     return false;
     88   // Creating the segments and the glyph id array
     89   CharacterMap* chars_to_glyph_ids = font_info_->chars_to_glyph_ids();
     90   SegmentList* segment_list = new SegmentList;
     91   IntegerList* glyph_id_array = new IntegerList;
     92   int32_t last_chararacter = -2;
     93   int32_t last_offset = 0;
     94   Ptr<CMapTable::CMapFormat4::Builder::Segment> current_segment;
     95 
     96   // For simplicity, we will have one segment per contiguous range.
     97   // To test the algorithm, we've replaced the original CMap with the CMap
     98   // generated by this code without removing any character.
     99   // Tuffy.ttf: CMap went from 3146 to 3972 bytes (1.7% to 2.17% of file)
    100   // AnonymousPro.ttf: CMap went from 1524 to 1900 bytes (0.96% to 1.2%)
    101   for (CharacterMap::iterator it = chars_to_glyph_ids->begin(),
    102            e = chars_to_glyph_ids->end(); it != e; ++it) {
    103     int32_t character = it->first;
    104     int32_t glyph_id = it->second.glyph_id();
    105     if (character != last_chararacter + 1) {  // new segment
    106       if (current_segment != NULL) {
    107         current_segment->set_end_count(last_chararacter);
    108         segment_list->push_back(current_segment);
    109       }
    110       // start_code = character
    111       // end_code = -1 (unknown for now)
    112       // id_delta = 0 (we don't use id_delta for this representation)
    113       // id_range_offset = last_offset (offset into the glyph_id_array)
    114       current_segment =
    115           new CMapTable::CMapFormat4::Builder::
    116           Segment(character, -1, 0, last_offset);
    117     }
    118     glyph_id_array->push_back(glyph_id);
    119     last_offset += DataSize::kSHORT;
    120     last_chararacter = character;
    121   }
    122   // The last segment is still open.
    123   current_segment->set_end_count(last_chararacter);
    124   segment_list->push_back(current_segment);
    125   // Updating the id_range_offset for every segment.
    126   for (int32_t i = 0, num_segs = segment_list->size(); i < num_segs; ++i) {
    127     Ptr<CMapTable::CMapFormat4::Builder::Segment> segment = segment_list->at(i);
    128     segment->set_id_range_offset(segment->id_range_offset()
    129                                  + (num_segs - i + 1) * DataSize::kSHORT);
    130   }
    131   // Adding the final, required segment.
    132   current_segment =
    133       new CMapTable::CMapFormat4::Builder::Segment(0xffff, 0xffff, 1, 0);
    134   segment_list->push_back(current_segment);
    135   // Writing the segments and glyph id array to the CMap
    136   cmap_builder->set_segments(segment_list);
    137   cmap_builder->set_glyph_id_array(glyph_id_array);
    138   delete segment_list;
    139   delete glyph_id_array;
    140   return true;
    141 }
    142 
    143 bool FontAssembler::AssembleGlyphAndLocaTables() {
    144   Ptr<LocaTable::Builder> loca_table_builder =
    145       down_cast<LocaTable::Builder*>
    146       (font_builder_->NewTableBuilder(Tag::loca));
    147   Ptr<GlyphTable::Builder> glyph_table_builder =
    148       down_cast<GlyphTable::Builder*>
    149       (font_builder_->NewTableBuilder(Tag::glyf));
    150 
    151   GlyphIdSet* resolved_glyph_ids = font_info_->resolved_glyph_ids();
    152   IntegerList loca_list;
    153   // Basic sanity check: all LOCA tables are of the same size
    154   // This is necessary but not suficient!
    155   int32_t previous_size = -1;
    156   for (FontIdMap::iterator it = font_info_->fonts()->begin();
    157        it != font_info_->fonts()->end(); ++it) {
    158     Ptr<LocaTable> loca_table =
    159         down_cast<LocaTable*>(font_info_->GetTable(it->first, Tag::loca));
    160     int32_t current_size = loca_table->header_length();
    161     if (previous_size != -1 && current_size != previous_size) {
    162       return false;
    163     }
    164     previous_size = current_size;
    165   }
    166 
    167   // Assuming all fonts referenced by the FontInfo are the subsets of the same
    168   // font, their loca tables should all have the same sizes.
    169   // We'll just get the size of the first font's LOCA table for simplicty.
    170   Ptr<LocaTable> first_loca_table =
    171     down_cast<LocaTable*>
    172     (font_info_->GetTable(font_info_->fonts()->begin()->first, Tag::loca));
    173   int32_t num_loca_glyphs = first_loca_table->num_glyphs();
    174   loca_list.resize(num_loca_glyphs);
    175   loca_list.push_back(0);
    176   int32_t last_glyph_id = 0;
    177   int32_t last_offset = 0;
    178   GlyphTable::GlyphBuilderList* glyph_builders =
    179       glyph_table_builder->GlyphBuilders();
    180 
    181   for (GlyphIdSet::iterator it = resolved_glyph_ids->begin(),
    182            e = resolved_glyph_ids->end(); it != e; ++it) {
    183     // Get the glyph for this resolved_glyph_id.
    184     int32_t resolved_glyph_id = it->glyph_id();
    185     int32_t font_id = it->font_id();
    186     // Get the LOCA table for the current glyph id.
    187     Ptr<LocaTable> loca_table =
    188         down_cast<LocaTable*>
    189         (font_info_->GetTable(font_id, Tag::loca));
    190     int32_t length = loca_table->GlyphLength(resolved_glyph_id);
    191     int32_t offset = loca_table->GlyphOffset(resolved_glyph_id);
    192 
    193     // Get the GLYF table for the current glyph id.
    194     Ptr<GlyphTable> glyph_table =
    195         down_cast<GlyphTable*>
    196         (font_info_->GetTable(font_id, Tag::glyf));
    197     GlyphPtr glyph;
    198     glyph.Attach(glyph_table->GetGlyph(offset, length));
    199 
    200     // The data reference by the glyph is copied into a new glyph and
    201     // added to the glyph_builders belonging to the glyph_table_builder.
    202     // When Build gets called, all the glyphs will be built.
    203     Ptr<ReadableFontData> data = glyph->ReadFontData();
    204     Ptr<WritableFontData> copy_data;
    205     copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length()));
    206     data->CopyTo(copy_data);
    207     GlyphBuilderPtr glyph_builder;
    208     glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data));
    209     glyph_builders->push_back(glyph_builder);
    210 
    211     // If there are missing glyphs between the last glyph_id and the
    212     // current resolved_glyph_id, since the LOCA table needs to have the same
    213     // size, the offset is kept the same.
    214     for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i)
    215       loca_list[i] = last_offset;
    216     last_offset += length;
    217     loca_list[resolved_glyph_id + 1] = last_offset;
    218     last_glyph_id = resolved_glyph_id + 1;
    219   }
    220   // If there are missing glyph ids, their loca entries must all point
    221   // to the same offset as the last valid glyph id making them all zero length.
    222   for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i)
    223     loca_list[i] = last_offset;
    224   loca_table_builder->SetLocaList(&loca_list);
    225   return true;
    226 }
    227 }
    228