This patch makes drawHighlightForText a completely cross-platform method
[WebKit-https.git] / WebCore / platform / win / FontWin.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "Font.h"
28
29 #include <cairo-win32.h>
30 #include "FontData.h"
31 #include "FontFallbackList.h"
32 #include "GraphicsContext.h"
33 #include "IntRect.h"
34
35 namespace WebCore {
36
37 FontData* getFontData(const FontDescription& fontDescription, const AtomicString& fontFace)
38 {
39     // FIXME: Look this up in a hashtable so that we can cache Cairo fonts.  For now we just grab the
40     // font over and over.
41     LOGFONTW winfont;
42     winfont.lfHeight = -fontDescription.computedPixelSize(); // The negative number is intentional.
43     winfont.lfWidth = 0;
44     winfont.lfEscapement = 0;
45     winfont.lfOrientation = 0;
46     winfont.lfUnderline = false;
47     winfont.lfStrikeOut = false;
48     winfont.lfCharSet = DEFAULT_CHARSET;
49     winfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
50     const int CLEARTYPE_QUALITY = 5;
51     winfont.lfQuality = CLEARTYPE_QUALITY; // FIXME: This is the Windows XP constant to force ClearType on.
52     winfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
53     winfont.lfItalic = fontDescription.italic();
54     winfont.lfWeight = fontDescription.weight() == cBoldWeight ? 700 : 400; // FIXME: Support weights for real.
55     int len = min(fontFace.length(), LF_FACESIZE - 1);
56     memcpy(winfont.lfFaceName, fontFace.characters(), len * sizeof(WORD));
57     winfont.lfFaceName[len] = '\0';
58
59     HFONT font = CreateFontIndirectW(&winfont);
60     
61     // Windows will always give us a valid pointer here, even if the face name is non-existent.  We have to double-check
62     // and see if the family name was really used.
63     HDC dc = GetDC((HWND)0);
64     SaveDC(dc);
65     SelectObject(dc, font);
66     
67     int ascent, descent, xHeight, lineSpacing;
68
69     TEXTMETRIC tm;
70     GetTextMetrics(dc, &tm);
71     ascent = tm.tmAscent;
72     descent = tm.tmDescent;
73     xHeight = ascent * 0.56f;  // Best guess for xHeight for non-Truetype fonts.
74     lineSpacing = tm.tmExternalLeading + tm.tmHeight;
75
76     OUTLINETEXTMETRIC otm;
77     if (GetOutlineTextMetrics(dc, sizeof(otm), &otm) > 0) {
78         // This is a TrueType font.  We might be able to get an accurate xHeight.
79         GLYPHMETRICS gm;
80         MAT2 mat = { 1, 0, 0, 1 }; // The identity matrix.
81         DWORD len = GetGlyphOutlineW(dc, 'x', GGO_METRICS, &gm, 0, 0, &mat);
82         if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0)
83             xHeight = int(gm.gmptGlyphOrigin.y + 0.5f);
84     }
85
86     WCHAR name[LF_FACESIZE];
87     unsigned resultLength = GetTextFaceW(dc, LF_FACESIZE, name);
88     if (resultLength > 0)
89         resultLength--; // ignore the null terminator
90     RestoreDC(dc, -1);
91     ReleaseDC(0, dc);
92     dc = 0;
93     if (!equalIgnoringCase(fontFace, String(name, resultLength))) {
94         DeleteObject(font);
95         return 0;
96     }
97     
98     // This font face is valid.  Create a FontData now.
99     FontData* result = new FontData(font, fontDescription);
100     result->setMetrics(ascent, descent, xHeight, lineSpacing);
101     return result;
102 }
103
104 FontFallbackList::FontFallbackList()
105 :m_pitch(UnknownPitch)
106 {
107     
108 }
109
110 FontFallbackList::~FontFallbackList()
111 {
112     deleteAllValues(m_fontList);
113 }
114
115 void FontFallbackList::determinePitch(const FontDescription& fontDescription) const
116 {
117     // FIXME: Implement this.
118     m_pitch = VariablePitch;
119 }
120
121 void FontFallbackList::invalidate()
122 {
123     // Delete the Cairo fonts.
124     m_pitch = UnknownPitch;
125     deleteAllValues(m_fontList);
126     m_fontList.clear();
127 }
128
129 FontData* FontFallbackList::primaryFont(const FontDescription& fontDescription) const
130 {
131     if (!m_fontList.isEmpty())
132         return m_fontList[0];
133
134     // We want to ensure that the primary Cairo font face exists.
135     for (const FontFamily* currFamily = &fontDescription.family(); 
136          currFamily;
137          currFamily = currFamily->next()) {
138         if (!currFamily->familyIsEmpty()) {
139             // Attempt to create a FontData.
140             FontData* font = getFontData(fontDescription, currFamily->family());
141             if (font) {
142                 m_fontList.append(font);
143                 return font;
144             }
145         }
146     }
147
148     // FIXME: Go ahead and use the serif default.  For now hardcode Times New Roman.
149     // We'll need a way to either get to the settings without going through a frame, or we'll
150     // need this to be passed in as part of the fallback list.
151     FontData* defaultFont = getFontData(fontDescription, AtomicString("Times New Roman"));
152     if (defaultFont)
153         m_fontList.append(defaultFont);
154     return defaultFont;
155 }
156
157 static IntSize hackishExtentForString(HDC dc, FontData* font, const TextRun& run, int tabWidth, int xpos)
158 {
159     SaveDC(dc);
160
161     SelectObject(dc, font->platformData().hfont());
162
163     // Get the text extent of the characters.
164     // FIXME: Eventually we will have to go glyph by glyph.  For now we just assume that all
165     // glyphs are present in the primary font.
166     // FIXME: Support letter-spacing, word-spacing, smallcaps.
167     // FIXME: Handle tabs (the tabWidth and xpos parameters)
168     // FIXME: Handle RTL.
169     SIZE s;
170     BOOL result = GetTextExtentPoint32W(dc, (WCHAR*)(run.characters()), run.length(), &s);
171
172     RestoreDC(dc, -1);
173
174     if (!result)
175         return IntSize();
176     return s;
177 }
178
179 float Font::floatWidth(const TextRun& run, int tabWidth, int xpos, bool runRounding) const
180 {
181     FontData* font = m_fontList->primaryFont(fontDescription());
182     if (!font)
183         return 0;
184
185     HDC dc = GetDC((HWND)0); // FIXME: Need a way to get to the real HDC.
186     IntSize runSize = hackishExtentForString(dc, font, run, tabWidth, xpos);
187     ReleaseDC(0, dc);
188     return runSize.width();
189 }
190
191 void Font::drawText(GraphicsContext* context, const TextRun& run, const IntPoint& point, int tabWidth, int xpos,
192                     int toAdd, TextDirection d, bool visuallyOrdered) const
193 {
194     FontData* font = m_fontList->primaryFont(fontDescription());
195     if (!font)
196         return;
197
198     cairo_surface_t* surface = cairo_get_target(context->platformContext());
199     HDC dc = cairo_win32_surface_get_dc(surface);
200
201     SaveDC(dc);
202     SelectObject(dc, font->platformData().hfont());
203
204     int x = point.x();
205     int y = point.y();
206     y -= font->ascent();
207
208     SetBkMode(dc, TRANSPARENT);
209     const Color& color = context->pen().color();
210     SetTextColor(dc, RGB(color.red(), color.green(), color.blue())); // FIXME: Need to support alpha in the text color.
211     TextOutW(dc, x, y, (LPCWSTR)(run.characters()), run.length());
212
213     RestoreDC(dc, -1);
214     // No need to ReleaseDC the HDC borrowed from cairo
215 }
216
217 FloatRect Font::selectionRectForText(const TextRun& run, const IntPoint& point, int h, int tabWidth, int xpos,
218                                    int toAdd, bool rtl, bool visuallyOrdered) const
219 {
220     FontData* font = m_fontList->primaryFont(fontDescription());
221     if (!font)
222         return IntRect();
223
224     HDC dc = GetDC((HWND)0); // FIXME: Need a way to get to the real HDC.
225     IntSize runSize = hackishExtentForString(dc, font, run, tabWidth, xpos);
226     ReleaseDC(0, dc);
227     return FloatRect(point, runSize);
228 }
229
230 int Font::checkSelectionPoint(const TextRun& run, int toAdd, int tabWidth, int xpos, int x,
231                               TextDirection, bool visuallyOrdered, bool includePartialGlyphs) const
232 {
233     FontData* font = m_fontList->primaryFont(fontDescription());
234     if (!font)
235         return 0;
236
237     HDC dc = GetDC((HWND)0); // FIXME: Need a way to get to the real HDC.
238
239     SaveDC(dc);
240     SelectObject(dc, font->platformData().hfont());
241     
242     int* caretPositions = (int*)fastMalloc(len * sizeof(int));
243     GCP_RESULTS results;
244     memset(&results, 0, sizeof(GCP_RESULTS));
245     results.lStructSize = sizeof(GCP_RESULTS);
246     results.lpCaretPos = caretPositions;
247     results.nGlyphs = run.length();
248     
249     GetCharacterPlacement(dc, (LPCTSTR)(run.characters()), run.length(), 0, &results, 0);
250
251     unsigned selectionOffset = 0;
252     while (selectionOffset < run.length() && caretPositions[selectionOffset] < x)
253         selectionOffset++;
254
255     fastFree(caretPositions);
256
257     RestoreDC(dc, -1);
258     ReleaseDC(0, dc);
259     return selectionOffset;
260 }
261
262 void Font::drawLineForText(GraphicsContext* context, const IntPoint& point, int yOffset, int width) const
263 {
264     IntPoint origin = point + IntSize(0, yOffset + 1);
265     IntPoint endPoint = origin + IntSize(width, 0);
266     const_cast<GraphicsContext*>(context)->drawLine(origin, endPoint);
267 }
268
269 void Font::drawLineForMisspelling(GraphicsContext* context, const IntPoint& point, int width) const
270 {
271 }
272
273 int Font::misspellingLineThickness(GraphicsContext* context) const
274 {
275     return 1;
276 }
277
278 }