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