bug 502906 - part 3 - factor out Uniscribe and GDI shapers from Windows GDI font code. r=jdaggett

--HG--
rename : gfx/thebes/src/gfxWindowsFonts.cpp => gfx/thebes/src/gfxUniscribeShaper.cpp
rename : gfx/thebes/public/gfxWindowsFonts.h => gfx/thebes/src/gfxUniscribeShaper.h
This commit is contained in:
Jonathan Kew 2010-03-15 09:34:25 +00:00
Родитель 5975e1fa49
Коммит 544fbd0636
20 изменённых файлов: 1458 добавлений и 2291 удалений

Просмотреть файл

@ -51,7 +51,6 @@ EXPORTS += gfxFT2Fonts.h \
gfxDDrawSurface.h \
$(NULL)
else
EXPORTS += gfxWindowsFonts.h
EXPORTS += gfxDWriteFonts.h
EXPORTS += gfxD2DSurface.h
endif

Просмотреть файл

@ -660,11 +660,11 @@ public:
virtual ~gfxFontShaper() { }
virtual void InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength) = 0;
virtual PRBool InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength) = 0;
protected:
// the font this shaper is working with
@ -697,6 +697,14 @@ public:
PRInt32 GetRefCount() { return mRefCnt; }
// options to specify the kind of AA to be used when creating a font
typedef enum {
kAntialiasDefault,
kAntialiasNone,
kAntialiasGrayscale,
kAntialiasSubpixel
} AntialiasOption;
protected:
nsAutoRefCnt mRefCnt;
@ -712,7 +720,8 @@ protected:
}
}
gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle);
gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
AntialiasOption anAAOption = kAntialiasDefault);
public:
virtual ~gfxFont();
@ -753,6 +762,11 @@ public:
virtual nsString GetUniqueName() { return GetName(); }
virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) {
// platforms where this actually matters should override
return nsnull;
}
// Font metrics
struct Metrics {
gfxFloat xHeight;
@ -910,17 +924,14 @@ public:
return mFontEntry->HasCharacter(ch);
}
void InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength) {
NS_ASSERTION(mShaper != nsnull, "no shaper?!");
if (!mShaper) {
return;
}
mShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength);
}
// Default implementation simply calls mShaper->InitTextRun().
// Override if the font class wants to give special handling
// to shaper failure.
virtual void InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
protected:
nsRefPtr<gfxFontEntry> mFontEntry;
@ -933,6 +944,9 @@ protected:
// synthetic bolding for environments where this is not supported by the platform
PRUint32 mSyntheticBoldOffset; // number of devunit pixels to offset double-strike, 0 ==> no bolding
// the AA setting requested for this font - may affect glyph bounds
AntialiasOption mAntialiasOption;
nsAutoPtr<gfxFontShaper> mShaper;
// some fonts have bad metrics, this method sanitize them.

Просмотреть файл

@ -1,205 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* John Daggett <jdaggett@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_WINDOWSFONTS_H
#define GFX_WINDOWSFONTS_H
#include "prtypes.h"
#include "gfxTypes.h"
#include "gfxColor.h"
#include "gfxFont.h"
#include "gfxMatrix.h"
#include "gfxFontUtils.h"
#include "gfxUserFontSet.h"
#include "nsDataHashtable.h"
#include <usp10.h>
#include <cairo-win32.h>
class GDIFontEntry;
/**********************************************************************
*
* class gfxWindowsFont
*
**********************************************************************/
class gfxWindowsFont : public gfxFont {
public:
gfxWindowsFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
cairo_antialias_t anAntialiasOption = CAIRO_ANTIALIAS_DEFAULT);
virtual ~gfxWindowsFont();
virtual const gfxFont::Metrics& GetMetrics();
HFONT GetHFONT() { return mFont; }
cairo_font_face_t *CairoFontFace();
cairo_scaled_font_t *CairoScaledFont();
SCRIPT_CACHE *ScriptCache() { return &mScriptCache; }
gfxFloat GetAdjustedSize() { MakeHFONT(); return mAdjustedSize; }
virtual nsString GetUniqueName();
virtual void Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
Spacing *aSpacing);
virtual RunMetrics Measure(gfxTextRun *aTextRun,
PRUint32 aStart, PRUint32 aEnd,
BoundingBoxType aBoundingBoxType,
gfxContext *aContextForTightBoundingBox,
Spacing *aSpacing);
virtual PRUint32 GetSpaceGlyph() {
GetMetrics(); // ensure that the metrics are computed but don't recompute them
return mSpaceGlyph;
};
PRBool IsValid() { GetMetrics(); return mIsValid; }
GDIFontEntry *GetFontEntry();
virtual void InitTextRun(gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
static already_AddRefed<gfxWindowsFont>
GetOrMakeFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle,
PRBool aNeedsBold = PR_FALSE);
protected:
HFONT MakeHFONT();
void FillLogFont(gfxFloat aSize);
HFONT mFont;
gfxFloat mAdjustedSize;
PRUint32 mSpaceGlyph;
private:
void ComputeMetrics();
SCRIPT_CACHE mScriptCache;
cairo_font_face_t *mFontFace;
cairo_scaled_font_t *mScaledFont;
gfxFont::Metrics *mMetrics;
LOGFONTW mLogFont;
cairo_antialias_t mAntialiasOption;
virtual PRBool SetupCairoFont(gfxContext *aContext);
};
/**********************************************************************
*
* class gfxWindowsFontGroup
*
**********************************************************************/
class THEBES_API gfxWindowsFontGroup : public gfxFontGroup {
public:
gfxWindowsFontGroup(const nsAString& aFamilies, const gfxFontStyle* aStyle, gfxUserFontSet *aUserFontSet);
virtual ~gfxWindowsFontGroup();
virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle);
virtual gfxTextRun *MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
const Parameters* aParams, PRUint32 aFlags);
virtual gfxTextRun *MakeTextRun(const PRUint8* aString, PRUint32 aLength,
const Parameters* aParams, PRUint32 aFlags);
const nsACString& GetGenericFamily() const {
return mGenericFamily;
}
void GroupFamilyListToArrayList(nsTArray<nsRefPtr<gfxFontEntry> > *list,
nsTArray<PRPackedBool> *aNeedsBold);
void FamilyListToArrayList(const nsString& aFamilies,
nsIAtom *aLangGroup,
nsTArray<nsRefPtr<gfxFontEntry> > *list);
virtual void UpdateFontList();
virtual gfxFloat GetUnderlineOffset();
gfxWindowsFont* GetFontAt(PRInt32 aFontIndex) {
// If it turns out to be hard for all clients that cache font
// groups to call UpdateFontList at appropriate times, we could
// instead consider just calling UpdateFontList from someplace
// more central (such as here).
NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
"Whoever was caching this font group should have "
"called UpdateFontList on it");
return static_cast<gfxWindowsFont*>(static_cast<gfxFont*>(mFonts[aFontIndex]));
}
protected:
void InitFontList();
void InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun, const char *aString, PRUint32 aLength);
void InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength);
void InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString, PRUint32 aLength);
already_AddRefed<gfxFont> WhichPrefFontSupportsChar(PRUint32 aCh);
already_AddRefed<gfxFont> WhichSystemFontSupportsChar(PRUint32 aCh);
already_AddRefed<gfxWindowsFont> WhichFontSupportsChar(const nsTArray<nsRefPtr<gfxFontEntry> >& fonts, PRUint32 ch);
void GetPrefFonts(nsIAtom *aLangGroup, nsTArray<nsRefPtr<gfxFontEntry> >& array);
void GetCJKPrefFonts(nsTArray<nsRefPtr<gfxFontEntry> >& array);
static PRBool FindWindowsFont(const nsAString& aName,
const nsACString& aGenericName,
void *closure);
PRBool HasFont(gfxFontEntry *aFontEntry);
private:
nsCString mGenericFamily;
nsTArray<PRPackedBool> mFontNeedsBold;
const char *mItemLangGroup; // used by pref-lang handling code
};
#endif /* GFX_WINDOWSFONTS_H */

