Add support for GDI text on Windows.
[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, float size, bool bold, bool oblique, bool useGDI)
132     : m_font(font)
133     , m_size(size)
134     , m_cgFont(0)
135     , m_syntheticBold(false)
136     , m_syntheticOblique(false)
137     , m_useGDI(useGDI)
138 {
139     HDC hdc = GetDC(0);
140     SaveDC(hdc);
141     
142     SelectObject(hdc, font);
143     UINT bufferSize = GetOutlineTextMetrics(hdc, 0, NULL);
144
145     ASSERT_WITH_MESSAGE(bufferSize != 0, "Bitmap fonts not supported with CoreGraphics.");
146
147     if (bufferSize != 0) {
148         OUTLINETEXTMETRICW* metrics = (OUTLINETEXTMETRICW*)malloc(bufferSize);
149
150         GetOutlineTextMetricsW(hdc, bufferSize, metrics);
151         WCHAR* faceName = (WCHAR*)((uintptr_t)metrics + (uintptr_t)metrics->otmpFaceName);
152
153         if (bold || oblique) {
154             LOGFONT logFont;
155
156             int len = min((int)wcslen(faceName), LF_FACESIZE - 1);
157             memcpy(logFont.lfFaceName, faceName, len * sizeof(WORD));
158             logFont.lfFaceName[len] = '\0';
159             logFont.lfCharSet = metrics->otmTextMetrics.tmCharSet;
160             logFont.lfPitchAndFamily = 0;
161
162             int styles = 0;
163             EnumFontFamiliesEx(hdc, &logFont, enumStylesCallback, reinterpret_cast<LPARAM>(&styles), 0);
164
165             // Check if we need to synthesize bold or oblique. The rule that complicates things here
166             // is that if the requested font is bold and oblique, and both a bold font and an oblique font
167             // exist, the bold font should be used, and oblique synthesized.
168             if (bold && oblique) {
169                 if (styles == 0) {
170                     m_syntheticBold = true;
171                     m_syntheticOblique = true;
172                 } else if (styles & Bold)
173                     m_syntheticOblique = true;
174                 else if (styles & Italic)
175                     m_syntheticBold = true;
176             } else if (bold && (!(styles & Bold)))
177                     m_syntheticBold = true;
178               else if (oblique && !(styles & Italic))
179                     m_syntheticOblique = true;
180         }
181
182         // For GDI text, synthetic bold and oblique never need to be set.
183         m_syntheticBold = m_syntheticOblique = false;
184
185         // Try the face name first.  Windows may end up localizing this name, and CG doesn't know about
186         // the localization.  If the create fails, we'll try the PostScript name.
187         RetainPtr<CFStringRef> fullName(AdoptCF, CFStringCreateWithCharacters(NULL, (const UniChar*)faceName, wcslen(faceName)));
188         m_cgFont = CGFontCreateWithFontName(fullName.get());
189         if (!m_cgFont) {
190             CFStringRef postScriptName = getPostScriptName(fullName.get(), hdc);
191             if (postScriptName) {
192                 m_cgFont = CGFontCreateWithFontName(postScriptName);
193                 ASSERT(m_cgFont);
194             }
195         }
196         free(metrics);
197     }
198
199     RestoreDC(hdc, -1);
200     ReleaseDC(0, hdc);
201 }
202
203 FontPlatformData::FontPlatformData(CGFontRef font, float size, bool bold, bool oblique)
204     : m_font(0)
205     , m_size(size)
206     , m_cgFont(font)
207     , m_syntheticBold(bold)
208     , m_syntheticOblique(oblique)
209     , m_useGDI(false)
210 {
211 }
212
213 FontPlatformData::~FontPlatformData()
214 {
215 }
216
217 }