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 "sfntly/font.h" 23 #include "sfntly/port/exception_type.h" 24 25 namespace sfntly { 26 /****************************************************************************** 27 * NameTable::NameEntryId class 28 ******************************************************************************/ 29 NameTable::NameEntryId::NameEntryId() 30 : platform_id_(0), 31 encoding_id_(0), 32 language_id_(0), 33 name_id_(0) { 34 } 35 36 NameTable::NameEntryId::NameEntryId(int32_t platform_id, 37 int32_t encoding_id, 38 int32_t language_id, 39 int32_t name_id) 40 : platform_id_(platform_id), 41 encoding_id_(encoding_id), 42 language_id_(language_id), 43 name_id_(name_id) { 44 } 45 46 NameTable::NameEntryId::NameEntryId(const NameTable::NameEntryId& rhs) { 47 *this = rhs; 48 } 49 50 const NameTable::NameEntryId& 51 NameTable::NameEntryId::operator=(const NameTable::NameEntryId& rhs) const { 52 platform_id_ = rhs.platform_id_; 53 encoding_id_ = rhs.encoding_id_; 54 language_id_ = rhs.language_id_; 55 name_id_ = rhs.name_id_; 56 return *this; 57 } 58 59 bool NameTable::NameEntryId::operator==(const NameEntryId& rhs) const { 60 return platform_id_ == rhs.platform_id_ && 61 encoding_id_ == rhs.encoding_id_ && 62 language_id_ == rhs.language_id_ && 63 name_id_ == rhs.name_id_; 64 } 65 66 bool NameTable::NameEntryId::operator<(const NameEntryId& rhs) const { 67 if (platform_id_ != rhs.platform_id_) return platform_id_ < rhs.platform_id_; 68 if (encoding_id_ != rhs.encoding_id_) return encoding_id_ < rhs.encoding_id_; 69 if (language_id_ != rhs.language_id_) return language_id_ < rhs.language_id_; 70 return name_id_ < rhs.name_id_; 71 } 72 73 /****************************************************************************** 74 * NameTable::NameEntry class 75 ******************************************************************************/ 76 NameTable::NameEntry::NameEntry() { 77 Init(0, 0, 0, 0, NULL); 78 } 79 80 NameTable::NameEntry::NameEntry(const NameEntryId& name_entry_id, 81 const ByteVector& name_bytes) { 82 Init(name_entry_id.platform_id(), 83 name_entry_id.encoding_id(), 84 name_entry_id.language_id(), 85 name_entry_id.name_id(), 86 &name_bytes); 87 } 88 89 NameTable::NameEntry::NameEntry(int32_t platform_id, 90 int32_t encoding_id, 91 int32_t language_id, 92 int32_t name_id, 93 const ByteVector& name_bytes) { 94 Init(platform_id, encoding_id, language_id, name_id, &name_bytes); 95 } 96 97 NameTable::NameEntry::~NameEntry() {} 98 99 ByteVector* NameTable::NameEntry::NameAsBytes() { 100 return &name_bytes_; 101 } 102 103 int32_t NameTable::NameEntry::NameBytesLength() { 104 return name_bytes_.size(); 105 } 106 107 UChar* NameTable::NameEntry::Name() { 108 return NameTable::ConvertFromNameBytes(&name_bytes_, 109 platform_id(), 110 encoding_id()); 111 } 112 113 bool NameTable::NameEntry::operator==(const NameEntry& rhs) const { 114 return (name_entry_id_ == rhs.name_entry_id_ && 115 name_bytes_ == rhs.name_bytes_); 116 } 117 118 void NameTable::NameEntry::Init(int32_t platform_id, 119 int32_t encoding_id, 120 int32_t language_id, 121 int32_t name_id, 122 const ByteVector* name_bytes) { 123 name_entry_id_ = NameEntryId(platform_id, encoding_id, language_id, name_id); 124 if (name_bytes) { 125 name_bytes_ = *name_bytes; 126 } else { 127 name_bytes_.clear(); 128 } 129 } 130 131 /****************************************************************************** 132 * NameTable::NameEntryBuilder class 133 ******************************************************************************/ 134 NameTable::NameEntryBuilder::NameEntryBuilder() { 135 Init(0, 0, 0, 0, NULL); 136 } 137 138 NameTable::NameEntryBuilder::NameEntryBuilder(const NameEntryId& name_entry_id, 139 const ByteVector& name_bytes) { 140 Init(name_entry_id.platform_id(), 141 name_entry_id.encoding_id(), 142 name_entry_id.language_id(), 143 name_entry_id.name_id(), 144 &name_bytes); 145 } 146 147 NameTable::NameEntryBuilder::NameEntryBuilder( 148 const NameEntryId& name_entry_id) { 149 Init(name_entry_id.platform_id(), 150 name_entry_id.encoding_id(), 151 name_entry_id.language_id(), 152 name_entry_id.name_id(), 153 NULL); 154 } 155 156 NameTable::NameEntryBuilder::NameEntryBuilder(NameEntry* b) { 157 Init(b->platform_id(), 158 b->encoding_id(), 159 b->language_id(), 160 b->name_id(), 161 b->NameAsBytes()); 162 } 163 164 NameTable::NameEntryBuilder::~NameEntryBuilder() {} 165 166 void NameTable::NameEntryBuilder::SetName(const UChar* name) { 167 if (name == NULL) { 168 name_entry_->name_bytes_.clear(); 169 return; 170 } 171 NameTable::ConvertToNameBytes(name, 172 name_entry_->platform_id(), 173 name_entry_->encoding_id(), 174 &name_entry_->name_bytes_); 175 } 176 177 void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes) { 178 name_entry_->name_bytes_.clear(); 179 std::copy(name_bytes.begin(), 180 name_bytes.end(), 181 name_entry_->name_bytes_.begin()); 182 } 183 184 void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes, 185 int32_t offset, 186 int32_t length) { 187 name_entry_->name_bytes_.clear(); 188 std::copy(name_bytes.begin() + offset, 189 name_bytes.begin() + offset + length, 190 name_entry_->name_bytes_.begin()); 191 } 192 193 void NameTable::NameEntryBuilder::Init(int32_t platform_id, 194 int32_t encoding_id, 195 int32_t language_id, 196 int32_t name_id, 197 const ByteVector* name_bytes) { 198 name_entry_ = new NameEntry(); 199 name_entry_->Init(platform_id, encoding_id, language_id, name_id, name_bytes); 200 } 201 202 /****************************************************************************** 203 * NameTable::NameEntryFilterInPlace class (C++ port only) 204 ******************************************************************************/ 205 NameTable::NameEntryFilterInPlace::NameEntryFilterInPlace(int32_t platform_id, 206 int32_t encoding_id, 207 int32_t language_id, 208 int32_t name_id) 209 : platform_id_(platform_id), 210 encoding_id_(encoding_id), 211 language_id_(language_id), 212 name_id_(name_id) { 213 } 214 215 bool NameTable::NameEntryFilterInPlace::Accept(int32_t platform_id, 216 int32_t encoding_id, 217 int32_t language_id, 218 int32_t name_id) { 219 return (platform_id_ == platform_id && 220 encoding_id_ == encoding_id && 221 language_id_ == language_id && 222 name_id_ == name_id); 223 } 224 225 /****************************************************************************** 226 * NameTable::NameEntryIterator class 227 ******************************************************************************/ 228 NameTable::NameEntryIterator::NameEntryIterator(NameTable* table) 229 : RefIterator<NameEntry, NameTable>(table), 230 name_index_(0), 231 filter_(NULL) { 232 } 233 234 NameTable::NameEntryIterator::NameEntryIterator(NameTable* table, 235 NameEntryFilter* filter) 236 : RefIterator<NameEntry, NameTable>(table), 237 name_index_(0), 238 filter_(filter) { 239 } 240 241 bool NameTable::NameEntryIterator::HasNext() { 242 if (!filter_) { 243 if (name_index_ < container()->NameCount()) { 244 return true; 245 } 246 return false; 247 } 248 for (; name_index_ < container()->NameCount(); ++name_index_) { 249 if (filter_->Accept(container()->PlatformId(name_index_), 250 container()->EncodingId(name_index_), 251 container()->LanguageId(name_index_), 252 container()->NameId(name_index_))) { 253 return true; 254 } 255 } 256 return false; 257 } 258 259 CALLER_ATTACH NameTable::NameEntry* NameTable::NameEntryIterator::Next() { 260 if (!HasNext()) 261 return NULL; 262 return container()->GetNameEntry(name_index_++); 263 } 264 265 /****************************************************************************** 266 * NameTable::Builder class 267 ******************************************************************************/ 268 NameTable::Builder::Builder(Header* header, WritableFontData* data) 269 : SubTableContainerTable::Builder(header, data) { 270 } 271 272 NameTable::Builder::Builder(Header* header, ReadableFontData* data) 273 : SubTableContainerTable::Builder(header, data) { 274 } 275 276 CALLER_ATTACH NameTable::Builder* 277 NameTable::Builder::CreateBuilder(Header* header, 278 WritableFontData* data) { 279 Ptr<NameTable::Builder> builder; 280 builder = new NameTable::Builder(header, data); 281 return builder.Detach(); 282 } 283 284 void NameTable::Builder::RevertNames() { 285 name_entry_map_.clear(); 286 set_model_changed(false); 287 } 288 289 int32_t NameTable::Builder::BuilderCount() { 290 GetNameBuilders(); // Ensure name_entry_map_ is built. 291 return (int32_t)name_entry_map_.size(); 292 } 293 294 bool NameTable::Builder::Has(int32_t platform_id, 295 int32_t encoding_id, 296 int32_t language_id, 297 int32_t name_id) { 298 NameEntryId probe(platform_id, encoding_id, language_id, name_id); 299 GetNameBuilders(); // Ensure name_entry_map_ is built. 300 return (name_entry_map_.find(probe) != name_entry_map_.end()); 301 } 302 303 CALLER_ATTACH NameTable::NameEntryBuilder* 304 NameTable::Builder::NameBuilder(int32_t platform_id, 305 int32_t encoding_id, 306 int32_t language_id, 307 int32_t name_id) { 308 NameEntryId probe(platform_id, encoding_id, language_id, name_id); 309 NameEntryBuilderMap builders; 310 GetNameBuilders(); // Ensure name_entry_map_ is built. 311 if (name_entry_map_.find(probe) != name_entry_map_.end()) { 312 return name_entry_map_[probe]; 313 } 314 NameEntryBuilderPtr builder = new NameEntryBuilder(probe); 315 name_entry_map_[probe] = builder; 316 return builder.Detach(); 317 } 318 319 bool NameTable::Builder::Remove(int32_t platform_id, 320 int32_t encoding_id, 321 int32_t language_id, 322 int32_t name_id) { 323 NameEntryId probe(platform_id, encoding_id, language_id, name_id); 324 GetNameBuilders(); // Ensure name_entry_map_ is built. 325 NameEntryBuilderMap::iterator position = name_entry_map_.find(probe); 326 if (position != name_entry_map_.end()) { 327 name_entry_map_.erase(position); 328 return true; 329 } 330 return false; 331 } 332 333 CALLER_ATTACH FontDataTable* 334 NameTable::Builder::SubBuildTable(ReadableFontData* data) { 335 FontDataTablePtr table = new NameTable(header(), data); 336 return table.Detach(); 337 } 338 339 void NameTable::Builder::SubDataSet() { 340 name_entry_map_.clear(); 341 set_model_changed(false); 342 } 343 344 int32_t NameTable::Builder::SubDataSizeToSerialize() { 345 if (name_entry_map_.empty()) { 346 return 0; 347 } 348 349 int32_t size = NameTable::Offset::kNameRecordStart + 350 name_entry_map_.size() * NameTable::Offset::kNameRecordSize; 351 for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(), 352 end = name_entry_map_.end(); 353 b != end; ++b) { 354 NameEntryBuilderPtr p = b->second; 355 NameEntry* entry = p->name_entry(); 356 size += entry->NameBytesLength(); 357 } 358 return size; 359 } 360 361 bool NameTable::Builder::SubReadyToSerialize() { 362 return !name_entry_map_.empty(); 363 } 364 365 int32_t NameTable::Builder::SubSerialize(WritableFontData* new_data) { 366 int32_t string_table_start_offset = 367 NameTable::Offset::kNameRecordStart + 368 name_entry_map_.size() * NameTable::Offset::kNameRecordSize; 369 370 // Header 371 new_data->WriteUShort(NameTable::Offset::kFormat, 0); 372 new_data->WriteUShort(NameTable::Offset::kCount, name_entry_map_.size()); 373 new_data->WriteUShort(NameTable::Offset::kStringOffset, 374 string_table_start_offset); 375 int32_t name_record_offset = NameTable::Offset::kNameRecordStart; 376 int32_t string_offset = 0; 377 // Note: we offered operator< in NameEntryId, which will be used by std::less, 378 // and therefore our map will act like TreeMap in Java to provide 379 // sorted key set. 380 for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(), 381 end = name_entry_map_.end(); 382 b != end; ++b) { 383 new_data->WriteUShort( 384 name_record_offset + NameTable::Offset::kNameRecordPlatformId, 385 b->first.platform_id()); 386 new_data->WriteUShort( 387 name_record_offset + NameTable::Offset::kNameRecordEncodingId, 388 b->first.encoding_id()); 389 new_data->WriteUShort( 390 name_record_offset + NameTable::Offset::kNameRecordLanguageId, 391 b->first.language_id()); 392 new_data->WriteUShort( 393 name_record_offset + NameTable::Offset::kNameRecordNameId, 394 b->first.name_id()); 395 NameEntry* builder_entry = b->second->name_entry(); 396 new_data->WriteUShort( 397 name_record_offset + NameTable::Offset::kNameRecordStringLength, 398 builder_entry->NameBytesLength()); 399 new_data->WriteUShort( 400 name_record_offset + NameTable::Offset::kNameRecordStringOffset, 401 string_offset); 402 name_record_offset += NameTable::Offset::kNameRecordSize; 403 string_offset += new_data->WriteBytes( 404 string_offset + string_table_start_offset, 405 builder_entry->NameAsBytes()); 406 } 407 408 return string_offset + string_table_start_offset; 409 } 410 411 void NameTable::Builder::Initialize(ReadableFontData* data) { 412 if (data) { 413 NameTablePtr table = new NameTable(header(), data); 414 Ptr<NameEntryIterator> name_iter; 415 name_iter.Attach(table->Iterator()); 416 while (name_iter->HasNext()) { 417 NameEntryPtr name_entry; 418 name_entry.Attach(name_iter->Next()); 419 NameEntryBuilderPtr name_entry_builder = new NameEntryBuilder(name_entry); 420 NameEntry* builder_entry = name_entry_builder->name_entry(); 421 NameEntryId probe = builder_entry->name_entry_id(); 422 name_entry_map_[probe] = name_entry_builder; 423 } 424 } 425 } 426 427 NameTable::NameEntryBuilderMap* NameTable::Builder::GetNameBuilders() { 428 if (name_entry_map_.empty()) { 429 Initialize(InternalReadData()); 430 } 431 set_model_changed(); 432 return &name_entry_map_; 433 } 434 435 /****************************************************************************** 436 * NameTable class 437 ******************************************************************************/ 438 NameTable::~NameTable() {} 439 440 int32_t NameTable::Format() { 441 return data_->ReadUShort(Offset::kFormat); 442 } 443 444 int32_t NameTable::NameCount() { 445 return data_->ReadUShort(Offset::kCount); 446 } 447 448 int32_t NameTable::PlatformId(int32_t index) { 449 return data_->ReadUShort(Offset::kNameRecordPlatformId + 450 OffsetForNameRecord(index)); 451 } 452 453 int32_t NameTable::EncodingId(int32_t index) { 454 return data_->ReadUShort(Offset::kNameRecordEncodingId + 455 OffsetForNameRecord(index)); 456 } 457 458 int32_t NameTable::LanguageId(int32_t index) { 459 return data_->ReadUShort(Offset::kNameRecordLanguageId + 460 OffsetForNameRecord(index)); 461 } 462 463 int32_t NameTable::NameId(int32_t index) { 464 return data_->ReadUShort(Offset::kNameRecordNameId + 465 OffsetForNameRecord(index)); 466 } 467 468 void NameTable::NameAsBytes(int32_t index, ByteVector* b) { 469 assert(b); 470 b->clear(); 471 472 int32_t length = NameLength(index); 473 if (length <= 0) 474 return; 475 476 b->resize(length); 477 data_->ReadBytes(NameOffset(index), &((*b)[0]), 0, length); 478 } 479 480 void 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 494 UChar* NameTable::Name(int32_t index) { 495 ByteVector b; 496 NameAsBytes(index, &b); 497 return ConvertFromNameBytes(&b, PlatformId(index), EncodingId(index)); 498 } 499 500 UChar* 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 512 CALLER_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 522 CALLER_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 537 CALLER_ATTACH NameTable::NameEntryIterator* NameTable::Iterator() { 538 Ptr<NameEntryIterator> output = new NameTable::NameEntryIterator(this); 539 return output.Detach(); 540 } 541 542 CALLER_ATTACH 543 NameTable::NameEntryIterator* NameTable::Iterator(NameEntryFilter* filter) { 544 Ptr<NameEntryIterator> output = 545 new NameTable::NameEntryIterator(this, filter); 546 return output.Detach(); 547 } 548 549 NameTable::NameTable(Header* header, ReadableFontData* data) 550 : SubTableContainerTable(header, data) {} 551 552 int32_t NameTable::StringOffset() { 553 return data_->ReadUShort(Offset::kStringOffset); 554 } 555 556 int32_t NameTable::OffsetForNameRecord(int32_t index) { 557 return Offset::kNameRecordStart + index * Offset::kNameRecordSize; 558 } 559 560 int32_t NameTable::NameLength(int32_t index) { 561 return data_->ReadUShort(Offset::kNameRecordStringLength + 562 OffsetForNameRecord(index)); 563 } 564 565 int32_t NameTable::NameOffset(int32_t index) { 566 return data_->ReadUShort(Offset::kNameRecordStringOffset + 567 OffsetForNameRecord(index)) + StringOffset(); 568 } 569 570 const 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 632 UConverter* 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 646 void 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 676 UChar* 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