bug 1015603 part 5 - implement fake small-caps in gfx using a reduced-size clone of the font. r=roc

This commit is contained in:
Jonathan Kew 2014-05-26 14:23:32 +01:00
parent 18ab93cab6
commit 35a3de2e99
5 changed files with 261 additions and 17 deletions

View File

@ -27,6 +27,8 @@
#include "gfxUserFontSet.h"
#include "gfxPlatformFontList.h"
#include "gfxScriptItemizer.h"
#include "nsSpecialCasingData.h"
#include "nsTextRunTransformations.h"
#include "nsUnicodeProperties.h"
#include "nsMathUtils.h"
#include "nsBidiUtils.h"
@ -5442,16 +5444,27 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
// create the glyph run for this range
if (matchedFont) {
aTextRun->AddGlyphRun(matchedFont, range.matchType,
aOffset + runStart, (matchedLength > 0));
// do glyph layout and record the resulting positioned glyphs
if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
aString + runStart,
aOffset + runStart,
matchedLength,
aRunScript)) {
// glyph layout failed! treat as missing glyphs
matchedFont = nullptr;
if (mStyle.smallCaps) {
if (!matchedFont->InitSmallCapsRun(aContext, aTextRun,
aString + runStart,
aOffset + runStart,
matchedLength,
range.matchType,
aRunScript)) {
matchedFont = nullptr;
}
} else {
aTextRun->AddGlyphRun(matchedFont, range.matchType,
aOffset + runStart, (matchedLength > 0));
// do glyph layout and record the resulting positioned glyphs
if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
aString + runStart,
aOffset + runStart,
matchedLength,
aRunScript)) {
// glyph layout failed! treat as missing glyphs
matchedFont = nullptr;
}
}
} else {
aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
@ -5535,6 +5548,180 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
}
}
bool
gfxFont::InitSmallCapsRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const uint8_t *aText,
uint32_t aOffset,
uint32_t aLength,
uint8_t aMatchType,
int32_t aScript)
{
NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
aLength);
return InitSmallCapsRun(aContext, aTextRun, unicodeString.get(),
aOffset, aLength, aMatchType, aScript);
}
bool
gfxFont::InitSmallCapsRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
uint8_t aMatchType,
int32_t aScript)
{
bool ok = true;
nsRefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
enum RunCaseState {
kUpperOrCaseless, // will be untouched by font-variant:small-caps
kLowercase, // will be uppercased and reduced
kSpecialUpper // specials: don't shrink, but apply uppercase mapping
};
RunCaseState runCase = kUpperOrCaseless;
uint32_t runStart = 0;
for (uint32_t i = 0; i <= aLength; ++i) {
RunCaseState chCase = kUpperOrCaseless;
// Unless we're at the end, figure out what treatment the current
// character will need.
if (i < aLength) {
uint32_t ch = aText[i];
if (NS_IS_HIGH_SURROGATE(ch) && i < aLength - 1 &&
NS_IS_LOW_SURROGATE(aText[i + 1])) {
ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
}
// Characters that aren't the start of a cluster are ignored here.
// They get added to whatever lowercase/non-lowercase run we're in.
if (IsClusterExtender(ch)) {
chCase = runCase;
} else {
uint32_t ch2 = ToUpperCase(ch);
if (ch != ch2 || mozilla::unicode::SpecialUpper(ch)) {
chCase = kLowercase;
}
else if (mStyle.language == nsGkAtoms::el) {
// In Greek, check for characters that will be modified by
// the GreekUpperCase mapping - this catches accented
// capitals where the accent is to be removed (bug 307039).
// These are handled by using the full-size font with the
// uppercasing transform.
GreekCasing::State state;
ch2 = GreekCasing::UpperCase(ch, state);
if (ch != ch2) {
chCase = kSpecialUpper;
}
}
}
}
// At the end of the text or when the current character needs different
// casing treatment from the current run, finish the run-in-progress
// and prepare to accumulate a new run.
// Note that we do not look at any source data for offset [i] here,
// as that would be invalid in the case where i==length.
if ((i == aLength || runCase != chCase) && runStart < i) {
uint32_t runLength = i - runStart;
gfxFont* f = this;
switch (runCase) {
case kUpperOrCaseless:
// just use the current font and the existing string
aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true);
if (!f->SplitAndInitTextRun(aContext, aTextRun,
aText + runStart,
aOffset + runStart, runLength,
aScript)) {
ok = false;
}
break;
case kLowercase:
// use reduced-size font, fall through to uppercase the text
f = smallCapsFont;
case kSpecialUpper:
// apply uppercase transform to the string
nsDependentSubstring origString(aText + runStart, runLength);
nsAutoString convertedString;
nsAutoTArray<bool,50> charsToMergeArray;
nsAutoTArray<bool,50> deletedCharsArray;
bool mergeNeeded = nsCaseTransformTextRunFactory::
TransformString(origString,
convertedString,
true,
mStyle.language,
charsToMergeArray,
deletedCharsArray);
if (mergeNeeded) {
// This is the hard case: the transformation caused chars
// to be inserted or deleted, so we can't shape directly
// into the destination textrun but have to handle the
// mismatch of character positions.
gfxTextRunFactory::Parameters params = {
aContext, nullptr, nullptr, nullptr, 0,
aTextRun->GetAppUnitsPerDevUnit()
};
nsAutoPtr<gfxTextRun> tempRun;
tempRun =
gfxTextRun::Create(&params, convertedString.Length(),
aTextRun->GetFontGroup(), 0);
tempRun->AddGlyphRun(f, aMatchType, 0, true);
if (!f->SplitAndInitTextRun(aContext, tempRun,
convertedString.BeginReading(),
0, convertedString.Length(),
aScript)) {
ok = false;
} else {
nsAutoPtr<gfxTextRun> mergedRun;
mergedRun =
gfxTextRun::Create(&params, runLength,
aTextRun->GetFontGroup(), 0);
MergeCharactersInTextRun(mergedRun, tempRun,
charsToMergeArray.Elements(),
deletedCharsArray.Elements());
aTextRun->CopyGlyphDataFrom(mergedRun, 0, runLength,
aOffset + runStart);
}
} else {
aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
true);
if (!f->SplitAndInitTextRun(aContext, aTextRun,
convertedString.BeginReading(),
aOffset + runStart, runLength,
aScript)) {
ok = false;
}
}
break;
}
runStart = i;
}
if (i < aLength) {
runCase = chCase;
}
}
return ok;
}
already_AddRefed<gfxFont>
gfxFont::GetSmallCapsFont()
{
gfxFontStyle style(*GetStyle());
style.size *= SMALL_CAPS_SCALE_FACTOR;
style.smallCaps = false;
gfxFontEntry* fe = GetFontEntry();
bool needsBold = style.weight >= 600 && !fe->IsBold();
return fe->FindOrMakeFont(&style, needsBold);
}
gfxTextRun *
gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
LazyReferenceContextGetter& aRefContextGetter)