Просмотреть файл

@ -55,12 +55,12 @@
#ifdef MOZ_FT2_FONTS
#include "gfxFT2Fonts.h"
#else
#include "gfxWindowsFonts.h"
#ifdef CAIRO_HAS_DWRITE_FONT
#include "gfxDWriteFonts.h"
#endif
#endif
#include "gfxPlatform.h"
#include "gfxContext.h"
#include "nsTArray.h"
#include "nsDataHashtable.h"
@ -71,6 +71,40 @@ typedef struct FT_LibraryRec_ *FT_Library;
#include <windows.h>
// Utility to get a Windows HDC from a thebes context,
// used by both GDI and Uniscribe font shapers
struct DCFromContext {
DCFromContext(gfxContext *aContext) {
dc = NULL;
nsRefPtr<gfxASurface> aSurface = aContext->CurrentSurface();
NS_ASSERTION(aSurface, "DCFromContext: null surface");
if (aSurface &&
(aSurface->GetType() == gfxASurface::SurfaceTypeWin32 ||
aSurface->GetType() == gfxASurface::SurfaceTypeWin32Printing))
{
dc = static_cast<gfxWindowsSurface*>(aSurface.get())->GetDC();
needsRelease = PR_FALSE;
}
if (!dc) {
dc = GetDC(NULL);
SetGraphicsMode(dc, GM_ADVANCED);
needsRelease = PR_TRUE;
}
}
~DCFromContext() {
if (needsRelease)
ReleaseDC(NULL, dc);
}
operator HDC () {
return dc;
}
HDC dc;
PRBool needsRelease;
};
class THEBES_API gfxWindowsPlatform : public gfxPlatform {
public:
gfxWindowsPlatform();
@ -147,10 +181,6 @@ public:
*/
virtual PRBool IsFontFormatSupported(nsIURI *aFontURI, PRUint32 aFormatFlags);
#ifndef MOZ_FT2_FONTS
virtual void SetupClusterBoundaries(gfxTextRun *aTextRun, const PRUnichar *aString);
#endif
/* Find a FontFamily/FontEntry object that represents a font on your system given a name */
gfxFontFamily *FindFontFamily(const nsAString& aName);
gfxFontEntry *FindFontEntry(const nsAString& aName, const gfxFontStyle& aFontStyle);

Просмотреть файл

@ -77,8 +77,10 @@ CPPSRCS += gfxDWriteFonts.cpp \
gfxDWriteFontList.cpp \
$(NULL)
endif
CPPSRCS += gfxWindowsFonts.cpp \
CPPSRCS += gfxGDIFont.cpp \
gfxGDIFontList.cpp \
gfxGDIShaper.cpp \
gfxUniscribeShaper.cpp \
$(NULL)
_OS_LIBS = usp10 msimg32
endif

Просмотреть файл

@ -102,7 +102,7 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
}
}
void
PRBool
gfxCoreTextShaper::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
@ -203,13 +203,19 @@ gfxCoreTextShaper::InitTextRun(gfxContext *aContext,
// Iterate through the glyph runs.
// Note that this includes the bidi wrapper, so we have to be careful
// not to include the extra glyphs from there
PRBool success = PR_TRUE;
for (PRUint32 runIndex = 0; runIndex < numRuns; runIndex++) {
CTRunRef aCTRun = (CTRunRef)::CFArrayGetValueAtIndex(glyphRuns, runIndex);
if (SetGlyphsFromRun(aTextRun, aCTRun, startOffset, aRunStart, aRunLength) != NS_OK)
if (SetGlyphsFromRun(aTextRun, aCTRun, startOffset,
aRunStart, aRunLength) != NS_OK) {
success = PR_FALSE;
break;
}
}
::CFRelease(line);
return success;
}
#define SMALL_GLYPH_RUN 128 // preallocated size of our auto arrays for per-glyph data;

Просмотреть файл

@ -57,11 +57,11 @@ public:
virtual ~gfxCoreTextShaper();
virtual void InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
virtual PRBool InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
// clean up static objects that may have been cached
static void Shutdown();

Просмотреть файл

