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 "sfntly/table/core/name_table.h"
18
19#include <stdio.h>
20#include <string.h>
21
22#include <unicode/unistr.h>
23
24#include "sfntly/font.h"
25#include "sfntly/port/exception_type.h"
26
27namespace sfntly {
28/******************************************************************************
29 * NameTable::NameEntryId class
30 ******************************************************************************/
31NameTable::NameEntryId::NameEntryId()
32    : platform_id_(0),
33      encoding_id_(0),
34      language_id_(0),
35      name_id_(0) {
36}
37
38NameTable::NameEntryId::NameEntryId(int32_t platform_id,
39                                    int32_t encoding_id,
40                                    int32_t language_id,
41                                    int32_t name_id)
42    : platform_id_(platform_id),
43      encoding_id_(encoding_id),
44      language_id_(language_id),
45      name_id_(name_id) {
46}
47
48NameTable::NameEntryId::NameEntryId(const NameTable::NameEntryId& rhs) {
49  *this = rhs;
50}
51
52const NameTable::NameEntryId&
53    NameTable::NameEntryId::operator=(const NameTable::NameEntryId& rhs) const {
54  platform_id_ = rhs.platform_id_;
55  encoding_id_ = rhs.encoding_id_;
56  language_id_ = rhs.language_id_;
57  name_id_ = rhs.name_id_;
58  return *this;
59}
60
61bool NameTable::NameEntryId::operator==(const NameEntryId& rhs) const {
62  return platform_id_ == rhs.platform_id_ &&
63         encoding_id_ == rhs.encoding_id_ &&
64         language_id_ == rhs.language_id_ &&
65         name_id_ == rhs.name_id_;
66}
67
68bool NameTable::NameEntryId::operator<(const NameEntryId& rhs) const {
69  if (platform_id_ != rhs.platform_id_) return platform_id_ < rhs.platform_id_;
70  if (encoding_id_ != rhs.encoding_id_) return encoding_id_ < rhs.encoding_id_;
71  if (language_id_ != rhs.language_id_) return language_id_ < rhs.language_id_;
72  return name_id_ < rhs.name_id_;
73}
74
75/******************************************************************************
76 * NameTable::NameEntry class
77 ******************************************************************************/
78NameTable::NameEntry::NameEntry() {
79  Init(0, 0, 0, 0, NULL);
80}
81
82NameTable::NameEntry::NameEntry(const NameEntryId& name_entry_id,
83                                const ByteVector& name_bytes) {
84  Init(name_entry_id.platform_id(),
85       name_entry_id.encoding_id(),
86       name_entry_id.language_id(),
87       name_entry_id.name_id(),
88       &name_bytes);
89}
90
91NameTable::NameEntry::NameEntry(int32_t platform_id,
92                                int32_t encoding_id,
93                                int32_t language_id,
94                                int32_t name_id,
95                                const ByteVector& name_bytes) {
96  Init(platform_id, encoding_id, language_id, name_id, &name_bytes);
97}
98
99NameTable::NameEntry::~NameEntry() {}
100
101ByteVector* NameTable::NameEntry::NameAsBytes() {
102  return &name_bytes_;
103}
104
105int32_t NameTable::NameEntry::NameBytesLength() {
106  return name_bytes_.size();
107}
108
109UChar* NameTable::NameEntry::Name() {
110  return NameTable::ConvertFromNameBytes(&name_bytes_,
111                                         platform_id(),
112                                         encoding_id());
113}
114
115bool NameTable::NameEntry::operator==(const NameEntry& rhs) const {
116  return (name_entry_id_ == rhs.name_entry_id_ &&
117          name_bytes_ == rhs.name_bytes_);
118}
119
120void NameTable::NameEntry::Init(int32_t platform_id,
121                                int32_t encoding_id,
122                                int32_t language_id,
123                                int32_t name_id,
124                                const ByteVector* name_bytes) {
125  name_entry_id_ = NameEntryId(platform_id, encoding_id, language_id, name_id);
126  if (name_bytes) {
127    name_bytes_ = *name_bytes;
128  } else {
129    name_bytes_.clear();
130  }
131}
132
133/******************************************************************************
134 * NameTable::NameEntryBuilder class
135 ******************************************************************************/
136NameTable::NameEntryBuilder::NameEntryBuilder() {
137  Init(0, 0, 0, 0, NULL);
138}
139
140NameTable::NameEntryBuilder::NameEntryBuilder(const NameEntryId& name_entry_id,
141                                              const ByteVector& name_bytes) {
142  Init(name_entry_id.platform_id(),
143       name_entry_id.encoding_id(),
144       name_entry_id.language_id(),
145       name_entry_id.name_id(),
146       &name_bytes);
147}
148
149NameTable::NameEntryBuilder::NameEntryBuilder(
150    const NameEntryId& name_entry_id) {
151  Init(name_entry_id.platform_id(),
152       name_entry_id.encoding_id(),
153       name_entry_id.language_id(),
154       name_entry_id.name_id(),
155       NULL);
156}
157
158NameTable::NameEntryBuilder::NameEntryBuilder(NameEntry* b) {
159  Init(b->platform_id(),
160       b->encoding_id(),
161       b->language_id(),
162       b->name_id(),
163       b->NameAsBytes());
164}
165
166NameTable::NameEntryBuilder::~NameEntryBuilder() {}
167
168void NameTable::NameEntryBuilder::SetName(const UChar* name) {
169  if (name == NULL) {
170    name_entry_->name_bytes_.clear();
171    return;
172  }
173  NameTable::ConvertToNameBytes(name,
174                                name_entry_->platform_id(),
175                                name_entry_->encoding_id(),
176                                &name_entry_->name_bytes_);
177}
178
179void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes) {
180  name_entry_->name_bytes_.clear();
181  std::copy(name_bytes.begin(),
182            name_bytes.end(),
183            name_entry_->name_bytes_.begin());
184}
185
186void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes,
187                                          int32_t offset,
188                                          int32_t length) {
189  name_entry_->name_bytes_.clear();
190  std::copy(name_bytes.begin() + offset,
191            name_bytes.begin() + offset + length,
192            name_entry_->name_bytes_.begin());
193}
194
195void NameTable::NameEntryBuilder::Init(int32_t platform_id,
196                                       int32_t encoding_id,
197                                       int32_t language_id,
198                                       int32_t name_id,
199                                       const ByteVector* name_bytes) {
200  name_entry_ = new NameEntry();
201  name_entry_->Init(platform_id, encoding_id, language_id, name_id, name_bytes);
202}
203
204/******************************************************************************
205 * NameTable::NameEntryFilterInPlace class (C++ port only)
206 ******************************************************************************/
207NameTable::NameEntryFilterInPlace::NameEntryFilterInPlace(int32_t platform_id,
208                                                          int32_t encoding_id,
209                                                          int32_t language_id,
210                                                          int32_t name_id)
211    : platform_id_(platform_id),
212      encoding_id_(encoding_id),
213      language_id_(language_id),
214      name_id_(name_id) {
215}
216
217bool NameTable::NameEntryFilterInPlace::Accept(int32_t platform_id,
218                                               int32_t encoding_id,
219                                               int32_t language_id,
220                                               int32_t name_id) {
221  return (platform_id_ == platform_id &&
222          encoding_id_ == encoding_id &&
223          language_id_ == language_id &&
224          name_id_ == name_id);
225}
226
227/******************************************************************************
228 * NameTable::NameEntryIterator class
229 ******************************************************************************/
230NameTable::NameEntryIterator::NameEntryIterator(NameTable* table)
231    : RefIterator<NameEntry, NameTable>(table),
232      name_index_(0),
233      filter_(NULL) {
234}
235
236NameTable::NameEntryIterator::NameEntryIterator(NameTable* table,
237                                                NameEntryFilter* filter)
238    : RefIterator<NameEntry, NameTable>(table),
239      name_index_(0),
240      filter_(filter) {
241}
242
243bool NameTable::NameEntryIterator::HasNext() {
244  if (!filter_) {
245    if (name_index_ < container()->NameCount()) {
246      return true;
247    }
248    return false;
249  }
250  for (; name_index_ < container()->NameCount(); ++name_index_) {
251    if (filter_->Accept(container()->PlatformId(name_index_),
252                        container()->EncodingId(name_index_),
253                        container()->LanguageId(name_index_),
254                        container()->NameId(name_index_))) {
255      return true;
256    }
257  }
258  return false;
259}
260
261CALLER_ATTACH NameTable::NameEntry* NameTable::NameEntryIterator::Next() {
262  if (!HasNext())
263    return NULL;
264  return container()->GetNameEntry(name_index_++);
265}
266
267/******************************************************************************
268 * NameTable::Builder class
269 ******************************************************************************/
270NameTable::Builder::Builder(Header* header, WritableFontData* data)
271    : SubTableContainerTable::Builder(header, data) {
272}
273
274NameTable::Builder::Builder(Header* header, ReadableFontData* data)
275    : SubTableContainerTable::Builder(header, data) {
276}
277
278CALLER_ATTACH NameTable::Builder*
279    NameTable::Builder::CreateBuilder(Header* header,
280                                      WritableFontData* data) {
281  Ptr<NameTable::Builder> builder;
282  builder = new NameTable::Builder(header, data);
283  return builder.Detach();
284}
285
286void NameTable::Builder::RevertNames() {
287  name_entry_map_.clear();
288  set_model_changed(false);
289}
290
291int32_t NameTable::Builder::BuilderCount() {
292  GetNameBuilders();  // Ensure name_entry_map_ is built.
293  return (int32_t)name_entry_map_.size();
294}
295
296bool NameTable::Builder::Has(int32_t platform_id,
297                             int32_t encoding_id,
298                             int32_t language_id,
299                             int32_t name_id) {
300  NameEntryId probe(platform_id, encoding_id, language_id, name_id);
301  GetNameBuilders();  // Ensure name_entry_map_ is built.
302  return (name_entry_map_.find(probe) != name_entry_map_.end());
303}
304
305CALLER_ATTACH NameTable::NameEntryBuilder*
306    NameTable::Builder::NameBuilder(int32_t platform_id,
307                                    int32_t encoding_id,
308                                    int32_t language_id,
309                                    int32_t name_id) {
310  NameEntryId probe(platform_id, encoding_id, language_id, name_id);
311  NameEntryBuilderMap builders;
312  GetNameBuilders();  // Ensure name_entry_map_ is built.
313  if (name_entry_map_.find(probe) != name_entry_map_.end()) {
314    return name_entry_map_[probe];
315  }
316  NameEntryBuilderPtr builder = new NameEntryBuilder(probe);
317  name_entry_map_[probe] = builder;
318  return builder.Detach();
319}
320
321bool NameTable::Builder::Remove(int32_t platform_id,
322                                int32_t encoding_id,
323                                int32_t language_id,
324                                int32_t name_id) {
325  NameEntryId probe(platform_id, encoding_id, language_id, name_id);
326  GetNameBuilders();  // Ensure name_entry_map_ is built.
327  NameEntryBuilderMap::iterator position = name_entry_map_.find(probe);
328  if (position != name_entry_map_.end()) {
329    name_entry_map_.erase(position);
330    return true;
331  }
332  return false;
333}
334
335CALLER_ATTACH FontDataTable*
336    NameTable::Builder::SubBuildTable(ReadableFontData* data) {
337  FontDataTablePtr table = new NameTable(header(), data);
338  return table.Detach();
339}
340
341void NameTable::Builder::SubDataSet() {
342  name_entry_map_.clear();
343  set_model_changed(false);
344}
345
346int32_t NameTable::Builder::SubDataSizeToSerialize() {
347  if (name_entry_map_.empty()) {
348    return 0;
349  }
350
351  int32_t size = NameTable::Offset::kNameRecordStart +
352                 name_entry_map_.size() * NameTable::Offset::kNameRecordSize;
353  for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(),
354                                     end = name_entry_map_.end();
355                                     b != end; ++b) {
356    NameEntryBuilderPtr p = b->second;
357    NameEntry* entry = p->name_entry();
358    size += entry->NameBytesLength();
359  }
360  return size;
361}
362
363bool NameTable::Builder::SubReadyToSerialize() {
364  return !name_entry_map_.empty();
365}
366
367int32_t NameTable::Builder::SubSerialize(WritableFontData* new_data) {
368  int32_t string_table_start_offset =
369      NameTable::Offset::kNameRecordStart +
370      name_entry_map_.size() * NameTable::Offset::kNameRecordSize;
371
372  // Header
373  new_data->WriteUShort(NameTable::Offset::kFormat, 0);
374  new_data->WriteUShort(NameTable::Offset::kCount, name_entry_map_.size());
375  new_data->WriteUShort(NameTable::Offset::kStringOffset,
376                        string_table_start_offset);
377  int32_t name_record_offset = NameTable::Offset::kNameRecordStart;
378  int32_t string_offset = 0;
379  // Note: we offered operator< in NameEntryId, which will be used by std::less,
380  //       and therefore our map will act like TreeMap in Java to provide
381  //       sorted key set.
382  for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(),
383                                     end = name_entry_map_.end();
384                                     b != end; ++b) {
385    new_data->WriteUShort(
386        name_record_offset + NameTable::Offset::kNameRecordPlatformId,
387        b->first.platform_id());
388    new_data->WriteUShort(
389        name_record_offset + NameTable::Offset::kNameRecordEncodingId,
390        b->first.encoding_id());
391    new_data->WriteUShort(
392        name_record_offset + NameTable::Offset::kNameRecordLanguageId,
393        b->first.language_id());
394    new_data->WriteUShort(
395        name_record_offset + NameTable::Offset::kNameRecordNameId,
396        b->first.name_id());
397    NameEntry* builder_entry = b->second->name_entry();
398    new_data->WriteUShort(
399        name_record_offset + NameTable::Offset::kNameRecordStringLength,
400        builder_entry->NameBytesLength());
401    new_data->WriteUShort(
402        name_record_offset + NameTable::Offset::kNameRecordStringOffset,
403        string_offset);
404    name_record_offset += NameTable::Offset::kNameRecordSize;
405    string_offset += new_data->WriteBytes(
406        string_offset + string_table_start_offset,
407        builder_entry->NameAsBytes());
408  }
409
410  return string_offset + string_table_start_offset;
411}
412
413void NameTable::Builder::Initialize(ReadableFontData* data) {
414  if (data) {
415    NameTablePtr table = new NameTable(header(), data);
416    Ptr<NameEntryIterator> name_iter;
417    name_iter.Attach(table->Iterator());
418    while (name_iter->HasNext()) {
419      NameEntryPtr name_entry;
420      name_entry.Attach(name_iter->Next());
421      NameEntryBuilderPtr name_entry_builder = new NameEntryBuilder(name_entry);
422      NameEntry* builder_entry = name_entry_builder->name_entry();
423      NameEntryId probe = builder_entry->name_entry_id();
424      name_entry_map_[probe] = name_entry_builder;
425    }
426  }
427}
428
429NameTable::NameEntryBuilderMap* NameTable::Builder::GetNameBuilders() {
430  if (name_entry_map_.empty()) {
431    Initialize(InternalReadData());
432  }
433  set_model_changed();
434  return &name_entry_map_;
435}
436
437/******************************************************************************
438 * NameTable class
439 ******************************************************************************/
440NameTable::~NameTable() {}
441
442int32_t NameTable::Format() {
443  return data_->ReadUShort(Offset::kFormat);
444}
445
446int32_t NameTable::NameCount() {
447  return data_->ReadUShort(Offset::kCount);
448}
449
450int32_t NameTable::PlatformId(int32_t index) {
451  return data_->ReadUShort(Offset::kNameRecordPlatformId +
452                           OffsetForNameRecord(index));
453}
454
455int32_t NameTable::EncodingId(int32_t index) {
456  return data_->ReadUShort(Offset::kNameRecordEncodingId +
457                           OffsetForNameRecord(index));
458}
459
460int32_t NameTable::LanguageId(int32_t index) {
461  return data_->ReadUShort(Offset::kNameRecordLanguageId +
462                           OffsetForNameRecord(index));
463}
464
465int32_t NameTable::NameId(int32_t index) {
466  return data_->ReadUShort(Offset::kNameRecordNameId +
467                           OffsetForNameRecord(index));
468}
469
470void NameTable::NameAsBytes(int32_t index, ByteVector* b) {
471  assert(b);
472  int32_t length = NameLength(index);
473  b->clear();
474  b->resize(length);
475  if (length > 0) {
476    data_->ReadBytes(NameOffset(index), &((*b)[0]), 0, length);
477  }
478}
479
480void NameTable::NameAsBytes(int32_t platform_id,
481                            int32_t encoding_id,
482                            int32_t language_id,
483                            int32_t name_id,
484                            ByteVector* b) {
485  assert(b);
486  NameEntryPtr entry;
487  entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id));
488  if (entry) {
489    ByteVector* name = entry->NameAsBytes();
490    std::copy(name->begin(), name->end(), b->begin());
491  }
492}
493
494UChar* NameTable::Name(int32_t index) {
495  ByteVector b;
496  NameAsBytes(index, &b);
497  return ConvertFromNameBytes(&b, PlatformId(index), EncodingId(index));
498}
499
500UChar* NameTable::Name(int32_t platform_id,
501                       int32_t encoding_id,
502                       int32_t language_id,
503                       int32_t name_id) {
504  NameEntryPtr entry;
505  entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id));
506  if (entry) {
507    return entry->Name();
508  }
509  return NULL;
510}
511
512CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t index) {
513  ByteVector b;
514  NameAsBytes(index, &b);
515  NameEntryPtr instance = new NameEntry(PlatformId(index),
516                                        EncodingId(index),
517                                        LanguageId(index),
518                                        NameId(index), b);
519  return instance.Detach();
520}
521
522CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t platform_id,
523                                                            int32_t encoding_id,
524                                                            int32_t language_id,
525                                                            int32_t name_id) {
526  NameTable::NameEntryFilterInPlace
527      filter(platform_id, encoding_id, language_id, name_id);
528  Ptr<NameTable::NameEntryIterator> name_entry_iter;
529  name_entry_iter.Attach(Iterator(&filter));
530  NameEntryPtr result;
531  if (name_entry_iter->HasNext()) {
532    result = name_entry_iter->Next();
533  }
534  return result;
535}
536
537CALLER_ATTACH NameTable::NameEntryIterator* NameTable::Iterator() {
538  Ptr<NameEntryIterator> output = new NameTable::NameEntryIterator(this);
539  return output.Detach();
540}
541
542CALLER_ATTACH
543NameTable::NameEntryIterator* NameTable::Iterator(NameEntryFilter* filter) {
544  Ptr<NameEntryIterator> output =
545      new NameTable::NameEntryIterator(this, filter);
546  return output.Detach();
547}
548
549NameTable::NameTable(Header* header, ReadableFontData* data)
550    : SubTableContainerTable(header, data) {}
551
552int32_t NameTable::StringOffset() {
553  return data_->ReadUShort(Offset::kStringOffset);
554}
555
556int32_t NameTable::OffsetForNameRecord(int32_t index) {
557  return Offset::kNameRecordStart + index * Offset::kNameRecordSize;
558}
559
560int32_t NameTable::NameLength(int32_t index) {
561  return data_->ReadUShort(Offset::kNameRecordStringLength +
562                           OffsetForNameRecord(index));
563}
564
565int32_t NameTable::NameOffset(int32_t index) {
566  return data_->ReadUShort(Offset::kNameRecordStringOffset +
567                           OffsetForNameRecord(index)) + StringOffset();
568}
569
570const char* NameTable::GetEncodingName(int32_t platform_id,
571                                       int32_t encoding_id) {
572  switch (platform_id) {
573    case PlatformId::kUnicode:
574      return "UTF-16BE";
575    case PlatformId::kMacintosh:
576      switch (encoding_id) {
577        case MacintoshEncodingId::kRoman:
578          return "MacRoman";
579        case MacintoshEncodingId::kJapanese:
580          return "Shift-JIS";
581        case MacintoshEncodingId::kChineseTraditional:
582          return "Big5";
583        case MacintoshEncodingId::kKorean:
584          return "EUC-KR";
585        case MacintoshEncodingId::kArabic:
586          return "MacArabic";
587        case MacintoshEncodingId::kHebrew:
588          return "MacHebrew";
589        case MacintoshEncodingId::kGreek:
590          return "MacGreek";
591        case MacintoshEncodingId::kRussian:
592          return "MacCyrillic";
593        case MacintoshEncodingId::kRSymbol:
594          return "MacSymbol";
595        case MacintoshEncodingId::kThai:
596          return "MacThai";
597        case MacintoshEncodingId::kChineseSimplified:
598          return "EUC-CN";
599        default:  // Note: unknown/unconfirmed cases are not ported.
600          break;
601      }
602      break;
603    case PlatformId::kISO:
604      break;
605    case PlatformId::kWindows:
606      switch (encoding_id) {
607        case WindowsEncodingId::kSymbol:
608        case WindowsEncodingId::kUnicodeUCS2:
609          return "UTF-16BE";
610        case WindowsEncodingId::kShiftJIS:
611          return "windows-933";
612        case WindowsEncodingId::kPRC:
613          return "windows-936";
614        case WindowsEncodingId::kBig5:
615          return "windows-950";
616        case WindowsEncodingId::kWansung:
617          return "windows-949";
618        case WindowsEncodingId::kJohab:
619          return "ms1361";
620        case WindowsEncodingId::kUnicodeUCS4:
621          return "UCS-4";
622      }
623      break;
624    case PlatformId::kCustom:
625      break;
626    default:
627      break;
628  }
629  return NULL;
630}
631
632UConverter* NameTable::GetCharset(int32_t platform_id, int32_t encoding_id) {
633  UErrorCode error_code = U_ZERO_ERROR;
634  UConverter* conv = ucnv_open(GetEncodingName(platform_id, encoding_id),
635                               &error_code);
636  if (U_SUCCESS(error_code)) {
637    return conv;
638  }
639
640  if (conv) {
641    ucnv_close(conv);
642  }
643  return NULL;
644}
645
646void NameTable::ConvertToNameBytes(const UChar* name,
647                                   int32_t platform_id,
648                                   int32_t encoding_id,
649                                   ByteVector* b) {
650  assert(b);
651  assert(name);
652  b->clear();
653  UConverter* cs = GetCharset(platform_id, encoding_id);
654  if (cs == NULL) {
655    return;
656  }
657
658  // Preflight to get buffer size.
659  UErrorCode error_code = U_ZERO_ERROR;
660  int32_t length = ucnv_fromUChars(cs, NULL, 0, name, -1, &error_code);
661  b->resize(length + 4);  // The longest termination "\0" is 4 bytes.
662  memset(&((*b)[0]), 0, length + 4);
663  error_code = U_ZERO_ERROR;
664  ucnv_fromUChars(cs,
665                  reinterpret_cast<char*>(&((*b)[0])),
666                  length + 4,
667                  name,
668                  -1,
669                  &error_code);
670  if (!U_SUCCESS(error_code)) {
671    b->clear();
672  }
673  ucnv_close(cs);
674}
675
676UChar* NameTable::ConvertFromNameBytes(ByteVector* name_bytes,
677                                       int32_t platform_id,
678                                       int32_t encoding_id) {
679  if (name_bytes == NULL || name_bytes->size() == 0) {
680    return NULL;
681  }
682  UConverter* cs = GetCharset(platform_id, encoding_id);
683  UErrorCode error_code = U_ZERO_ERROR;
684  if (cs == NULL) {
685    char buffer[11] = {0};
686#if defined (WIN32)
687    _itoa_s(platform_id, buffer, 16);
688#else
689    snprintf(buffer, sizeof(buffer), "%x", platform_id);
690#endif
691    UChar* result = new UChar[12];
692    memset(result, 0, sizeof(UChar) * 12);
693    cs = ucnv_open("utf-8", &error_code);
694    if (U_SUCCESS(error_code)) {
695      ucnv_toUChars(cs, result, 12, buffer, 11, &error_code);
696      ucnv_close(cs);
697      if (U_SUCCESS(error_code)) {
698        return result;
699      }
700    }
701    delete[] result;
702    return NULL;
703  }
704
705  // No preflight needed here, we will be bigger.
706  UChar* output_buffer = new UChar[name_bytes->size() + 1];
707  memset(output_buffer, 0, sizeof(UChar) * (name_bytes->size() + 1));
708  int32_t length = ucnv_toUChars(cs,
709                                 output_buffer,
710                                 name_bytes->size(),
711                                 reinterpret_cast<char*>(&((*name_bytes)[0])),
712                                 name_bytes->size(),
713                                 &error_code);
714  ucnv_close(cs);
715  if (length > 0) {
716    return output_buffer;
717  }
718
719  delete[] output_buffer;
720  return NULL;
721}
722
723}  // namespace sfntly
724