7f747710d1adeb3a1425e6dad39a4f84fa1c0295
[WebKit-https.git] / WebCore / platform / graphics / win / FontPlatformDataWin.cpp
1 /*
2  * This file is part of the internal font implementation.  It should not be included by anyone other than
3  * FontMac.cpp, FontWin.cpp and Font.cpp.
4  *
5  * Copyright (C) 2006, 2007 Apple Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "FontPlatformData.h"
26
27 #include "PlatformString.h"
28 #include "StringHash.h"
29 #include <ApplicationServices/ApplicationServices.h>
30 #include <wtf/HashMap.h>
31 #include <wtf/RetainPtr.h>
32 #include <wtf/Vector.h>
33
34 using std::min;
35
36 namespace WebCore {
37
38 static const int Bold = (1 << 0);
39 static const int Italic = (1 << 1);
40 static const int BoldOblique = (1 << 2);
41
42 static inline USHORT readBigEndianWord(const BYTE* word) { return (word[0] << 8) | word[1]; }
43
44 static CFStringRef getPostScriptName(CFStringRef faceName, HDC dc)
45 {
46     const DWORD cMaxNameTableSize = 1024 * 1024;
47
48     static HashMap<String, RetainPtr<CFStringRef> > nameMap;
49
50     // Check our hash first.
51     String faceString(faceName);
52     RetainPtr<CFStringRef> result = nameMap.get(faceString);
53     if (result)
54         return result.get();
55
56     // We need to obtain the PostScript name from the name table and use it instead,.
57     DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards
58     if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize)
59         return NULL;
60    
61     Vector<BYTE> bufferVector(bufferSize);
62     BYTE* buffer = bufferVector.data();
63     if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR)
64         return NULL;
65
66     if (bufferSize < 6)
67         return NULL;
68
69     USHORT numberOfRecords = readBigEndianWord(buffer + 2);
70     UINT stringsOffset = readBigEndianWord(buffer + 4);
71     if (bufferSize < stringsOffset)
72         return NULL;
73
74     BYTE* strings = buffer + stringsOffset;
75
76     // Now walk each name record looking for a Mac or Windows PostScript name.
77     UINT offset = 6;
78     for (int i = 0; i < numberOfRecords; i++) {
79         if (bufferSize < offset + 12)
80             return NULL;
81
82         USHORT platformID = readBigEndianWord(buffer + offset);
83         USHORT encodingID = readBigEndianWord(buffer + offset + 2);
84         USHORT languageID = readBigEndianWord(buffer + offset + 4);
85         USHORT nameID = readBigEndianWord(buffer + offset + 6);
86         USHORT length = readBigEndianWord(buffer + offset + 8);
87         USHORT nameOffset = readBigEndianWord(buffer + offset + 10);
88
89         if (platformID == 3 && encodingID == 1 && languageID == 0x409 && nameID == 6) {
90             // This is a Windows PostScript name and is therefore UTF-16.
91             // Pass Big Endian as the encoding.
92             if (bufferSize < stringsOffset + nameOffset + length)
93                 return NULL;
94             result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingUTF16BE, false));
95             break;
96         } else if (platformID == 1 && encodingID == 0 && languageID == 0 && nameID == 6) {
97             // This is a Mac PostScript name and is therefore ASCII.
98             // See http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
99             if (bufferSize < stringsOffset + nameOffset + length)
100                 return NULL;
101             result.adoptCF(CFStringCreateWithBytes(NULL, strings + nameOffset, length, kCFStringEncodingASCII, false));
102             break;
103         }
104
105         offset += 12;
106     }
107
108     if (result)
109         nameMap.set(faceString, result);
110     return result.get();
111 }
112
113 static int CALLBACK enumStylesCallback(const LOGFONT* logFont, const TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam)
114 {
115     int *style = reinterpret_cast<int*>(lParam);
116
117     // FIXME: In order to accommodate Lucida we go ahead and consider a weight of 600 to be bold.
118     // This does mean we'll consider demibold and semibold fonts on windows to also be bold.  This
119     // is rare enough that it seems like an ok hack for now.
120     if (logFont->lfWeight >= 600) {
121         if (logFont->lfItalic)
122             *style |= BoldOblique;
123         else
124             *style |= Bold;
125     } else if (logFont->lfItalic)
126         *style |= Italic;
127
128     return 1;
129 }
130
131 FontPlatformData::FontPlatformData(HFONT font, int size, bool bold, bool oblique)
132     : m_font(font)
133     , m_size(size)
134     , m_cgFont(0)
135     , m_syntheticBold(false)
136     , m_syntheticOblique(false)
137 {
138     HDC hdc = GetDC(0);
139     SaveDC(hdc);
140     
141     SelectObject(hdc, font);
142     UINT bufferSize = GetOutlineTextMetrics(hdc, 0, NULL);
143
144     ASSERT_WITH_MESSAGE(bufferSize != 0, "Bitmap fonts not supported with CoreGraphics.");
145
146     if (bufferSize != 0) {
147         OUTLINETEXTMETRICW* metrics = (OUTLINETEXTMETRICW*)malloc(bufferSize);
148
149         GetOutlineTextMetricsW(hdc, bufferSize, metrics);
150         WCHAR* faceName = (WCHAR*)((uintptr_t)metrics + (uintptr_t)metrics->otmpFaceName);
151
152         if (bold || oblique) {
153             LOGFONT logFont;
154
155             int len = min((int)wcslen(faceName), LF_FACESIZE - 1);
156             memcpy(logFont.lfFaceName, faceName, len * sizeof(WORD));
157             logFont.lfFaceName[len] = '\0';
158             logFont.lfCharSet = metrics->otmTextMetrics.tmCharSet;
159             logFont.lfPitchAndFamily = 0;
160
161             int styles = 0;
162             EnumFontFamiliesEx(hdc, &logFont, enumStylesCallback, reinterpret_cast<LPARAM>(&styles), 0);
163
164             // Check if we need to synthesize bold or oblique. The rule that complicates things here
165             // is that if the requested font is bold and oblique, and both a bold font and an oblique font
166             // exist, the bold font should be used, and oblique synthesized.
167             if (bold && oblique) {
168                 if (styles == 0) {
169                     m_syntheticBold = true;
170                     m_syntheticOblique = true;
171                 } else if (styles & Bold)
172                     m_syntheticOblique = true;
173                 else if (styles & Italic)
174                     m_syntheticBold = true;
175             } else if (bold && (!(styles & Bold)))
176                     m_syntheticBold = true;
177               else if (oblique && !(styles & Italic))
178                     m_syntheticOblique = true;
179         }
180
181         // Try the face name first.  Windows may end up localizing this name, and CG doesn't know about
182         // the localization.  If the create fails, we'll try the PostScript name.
183         RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName)));
184         m_cgFont = CGFontCreateWithFontName(fullName.get());
185         if (!m_cgFont) {
186             CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc);
187             if (postScriptName) {
188                 m_cgFont = CGFontCreateWithFontName(postScriptName);
189                 ASSERT(m_cgFont);
190             }
191         }
192         free(metrics);
193     }
194
195     RestoreDC(hdc, -1);
196     ReleaseDC(0, hdc);
197 }
198
199 FontPlatformData::FontPlatformData(CGFontRef font, int size, bool bold, bool oblique)
200     : m_font(0)
201     , m_size(size)
202     , m_cgFont(font)
203     , m_syntheticBold(bold)
204     , m_syntheticOblique(oblique)
205 {
206 }
207
208 FontPlatformData::~FontPlatformData()
209 {
210 }
211
212 }