@ -807,9 +807,11 @@ gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, PRBool aOtherIsOnLeft
mAdvanceWidth += aOther.mAdvanceWidth;
}
gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle) :
gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
AntialiasOption anAAOption) :
mFontEntry(aFontEntry), mIsValid(PR_TRUE),
mStyle(*aFontStyle), mSyntheticBoldOffset(0),
mAntialiasOption(anAAOption),
mShaper(nsnull)
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
@ -1062,9 +1064,26 @@ NeedsGlyphExtents(gfxTextRun *aTextRun)
gfxFont::RunMetrics
gfxFont::Measure(gfxTextRun *aTextRun,
PRUint32 aStart, PRUint32 aEnd,
BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
BoundingBoxType aBoundingBoxType,
gfxContext *aRefContext,
Spacing *aSpacing)
{
// If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
// and the underlying cairo font may be antialiased,
// we need to create a copy in order to avoid getting cached extents.
// This is inefficient, but only used by MathML layout at present.
if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
mAntialiasOption != kAntialiasNone) {
nsAutoPtr<gfxFont> tempFont(CopyWithAntialiasOption(kAntialiasNone));
// if font subclass doesn't implement CopyWithAntialiasOption(),
// it will return null and we'll proceed to use the existing font
if (tempFont) {
return tempFont->Measure(aTextRun, aStart, aEnd,
TIGHT_HINTED_OUTLINE_EXTENTS,
aRefContext, aSpacing);
}
}
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
// Current position in appunits
const gfxFont::Metrics& fontMetrics = GetMetrics();
@ -1171,6 +1190,23 @@ gfxFont::Measure(gfxTextRun *aTextRun,
return metrics;
}
void
gfxFont::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength)
{
NS_ASSERTION(mShaper != nsnull, "no shaper?!");
if (!mShaper) {
return;
}
PRBool ok = mShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength);
NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
}
gfxGlyphExtents *
gfxFont::GetOrCreateGlyphExtents(PRUint32 aAppUnitsPerDevUnit) {
PRUint32 i;

Просмотреть файл

@ -0,0 +1,330 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2005-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* Mats Palmgren <mats.palmgren@bredband.net>
* John Daggett <jdaggett@mozilla.com>
* Jonathan Kew <jfkthame@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxGDIFont.h"
#include "gfxGDIShaper.h"
#include "gfxUniscribeShaper.h"
#include "gfxWindowsPlatform.h"
#include "gfxContext.h"
#include "cairo-win32.h"
#define ROUND(x) floor((x) + 0.5)
static inline cairo_antialias_t
GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
{
switch (anAntialiasOption) {
default:
case gfxFont::kAntialiasDefault:
return CAIRO_ANTIALIAS_DEFAULT;
case gfxFont::kAntialiasNone:
return CAIRO_ANTIALIAS_NONE;
case gfxFont::kAntialiasGrayscale:
return CAIRO_ANTIALIAS_GRAY;
case gfxFont::kAntialiasSubpixel:
return CAIRO_ANTIALIAS_SUBPIXEL;
}
}
gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
PRBool aNeedsBold,
AntialiasOption anAAOption)
: gfxFont(aFontEntry, aFontStyle, anAAOption),
mNeedsBold(aNeedsBold),
mFont(NULL),
mFontFace(nsnull),
mScaledFont(nsnull),
mAdjustedSize(0.0)
{
// InitMetrics will handle the sizeAdjust factor and set mAdjustedSize,
// fill in our mLogFont structure and create mFont
InitMetrics();
if (!mIsValid) {
return;
}
mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&mLogFont,
mFont);
cairo_matrix_t sizeMatrix, ctm;
cairo_matrix_init_identity(&ctm);
cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
cairo_font_options_t *fontOptions = cairo_font_options_create();
if (anAAOption != kAntialiasDefault) {
cairo_font_options_set_antialias(fontOptions,
GetCairoAntialiasOption(anAAOption));
}
mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix,
&ctm, fontOptions);
cairo_font_options_destroy(fontOptions);
cairo_status_t cairoerr = cairo_scaled_font_status(mScaledFont);
if (cairoerr != CAIRO_STATUS_SUCCESS) {
mIsValid = PR_FALSE;
#ifdef DEBUG
char warnBuf[1024];
sprintf(warnBuf, "Failed to create scaled font: %s status: %d",
NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(), cairoerr);
NS_WARNING(warnBuf);
#endif
}
if (aFontEntry->mForceGDI) {
mShaper = new gfxGDIShaper(this);
} else {
mShaper = new gfxUniscribeShaper(this);
}
}
gfxGDIFont::~gfxGDIFont()
{
if (mScaledFont) {
cairo_scaled_font_destroy(mScaledFont);
}
if (mFontFace) {
cairo_font_face_destroy(mFontFace);
}
if (mFont) {
::DeleteObject(mFont);
}
}
gfxFont*
gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
{
return new gfxGDIFont(static_cast<GDIFontEntry*>(mFontEntry.get()),
&mStyle, mNeedsBold, anAAOption);
}
void
gfxGDIFont::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength)
{
PRBool ok = mShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength);
if (!ok) {
// shaping failed; if we were using uniscribe, fall back to GDI
GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
if (!fe->mForceGDI) {
NS_WARNING("uniscribe failed, switching to GDI shaper");
fe->mForceGDI = PR_TRUE;
mShaper = new gfxGDIShaper(this);
ok = mShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength);
}
}
NS_WARN_IF_FALSE(ok, "shaper failed, expect broken or missing text");
}
const gfxFont::Metrics&
gfxGDIFont::GetMetrics()
{
return mMetrics;
}
PRUint32
gfxGDIFont::GetSpaceGlyph()
{
return mSpaceGlyph;
}
PRBool
gfxGDIFont::SetupCairoFont(gfxContext *aContext)
{
if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
// Don't cairo_set_scaled_font as that would propagate the error to
// the cairo_t, precluding any further drawing.
return PR_FALSE;
}
cairo_set_scaled_font(aContext->GetCairo(), mScaledFont);
return PR_TRUE;
}
void
gfxGDIFont::InitMetrics()
{
if (mAdjustedSize == 0.0) {
mAdjustedSize = mStyle.size;
if (mStyle.sizeAdjust != 0.0 && mAdjustedSize > 0.0) {
// to implement font-size-adjust, we first create the "unadjusted" font
FillLogFont(mAdjustedSize);
mFont = ::CreateFontIndirectW(&mLogFont);
// initialize its metrics, then delete the font
InitMetrics();
::DeleteObject(mFont);
mFont = nsnull;
// calculate the properly adjusted size, and then proceed
gfxFloat aspect = mMetrics.xHeight / mMetrics.emHeight;
mAdjustedSize = mStyle.GetAdjustedSize(aspect);
}
}
if (!mFont) {
FillLogFont(mAdjustedSize);
mFont = ::CreateFontIndirectW(&mLogFont);
}
AutoDC dc;
SetGraphicsMode(dc.GetDC(), GM_ADVANCED);
AutoSelectFont selectFont(dc.GetDC(), mFont);
// Get font metrics
OUTLINETEXTMETRIC oMetrics;
TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) {
mMetrics.superscriptOffset = (double)oMetrics.otmptSuperscriptOffset.y;
// Some fonts have wrong sign on their subscript offset, bug 410917.
mMetrics.subscriptOffset = fabs((double)oMetrics.otmptSubscriptOffset.y);
mMetrics.strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
mMetrics.strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
mMetrics.underlineSize = (double)oMetrics.otmsUnderscoreSize;
mMetrics.underlineOffset = (double)oMetrics.otmsUnderscorePosition;
const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
GLYPHMETRICS gm;
DWORD len = GetGlyphOutlineW(dc.GetDC(), PRUnichar('x'), GGO_METRICS, &gm, 0, nsnull, &kIdentityMatrix);
if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
// 56% of ascent, best guess for true type
mMetrics.xHeight = ROUND((double)metrics.tmAscent * 0.56);
} else {
mMetrics.xHeight = gm.gmptGlyphOrigin.y;
}
mMetrics.emHeight = metrics.tmHeight - metrics.tmInternalLeading;
gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
mMetrics.emAscent = ROUND(mMetrics.emHeight * (double)oMetrics.otmAscent / typEmHeight);
mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
} else {
// Make a best-effort guess at extended metrics
// this is based on general typographic guidelines
// GetTextMetrics can fail if the font file has been removed
// or corrupted recently.
BOOL result = GetTextMetrics(dc.GetDC(), &metrics);
if (!result) {
NS_WARNING("Missing or corrupt font data, fasten your seatbelt");
mIsValid = PR_FALSE;
memset(&mMetrics, 0, sizeof(mMetrics));
return;
}
mMetrics.xHeight = ROUND((float)metrics.tmAscent * 0.56f); // 56% of ascent, best guess for non-true type
mMetrics.superscriptOffset = mMetrics.xHeight;
mMetrics.subscriptOffset = mMetrics.xHeight;
mMetrics.strikeoutSize = 1;
mMetrics.strikeoutOffset = ROUND(mMetrics.xHeight / 2.0f); // 50% of xHeight
mMetrics.underlineSize = 1;
mMetrics.underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
mMetrics.emHeight = metrics.tmHeight - metrics.tmInternalLeading;
mMetrics.emAscent = metrics.tmAscent - metrics.tmInternalLeading;
mMetrics.emDescent = metrics.tmDescent;
}
mMetrics.internalLeading = metrics.tmInternalLeading;
mMetrics.externalLeading = metrics.tmExternalLeading;
mMetrics.maxHeight = metrics.tmHeight;
mMetrics.maxAscent = metrics.tmAscent;
mMetrics.maxDescent = metrics.tmDescent;
mMetrics.maxAdvance = metrics.tmMaxCharWidth;
mMetrics.aveCharWidth = PR_MAX(1, metrics.tmAveCharWidth);
// The font is monospace when TMPF_FIXED_PITCH is *not* set!
// See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx
if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
mMetrics.maxAdvance = mMetrics.aveCharWidth;
}
// Cache the width of a single space.
SIZE size;
GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size);
mMetrics.spaceWidth = ROUND(size.cx);
// Cache the width of digit zero.
// XXX MSDN (http://msdn.microsoft.com/en-us/library/ms534223.aspx)
// does not say what the failure modes for GetTextExtentPoint32 are -
// is it safe to assume it will fail iff the font has no '0'?
if (GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size))
mMetrics.zeroOrAveCharWidth = ROUND(size.cx);
else
mMetrics.zeroOrAveCharWidth = mMetrics.aveCharWidth;
mSpaceGlyph = 0;
if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
WORD glyph;
DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret != GDI_ERROR && glyph != 0xFFFF) {
mSpaceGlyph = glyph;
}
}
SanitizeMetrics(&mMetrics, GetFontEntry()->mIsBadUnderlineFont);
mIsValid = PR_TRUE;
}
void
gfxGDIFont::FillLogFont(gfxFloat aSize)
{
GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
PRUint16 weight = mNeedsBold ? 700 : fe->Weight();
PRBool italic = (mStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE));
// if user font, disable italics/bold if defined to be italics/bold face
// this avoids unwanted synthetic italics/bold
if (fe->mIsUserFont) {
if (fe->IsItalic())
italic = PR_FALSE; // avoid synthetic italic
if (fe->IsBold()) {
weight = 400; // avoid synthetic bold
}
}
fe->FillLogFont(&mLogFont, italic, weight, aSize);
}

103
gfx/thebes/src/gfxGDIFont.h Normal file
Просмотреть файл

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2005-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* John Daggett <jdaggett@mozilla.com>
* Jonathan Kew <jfkthame@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_GDIFONT_H
#define GFX_GDIFONT_H
#include "gfxFont.h"
#include "gfxGDIFontList.h"
#include "cairo.h"
class gfxGDIFont : public gfxFont
{
public:
gfxGDIFont(GDIFontEntry *aFontEntry,
const gfxFontStyle *aFontStyle,
PRBool aNeedsBold,
AntialiasOption anAAOption = kAntialiasDefault);
virtual ~gfxGDIFont();
HFONT GetHFONT() const { return mFont; }
float GetAdjustedSize() const { return mAdjustedSize; }
cairo_font_face_t *CairoFontFace() { return mFontFace; }
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; }
/* overrides for the pure virtual methods in gfxFont */
virtual const gfxFont::Metrics& GetMetrics();
virtual PRUint32 GetSpaceGlyph();
virtual PRBool SetupCairoFont(gfxContext *aContext);
/* required for MathML to suppress effects of ClearType "padding" */
virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption);
/* override to check for uniscribe failure and fall back to GDI */
virtual void InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
protected:
void InitMetrics();
void FillLogFont(gfxFloat aSize);
float GetCharWidth(PRUnichar aUniChar, PRUint32 *aGlyphID);
float GetCharHeight(PRUnichar aUniChar);
PRBool mNeedsBold;
LOGFONTW mLogFont;
HFONT mFont;
cairo_font_face_t *mFontFace;
cairo_scaled_font_t *mScaledFont;
Metrics mMetrics;
PRUint32 mSpaceGlyph;
float mAdjustedSize;
};
#endif /* GFX_GDIFONT_H */

Просмотреть файл

@ -43,7 +43,7 @@
#include "gfxWindowsPlatform.h"
#include "gfxUserFontSet.h"
#include "gfxFontUtils.h"
#include "gfxWindowsFonts.h"
#include "gfxGDIFont.h"
#include "nsServiceManagerUtils.h"
#include "nsTArray.h"
@ -55,6 +55,8 @@
#include "nsISimpleEnumerator.h"
#include "nsIWindowsRegKey.h"
#include <usp10.h>
#define ROUND(x) floor((x) + 0.5)
#ifdef PR_LOGGING
@ -218,9 +220,9 @@ GDIFontEntry::ReadCMAP()
}
gfxFont *
GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, PRBool /*aNeedsBold*/)
GDIFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle, PRBool aNeedsBold)
{
return new gfxWindowsFont(this, aFontStyle);
return new gfxGDIFont(this, aFontStyle, aNeedsBold);
}
nsresult
@ -283,10 +285,10 @@ GDIFontEntry::TestCharacterMap(PRUint32 aCh)
fakeStyle.style = FONT_STYLE_ITALIC;
fakeStyle.weight = mWeight * 100;
nsRefPtr<gfxWindowsFont> font =
gfxWindowsFont::GetOrMakeFont(this, &fakeStyle);
if (!font->IsValid())
nsRefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, PR_FALSE);
if (!tempFont || !tempFont->Valid())
return PR_FALSE;
gfxGDIFont *font = static_cast<gfxGDIFont*>(tempFont.get());
HDC dc = GetDC((HWND)nsnull);
SetGraphicsMode(dc, GM_ADVANCED);
@ -297,14 +299,15 @@ GDIFontEntry::TestCharacterMap(PRUint32 aCh)
WORD glyph[1];
PRBool hasGlyph = PR_FALSE;
if (IsType1()) {
if (IsType1() || mForceGDI) {
// Type1 fonts and uniscribe APIs don't get along. ScriptGetCMap will return E_HANDLE
DWORD ret = GetGlyphIndicesW(dc, str, 1, glyph, GGI_MARK_NONEXISTING_GLYPHS);
if (ret != GDI_ERROR && glyph[0] != 0xFFFF)
hasGlyph = PR_TRUE;
} else {
// ScriptGetCMap works better than GetGlyphIndicesW for things like bitmap/vector fonts
HRESULT rv = ScriptGetCMap(dc, font->ScriptCache(), str, 1, 0, glyph);
SCRIPT_CACHE sc = NULL;
HRESULT rv = ScriptGetCMap(dc, &sc, str, 1, 0, glyph);
if (rv == S_OK)
hasGlyph = PR_TRUE;
}

Просмотреть файл

@ -69,9 +69,12 @@ private:
class AutoSelectFont // select a font into the given DC, and auto-restore
{
public:
AutoSelectFont(HDC aDC, LOGFONTW *aLogFont) {
AutoSelectFont(HDC aDC, LOGFONTW *aLogFont)
: mOwnsFont(PR_FALSE)
{
mFont = ::CreateFontIndirectW(aLogFont);
if (mFont) {
mOwnsFont = PR_TRUE;
mDC = aDC;
mOldFont = (HFONT)::SelectObject(aDC, mFont);
} else {
@ -79,7 +82,9 @@ public:
}
}
AutoSelectFont(HDC aDC, HFONT aFont) {
AutoSelectFont(HDC aDC, HFONT aFont)
: mOwnsFont(PR_FALSE)
{
mDC = aDC;
mFont = aFont;
mOldFont = (HFONT)::SelectObject(aDC, aFont);
@ -88,6 +93,9 @@ public:
~AutoSelectFont() {
if (mOldFont) {
::SelectObject(mDC, mOldFont);
if (mOwnsFont) {
::DeleteObject(mFont);
}
}
}
@ -100,9 +108,10 @@ public:
}
private:
HDC mDC;
HFONT mFont;
HFONT mOldFont;
HDC mDC;
HFONT mFont;
HFONT mOldFont;
PRBool mOwnsFont;
};
/**

Просмотреть файл

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2005-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* Mats Palmgren <mats.palmgren@bredband.net>
* John Daggett <jdaggett@mozilla.com>
* Jonathan Kew <jfkthame@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//#define FORCE_PR_LOG
#include "gfxGDIShaper.h"
/**********************************************************************
*
* class gfxGDIShaper
*
**********************************************************************/
PRBool
gfxGDIShaper::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength)
{
gfxGDIFont *f = static_cast<gfxGDIFont*>(mFont);
DCFromContext dc(aContext);
AutoSelectFont fs(dc, f->GetHFONT());
nsAutoTArray<WORD,500> glyphArray;
if (!glyphArray.SetLength(aRunLength)) {
return PR_FALSE;
}
WORD *glyphs = glyphArray.Elements();
DWORD ret = ::GetGlyphIndicesW(dc, aString + aRunStart, aRunLength,
glyphs, GGI_MARK_NONEXISTING_GLYPHS);
if (ret == GDI_ERROR) {
return PR_FALSE;
}
SIZE size;
nsAutoTArray<int,500> partialWidthArray;
if (!partialWidthArray.SetLength(aRunLength)) {
return PR_FALSE;
}
BOOL success = ::GetTextExtentExPointI(dc,
glyphs,
aRunLength,
INT_MAX,
NULL,
partialWidthArray.Elements(),
&size);
if (!success) {
return PR_FALSE;
}
gfxTextRun::CompressedGlyph g;
PRUint32 i;
PRInt32 lastWidth = 0;
PRUint32 appUnitsPerDevPixel = aTextRun->GetAppUnitsPerDevUnit();
for (i = 0; i < aRunLength; ++i) {
PRInt32 advancePixels = partialWidthArray[i] - lastWidth;
lastWidth = partialWidthArray[i];
PRInt32 advanceAppUnits = advancePixels*appUnitsPerDevPixel;
WCHAR glyph = glyphs[i];
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(
aTextRun->GetChar(aRunStart + i)),
"Invalid character detected!");
if (advanceAppUnits >= 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aTextRun->SetSimpleGlyph(aRunStart + i,
g.SetSimpleGlyph(advanceAppUnits, glyph));
} else {
gfxTextRun::DetailedGlyph details;
details.mGlyphID = glyph;
details.mAdvance = advanceAppUnits;
details.mXOffset = 0;
details.mYOffset = 0;
aTextRun->SetGlyphs(aRunStart + i,
g.SetComplex(PR_TRUE, PR_TRUE, 1),
&details);
}
}
return PR_TRUE;
}

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonathan Kew <jfkthame@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_GDISHAPER_H
#define GFX_GDISHAPER_H
#include "gfxGDIFont.h"
class gfxGDIShaper : public gfxFontShaper
{
public:
gfxGDIShaper(gfxGDIFont *aFont)
: gfxFontShaper(aFont) { }
virtual ~gfxGDIShaper() { }
virtual PRBool InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
};
#endif /* GFX_GDISHAPER_H */

Просмотреть файл

@ -0,0 +1,620 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2005-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* Mats Palmgren <mats.palmgren@bredband.net>
* John Daggett <jdaggett@mozilla.com>
* Jonathan Kew <jfkthame@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//#define FORCE_PR_LOG
#include "prtypes.h"
#include "gfxTypes.h"
#include "gfxContext.h"
#include "gfxUniscribeShaper.h"
#include "gfxWindowsPlatform.h"
#include "gfxAtoms.h"
#include "gfxFontTest.h"
#include "cairo.h"
#include "cairo-win32.h"
#include <windows.h>
#include "nsTArray.h"
#include "prlog.h"
#include "prinit.h"
static PRLogModuleInfo *gFontLog = PR_NewLogModule("winfonts");
/**********************************************************************
*
* class gfxUniscribeShaper
*
**********************************************************************/
#define ESTIMATE_MAX_GLYPHS(L) (((3 * (L)) >> 1) + 16)
class UniscribeItem
{
public:
UniscribeItem(gfxContext *aContext, HDC aDC,
gfxUniscribeShaper *aShaper,
const PRUnichar *aString, PRUint32 aLength,
SCRIPT_ITEM *aItem) :
mContext(aContext), mDC(aDC),
mShaper(aShaper),
mItemString(aString), mItemLength(aLength),
mAlternativeString(nsnull), mScriptItem(aItem),
mScript(aItem->a.eScript),
mNumGlyphs(0), mMaxGlyphs(ESTIMATE_MAX_GLYPHS(aLength)),
mFontSelected(PR_FALSE)
{
NS_ASSERTION(mMaxGlyphs < 65535, "UniscribeItem is too big, ScriptShape() will fail!");
}
~UniscribeItem() {
free(mAlternativeString);
}
PRBool AllocateBuffers() {
return (mGlyphs.SetLength(mMaxGlyphs) &&
mClusters.SetLength(mItemLength + 1) &&
mAttr.SetLength(mMaxGlyphs));
}
/* possible return values:
* S_OK - things succeeded
* GDI_ERROR - things failed to shape. Might want to try again after calling DisableShaping()
*/
HRESULT Shape() {
HRESULT rv;
HDC shapeDC = nsnull;
const PRUnichar *str = mAlternativeString ? mAlternativeString : mItemString;
mScriptItem->a.fLogicalOrder = PR_TRUE;
SCRIPT_ANALYSIS sa = mScriptItem->a;
while (PR_TRUE) {
rv = ScriptShape(shapeDC, mShaper->ScriptCache(),
str, mItemLength,
mMaxGlyphs, &sa,
mGlyphs.Elements(), mClusters.Elements(),
mAttr.Elements(), &mNumGlyphs);
if (rv == E_OUTOFMEMORY) {
mMaxGlyphs *= 2;
if (!mGlyphs.SetLength(mMaxGlyphs) ||
!mAttr.SetLength(mMaxGlyphs)) {
return E_OUTOFMEMORY;
}
continue;
}
// Uniscribe can't do shaping with some fonts, so it sets the
// fNoGlyphIndex flag in the SCRIPT_ANALYSIS structure to indicate
// this. This occurs with CFF fonts loaded with
// AddFontMemResourceEx but it's not clear what the other cases
// are. We return an error so our caller can try fallback shaping.
// see http://msdn.microsoft.com/en-us/library/ms776520(VS.85).aspx
if (sa.fNoGlyphIndex) {
return GDI_ERROR;
}
if (rv == E_PENDING) {
if (shapeDC == mDC) {
// we already tried this once, something failed, give up
return E_PENDING;
}
SelectFont();
shapeDC = mDC;
continue;
}
// http://msdn.microsoft.com/en-us/library/dd368564(VS.85).aspx:
// Uniscribe will return this if "the font corresponding to the
// DC does not support the script required by the run...".
// In this case, we'll set the script code to SCRIPT_UNDEFINED
// and try again, so that we'll at least get glyphs even though
// they won't necessarily have proper shaping.
// (We probably shouldn't have selected this font at all,
// but it's too late to fix that here.)
if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
sa.eScript = SCRIPT_UNDEFINED;
NS_WARNING("Uniscribe says font does not support script needed");
continue;
}
return rv;
}
}
PRBool ShapingEnabled() {
return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
}
void DisableShaping() {
mScriptItem->a.eScript = SCRIPT_UNDEFINED;
// Note: If we disable the shaping by using SCRIPT_UNDEFINED and
// the string has the surrogate pair, ScriptShape API is
// *sometimes* crashed. Therefore, we should replace the surrogate
// pair to U+FFFD. See bug 341500.
GenerateAlternativeString();
}
void EnableShaping() {
mScriptItem->a.eScript = mScript;
if (mAlternativeString) {
free(mAlternativeString);
mAlternativeString = nsnull;
}
}
PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex) {
return (mGlyphs[aGlyphIndex] == aSFP->wgDefault);
}
HRESULT Place() {
HRESULT rv;
HDC placeDC = nsnull;
if (!mOffsets.SetLength(mNumGlyphs) ||
!mAdvances.SetLength(mNumGlyphs)) {
return E_OUTOFMEMORY;
}
SCRIPT_ANALYSIS sa = mScriptItem->a;
while (PR_TRUE) {
rv = ScriptPlace(placeDC, mShaper->ScriptCache(),
mGlyphs.Elements(), mNumGlyphs,
mAttr.Elements(), &sa,
mAdvances.Elements(), mOffsets.Elements(), NULL);
if (rv == E_PENDING) {
SelectFont();
placeDC = mDC;
continue;
}
if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
sa.eScript = SCRIPT_UNDEFINED;
continue;
}
break;
}
return rv;
}
void ScriptFontProperties(SCRIPT_FONTPROPERTIES *sfp) {
HRESULT rv;
memset(sfp, 0, sizeof(SCRIPT_FONTPROPERTIES));
sfp->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
rv = ScriptGetFontProperties(NULL, mShaper->ScriptCache(),
sfp);
if (rv == E_PENDING) {
SelectFont();
rv = ScriptGetFontProperties(mDC, mShaper->ScriptCache(),
sfp);
}
}
void SaveGlyphs(gfxTextRun *aRun, PRUint32 aRunStart) {
PRUint32 offsetInRun = aRunStart + mScriptItem->iCharPos;
// XXX We should store this in the item and only fetch it once
SCRIPT_FONTPROPERTIES sfp;
ScriptFontProperties(&sfp);
PRUint32 offset = 0;
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
gfxTextRun::CompressedGlyph g;
const PRUint32 appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
while (offset < mItemLength) {
PRUint32 runOffset = offsetInRun + offset;
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
g.SetComplex(aRun->IsClusterStart(runOffset), PR_FALSE, 0);
aRun->SetGlyphs(runOffset, g, nsnull);
} else {
// Count glyphs for this character
PRUint32 k = mClusters[offset];
PRUint32 glyphCount = mNumGlyphs - k;
PRUint32 nextClusterOffset;
PRBool missing = IsGlyphMissing(&sfp, k);
for (nextClusterOffset = offset + 1; nextClusterOffset < mItemLength; ++nextClusterOffset) {
if (mClusters[nextClusterOffset] > k) {
glyphCount = mClusters[nextClusterOffset] - k;
break;
}
}
PRUint32 j;
for (j = 1; j < glyphCount; ++j) {
if (IsGlyphMissing(&sfp, k + j)) {
missing = PR_TRUE;
}
}
PRInt32 advance = mAdvances[k]*appUnitsPerDevUnit;
WORD glyph = mGlyphs[k];
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(mItemString[offset]),
"invalid character detected");
if (missing) {
if (NS_IS_HIGH_SURROGATE(mItemString[offset]) &&
offset + 1 < mItemLength &&
NS_IS_LOW_SURROGATE(mItemString[offset + 1])) {
aRun->SetMissingGlyph(runOffset,
SURROGATE_TO_UCS4(mItemString[offset],
mItemString[offset + 1]));
} else {
aRun->SetMissingGlyph(runOffset, mItemString[offset]);
}
} else if (glyphCount == 1 && advance >= 0 &&
mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
aRun->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
return;
}
PRUint32 i;
for (i = 0; i < glyphCount; ++i) {
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
details->mGlyphID = mGlyphs[k + i];
details->mAdvance = mAdvances[k + i]*appUnitsPerDevUnit;
details->mXOffset = float(mOffsets[k + i].du)*appUnitsPerDevUnit*aRun->GetDirection();
details->mYOffset = - float(mOffsets[k + i].dv)*appUnitsPerDevUnit;
}
aRun->SetGlyphs(runOffset,
g.SetComplex(PR_TRUE, PR_TRUE, glyphCount), detailedGlyphs.Elements());
}
}
++offset;
}
}
void SelectFont() {
if (mFontSelected)
return;
cairo_t *cr = mContext->GetCairo();
cairo_set_font_face(cr, mShaper->GetFont()->CairoFontFace());
cairo_set_font_size(cr, mShaper->GetFont()->GetAdjustedSize());
cairo_scaled_font_t *scaledFont = mShaper->GetFont()->CairoScaledFont();
cairo_win32_scaled_font_select_font(scaledFont, mDC);
mFontSelected = PR_TRUE;
}
private:
void GenerateAlternativeString() {
if (mAlternativeString)
free(mAlternativeString);
mAlternativeString = (PRUnichar *)malloc(mItemLength * sizeof(PRUnichar));
if (!mAlternativeString)
return;
memcpy((void *)mAlternativeString, (const void *)mItemString,
mItemLength * sizeof(PRUnichar));
for (PRUint32 i = 0; i < mItemLength; i++) {
if (NS_IS_HIGH_SURROGATE(mItemString[i]) || NS_IS_LOW_SURROGATE(mItemString[i]))
mAlternativeString[i] = PRUnichar(0xFFFD);
}
}
private:
nsRefPtr<gfxContext> mContext;
HDC mDC;
gfxUniscribeShaper *mShaper;
SCRIPT_ITEM *mScriptItem;
WORD mScript;
public:
// these point to the full string/length of the item
const PRUnichar *mItemString;
const PRUint32 mItemLength;
private:
PRUnichar *mAlternativeString;
#define AVERAGE_ITEM_LENGTH 40
nsAutoTArray<WORD, PRUint32(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mGlyphs;
nsAutoTArray<WORD, AVERAGE_ITEM_LENGTH + 1> mClusters;
nsAutoTArray<SCRIPT_VISATTR, PRUint32(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mAttr;
nsAutoTArray<GOFFSET, 2 * AVERAGE_ITEM_LENGTH> mOffsets;
nsAutoTArray<int, 2 * AVERAGE_ITEM_LENGTH> mAdvances;
#undef AVERAGE_ITEM_LENGTH
int mMaxGlyphs;
int mNumGlyphs;
PRPackedBool mFontSelected;
};
#define MAX_ITEM_LENGTH 32768
static PRUint32 FindNextItemStart(int aOffset, int aLimit,
nsTArray<SCRIPT_LOGATTR> &aLogAttr,
const PRUnichar *aString)
{
if (aOffset + MAX_ITEM_LENGTH >= aLimit) {
// The item starting at aOffset can't be longer than the max length,
// so starting the next item at aLimit won't cause ScriptShape() to fail.
return aLimit;
}
// Try to start the next item before or after a space, since spaces
// don't kern or ligate.
PRUint32 off;
int boundary = -1;
for (off = MAX_ITEM_LENGTH; off > 1; --off) {
if (aLogAttr[off].fCharStop) {
if (off > boundary) {
boundary = off;
}
if (aString[aOffset+off] == ' ' || aString[aOffset+off - 1] == ' ')
return aOffset+off;
}
}
// Try to start the next item at the last cluster boundary in the range.
if (boundary > 0) {
return aOffset+boundary;
}
// No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break
// on the size limit. It won't be visually plesaing, but at least it
// won't cause ScriptShape() to fail.
return aOffset + MAX_ITEM_LENGTH;
}
class Uniscribe
{
public:
Uniscribe(const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) :
mString(aString), mLength(aLength), mIsRTL(aIsRTL)
{
}
~Uniscribe() {
}
void Init() {
memset(&mControl, 0, sizeof(SCRIPT_CONTROL));
memset(&mState, 0, sizeof(SCRIPT_STATE));
// Lock the direction. Don't allow the itemizer to change directions
// based on character type.
mState.uBidiLevel = mIsRTL;
mState.fOverrideDirection = PR_TRUE;
}
private:
// Append mItems[aIndex] to aDest, adding extra items to aDest to ensure
// that no item is too long for ScriptShape() to handle. See bug 366643.
nsresult CopyItemSplitOversize(int aIndex, nsTArray<SCRIPT_ITEM> &aDest) {
aDest.AppendElement(mItems[aIndex]);
const int itemLength = mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos;
if (ESTIMATE_MAX_GLYPHS(itemLength) > 65535) {
// This items length would cause ScriptShape() to fail. We need to
// add extra items here so that no item's length could cause the fail.
// Get cluster boundaries, so we can break cleanly if possible.
nsTArray<SCRIPT_LOGATTR> logAttr;
if (!logAttr.SetLength(itemLength))
return NS_ERROR_FAILURE;
HRESULT rv = ScriptBreak(mString+mItems[aIndex].iCharPos, itemLength,
&mItems[aIndex].a, logAttr.Elements());
if (FAILED(rv))
return NS_ERROR_FAILURE;
const int nextItemStart = mItems[aIndex+1].iCharPos;
int start = FindNextItemStart(mItems[aIndex].iCharPos,
nextItemStart, logAttr, mString);
while (start < nextItemStart) {
SCRIPT_ITEM item = mItems[aIndex];
item.iCharPos = start;
aDest.AppendElement(item);
start = FindNextItemStart(start, nextItemStart, logAttr, mString);
}
}
return NS_OK;
}
public:
int Itemize() {
HRESULT rv;
int maxItems = 5;
Init();
// Allocate space for one more item than expected, to handle a rare
// overflow in ScriptItemize (pre XP SP2). See bug 366643.
if (!mItems.SetLength(maxItems + 1)) {
return 0;
}
while ((rv = ScriptItemize(mString, mLength, maxItems, &mControl, &mState,
mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
maxItems *= 2;
if (!mItems.SetLength(maxItems + 1)) {
return 0;
}
Init();
}
if (ESTIMATE_MAX_GLYPHS(mLength) > 65535) {
// Any item of length > 43680 will cause ScriptShape() to fail, as its
// mMaxGlyphs value will be greater than 65535 (43680*1.5+16>65535). So we
// need to break up items which are longer than that upon cluster boundaries.
// See bug 394751 for details.
nsTArray<SCRIPT_ITEM> items;
for (int i=0; i<mNumItems; i++) {
nsresult nrs = CopyItemSplitOversize(i, items);
NS_ASSERTION(NS_SUCCEEDED(nrs), "CopyItemSplitOversize() failed");
}
items.AppendElement(mItems[mNumItems]); // copy terminator.
mItems = items;
mNumItems = items.Length() - 1; // Don't count the terminator.
}
return mNumItems;
}
PRUint32 ItemsLength() {
return mNumItems;
}
SCRIPT_ITEM *ScriptItem(PRUint32 i) {
NS_ASSERTION(i <= (PRUint32)mNumItems, "Trying to get out of bounds item");
return &mItems[i];
}
private:
const PRUnichar *mString;
const PRUint32 mLength;
const PRBool mIsRTL;
SCRIPT_CONTROL mControl;
SCRIPT_STATE mState;
nsTArray<SCRIPT_ITEM> mItems;
int mNumItems;
};
gfxUniscribeShaper::gfxUniscribeShaper(gfxGDIFont *aFont)
: gfxFontShaper(aFont)
, mScriptCache(NULL)
{
}
gfxUniscribeShaper::~gfxUniscribeShaper()
{
}
PRBool
gfxUniscribeShaper::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength)
{
DCFromContext aDC(aContext);
const PRBool isRTL = aTextRun->IsRightToLeft();
PRBool result = PR_TRUE;
HRESULT rv;
gfxGDIFont *font = static_cast<gfxGDIFont*>(mFont);
AutoSelectFont fs(aDC, font->GetHFONT());
Uniscribe us(aString + aRunStart, aRunLength, isRTL);
/* itemize the string */
int numItems = us.Itemize();
SaveDC(aDC);
for (int i = 0; i < numItems; ++i) {
UniscribeItem item(aContext, aDC, this,
aString + aRunStart + us.ScriptItem(i)->iCharPos,
us.ScriptItem(i+1)->iCharPos - us.ScriptItem(i)->iCharPos,
us.ScriptItem(i));
if (!item.AllocateBuffers()) {
result = PR_FALSE;
break;
}
if (!item.ShapingEnabled()) {
item.EnableShaping();
}
rv = item.Shape();
if (FAILED(rv)) {
PR_LOG(gFontLog, PR_LOG_DEBUG, ("shaping failed"));
// we know we have the glyphs to display this font already
// so Uniscribe just doesn't know how to shape the script.
// Render the glyphs without shaping.
item.DisableShaping();
rv = item.Shape();
}
#ifdef DEBUG
if (FAILED(rv)) {
NS_WARNING("Uniscribe failed to shape with font");
}
#endif
if (SUCCEEDED(rv)) {
rv = item.Place();
#ifdef DEBUG
if (FAILED(rv)) {
// crap fonts may fail when placing (e.g. funky free fonts)
NS_WARNING("Uniscribe failed to place with font");
}
#endif
}
if (FAILED(rv)) {
aTextRun->ResetGlyphRuns();
// Uniscribe doesn't like this font for some reason.
// Returning FALSE will make the gfxGDIFont discard this
// shaper and replace it with a "dumb" GDI one.
result = PR_FALSE;
break;
}
item.SaveGlyphs(aTextRun, aRunStart);
}
RestoreDC(aDC, -1);
return result;
}

Просмотреть файл

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2005-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Stuart Parmenter <stuart@mozilla.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* John Daggett <jdaggett@mozilla.com>
* Jonathan Kew <jfkthame@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef GFX_UNISCRIBESHAPER_H
#define GFX_UNISCRIBESHAPER_H
#include "prtypes.h"
#include "gfxTypes.h"
#include "gfxGDIFont.h"
#include <usp10.h>
#include <cairo-win32.h>
class gfxUniscribeShaper : public gfxFontShaper
{
public:
gfxUniscribeShaper(gfxGDIFont *aFont);
virtual ~gfxUniscribeShaper();
virtual PRBool InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
SCRIPT_CACHE *ScriptCache() { return &mScriptCache; }
gfxGDIFont *GetFont() { return static_cast<gfxGDIFont*>(mFont); }
private:
SCRIPT_CACHE mScriptCache;
};
#endif /* GFX_UNISCRIBESHAPER_H */

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -62,8 +62,8 @@
#include "cairo-ft.h"
#include "nsAppDirectoryServiceDefs.h"
#else
#include "gfxWindowsFonts.h"
#include "gfxGDIFontList.h"
#include "gfxGDIFont.h"
#ifdef CAIRO_HAS_DWRITE_FONT
#include "gfxDWriteFontList.h"
#include "gfxDWriteFonts.h"
@ -319,7 +319,7 @@ gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies,
return new gfxDWriteFontGroup(aFamilies, aStyle, aUserFontSet);
} else {
#endif
return new gfxWindowsFontGroup(aFamilies, aStyle, aUserFontSet);
return new gfxFontGroup(aFamilies, aStyle, aUserFontSet);
#ifdef CAIRO_HAS_DWRITE_FONT
}
#endif
@ -443,67 +443,6 @@ gfxWindowsPlatform::GetFTLibrary()
}
#endif
#ifndef MOZ_FT2_FONTS
void
gfxWindowsPlatform::SetupClusterBoundaries(gfxTextRun *aTextRun,
const PRUnichar *aString)
{
NS_ABORT_IF_FALSE(sizeof(WCHAR) == sizeof(PRUnichar),
"WCHAR/PRUnichar not compatible");
PRUint32 length = aTextRun->GetLength();
nsAutoTArray<SCRIPT_ITEM,4> items;
PRUint32 maxItems = 4;
int numItems;
HRESULT result;
PRUint32 i, j;
do {
items.SetLength(maxItems);
result = ::ScriptItemize(aString, length,
maxItems - 1,
NULL,
NULL,
items.Elements(),
&numItems);
maxItems <<= 1;
if (maxItems > INT_MAX)
break;
} while (result == E_OUTOFMEMORY);
if (result != 0) {
return;
}
nsTArray<SCRIPT_LOGATTR> attrs;
for (i = 0; i < PRUint32(numItems); ++i) {
PRUint32 offset = items[i].iCharPos;
length = items[i + 1].iCharPos - offset;
if (attrs.Length() < length) {
attrs.SetLength(length);
}
result = ::ScriptBreak(aString + offset, length,
&items[i].a,
attrs.Elements());
if (result != 0) {
break;
}
for (j = 1; j < length; ++j) {
if (!attrs[j].fCharStop) {
gfxTextRun::CompressedGlyph g;
// Remember that this character is not the start of a cluster
// by setting its glyph data to "not a cluster start",
// "is a ligature start", with no glyphs.
aTextRun->SetGlyphs(offset + j,
g.SetComplex(PR_FALSE, PR_TRUE, 0),
nsnull);
}
}
}
}
#endif
void
gfxWindowsPlatform::InitDisplayCaps()
{

Просмотреть файл

@ -11,6 +11,9 @@
}
</style>
<body>
<p>നെ തുടര്‍ന്നാണ് ആര‍.</p>
<p>നെ തുടര്‍ന്നാണ് ആര&zwj;&zwnj;.</p>
<!-- note the extra zwnj before the period; this is needed to prevent
the zwj causing font fallback choices to propagate further
(see gfxFontGroup::FindFontForChar). -->
</body>
</html>

Просмотреть файл

@ -1258,7 +1258,7 @@ fails == 472020-2.xul 472020-2-ref.xul
== 481024-1e.html 481024-1-ref.html
!= 481948-1.html 481948-1-ref.html
!= 481948-2.html 481948-2-ref.html
fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 481948-3.html 481948-3-ref.html # different behavior on Linux, see bug 488364
random-if(MOZ_WIDGET_TOOLKIT=="windows") fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 481948-3.html 481948-3-ref.html # questionable test, see bug 488364
== 482398-1.html 482398-1-ref.html
== 482592-1a.xhtml 482592-1-ref.html
== 482592-1b.xhtml 482592-1-ref.html