1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
6 #if defined(MOZ_WIDGET_GTK)
7 #include "gfxPlatformGtk.h"
8 #define gfxToolkitPlatform gfxPlatformGtk
9 #elif defined(MOZ_WIDGET_QT)
10 #include <qfontinfo.h>
11 #include "gfxQtPlatform.h"
12 #define gfxToolkitPlatform gfxQtPlatform
14 #include "gfxWindowsPlatform.h"
15 #define gfxToolkitPlatform gfxWindowsPlatform
16 #elif defined(ANDROID)
17 #include "gfxAndroidPlatform.h"
18 #define gfxToolkitPlatform gfxAndroidPlatform
22 #include "gfxFT2Fonts.h"
23 #include "gfxFT2FontBase.h"
24 #include "gfxFT2Utils.h"
25 #include "gfxFT2FontList.h"
27 #include "nsGkAtoms.h"
29 #include "nsUnicodeRange.h"
31 #include "nsXULAppAPI.h"
36 #include "mozilla/MemoryReporting.h"
37 #include "mozilla/Preferences.h"
38 #include "mozilla/gfx/2D.h"
45 gfxFT2Font::ShapeText(gfxContext
*aContext
,
46 const char16_t
*aText
,
50 gfxShapedText
*aShapedText
)
52 if (!gfxFont::ShapeText(aContext
, aText
, aOffset
, aLength
, aScript
,
54 // harfbuzz must have failed(?!), just render raw glyphs
55 AddRange(aText
, aOffset
, aLength
, aShapedText
);
56 PostShapingFixup(aContext
, aText
, aOffset
, aLength
, aShapedText
);
63 gfxFT2Font::AddRange(const char16_t
*aText
, uint32_t aOffset
,
64 uint32_t aLength
, gfxShapedText
*aShapedText
)
66 const uint32_t appUnitsPerDevUnit
= aShapedText
->GetAppUnitsPerDevUnit();
67 // we'll pass this in/figure it out dynamically, but at this point there can be only one face.
68 gfxFT2LockedFace
faceLock(this);
69 FT_Face face
= faceLock
.get();
71 gfxShapedText::CompressedGlyph
*charGlyphs
=
72 aShapedText
->GetCharacterGlyphs();
74 const gfxFT2Font::CachedGlyphData
*cgd
= nullptr, *cgdNext
= nullptr;
76 FT_UInt spaceGlyph
= GetSpaceGlyph();
78 for (uint32_t i
= 0; i
< aLength
; i
++, aOffset
++) {
79 char16_t ch
= aText
[i
];
82 // treat this null byte as a missing glyph, don't create a glyph for it
83 aShapedText
->SetMissingGlyph(aOffset
, 0, this);
87 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(ch
), "Invalid char detected");
93 cgd
= GetGlyphDataForChar(ch
);
96 FT_UInt gid
= cgd
->glyphIndex
;
100 advance
= -1; // trigger the missing glyphs case below
102 // find next character and its glyph -- in case they exist
103 // and exist in the current font face -- to compute kerning
106 FT_Pos lsbDeltaNext
= 0;
108 if (FT_HAS_KERNING(face
) && i
+ 1 < aLength
) {
109 chNext
= aText
[i
+ 1];
111 cgdNext
= GetGlyphDataForChar(chNext
);
112 gidNext
= cgdNext
->glyphIndex
;
113 if (gidNext
&& gidNext
!= spaceGlyph
)
114 lsbDeltaNext
= cgdNext
->lsbDelta
;
118 advance
= cgd
->xAdvance
;
120 // now add kerning to the current glyph's advance
121 if (chNext
&& gidNext
) {
122 FT_Vector kerning
; kerning
.x
= 0;
123 FT_Get_Kerning(face
, gid
, gidNext
, FT_KERNING_DEFAULT
, &kerning
);
124 advance
+= kerning
.x
;
125 if (cgd
->rsbDelta
- lsbDeltaNext
>= 32) {
127 } else if (cgd
->rsbDelta
- lsbDeltaNext
< -32) {
132 // convert 26.6 fixed point to app units
133 // round rather than truncate to nearest pixel
134 // because these advances are often scaled
135 advance
= ((advance
* appUnitsPerDevUnit
+ 32) >> 6);
139 gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance
) &&
140 gfxShapedText::CompressedGlyph::IsSimpleGlyphID(gid
)) {
141 charGlyphs
[aOffset
].SetSimpleGlyph(advance
, gid
);
142 } else if (gid
== 0) {
143 // gid = 0 only happens when the glyph is missing from the font
144 aShapedText
->SetMissingGlyph(aOffset
, ch
, this);
146 gfxTextRun::DetailedGlyph details
;
147 details
.mGlyphID
= gid
;
148 NS_ASSERTION(details
.mGlyphID
== gid
,
149 "Seriously weird glyph ID detected!");
150 details
.mAdvance
= advance
;
151 details
.mXOffset
= 0;
152 details
.mYOffset
= 0;
153 gfxShapedText::CompressedGlyph g
;
154 g
.SetComplex(charGlyphs
[aOffset
].IsClusterStart(), true, 1);
155 aShapedText
->SetGlyphs(aOffset
, g
, &details
);
160 gfxFT2Font::gfxFT2Font(cairo_scaled_font_t
*aCairoFont
,
161 FT2FontEntry
*aFontEntry
,
162 const gfxFontStyle
*aFontStyle
,
164 : gfxFT2FontBase(aCairoFont
, aFontEntry
, aFontStyle
)
165 , mCharGlyphCache(32)
167 NS_ASSERTION(mFontEntry
, "Unable to find font entry for font. Something is whack.");
168 mApplySyntheticBold
= aNeedsBold
;
171 gfxFT2Font::~gfxFT2Font()
176 * Look up the font in the gfxFont cache. If we don't find it, create one.
177 * In either case, add a ref, append it to the aFonts array, and return it ---
178 * except for OOM in which case we do nothing and return null.
180 already_AddRefed
<gfxFT2Font
>
181 gfxFT2Font::GetOrMakeFont(const nsAString
& aName
, const gfxFontStyle
*aStyle
,
185 FT2FontEntry
*fe
= static_cast<FT2FontEntry
*>
186 (gfxPlatformFontList::PlatformFontList()->
187 FindFontForFamily(aName
, aStyle
, aNeedsBold
));
189 FT2FontEntry
*fe
= static_cast<FT2FontEntry
*>
190 (gfxToolkitPlatform::GetPlatform()->FindFontEntry(aName
, *aStyle
));
193 NS_WARNING("Failed to find font entry for font!");
197 nsRefPtr
<gfxFT2Font
> font
= GetOrMakeFont(fe
, aStyle
, aNeedsBold
);
198 return font
.forget();
201 already_AddRefed
<gfxFT2Font
>
202 gfxFT2Font::GetOrMakeFont(FT2FontEntry
*aFontEntry
, const gfxFontStyle
*aStyle
,
205 nsRefPtr
<gfxFont
> font
= gfxFontCache::GetCache()->Lookup(aFontEntry
, aStyle
);
207 cairo_scaled_font_t
*scaledFont
= aFontEntry
->CreateScaledFont(aStyle
);
211 font
= new gfxFT2Font(scaledFont
, aFontEntry
, aStyle
, aNeedsBold
);
212 cairo_scaled_font_destroy(scaledFont
);
216 gfxFontCache::GetCache()->AddNew(font
);
218 return font
.forget().downcast
<gfxFT2Font
>();
222 gfxFT2Font::FillGlyphDataForChar(uint32_t ch
, CachedGlyphData
*gd
)
224 gfxFT2LockedFace
faceLock(this);
225 FT_Face face
= faceLock
.get();
227 if (!face
->charmap
|| face
->charmap
->encoding
!= FT_ENCODING_UNICODE
) {
228 FT_Select_Charmap(face
, FT_ENCODING_UNICODE
);
230 FT_UInt gid
= FT_Get_Char_Index(face
, ch
);
233 // this font doesn't support this char!
234 NS_ASSERTION(gid
!= 0, "We don't have a glyph, but font indicated that it supported this char in tables?");
239 FT_Int32 flags
= gfxPlatform::GetPlatform()->FontHintingEnabled() ?
241 (FT_LOAD_NO_AUTOHINT
| FT_LOAD_NO_HINTING
);
242 FT_Error err
= FT_Load_Glyph(face
, gid
, flags
);
245 // hmm, this is weird, we failed to load a glyph that we had?
246 NS_WARNING("Failed to load glyph that we got from Get_Char_index");
252 gd
->glyphIndex
= gid
;
253 gd
->lsbDelta
= face
->glyph
->lsb_delta
;
254 gd
->rsbDelta
= face
->glyph
->rsb_delta
;
255 gd
->xAdvance
= face
->glyph
->advance
.x
;
259 gfxFT2Font::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
,
260 FontCacheSizes
* aSizes
) const
262 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
263 aSizes
->mFontInstances
+=
264 mCharGlyphCache
.SizeOfExcludingThis(nullptr, aMallocSizeOf
);
268 gfxFT2Font::AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
,
269 FontCacheSizes
* aSizes
) const
271 aSizes
->mFontInstances
+= aMallocSizeOf(this);
272 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
276 mozilla::TemporaryRef
<mozilla::gfx::GlyphRenderingOptions
>
277 gfxFT2Font::GetGlyphRenderingOptions()
279 mozilla::gfx::FontHinting hinting
;
281 if (gfxPlatform::GetPlatform()->FontHintingEnabled()) {
282 hinting
= mozilla::gfx::FontHinting::NORMAL
;
284 hinting
= mozilla::gfx::FontHinting::NONE
;
287 // We don't want to force the use of the autohinter over the font's built in hints
288 return mozilla::gfx::Factory::CreateCairoGlyphRenderingOptions(hinting
, false);