View File

@ -60,6 +60,8 @@ class nsILanguageAtomService;
#define NO_FONT_LANGUAGE_OVERRIDE 0
#define SMALL_CAPS_SCALE_FACTOR 0.8
struct FontListSizes;
struct gfxTextRunDrawCallbacks;
@ -1806,6 +1808,22 @@ public:
return mFontEntry->GetUVSGlyph(aCh, aVS);
}
bool InitSmallCapsRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const uint8_t *aText,
uint32_t aOffset,
uint32_t aLength,
uint8_t aMatchType,
int32_t aScript);
bool InitSmallCapsRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
uint8_t aMatchType,
int32_t aScript);
// call the (virtual) InitTextRun method to do glyph generation/shaping,
// limiting the length of text passed by processing the run in multiple
// segments if necessary
@ -1914,6 +1932,13 @@ public:
}
protected:
// Return a font that is a "clone" of this one, but reduced to 80% size
// (and with the smallCaps style set to false).
// Default implementation relies on gfxFontEntry::CreateFontInstance;
// backends that don't implement that will need to override this and use
// an alternative technique. (gfxPangoFonts, I'm looking at you...)
virtual already_AddRefed<gfxFont> GetSmallCapsFont();
// subclasses may provide (possibly hinted) glyph widths (in font units);
// if they do not override this, harfbuzz will use unhinted widths
// derived from the font tables

View File

@ -661,6 +661,8 @@ public:
#endif
protected:
virtual already_AddRefed<gfxFont> GetSmallCapsFont();
virtual bool ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
@ -669,12 +671,6 @@ protected:
gfxShapedText *aShapedText,
bool aPreferPlatformShaping);
bool InitGlyphRunWithPango(const char16_t *aString,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
private:
gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle);
@ -1601,6 +1597,41 @@ gfxFcFont::~gfxFcFont()
nullptr);
}
already_AddRefed<gfxFont>
gfxFcFont::GetSmallCapsFont()
{
gfxFontStyle style(*GetStyle());
style.size *= SMALL_CAPS_SCALE_FACTOR;
style.smallCaps = false;
gfxFcFontEntry* fe = static_cast<gfxFcFontEntry*>(GetFontEntry());
nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(fe, &style);
if (font) {
return font.forget();
}
cairo_matrix_t fontMatrix;
cairo_scaled_font_get_font_matrix(mScaledFont, &fontMatrix);
cairo_matrix_scale(&fontMatrix,
SMALL_CAPS_SCALE_FACTOR, SMALL_CAPS_SCALE_FACTOR);
cairo_matrix_t ctm;
cairo_scaled_font_get_ctm(mScaledFont, &ctm);
cairo_font_options_t *options = cairo_font_options_create();
cairo_scaled_font_get_font_options(mScaledFont, options);
cairo_scaled_font_t *smallFont =
cairo_scaled_font_create(cairo_scaled_font_get_font_face(mScaledFont),
&fontMatrix, &ctm, options);
cairo_font_options_destroy(options);
font = new gfxFcFont(smallFont, fe, &style);
gfxFontCache::GetCache()->AddNew(font);
cairo_scaled_font_destroy(smallFont);
return font.forget();
}
bool
gfxFcFont::ShapeText(gfxContext *aContext,
const char16_t *aText,

View File

@ -32,6 +32,7 @@ EXPORTS += [
'nsQueryFrame.h',
'nsSplittableFrame.h',
'nsSubDocumentFrame.h',
'nsTextRunTransformations.h',
'ScrollbarActivity.h',
'Selection.h',
'WritingModes.h',

View File

@ -9,9 +9,9 @@
#include "mozilla/Attributes.h"
#include "mozilla/MemoryReporting.h"
#include "gfxFont.h"
#include "nsStyleContext.h"
class nsTransformedTextRun;
class nsStyleContext;
class nsTransformingTextRunFactory {
public: