WebCore:
[WebKit-https.git] / WebCore / kwq / KWQFontMetrics.mm
1 /*
2  * Copyright (C) 2004 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 #import "KWQFontMetrics.h"
27
28 #import <Cocoa/Cocoa.h>
29
30 #import "KWQFont.h"
31 #import "KWQLogging.h"
32 #import "KWQFoundationExtras.h"
33
34 #import "WebCoreTextRenderer.h"
35 #import "WebCoreTextRendererFactory.h"
36
37 // We know that none of the ObjC calls here will raise exceptions
38 // because they are all calls to WebCoreTextRenderer, which has a
39 // contract of not raising.
40
41 struct QFontMetricsPrivate
42 {
43     QFontMetricsPrivate(const QFont &font)
44         : refCount(0), _font(font), _renderer(nil)
45     {
46     }
47     ~QFontMetricsPrivate()
48     {
49         KWQRelease(_renderer);
50     }
51     id <WebCoreTextRenderer> getRenderer()
52     {
53         if (!_renderer) {
54             _renderer = KWQRetain([[WebCoreTextRendererFactory sharedFactory]
55                 rendererWithFont:_font.getNSFont()
56                 usingPrinterFont:_font.isPrinterFont()]);
57         }
58         return _renderer;
59     }
60     
61     const QFont &font() const { return _font; }
62     void setFont(const QFont &font)
63     {
64         if (_font == font) {
65             return;
66         }
67         _font = font;
68         KWQRelease(_renderer);
69         _renderer = nil;
70     }
71     
72     int refCount;
73     
74 private:
75     QFont _font;
76     id <WebCoreTextRenderer> _renderer;
77     
78     QFontMetricsPrivate(const QFontMetricsPrivate&);
79     QFontMetricsPrivate& operator=(const QFontMetricsPrivate&);
80 };
81
82 QFontMetrics::QFontMetrics()
83 {
84 }
85
86 QFontMetrics::QFontMetrics(const QFont &font)
87     : data(new QFontMetricsPrivate(font))
88 {
89 }
90
91 QFontMetrics::QFontMetrics(const QFontMetrics &other)
92     : data(other.data)
93 {
94 }
95
96 QFontMetrics::~QFontMetrics()
97 {
98 }
99
100 QFontMetrics &QFontMetrics::operator=(const QFontMetrics &other)
101 {
102     data = other.data;
103     return *this;
104 }
105
106 void QFontMetrics::setFont(const QFont &font)
107 {
108     if (data.isNull()) {
109         data = KWQRefPtr<QFontMetricsPrivate>(new QFontMetricsPrivate(font));
110     } else {
111         data->setFont(font);
112     }
113 }
114
115 int QFontMetrics::ascent() const
116 {
117     if (data.isNull()) {
118         ERROR("called ascent on an empty QFontMetrics");
119         return 0;
120     }
121     
122     return [data->getRenderer() ascent];
123 }
124
125 int QFontMetrics::descent() const
126 {
127     if (data.isNull()) {
128         ERROR("called descent on an empty QFontMetrics");
129         return 0;
130     }
131     
132     return [data->getRenderer() descent];
133 }
134
135 int QFontMetrics::height() const
136 {
137     // According to Qt documentation: 
138     // "This is always equal to ascent()+descent()+1 (the 1 is for the base line)."
139     // We DO NOT match the Qt behavior here.  This is intentional.
140     return ascent() + descent();
141 }
142
143 int QFontMetrics::lineSpacing() const
144 {
145     if (data.isNull()) {
146         ERROR("called lineSpacing on an empty QFontMetrics");
147         return 0;
148     }
149     return [data->getRenderer() lineSpacing];
150 }
151
152 float QFontMetrics::xHeight() const
153 {
154     if (data.isNull()) {
155         ERROR("called xHeight on an empty QFontMetrics");
156         return 0;
157     }
158     return [data->getRenderer() xHeight];
159 }
160
161 int QFontMetrics::width(QChar qc) const
162 {
163     if (data.isNull()) {
164         ERROR("called width on an empty QFontMetrics");
165         return 0;
166     }
167     
168     UniChar c = qc.unicode();
169
170     CREATE_FAMILY_ARRAY(data->font(), families);
171
172     WebCoreTextRun run;
173     WebCoreInitializeTextRun(&run, &c, 1, 0, 1);
174     
175     WebCoreTextStyle style;
176     WebCoreInitializeEmptyTextStyle(&style);
177     style.families = families;
178
179     return ROUND_TO_INT([data->getRenderer() floatWidthForRun:&run style:&style widths:0]);
180 }
181
182 int QFontMetrics::charWidth(const QString &s, int pos) const
183 {
184     return width(s[pos]);
185 }
186
187 int QFontMetrics::width(char c) const
188 {
189     if (data.isNull()) {
190         ERROR("called width on an empty QFontMetrics");
191         return 0;
192     }
193     
194     UniChar ch = (uchar) c;
195
196     CREATE_FAMILY_ARRAY(data->font(), families);
197
198     WebCoreTextRun run;
199     WebCoreInitializeTextRun(&run, &ch, 1, 0, 1);
200     
201     WebCoreTextStyle style;
202     WebCoreInitializeEmptyTextStyle(&style);
203     style.families = families;
204
205     return ROUND_TO_INT([data->getRenderer() floatWidthForRun:&run style:&style widths:0]);
206 }
207
208 int QFontMetrics::width(const QString &qstring, int len) const
209 {
210     if (data.isNull()) {
211         ERROR("called width on an empty QFontMetrics");
212         return 0;
213     }
214     
215     CREATE_FAMILY_ARRAY(data->font(), families);
216
217     int length = len == -1 ? qstring.length() : len;
218
219     WebCoreTextRun run;
220     WebCoreInitializeTextRun(&run, (const UniChar *)qstring.unicode(), length, 0, length);
221     
222     WebCoreTextStyle style;
223     WebCoreInitializeEmptyTextStyle(&style);
224     style.families = families;
225
226     return ROUND_TO_INT([data->getRenderer() floatWidthForRun:&run style:&style widths:0]);
227 }
228
229 int QFontMetrics::width(const QChar *uchars, int len) const
230 {
231     if (data.isNull()) {
232         ERROR("called width on an empty QFontMetrics");
233         return 0;
234     }
235     
236     CREATE_FAMILY_ARRAY(data->font(), families);
237
238     WebCoreTextRun run;
239     WebCoreInitializeTextRun(&run, (const UniChar *)uchars, len, 0, len);
240     
241     WebCoreTextStyle style;
242     WebCoreInitializeEmptyTextStyle(&style);
243     style.families = families;
244
245     return ROUND_TO_INT([data->getRenderer() floatWidthForRun:&run style:&style widths:0]);
246 }
247
248 float QFontMetrics::floatWidth(const QChar *uchars, int slen, int pos, int len,
249                                int letterSpacing, int wordSpacing, bool smallCaps) const
250 {
251     if (data.isNull()) {
252         ERROR("called floatWidth on an empty QFontMetrics");
253         return 0;
254     }
255     
256     CREATE_FAMILY_ARRAY(data->font(), families);
257
258     WebCoreTextRun run;
259     WebCoreInitializeTextRun(&run, (const UniChar *)uchars, slen, pos, pos+len);
260     
261     WebCoreTextStyle style;
262     WebCoreInitializeEmptyTextStyle(&style);
263     style.letterSpacing = letterSpacing;
264     style.wordSpacing = wordSpacing;
265     style.smallCaps = smallCaps;
266     style.families = families;
267
268     return ROUND_TO_INT([data->getRenderer() floatWidthForRun:&run style:&style widths:0]);
269 }
270
271 float QFontMetrics::floatCharacterWidths(const QChar *uchars, int slen, int pos, int len, int toAdd, float *buffer, int letterSpacing, int wordSpacing, bool smallCaps) const
272 {
273     if (data.isNull()) {
274         ERROR("called floatCharacterWidths on an empty QFontMetrics");
275         return 0;
276     }
277     
278     CREATE_FAMILY_ARRAY(data->font(), families);
279
280     WebCoreTextRun run;
281     WebCoreInitializeTextRun(&run, (const UniChar *)uchars, slen, pos, pos+len);
282     
283     WebCoreTextStyle style;
284     WebCoreInitializeEmptyTextStyle(&style);
285     style.letterSpacing = letterSpacing;
286     style.wordSpacing = wordSpacing;
287     style.smallCaps = smallCaps;
288     style.padding = toAdd;
289     style.families = families;
290
291     return [data->getRenderer() floatWidthForRun:&run style:&style widths:buffer];
292 }
293
294 int QFontMetrics::checkSelectionPoint (QChar *s, int slen, int pos, int len, int toAdd, int letterSpacing, int wordSpacing, bool smallCaps, int x, bool reversed, bool includePartialGlyphs) const
295 {
296     if (data.isNull()) {
297         ERROR("called floatWidth on an empty QFontMetrics");
298         return 0;
299     }
300     
301     CREATE_FAMILY_ARRAY(data->font(), families);
302     WebCoreTextRun run;
303     WebCoreInitializeTextRun(&run, (const UniChar *)s, slen, pos, pos+len);
304     
305     WebCoreTextStyle style;
306     WebCoreInitializeEmptyTextStyle(&style);
307     style.letterSpacing = letterSpacing;
308     style.wordSpacing = wordSpacing;
309     style.smallCaps = smallCaps;
310     style.families = families;
311     style.padding = toAdd;
312     style.rtl = reversed;
313
314     return [data->getRenderer() pointToOffset:&run style:&style position:x reversed:reversed includePartialGlyphs:includePartialGlyphs];
315 }
316
317 QRect QFontMetrics::boundingRect(QChar c) const
318 {
319     return QRect(0, 0, width(c), height());
320 }
321
322 QRect QFontMetrics::boundingRect(const QString &qstring, int len) const
323 {
324     return QRect(0, 0, width(qstring, len), height());
325 }
326
327 QRect QFontMetrics::boundingRect(int x, int y, int width, int height, int flags, const QString &str) const
328 {
329     // FIXME: need to support word wrapping?
330     return QRect(x, y, width, height).intersect(boundingRect(str));
331 }
332
333 QSize QFontMetrics::size(int, const QString &qstring) const
334 {
335     return QSize(width(qstring), height());
336 }