130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun/*
230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * Copyright 2011 Google Inc. All Rights Reserved.
330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun *
430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * Licensed under the Apache License, Version 2.0 (the "License");
530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * you may not use this file except in compliance with the License.
630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * You may obtain a copy of the License at
730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun *
830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun *      http://www.apache.org/licenses/LICENSE-2.0
930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun *
1030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * Unless required by applicable law or agreed to in writing, software
1130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * distributed under the License is distributed on an "AS IS" BASIS,
1230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * See the License for the specific language governing permissions and
1430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * limitations under the License.
1530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun */
1630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
1730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#include "sfntly/table/font_data_table.h"
1830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
1930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#include "sfntly/data/font_output_stream.h"
2030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
2130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunnamespace sfntly {
2230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
2330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun/******************************************************************************
2430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * FontDataTable class
2530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun ******************************************************************************/
2630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
2730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::FontDataTable(ReadableFontData* data) {
2830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  data_ = data;
2930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
3030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
3130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::~FontDataTable() {}
3230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
3330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunReadableFontData* FontDataTable::ReadFontData() {
3430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return data_;
3530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
3630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
3730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunint32_t FontDataTable::DataLength() {
3830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return data_->Length();
3930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
4030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
4130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunint32_t FontDataTable::Serialize(OutputStream* os) {
4230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return data_->CopyTo(os);
4330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
4430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
4530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunint32_t FontDataTable::Serialize(WritableFontData* data) {
4630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return data_->CopyTo(data);
4730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
4830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
4930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun/******************************************************************************
5030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun * FontDataTable::Builder class
5130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun ******************************************************************************/
5230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunCALLER_ATTACH WritableFontData* FontDataTable::Builder::Data() {
5330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  WritableFontDataPtr new_data;
5430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  if (model_changed_) {
5530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    if (!SubReadyToSerialize()) {
5630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#if !defined (SFNTLY_NO_EXCEPTION)
5730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      throw IOException("Table not ready to build.");
5830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#endif
5930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      return NULL;
6030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    }
6130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    int32_t size = SubDataSizeToSerialize();
6230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    new_data.Attach(WritableFontData::CreateWritableFontData(size));
6330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    SubSerialize(new_data);
6430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  } else {
6530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    ReadableFontDataPtr data = InternalReadData();
6630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    new_data.Attach(WritableFontData::CreateWritableFontData(
6730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun                        data != NULL ? data->Length() : 0));
6830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    if (data != NULL) {
6930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      data->CopyTo(new_data);
7030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    }
7130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  }
7230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return new_data.Detach();
7330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
7430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
7530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunvoid FontDataTable::Builder::SetData(ReadableFontData* data) {
7630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  InternalSetData(data, true);
7730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
7830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
7930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
8030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunCALLER_ATTACH FontDataTable* FontDataTable::Builder::Build() {
8130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  FontDataTablePtr table;  // NULL default table
8230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  ReadableFontDataPtr data = InternalReadData();
8330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  if (model_changed_) {
8430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    // Let subclass serialize from model.
8530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    if (!SubReadyToSerialize()) {
8630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#if !defined (SFNTLY_NO_EXCEPTION)
8730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      throw IOException("Table not ready to build.");
8830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#endif
8930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      return NULL;
9030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    }
9130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    int32_t size = SubDataSizeToSerialize();
9230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    WritableFontDataPtr new_data;
9330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    new_data.Attach(WritableFontData::CreateWritableFontData(size));
9430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    SubSerialize(new_data);
9530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    data = new_data;
9630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  }
9730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
9830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  if (data != NULL) {
9930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    table = SubBuildTable(data);
10030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    NotifyPostTableBuild(table);
10130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  }
10230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
10330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  r_data_.Release();
10430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  w_data_.Release();
10530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return table;
10630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
10730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
10830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunbool FontDataTable::Builder::ReadyToBuild() {
10930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return true;
11030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
11130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
11230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunReadableFontData* FontDataTable::Builder::InternalReadData() {
11330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return (r_data_ != NULL) ? r_data_.p_ :
11430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun                             static_cast<ReadableFontData*>(w_data_.p_);
11530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
11630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
11730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunWritableFontData* FontDataTable::Builder::InternalWriteData() {
11830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  if (w_data_ == NULL) {
11930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    WritableFontDataPtr new_data;
12030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    new_data.Attach(WritableFontData::CreateWritableFontData(
12130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun                        r_data_ == NULL ? 0 : r_data_->Length()));
12230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#if !defined (SFNTLY_NO_EXCEPTION)
12330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    try {
12430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#endif
12530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      if (r_data_) {
12630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun        r_data_->CopyTo(new_data);
12730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      }
12830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#if !defined (SFNTLY_NO_EXCEPTION)
12930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    } catch (IOException& e) {
13030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      // TODO(stuartg): fix when IOExceptions are cleaned up
13130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    }
13230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun#endif
13330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    InternalSetData(new_data, false);
13430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  }
13530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  return w_data_.p_;
13630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
13730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
13830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::Builder::Builder()
13930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    : model_changed_(false),
14030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      contained_model_changed_(false),
14130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      data_changed_(false) {
14230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
14330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
14430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::Builder::Builder(int32_t data_size)
14530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    : model_changed_(false),
14630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      contained_model_changed_(false),
14730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      data_changed_(false) {
14830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  w_data_.Attach(WritableFontData::CreateWritableFontData(data_size));
14930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
15030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
15130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::Builder::Builder(WritableFontData* data)
15230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    : model_changed_(false),
15330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      contained_model_changed_(false),
15430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      data_changed_(false) {
15530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  w_data_ = data;
15630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
15730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
15830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::Builder::Builder(ReadableFontData* data)
15930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    : model_changed_(false),
16030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      contained_model_changed_(false),
16130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun      data_changed_(false) {
16230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  r_data_ = data;
16330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
16430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
16530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim GurunFontDataTable::Builder::~Builder() {
16630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
16730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
16830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunvoid FontDataTable::Builder::NotifyPostTableBuild(FontDataTable* table) {
16930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  // Default: NOP.
17030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  UNREFERENCED_PARAMETER(table);
17130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
17230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
17330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunvoid FontDataTable::Builder::InternalSetData(WritableFontData* data,
17430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun                                             bool data_changed) {
17530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  w_data_ = data;
17630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  r_data_ = NULL;
17730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  if (data_changed) {
17830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    data_changed_ = true;
17930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    SubDataSet();
18030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  }
18130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
18230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
18330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurunvoid FontDataTable::Builder::InternalSetData(ReadableFontData* data,
18430d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun                                             bool data_changed) {
18530d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  w_data_ = NULL;
18630d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  r_data_ = data;
18730d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  if (data_changed) {
18830d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    data_changed_ = true;
18930d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun    SubDataSet();
19030d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun  }
19130d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}
19230d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun
19330d4e1f3d81ad9f7a1aa14ce6d2ceb5df56c15cdSelim Gurun}  // namespace sfntly
194