Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "ScaledFontFontconfig.h"
7 : #include "UnscaledFontFreeType.h"
8 : #include "Logging.h"
9 :
10 : #ifdef USE_SKIA
11 : #include "skia/include/ports/SkTypeface_cairo.h"
12 : #endif
13 :
14 : #include <fontconfig/fcfreetype.h>
15 :
16 : namespace mozilla {
17 : namespace gfx {
18 :
19 : // On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
20 : // an SkFontHost implementation that allows Skia to render using this.
21 : // This is mainly because FT_Face is not good for sharing between libraries, which
22 : // is a requirement when we consider runtime switchable backends and so on
23 21 : ScaledFontFontconfig::ScaledFontFontconfig(cairo_scaled_font_t* aScaledFont,
24 : FcPattern* aPattern,
25 : const RefPtr<UnscaledFont>& aUnscaledFont,
26 21 : Float aSize)
27 : : ScaledFontBase(aUnscaledFont, aSize),
28 21 : mPattern(aPattern)
29 : {
30 21 : SetCairoScaledFont(aScaledFont);
31 21 : FcPatternReference(aPattern);
32 21 : }
33 :
34 63 : ScaledFontFontconfig::~ScaledFontFontconfig()
35 : {
36 21 : FcPatternDestroy(mPattern);
37 63 : }
38 :
39 : #ifdef USE_SKIA
40 21 : SkTypeface* ScaledFontFontconfig::GetSkTypeface()
41 : {
42 21 : if (!mTypeface) {
43 21 : mTypeface = SkCreateTypefaceFromCairoFTFontWithFontconfig(mScaledFont, mPattern);
44 : }
45 :
46 21 : return mTypeface;
47 : }
48 : #endif
49 :
50 0 : ScaledFontFontconfig::InstanceData::InstanceData(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern)
51 : : mFlags(0)
52 : , mHintStyle(FC_HINT_NONE)
53 : , mSubpixelOrder(FC_RGBA_UNKNOWN)
54 0 : , mLcdFilter(FC_LCD_LEGACY)
55 : {
56 : // Record relevant Fontconfig properties into instance data.
57 : FcBool autohint;
58 0 : if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch && autohint) {
59 0 : mFlags |= AUTOHINT;
60 : }
61 : FcBool bitmap;
62 0 : if (FcPatternGetBool(aPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) == FcResultMatch && bitmap) {
63 0 : mFlags |= EMBEDDED_BITMAP;
64 : }
65 : FcBool embolden;
66 0 : if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch && embolden) {
67 0 : mFlags |= EMBOLDEN;
68 : }
69 : FcBool vertical;
70 0 : if (FcPatternGetBool(aPattern, FC_VERTICAL_LAYOUT, 0, &vertical) == FcResultMatch && vertical) {
71 0 : mFlags |= VERTICAL_LAYOUT;
72 : }
73 :
74 : FcBool antialias;
75 0 : if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &antialias) != FcResultMatch || antialias) {
76 0 : mFlags |= ANTIALIAS;
77 :
78 : // Only record subpixel order and lcd filtering if antialiasing is enabled.
79 : int rgba;
80 0 : if (FcPatternGetInteger(aPattern, FC_RGBA, 0, &rgba) == FcResultMatch) {
81 0 : mSubpixelOrder = rgba;
82 : }
83 : int filter;
84 0 : if (FcPatternGetInteger(aPattern, FC_LCD_FILTER, 0, &filter) == FcResultMatch) {
85 0 : mLcdFilter = filter;
86 : }
87 : }
88 :
89 0 : cairo_font_options_t* fontOptions = cairo_font_options_create();
90 0 : cairo_scaled_font_get_font_options(aScaledFont, fontOptions);
91 : // For printer fonts, Cairo hint metrics and hinting will be disabled.
92 : // For other fonts, allow hint metrics and hinting.
93 0 : if (cairo_font_options_get_hint_metrics(fontOptions) != CAIRO_HINT_METRICS_OFF) {
94 0 : mFlags |= HINT_METRICS;
95 :
96 : FcBool hinting;
97 0 : if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch || hinting) {
98 : int hintstyle;
99 0 : if (FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &hintstyle) != FcResultMatch) {
100 0 : hintstyle = FC_HINT_FULL;
101 : }
102 0 : mHintStyle = hintstyle;
103 : }
104 : }
105 0 : cairo_font_options_destroy(fontOptions);
106 :
107 : // Some fonts supply an adjusted size or otherwise use the font matrix for italicization.
108 : // Record the scale and the skew to accomodate both of these cases.
109 : cairo_matrix_t fontMatrix;
110 0 : cairo_scaled_font_get_font_matrix(aScaledFont, &fontMatrix);
111 0 : mScale = Float(fontMatrix.xx);
112 0 : mSkew = Float(fontMatrix.xy);
113 0 : }
114 :
115 : void
116 0 : ScaledFontFontconfig::InstanceData::SetupPattern(FcPattern* aPattern) const
117 : {
118 0 : if (mFlags & AUTOHINT) {
119 0 : FcPatternAddBool(aPattern, FC_AUTOHINT, FcTrue);
120 : }
121 0 : if (mFlags & EMBEDDED_BITMAP) {
122 0 : FcPatternAddBool(aPattern, FC_EMBEDDED_BITMAP, FcTrue);
123 : }
124 0 : if (mFlags & EMBOLDEN) {
125 0 : FcPatternAddBool(aPattern, FC_EMBOLDEN, FcTrue);
126 : }
127 0 : if (mFlags & VERTICAL_LAYOUT) {
128 0 : FcPatternAddBool(aPattern, FC_VERTICAL_LAYOUT, FcTrue);
129 : }
130 :
131 0 : if (mFlags & ANTIALIAS) {
132 0 : FcPatternAddBool(aPattern, FC_ANTIALIAS, FcTrue);
133 0 : if (mSubpixelOrder != FC_RGBA_UNKNOWN) {
134 0 : FcPatternAddInteger(aPattern, FC_RGBA, mSubpixelOrder);
135 : }
136 0 : if (mLcdFilter != FC_LCD_LEGACY) {
137 0 : FcPatternAddInteger(aPattern, FC_LCD_FILTER, mLcdFilter);
138 : }
139 : } else {
140 0 : FcPatternAddBool(aPattern, FC_ANTIALIAS, FcFalse);
141 : }
142 :
143 0 : if (mHintStyle) {
144 0 : FcPatternAddBool(aPattern, FC_HINTING, FcTrue);
145 0 : FcPatternAddInteger(aPattern, FC_HINT_STYLE, mHintStyle);
146 : } else {
147 0 : FcPatternAddBool(aPattern, FC_HINTING, FcFalse);
148 : }
149 0 : }
150 :
151 : void
152 0 : ScaledFontFontconfig::InstanceData::SetupFontOptions(cairo_font_options_t* aFontOptions) const
153 : {
154 : // Try to build a sane initial set of Cairo font options based on the Fontconfig
155 : // pattern.
156 0 : if (mFlags & HINT_METRICS) {
157 : // For regular (non-printer) fonts, enable hint metrics as well as hinting
158 : // and (possibly subpixel) antialiasing.
159 0 : cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_ON);
160 :
161 : cairo_hint_style_t hinting;
162 0 : switch (mHintStyle) {
163 : case FC_HINT_NONE:
164 0 : hinting = CAIRO_HINT_STYLE_NONE;
165 0 : break;
166 : case FC_HINT_SLIGHT:
167 0 : hinting = CAIRO_HINT_STYLE_SLIGHT;
168 0 : break;
169 : case FC_HINT_MEDIUM:
170 : default:
171 0 : hinting = CAIRO_HINT_STYLE_MEDIUM;
172 0 : break;
173 : case FC_HINT_FULL:
174 0 : hinting = CAIRO_HINT_STYLE_FULL;
175 0 : break;
176 : }
177 0 : cairo_font_options_set_hint_style(aFontOptions, hinting);
178 :
179 0 : if (mFlags & ANTIALIAS) {
180 0 : cairo_subpixel_order_t subpixel = CAIRO_SUBPIXEL_ORDER_DEFAULT;
181 0 : switch (mSubpixelOrder) {
182 : case FC_RGBA_RGB:
183 0 : subpixel = CAIRO_SUBPIXEL_ORDER_RGB;
184 0 : break;
185 : case FC_RGBA_BGR:
186 0 : subpixel = CAIRO_SUBPIXEL_ORDER_BGR;
187 0 : break;
188 : case FC_RGBA_VRGB:
189 0 : subpixel = CAIRO_SUBPIXEL_ORDER_VRGB;
190 0 : break;
191 : case FC_RGBA_VBGR:
192 0 : subpixel = CAIRO_SUBPIXEL_ORDER_VBGR;
193 0 : break;
194 : default:
195 0 : break;
196 : }
197 0 : if (subpixel != CAIRO_SUBPIXEL_ORDER_DEFAULT) {
198 0 : cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_SUBPIXEL);
199 0 : cairo_font_options_set_subpixel_order(aFontOptions, subpixel);
200 : } else {
201 0 : cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_GRAY);
202 : }
203 : } else {
204 0 : cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_NONE);
205 : }
206 : } else {
207 : // For printer fonts, disable hint metrics and hinting. Don't allow subpixel
208 : // antialiasing.
209 0 : cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_OFF);
210 0 : cairo_font_options_set_hint_style(aFontOptions, CAIRO_HINT_STYLE_NONE);
211 0 : cairo_font_options_set_antialias(aFontOptions,
212 0 : mFlags & ANTIALIAS ? CAIRO_ANTIALIAS_GRAY : CAIRO_ANTIALIAS_NONE);
213 : }
214 0 : }
215 :
216 : void
217 0 : ScaledFontFontconfig::InstanceData::SetupFontMatrix(cairo_matrix_t* aFontMatrix) const
218 : {
219 : // Build a font matrix that will reproduce a possibly adjusted size
220 : // and any italics/skew. This is just the concatenation of a simple
221 : // scale matrix with a matrix that skews on the X axis.
222 0 : cairo_matrix_init(aFontMatrix, mScale, 0, mSkew, mScale, 0, 0);
223 0 : }
224 :
225 : bool
226 0 : ScaledFontFontconfig::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton)
227 : {
228 0 : InstanceData instance(GetCairoScaledFont(), mPattern);
229 :
230 0 : aCb(reinterpret_cast<uint8_t*>(&instance), sizeof(instance), aBaton);
231 0 : return true;
232 : }
233 :
234 : already_AddRefed<ScaledFont>
235 0 : UnscaledFontFontconfig::CreateScaledFont(Float aGlyphSize,
236 : const uint8_t* aInstanceData,
237 : uint32_t aInstanceDataLength)
238 : {
239 0 : if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) {
240 0 : gfxWarning() << "Fontconfig scaled font instance data is truncated.";
241 0 : return nullptr;
242 : }
243 : const ScaledFontFontconfig::InstanceData *instanceData =
244 0 : reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(aInstanceData);
245 : return ScaledFontFontconfig::CreateFromInstanceData(*instanceData, this, aGlyphSize,
246 0 : mNativeFontResource.get());
247 : }
248 :
249 : static cairo_user_data_key_t sNativeFontResourceKey;
250 :
251 : static void
252 0 : ReleaseNativeFontResource(void* aData)
253 : {
254 0 : static_cast<NativeFontResource*>(aData)->Release();
255 0 : }
256 :
257 : already_AddRefed<ScaledFont>
258 0 : ScaledFontFontconfig::CreateFromInstanceData(const InstanceData& aInstanceData,
259 : UnscaledFontFontconfig* aUnscaledFont,
260 : Float aSize,
261 : NativeFontResource* aNativeFontResource)
262 : {
263 0 : FcPattern* pattern = FcPatternCreate();
264 0 : if (!pattern) {
265 0 : gfxWarning() << "Failing initializing Fontconfig pattern for scaled font";
266 0 : return nullptr;
267 : }
268 0 : if (aUnscaledFont->GetFace()) {
269 0 : FcPatternAddFTFace(pattern, FC_FT_FACE, aUnscaledFont->GetFace());
270 : } else {
271 0 : FcPatternAddString(pattern, FC_FILE, reinterpret_cast<const FcChar8*>(aUnscaledFont->GetFile()));
272 0 : FcPatternAddInteger(pattern, FC_INDEX, aUnscaledFont->GetIndex());
273 : }
274 0 : FcPatternAddDouble(pattern, FC_PIXEL_SIZE, aSize);
275 0 : aInstanceData.SetupPattern(pattern);
276 :
277 0 : cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(pattern);
278 0 : if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
279 0 : gfxWarning() << "Failed creating Cairo font face for Fontconfig pattern";
280 0 : FcPatternDestroy(pattern);
281 0 : return nullptr;
282 : }
283 :
284 0 : if (aNativeFontResource) {
285 : // Bug 1362117 - Cairo may keep the font face alive after the owning NativeFontResource
286 : // was freed. To prevent this, we must bind the NativeFontResource to the font face so that
287 : // it stays alive at least as long as the font face.
288 0 : if (cairo_font_face_set_user_data(font,
289 : &sNativeFontResourceKey,
290 : aNativeFontResource,
291 : ReleaseNativeFontResource) != CAIRO_STATUS_SUCCESS) {
292 0 : gfxWarning() << "Failed binding NativeFontResource to Cairo font face";
293 0 : cairo_font_face_destroy(font);
294 0 : FcPatternDestroy(pattern);
295 0 : return nullptr;
296 : }
297 0 : aNativeFontResource->AddRef();
298 : }
299 :
300 : cairo_matrix_t sizeMatrix;
301 0 : aInstanceData.SetupFontMatrix(&sizeMatrix);
302 :
303 : cairo_matrix_t identityMatrix;
304 0 : cairo_matrix_init_identity(&identityMatrix);
305 :
306 0 : cairo_font_options_t *fontOptions = cairo_font_options_create();
307 0 : aInstanceData.SetupFontOptions(fontOptions);
308 :
309 : cairo_scaled_font_t* cairoScaledFont =
310 0 : cairo_scaled_font_create(font, &sizeMatrix, &identityMatrix, fontOptions);
311 :
312 0 : cairo_font_options_destroy(fontOptions);
313 0 : cairo_font_face_destroy(font);
314 :
315 0 : if (cairo_scaled_font_status(cairoScaledFont) != CAIRO_STATUS_SUCCESS) {
316 0 : gfxWarning() << "Failed creating Cairo scaled font for font face";
317 0 : FcPatternDestroy(pattern);
318 0 : return nullptr;
319 : }
320 :
321 : RefPtr<ScaledFontFontconfig> scaledFont =
322 0 : new ScaledFontFontconfig(cairoScaledFont, pattern, aUnscaledFont, aSize);
323 :
324 0 : cairo_scaled_font_destroy(cairoScaledFont);
325 0 : FcPatternDestroy(pattern);
326 :
327 0 : return scaledFont.forget();
328 : }
329 :
330 : already_AddRefed<UnscaledFont>
331 0 : UnscaledFontFontconfig::CreateFromFontDescriptor(const uint8_t* aData, uint32_t aDataLength)
332 : {
333 0 : if (aDataLength < sizeof(FontDescriptor)) {
334 0 : gfxWarning() << "Fontconfig font descriptor is truncated.";
335 0 : return nullptr;
336 : }
337 0 : const FontDescriptor* desc = reinterpret_cast<const FontDescriptor*>(aData);
338 0 : if (desc->mPathLength < 1 ||
339 0 : desc->mPathLength > aDataLength - sizeof(FontDescriptor)) {
340 0 : gfxWarning() << "Pathname in Fontconfig font descriptor has invalid size.";
341 0 : return nullptr;
342 : }
343 0 : const char* path = reinterpret_cast<const char*>(aData + sizeof(FontDescriptor));
344 0 : if (path[desc->mPathLength - 1] != '\0') {
345 0 : gfxWarning() << "Pathname in Fontconfig font descriptor is not terminated.";
346 0 : return nullptr;
347 : }
348 :
349 0 : RefPtr<UnscaledFont> unscaledFont = new UnscaledFontFontconfig(path, desc->mIndex);
350 0 : return unscaledFont.forget();
351 : }
352 :
353 : } // namespace gfx
354 : } // namespace mozilla
|