static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc) { const DWORD cMaxNameTableSize = 1024 * 1024; static HashMap<String, RetainPtr<CFStringRef> > nameMap; // Check our hash first. String faceString(faceName); RetainPtr<CFStringRef> result = nameMap.get(faceString); if (result) return result.get(); // We need to obtain the PostScript name from the name table and use it instead,. DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize) return NULL; Vector<BYTE> bufferVector(bufferSize); BYTE* buffer = bufferVector.data(); if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR) return NULL; if (bufferSize < 6) return NULL; USHORT numberOfRecords = readBigEndianWord(buffer + 2); UINT stringsOffset = readBigEndianWord(buffer + 4); if (bufferSize < stringsOffset) return NULL; BYTE* strings = buffer + stringsOffset; // Now walk each name record looking for a Mac or Windows PostScript name. UINT offset = 6; for (int i = 0; i < numberOfRecords; i++) { if (bufferSize < offset + 12) return NULL; USHORT platformID = readBigEndianWord(buffer + offset); USHORT encodingID = readBigEndianWord(buffer + offset + 2); USHORT languageID = readBigEndianWord(buffer + offset + 4); USHORT nameID = readBigEndianWord(buffer + offset + 6); USHORT length = readBigEndianWord(buffer + offset + 8); USHORT nameOffset = readBigEndianWord(buffer + offset + 10); if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) { // This is a Windows PostScript name and is therefore UTF-16. // Pass Big Endian as the encoding. if (bufferSize < stringsOffset + nameOffset + length) return NULL; result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false)); break; } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) { // This is a Mac PostScript name and is therefore ASCII. // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html if (bufferSize < stringsOffset + nameOffset + length) return NULL; result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false)); break; } offset += 12; } if (result) nameMap.set(faceString, result); return result.get(); }
CString String::utf8(UTF8ConversionMode mode) const { unsigned length = this->length(); if (!length) return CString("", 0); // Allocate a buffer big enough to hold all the characters // (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes). // Optimization ideas, if we find this function is hot: // * We could speculatively create a CStringBuffer to contain 'length' // characters, and resize if necessary (i.e. if the buffer contains // non-ascii characters). (Alternatively, scan the buffer first for // ascii characters, so we know this will be sufficient). // * We could allocate a CStringBuffer with an appropriate size to // have a good chance of being able to write the string into the // buffer without reallocing (say, 1.5 x length). if (length > std::numeric_limits<unsigned>::max() / 3) return CString(); Vector<char, 1024> bufferVector(length * 3); char* buffer = bufferVector.data(); if (is8Bit()) { const LChar* characters = this->characters8(); ConversionResult result = convertLatin1ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size()); ASSERT_UNUSED(result, result != targetExhausted); // (length * 3) should be sufficient for any conversion } else { const UChar* characters = this->characters16(); if (mode == StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD) { const UChar* charactersEnd = characters + length; char* bufferEnd = buffer + bufferVector.size(); while (characters < charactersEnd) { // Use strict conversion to detect unpaired surrogates. ConversionResult result = convertUTF16ToUTF8(&characters, charactersEnd, &buffer, bufferEnd, true); ASSERT(result != targetExhausted); // Conversion fails when there is an unpaired surrogate. Put // replacement character (U+FFFD) instead of the unpaired // surrogate. if (result != conversionOK) { ASSERT((0xD800 <= *characters && *characters <= 0xDFFF)); // There should be room left, since one UChar hasn't been // converted. ASSERT((buffer + 3) <= bufferEnd); putUTF8Triple(buffer, replacementCharacter); ++characters; } } } else { bool strict = mode == StrictUTF8Conversion; ConversionResult result = convertUTF16ToUTF8(&characters, characters + length, &buffer, buffer + bufferVector.size(), strict); ASSERT(result != targetExhausted); // (length * 3) should be sufficient for any conversion // Only produced from strict conversion. if (result == sourceIllegal) { ASSERT(strict); return CString(); } // Check for an unconverted high surrogate. if (result == sourceExhausted) { if (strict) return CString(); // This should be one unpaired high surrogate. Treat it the same // was as an unpaired high surrogate would have been handled in // the middle of a string with non-strict conversion - which is // to say, simply encode it to UTF-8. ASSERT((characters + 1) == (this->characters16() + length)); ASSERT((*characters >= 0xD800) && (*characters <= 0xDBFF)); // There should be room left, since one UChar hasn't been // converted. ASSERT((buffer + 3) <= (buffer + bufferVector.size())); putUTF8Triple(buffer, *characters); } } } return CString(bufferVector.data(), buffer - bufferVector.data()); }