зеркало из https://github.com/mozilla/pjs.git
Bug 505284 - bottom of fonts cut off in dialogs. r=karlt
This commit is contained in:
Родитель
3913d8cc7a
Коммит
3327ced111
|
@ -43,6 +43,7 @@ EXPORTS += gfxPDFSurface.h
|
|||
|
||||
ifdef WINCE
|
||||
EXPORTS += gfxFT2Fonts.h \
|
||||
gfxFT2FontBase.h \
|
||||
gfxDDrawSurface.h \
|
||||
$(NULL)
|
||||
else
|
||||
|
@ -67,8 +68,9 @@ ifdef MOZ_DFB
|
|||
EXPORTS += gfxDirectFBSurface.h
|
||||
endif
|
||||
|
||||
EXPORTS += gfxPlatformGtk.h gfxGdkNativeRenderer.h
|
||||
EXPORTS += gfxPDFSurface.h gfxPSSurface.h
|
||||
EXPORTS += gfxPlatformGtk.h gfxGdkNativeRenderer.h
|
||||
EXPORTS += gfxPDFSurface.h gfxPSSurface.h
|
||||
EXPORTS += gfxFT2FontBase.h
|
||||
|
||||
endif
|
||||
|
||||
|
@ -76,6 +78,7 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),qt)
|
|||
EXPORTS += gfxQtPlatform.h gfxQPainterSurface.h
|
||||
EXPORTS += gfxXlibSurface.h gfxQtNativeRenderer.h
|
||||
EXPORTS += gfxFT2Fonts.h
|
||||
EXPORTS += gfxFT2FontBase.h
|
||||
endif
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
|
||||
|
@ -89,6 +92,7 @@ endif
|
|||
ifeq ($(MOZ_WIDGET_TOOLKIT),beos)
|
||||
EXPORTS += gfxBeOSSurface.h gfxBeOSPlatform.h
|
||||
EXPORTS += gfxPangoFonts.h
|
||||
EXPORTS += gfxFT2FontBase.h
|
||||
EXPORTS += gfxPDFSurface.h
|
||||
endif
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- 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) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@mozilla.com>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Behdad Esfahbod <behdad@gnome.org>
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
|
||||
* Takuro Ashie <ashie@clear-code.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_FT2FONTBASE_H
|
||||
#define GFX_FT2FONTBASE_H
|
||||
|
||||
#include "cairo.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxFont.h"
|
||||
|
||||
class gfxFT2FontBase : public gfxFont {
|
||||
public:
|
||||
gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle);
|
||||
virtual ~gfxFT2FontBase();
|
||||
|
||||
PRUint32 GetGlyph(PRUint32 aCharCode);
|
||||
void GetGlyphExtents(PRUint32 aGlyph,
|
||||
cairo_text_extents_t* aExtents);
|
||||
virtual const gfxFont::Metrics& GetMetrics();
|
||||
virtual nsString GetUniqueName();
|
||||
virtual PRUint32 GetSpaceGlyph();
|
||||
|
||||
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext);
|
||||
|
||||
protected:
|
||||
cairo_scaled_font_t *mScaledFont;
|
||||
PRUint32 mSpaceGlyph;
|
||||
PRBool mHasMetrics;
|
||||
Metrics mMetrics;
|
||||
};
|
||||
|
||||
#endif /* GFX_FT2FONTBASE_H */
|
|
@ -36,12 +36,13 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef GFX_GTKDFBFONTS_H
|
||||
#define GFX_GTKDFBFONTS_H
|
||||
#ifndef GFX_FT2FONTS_H
|
||||
#define GFX_FT2FONTS_H
|
||||
|
||||
#include "cairo.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "gfxFont.h"
|
||||
#include "gfxFT2FontBase.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxFontUtils.h"
|
||||
#include "gfxUserFontSet.h"
|
||||
|
@ -106,21 +107,14 @@ public:
|
|||
};
|
||||
|
||||
|
||||
|
||||
class gfxFT2Font : public gfxFont {
|
||||
class gfxFT2Font : public gfxFT2FontBase {
|
||||
public: // new functions
|
||||
gfxFT2Font(FontEntry *aFontEntry,
|
||||
gfxFT2Font(cairo_scaled_font_t *aCairoFont,
|
||||
FontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle);
|
||||
virtual ~gfxFT2Font ();
|
||||
|
||||
virtual const gfxFont::Metrics& GetMetrics();
|
||||
|
||||
cairo_font_face_t *CairoFontFace();
|
||||
cairo_scaled_font_t *CairoScaledFont();
|
||||
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext);
|
||||
virtual nsString GetUniqueName();
|
||||
virtual PRUint32 GetSpaceGlyph();
|
||||
|
||||
FontEntry *GetFontEntry();
|
||||
|
||||
|
@ -156,27 +150,7 @@ public: // new functions
|
|||
return &entry->mData;
|
||||
}
|
||||
|
||||
class FaceLock {
|
||||
public:
|
||||
FaceLock(gfxFT2Font *font);
|
||||
~FaceLock();
|
||||
|
||||
FT_Face Face() { return mFace; }
|
||||
|
||||
protected:
|
||||
cairo_scaled_font_t *mScaledFont;
|
||||
FT_Face mFace;
|
||||
};
|
||||
|
||||
protected:
|
||||
cairo_scaled_font_t *mScaledFont;
|
||||
|
||||
PRBool mHasSpaceGlyph;
|
||||
PRUint32 mSpaceGlyph;
|
||||
PRBool mHasMetrics;
|
||||
Metrics mMetrics;
|
||||
gfxFloat mAdjustedSize;
|
||||
|
||||
void FillGlyphDataForChar(PRUint32 ch, CachedGlyphData *gd);
|
||||
|
||||
typedef nsBaseHashtableET<nsUint32HashKey, CachedGlyphData> CharGlyphMapEntryType;
|
||||
|
@ -244,5 +218,5 @@ protected: // new functions
|
|||
nsString mString;
|
||||
};
|
||||
|
||||
#endif /* GFX_GTKDFBFONTS_H */
|
||||
#endif /* GFX_FT2FONTS_H */
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ CPPSRCS += gfxWindowsPlatform.cpp \
|
|||
|
||||
ifdef WINCE
|
||||
CPPSRCS += gfxFT2Fonts.cpp \
|
||||
gfxFT2FontBase.cpp \
|
||||
gfxFT2Utils.cpp \
|
||||
gfxDDrawSurface.cpp \
|
||||
$(NULL)
|
||||
EXTRA_DSO_LDOPTS += ddraw.lib
|
||||
|
@ -96,6 +98,8 @@ endif
|
|||
CPPSRCS += gfxPlatformGtk.cpp gfxGdkNativeRenderer.cpp
|
||||
CPPSRCS += gfxPDFSurface.cpp gfxPSSurface.cpp
|
||||
CPPSRCS += gfxFontconfigUtils.cpp
|
||||
CPPSRCS += gfxFT2FontBase.cpp
|
||||
CPPSRCS += gfxFT2Utils.cpp
|
||||
CPPSRCS += nsUnicodeRange.cpp
|
||||
|
||||
ifdef MOZ_X11
|
||||
|
@ -117,6 +121,8 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),qt)
|
|||
CPPSRCS += gfxQtPlatform.cpp gfxQPainterSurface.cpp
|
||||
CPPSRCS += gfxXlibSurface.cpp gfxQtNativeRenderer.cpp
|
||||
CPPSRCS += gfxFT2Fonts.cpp
|
||||
CPPSRCS += gfxFT2FontBase.cpp
|
||||
CPPSRCS += gfxFT2Utils.cpp
|
||||
CPPSRCS += gfxFontconfigUtils.cpp
|
||||
CPPSRCS += nsUnicodeRange.cpp
|
||||
#CSRCS = cairo-xlib-utils.c
|
||||
|
@ -126,6 +132,8 @@ endif
|
|||
ifeq ($(MOZ_WIDGET_TOOLKIT),beos)
|
||||
CPPSRCS += gfxBeOSSurface.cpp gfxBeOSPlatform.cpp
|
||||
CPPSRCS += gfxPangoFonts.cpp
|
||||
CPPSRCS += gfxFT2FontBase.cpp
|
||||
CPPSRCS += gfxFT2Utils.cpp
|
||||
#CPPSRCS += gfxPDFSurface.cpp
|
||||
CPPSRCS += gfxFontconfigUtils.cpp
|
||||
CPPSRCS += nsUnicodeRange.cpp
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
/* -*- 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) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@mozilla.com>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Behdad Esfahbod <behdad@gnome.org>
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
|
||||
* Takuro Ashie <ashie@clear-code.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 "gfxFT2FontBase.h"
|
||||
#include "gfxFT2Utils.h"
|
||||
|
||||
gfxFT2FontBase::gfxFT2FontBase(cairo_scaled_font_t *aScaledFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle)
|
||||
: gfxFont(aFontEntry, aFontStyle),
|
||||
mScaledFont(aScaledFont),
|
||||
mSpaceGlyph(0),
|
||||
mHasMetrics(PR_FALSE)
|
||||
{
|
||||
cairo_scaled_font_reference(mScaledFont);
|
||||
}
|
||||
|
||||
gfxFT2FontBase::~gfxFT2FontBase()
|
||||
{
|
||||
cairo_scaled_font_destroy(mScaledFont);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
gfxFT2FontBase::GetGlyph(PRUint32 aCharCode)
|
||||
{
|
||||
// FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching
|
||||
// through all the postscript glyph names in the font. Therefore use a
|
||||
// lightweight cache, which is stored on the cairo_font_face_t.
|
||||
|
||||
cairo_font_face_t *face =
|
||||
cairo_scaled_font_get_font_face(CairoScaledFont());
|
||||
|
||||
if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
// This cache algorithm and size is based on what is done in
|
||||
// cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I
|
||||
// think the concept is that adjacent characters probably come mostly from
|
||||
// one Unicode block. This assumption is probably not so valid with
|
||||
// scripts with large character sets as used for East Asian languages.
|
||||
|
||||
struct CmapCacheSlot {
|
||||
PRUint32 mCharCode;
|
||||
PRUint32 mGlyphIndex;
|
||||
};
|
||||
const PRUint32 kNumSlots = 256;
|
||||
static cairo_user_data_key_t sCmapCacheKey;
|
||||
|
||||
CmapCacheSlot *slots = static_cast<CmapCacheSlot*>
|
||||
(cairo_font_face_get_user_data(face, &sCmapCacheKey));
|
||||
|
||||
if (!slots) {
|
||||
// cairo's caches can keep some cairo_font_faces alive past our last
|
||||
// destroy, so the destroy function (free) for the cache must be
|
||||
// callable from cairo without any assumptions about what other
|
||||
// modules have not been shutdown.
|
||||
slots = static_cast<CmapCacheSlot*>
|
||||
(calloc(kNumSlots, sizeof(CmapCacheSlot)));
|
||||
if (!slots)
|
||||
return 0;
|
||||
|
||||
cairo_status_t status =
|
||||
cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free);
|
||||
if (status != CAIRO_STATUS_SUCCESS) { // OOM
|
||||
free(slots);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invalidate slot 0 by setting its char code to something that would
|
||||
// never end up in slot 0. All other slots are already invalid
|
||||
// because they have mCharCode = 0 and a glyph for char code 0 will
|
||||
// always be in the slot 0.
|
||||
slots[0].mCharCode = 1;
|
||||
}
|
||||
|
||||
CmapCacheSlot *slot = &slots[aCharCode % kNumSlots];
|
||||
if (slot->mCharCode != aCharCode) {
|
||||
slot->mCharCode = aCharCode;
|
||||
slot->mGlyphIndex = gfxFT2LockedFace(this).GetGlyph(aCharCode);
|
||||
}
|
||||
|
||||
return slot->mGlyphIndex;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFT2FontBase::GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents)
|
||||
{
|
||||
NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
|
||||
|
||||
cairo_glyph_t glyphs[1];
|
||||
glyphs[0].index = aGlyph;
|
||||
glyphs[0].x = 0.0;
|
||||
glyphs[0].y = 0.0;
|
||||
// cairo does some caching for us here but perhaps a small gain could be
|
||||
// made by caching more. It is usually only the advance that is needed,
|
||||
// so caching only the advance could allow many requests to be cached with
|
||||
// little memory use. Ideally this cache would be merged with
|
||||
// gfxGlyphExtents.
|
||||
cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
|
||||
}
|
||||
|
||||
const gfxFont::Metrics&
|
||||
gfxFT2FontBase::GetMetrics()
|
||||
{
|
||||
if (mHasMetrics)
|
||||
return mMetrics;
|
||||
|
||||
if (NS_UNLIKELY(GetStyle()->size <= 0.0)) {
|
||||
new(&mMetrics) gfxFont::Metrics(); // zero initialize
|
||||
mSpaceGlyph = 0;
|
||||
} else {
|
||||
gfxFT2LockedFace(this).GetMetrics(&mMetrics, &mSpaceGlyph);
|
||||
}
|
||||
|
||||
SanitizeMetrics(&mMetrics, PR_FALSE);
|
||||
|
||||
#if 0
|
||||
// printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
|
||||
// printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
|
||||
|
||||
fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
|
||||
fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
|
||||
fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
|
||||
fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
|
||||
fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
|
||||
fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f suOff: %f suSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset);
|
||||
#endif
|
||||
|
||||
mHasMetrics = PR_TRUE;
|
||||
return mMetrics;
|
||||
}
|
||||
|
||||
nsString
|
||||
gfxFT2FontBase::GetUniqueName()
|
||||
{
|
||||
return GetName();
|
||||
}
|
||||
|
||||
// Get the glyphID of a space
|
||||
PRUint32
|
||||
gfxFT2FontBase::GetSpaceGlyph()
|
||||
{
|
||||
NS_ASSERTION(GetStyle()->size != 0,
|
||||
"forgot to short-circuit a text run with zero-sized font?");
|
||||
GetMetrics();
|
||||
return mSpaceGlyph;
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfxFT2FontBase::SetupCairoFont(gfxContext *aContext)
|
||||
{
|
||||
cairo_t *cr = aContext->GetCairo();
|
||||
|
||||
// The scaled font ctm is not relevant right here because
|
||||
// cairo_set_scaled_font does not record the scaled font itself, but
|
||||
// merely the font_face, font_matrix, font_options. The scaled_font used
|
||||
// for the target can be different from the scaled_font passed to
|
||||
// cairo_set_scaled_font. (Unfortunately we have measured only for an
|
||||
// identity ctm.)
|
||||
cairo_scaled_font_t *cairoFont = CairoScaledFont();
|
||||
|
||||
if (cairo_scaled_font_status(cairoFont) != 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;
|
||||
}
|
||||
// Thoughts on which font_options to set on the context:
|
||||
//
|
||||
// cairoFont has been created for screen rendering.
|
||||
//
|
||||
// When the context is being used for screen rendering, we should set
|
||||
// font_options such that the same scaled_font gets used (when the ctm is
|
||||
// the same). The use of explicit font_options recorded in
|
||||
// CreateScaledFont ensures that this will happen.
|
||||
//
|
||||
// XXXkt: For pdf and ps surfaces, I don't know whether it's better to
|
||||
// remove surface-specific options, or try to draw with the same
|
||||
// scaled_font that was used to measure. As the same font_face is being
|
||||
// used, its font_options will often override some values anyway (unless
|
||||
// perhaps we remove those from the FcPattern at face creation).
|
||||
//
|
||||
// I can't see any significant difference in printing, irrespective of
|
||||
// what is set here. It's too late to change things here as measuring has
|
||||
// already taken place. We should really be measuring with a different
|
||||
// font for pdf and ps surfaces (bug 403513).
|
||||
cairo_set_scaled_font(cr, cairoFont);
|
||||
return PR_TRUE;
|
||||
}
|
|
@ -50,6 +50,8 @@
|
|||
#endif
|
||||
#include "gfxTypes.h"
|
||||
#include "gfxFT2Fonts.h"
|
||||
#include "gfxFT2FontBase.h"
|
||||
#include "gfxFT2Utils.h"
|
||||
#include <locale.h>
|
||||
#include "cairo-ft.h"
|
||||
#include FT_TRUETYPE_TAGS_H
|
||||
|
@ -711,8 +713,8 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
|
|||
{
|
||||
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
|
||||
// we'll pass this in/figure it out dynamically, but at this point there can be only one face.
|
||||
gfxFT2Font::FaceLock faceLock(font);
|
||||
FT_Face face = faceLock.Face();
|
||||
gfxFT2LockedFace faceLock(font);
|
||||
FT_Face face = faceLock.get();
|
||||
|
||||
gfxTextRun::CompressedGlyph g;
|
||||
|
||||
|
@ -803,33 +805,14 @@ gfxFT2FontGroup::AddRange(gfxTextRun *aTextRun, gfxFT2Font *font, const PRUnicha
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Stack-based face lock helper
|
||||
*/
|
||||
gfxFT2Font::FaceLock::FaceLock(gfxFT2Font *font)
|
||||
{
|
||||
mScaledFont = font->CairoScaledFont();
|
||||
mFace = cairo_ft_scaled_font_lock_face(mScaledFont);
|
||||
}
|
||||
|
||||
gfxFT2Font::FaceLock::~FaceLock()
|
||||
{
|
||||
cairo_ft_scaled_font_unlock_face(mScaledFont);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfxFT2Font
|
||||
*/
|
||||
gfxFT2Font::gfxFT2Font(FontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle)
|
||||
: gfxFont(aFontEntry, aFontStyle),
|
||||
mScaledFont(nsnull),
|
||||
mHasSpaceGlyph(PR_FALSE),
|
||||
mSpaceGlyph(0),
|
||||
mHasMetrics(PR_FALSE),
|
||||
mAdjustedSize(0)
|
||||
gfxFT2Font::gfxFT2Font(cairo_scaled_font_t *aCairoFont,
|
||||
FontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle)
|
||||
: gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle)
|
||||
{
|
||||
mFontEntry = aFontEntry;
|
||||
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
|
||||
|
||||
mCharGlyphCache.Init(64);
|
||||
|
@ -837,155 +820,6 @@ gfxFT2Font::gfxFT2Font(FontEntry *aFontEntry,
|
|||
|
||||
gfxFT2Font::~gfxFT2Font()
|
||||
{
|
||||
if (mScaledFont) {
|
||||
cairo_scaled_font_destroy(mScaledFont);
|
||||
mScaledFont = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
const gfxFont::Metrics&
|
||||
gfxFT2Font::GetMetrics()
|
||||
{
|
||||
if (mHasMetrics)
|
||||
return mMetrics;
|
||||
|
||||
mMetrics.emHeight = GetStyle()->size;
|
||||
|
||||
gfxFT2Font::FaceLock faceLock(this);
|
||||
FT_Face face = faceLock.Face();
|
||||
|
||||
if (!face) {
|
||||
// Abort here already, otherwise we crash in the following
|
||||
// this can happen if the font-size requested is zero.
|
||||
// The metrics will be incomplete, but then we don't care.
|
||||
return mMetrics;
|
||||
}
|
||||
|
||||
mMetrics.emHeight = GetStyle()->size;
|
||||
|
||||
FT_UInt gid; // glyph ID
|
||||
|
||||
const double emUnit = 1.0 * face->units_per_EM;
|
||||
const double xScale = face->size->metrics.x_ppem / emUnit;
|
||||
const double yScale = face->size->metrics.y_ppem / emUnit;
|
||||
|
||||
// cache properties of space
|
||||
GetSpaceGlyph();
|
||||
|
||||
// properties of 'x', also use its width as average width
|
||||
gid = FT_Get_Char_Index(face, 'x'); // select the glyph
|
||||
if (gid) {
|
||||
// Load glyph into glyph slot. Here, use no_scale to get font units.
|
||||
FT_Load_Glyph(face, gid, FT_LOAD_NO_SCALE);
|
||||
mMetrics.xHeight = face->glyph->metrics.height * yScale;
|
||||
mMetrics.aveCharWidth = face->glyph->metrics.width * xScale;
|
||||
} else {
|
||||
// this font doesn't have an 'x'...
|
||||
// fake these metrics using a fraction of the font size
|
||||
mMetrics.xHeight = mMetrics.emHeight * 0.5;
|
||||
mMetrics.aveCharWidth = mMetrics.emHeight * 0.5;
|
||||
}
|
||||
|
||||
// compute an adjusted size if we need to
|
||||
if (mAdjustedSize == 0 && GetStyle()->sizeAdjust != 0) {
|
||||
gfxFloat aspect = mMetrics.xHeight / GetStyle()->size;
|
||||
mAdjustedSize = GetStyle()->GetAdjustedSize(aspect);
|
||||
mMetrics.emHeight = mAdjustedSize;
|
||||
}
|
||||
|
||||
// now load the OS/2 TrueType table to load access some more properties
|
||||
TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
|
||||
if (os2 && os2->version != 0xFFFF) { // should be there if not old Mac font
|
||||
// if we are here we can improve the avgCharWidth
|
||||
mMetrics.aveCharWidth = os2->xAvgCharWidth * xScale;
|
||||
|
||||
mMetrics.superscriptOffset = os2->ySuperscriptYOffset * yScale;
|
||||
mMetrics.superscriptOffset = PR_MAX(1, mMetrics.superscriptOffset);
|
||||
// some fonts have the incorrect sign (from gfxPangoFonts)
|
||||
mMetrics.subscriptOffset = fabs(os2->ySubscriptYOffset * yScale);
|
||||
mMetrics.subscriptOffset = PR_MAX(1, fabs(mMetrics.subscriptOffset));
|
||||
mMetrics.strikeoutOffset = os2->yStrikeoutPosition * yScale;
|
||||
mMetrics.strikeoutSize = os2->yStrikeoutSize * yScale;
|
||||
} else {
|
||||
// use fractions of emHeight instead of xHeight for these to be more robust
|
||||
mMetrics.superscriptOffset = mMetrics.emHeight * 0.5;
|
||||
mMetrics.subscriptOffset = mMetrics.emHeight * 0.2;
|
||||
mMetrics.strikeoutOffset = mMetrics.emHeight * 0.3;
|
||||
mMetrics.strikeoutSize = face->underline_thickness * yScale;
|
||||
}
|
||||
// seems that underlineOffset really has to be negative
|
||||
mMetrics.underlineOffset = face->underline_position * yScale;
|
||||
mMetrics.underlineSize = face->underline_thickness * yScale;
|
||||
|
||||
// descents are negative in FT but Thebes wants them positive
|
||||
mMetrics.emAscent = face->ascender * yScale;
|
||||
mMetrics.emDescent = -face->descender * yScale;
|
||||
mMetrics.maxHeight = face->height * yScale;
|
||||
mMetrics.maxAscent = face->bbox.yMax * yScale;
|
||||
mMetrics.maxDescent = -face->bbox.yMin * yScale;
|
||||
mMetrics.maxAdvance = face->max_advance_width * xScale;
|
||||
// leading are not available directly (only for WinFNTs)
|
||||
double lineHeight = mMetrics.maxAscent + mMetrics.maxDescent;
|
||||
if (lineHeight > mMetrics.emHeight) {
|
||||
mMetrics.internalLeading = lineHeight - mMetrics.emHeight;
|
||||
} else {
|
||||
mMetrics.internalLeading = 0;
|
||||
}
|
||||
mMetrics.externalLeading = 0; // normal value for OS/2 fonts, too
|
||||
|
||||
SanitizeMetrics(&mMetrics, PR_FALSE);
|
||||
|
||||
/*
|
||||
printf("gfxOS2Font[%#x]::GetMetrics():\n"
|
||||
" emHeight=%f == %f=gfxFont::style.size == %f=adjSz\n"
|
||||
" maxHeight=%f xHeight=%f\n"
|
||||
" aveCharWidth=%f==xWidth spaceWidth=%f\n"
|
||||
" supOff=%f SubOff=%f strOff=%f strSz=%f\n"
|
||||
" undOff=%f undSz=%f intLead=%f extLead=%f\n"
|
||||
" emAsc=%f emDesc=%f maxH=%f\n"
|
||||
" maxAsc=%f maxDes=%f maxAdv=%f\n",
|
||||
(unsigned)this,
|
||||
mMetrics.emHeight, GetStyle()->size, mAdjustedSize,
|
||||
mMetrics.maxHeight, mMetrics.xHeight,
|
||||
mMetrics.aveCharWidth, mMetrics.spaceWidth,
|
||||
mMetrics.superscriptOffset, mMetrics.subscriptOffset,
|
||||
mMetrics.strikeoutOffset, mMetrics.strikeoutSize,
|
||||
mMetrics.underlineOffset, mMetrics.underlineSize,
|
||||
mMetrics.internalLeading, mMetrics.externalLeading,
|
||||
mMetrics.emAscent, mMetrics.emDescent, mMetrics.maxHeight,
|
||||
mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance
|
||||
);
|
||||
*/
|
||||
|
||||
// XXX mMetrics.height needs to be set.
|
||||
|
||||
mHasMetrics = PR_TRUE;
|
||||
return mMetrics;
|
||||
}
|
||||
|
||||
|
||||
nsString
|
||||
gfxFT2Font::GetUniqueName()
|
||||
{
|
||||
return GetName();
|
||||
}
|
||||
|
||||
PRUint32
|
||||
gfxFT2Font::GetSpaceGlyph()
|
||||
{
|
||||
NS_ASSERTION (GetStyle()->size != 0, "forgot to short-circuit a text run with zero-sized font?");
|
||||
|
||||
if (!mHasSpaceGlyph) {
|
||||
const CachedGlyphData *gdata = GetGlyphDataForChar(' ');
|
||||
|
||||
mSpaceGlyph = gdata->glyphIndex;
|
||||
NS_ASSERTION(mSpaceGlyph != 0, "Font has no space glyph!");
|
||||
|
||||
mMetrics.spaceWidth = MOZ_FT_TRUNC(gdata->xAdvance);
|
||||
mHasSpaceGlyph = PR_TRUE;
|
||||
}
|
||||
|
||||
return mSpaceGlyph;
|
||||
}
|
||||
|
||||
cairo_font_face_t *
|
||||
|
@ -998,59 +832,45 @@ gfxFT2Font::CairoFontFace()
|
|||
return GetFontEntry()->CairoFontFace();
|
||||
}
|
||||
|
||||
cairo_scaled_font_t *
|
||||
gfxFT2Font::CairoScaledFont()
|
||||
static cairo_scaled_font_t *
|
||||
CreateScaledFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle)
|
||||
{
|
||||
if (!mScaledFont) {
|
||||
cairo_matrix_t sizeMatrix;
|
||||
cairo_matrix_t identityMatrix;
|
||||
cairo_scaled_font_t *scaledFont = NULL;
|
||||
|
||||
// XXX deal with adjusted size
|
||||
cairo_matrix_init_scale(&sizeMatrix, mStyle.size, mStyle.size);
|
||||
cairo_matrix_init_identity(&identityMatrix);
|
||||
cairo_matrix_t sizeMatrix;
|
||||
cairo_matrix_t identityMatrix;
|
||||
|
||||
// synthetic oblique by skewing via the font matrix
|
||||
PRBool needsOblique = (!mFontEntry->mItalic && (mStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)));
|
||||
// XXX deal with adjusted size
|
||||
cairo_matrix_init_scale(&sizeMatrix, aStyle->size, aStyle->size);
|
||||
cairo_matrix_init_identity(&identityMatrix);
|
||||
|
||||
if (needsOblique) {
|
||||
const double kSkewFactor = 0.25;
|
||||
// synthetic oblique by skewing via the font matrix
|
||||
PRBool needsOblique = (!aFontEntry->mItalic && (aStyle->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)));
|
||||
|
||||
cairo_matrix_t style;
|
||||
cairo_matrix_init(&style,
|
||||
1, //xx
|
||||
0, //yx
|
||||
-1 * kSkewFactor, //xy
|
||||
1, //yy
|
||||
0, //x0
|
||||
0); //y0
|
||||
cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
|
||||
}
|
||||
if (needsOblique) {
|
||||
const double kSkewFactor = 0.25;
|
||||
|
||||
cairo_font_options_t *fontOptions = cairo_font_options_create();
|
||||
mScaledFont = cairo_scaled_font_create(CairoFontFace(), &sizeMatrix,
|
||||
&identityMatrix, fontOptions);
|
||||
cairo_font_options_destroy(fontOptions);
|
||||
cairo_matrix_t style;
|
||||
cairo_matrix_init(&style,
|
||||
1, //xx
|
||||
0, //yx
|
||||
-1 * kSkewFactor, //xy
|
||||
1, //yy
|
||||
0, //x0
|
||||
0); //y0
|
||||
cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
|
||||
}
|
||||
|
||||
NS_ASSERTION(mAdjustedSize == 0.0 ||
|
||||
cairo_scaled_font_status(mScaledFont) == CAIRO_STATUS_SUCCESS,
|
||||
cairo_font_options_t *fontOptions = cairo_font_options_create();
|
||||
scaledFont = cairo_scaled_font_create(aFontEntry->CairoFontFace(),
|
||||
&sizeMatrix,
|
||||
&identityMatrix, fontOptions);
|
||||
cairo_font_options_destroy(fontOptions);
|
||||
|
||||
NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
|
||||
"Failed to make scaled font");
|
||||
|
||||
return mScaledFont;
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfxFT2Font::SetupCairoFont(gfxContext *aContext)
|
||||
{
|
||||
cairo_scaled_font_t *scaledFont = CairoScaledFont();
|
||||
|
||||
if (cairo_scaled_font_status(scaledFont) != 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(), scaledFont);
|
||||
return PR_TRUE;
|
||||
return scaledFont;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1076,7 +896,9 @@ gfxFT2Font::GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle)
|
|||
{
|
||||
nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(aFontEntry->Name(), aStyle);
|
||||
if (!font) {
|
||||
font = new gfxFT2Font(aFontEntry, aStyle);
|
||||
cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontEntry, aStyle);
|
||||
font = new gfxFT2Font(scaledFont, aFontEntry, aStyle);
|
||||
cairo_scaled_font_destroy(scaledFont);
|
||||
if (!font)
|
||||
return nsnull;
|
||||
gfxFontCache::GetCache()->AddNew(font);
|
||||
|
@ -1089,8 +911,8 @@ gfxFT2Font::GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle)
|
|||
void
|
||||
gfxFT2Font::FillGlyphDataForChar(PRUint32 ch, CachedGlyphData *gd)
|
||||
{
|
||||
gfxFT2Font::FaceLock faceLock(this);
|
||||
FT_Face face = faceLock.Face();
|
||||
gfxFT2LockedFace faceLock(this);
|
||||
FT_Face face = faceLock.get();
|
||||
|
||||
FT_UInt gid = FT_Get_Char_Index(face, ch);
|
||||
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
/* -*- 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) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@mozilla.com>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Behdad Esfahbod <behdad@gnome.org>
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
|
||||
* Takuro Ashie <ashie@clear-code.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 "gfxFT2FontBase.h"
|
||||
#include "gfxFT2Utils.h"
|
||||
#include FT_TRUETYPE_TAGS_H
|
||||
#include FT_TRUETYPE_TABLES_H
|
||||
|
||||
// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
|
||||
static inline FT_Long
|
||||
ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
|
||||
{
|
||||
FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
|
||||
return ROUND_26_6_TO_INT(fixed26dot6);
|
||||
}
|
||||
|
||||
// Snap a line to pixels while keeping the center and size of the line as
|
||||
// close to the original position as possible.
|
||||
//
|
||||
// Pango does similar snapping for underline and strikethrough when fonts are
|
||||
// hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
|
||||
// top and size of lines. Optimizing the distance between the line and
|
||||
// baseline is probably good for the gap between text and underline, but
|
||||
// optimizing the center of the line is better for positioning strikethough.
|
||||
static void
|
||||
SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
|
||||
{
|
||||
gfxFloat snappedSize = PR_MAX(NS_floor(aSize + 0.5), 1.0);
|
||||
// Correct offset for change in size
|
||||
gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
|
||||
// Snap offset
|
||||
aOffset = NS_floor(offset + 0.5);
|
||||
aSize = snappedSize;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics,
|
||||
PRUint32* aSpaceGlyph)
|
||||
{
|
||||
NS_PRECONDITION(aMetrics != NULL, "aMetrics must not be NULL");
|
||||
NS_PRECONDITION(aSpaceGlyph != NULL, "aSpaceGlyph must not be NULL");
|
||||
|
||||
if (NS_UNLIKELY(!mFace)) {
|
||||
// No face. This unfortunate situation might happen if the font
|
||||
// file is (re)moved at the wrong time.
|
||||
aMetrics->emHeight = mGfxFont->GetStyle()->size;
|
||||
aMetrics->emAscent = 0.8 * aMetrics->emHeight;
|
||||
aMetrics->emDescent = 0.2 * aMetrics->emHeight;
|
||||
aMetrics->maxAscent = aMetrics->emAscent;
|
||||
aMetrics->maxDescent = aMetrics->maxDescent;
|
||||
aMetrics->maxHeight = aMetrics->emHeight;
|
||||
aMetrics->internalLeading = 0.0;
|
||||
aMetrics->externalLeading = 0.2 * aMetrics->emHeight;
|
||||
aSpaceGlyph = 0;
|
||||
aMetrics->spaceWidth = 0.5 * aMetrics->emHeight;
|
||||
aMetrics->maxAdvance = aMetrics->spaceWidth;
|
||||
aMetrics->aveCharWidth = aMetrics->spaceWidth;
|
||||
aMetrics->zeroOrAveCharWidth = aMetrics->spaceWidth;
|
||||
aMetrics->xHeight = 0.5 * aMetrics->emHeight;
|
||||
aMetrics->underlineSize = aMetrics->emHeight / 14.0;
|
||||
aMetrics->underlineOffset = -aMetrics->underlineSize;
|
||||
aMetrics->strikeoutOffset = 0.25 * aMetrics->emHeight;
|
||||
aMetrics->strikeoutSize = aMetrics->underlineSize;
|
||||
aMetrics->superscriptOffset = aMetrics->xHeight;
|
||||
aMetrics->subscriptOffset = aMetrics->xHeight;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
|
||||
|
||||
gfxFloat emHeight;
|
||||
// Scale for vertical design metric conversion: pixels per design unit.
|
||||
gfxFloat yScale;
|
||||
if (FT_IS_SCALABLE(mFace)) {
|
||||
// Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
|
||||
// have subpixel accuracy.
|
||||
//
|
||||
// FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
|
||||
// (fractional) value is a factor that converts vertical metrics from
|
||||
// design units to units of 1/64 pixels, so that the result may be
|
||||
// interpreted as pixels in 26.6 fixed point format.
|
||||
yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
|
||||
emHeight = mFace->units_per_EM * yScale;
|
||||
} else { // Not scalable.
|
||||
// FT_Size_Metrics doc says x_scale is "only relevant for scalable
|
||||
// font formats".
|
||||
gfxFloat emUnit = mFace->units_per_EM;
|
||||
emHeight = ftMetrics.y_ppem;
|
||||
yScale = emHeight / emUnit;
|
||||
}
|
||||
|
||||
TT_OS2 *os2 =
|
||||
static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
|
||||
|
||||
aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
|
||||
aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
|
||||
aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
|
||||
|
||||
gfxFloat lineHeight;
|
||||
if (os2 && os2->sTypoAscender) {
|
||||
aMetrics->emAscent = os2->sTypoAscender * yScale;
|
||||
aMetrics->emDescent = -os2->sTypoDescender * yScale;
|
||||
FT_Short typoHeight =
|
||||
os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
|
||||
lineHeight = typoHeight * yScale;
|
||||
|
||||
// maxAscent/maxDescent get used for frame heights, and some fonts
|
||||
// don't have the HHEA table ascent/descent set (bug 279032).
|
||||
if (aMetrics->emAscent > aMetrics->maxAscent)
|
||||
aMetrics->maxAscent = aMetrics->emAscent;
|
||||
if (aMetrics->emDescent > aMetrics->maxDescent)
|
||||
aMetrics->maxDescent = aMetrics->emDescent;
|
||||
} else {
|
||||
aMetrics->emAscent = aMetrics->maxAscent;
|
||||
aMetrics->emDescent = aMetrics->maxDescent;
|
||||
lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
|
||||
}
|
||||
|
||||
cairo_text_extents_t extents;
|
||||
*aSpaceGlyph = GetCharExtents(' ', &extents);
|
||||
if (*aSpaceGlyph) {
|
||||
aMetrics->spaceWidth = extents.x_advance;
|
||||
} else {
|
||||
aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
|
||||
}
|
||||
|
||||
aMetrics->zeroOrAveCharWidth = 0.0;
|
||||
if (GetCharExtents('0', &extents)) {
|
||||
aMetrics->zeroOrAveCharWidth = extents.x_advance;
|
||||
}
|
||||
|
||||
// Prefering a measured x over sxHeight because sxHeight doesn't consider
|
||||
// hinting, but maybe the x extents are not quite right in some fancy
|
||||
// script fonts. CSS 2.1 suggests possibly using the height of an "o",
|
||||
// which would have a more consistent glyph across fonts.
|
||||
if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
|
||||
aMetrics->xHeight = -extents.y_bearing;
|
||||
aMetrics->aveCharWidth = extents.x_advance;
|
||||
} else {
|
||||
if (os2 && os2->sxHeight) {
|
||||
aMetrics->xHeight = os2->sxHeight * yScale;
|
||||
} else {
|
||||
// CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
|
||||
// impossible or impractical to determine the x-height, a value of
|
||||
// 0.5em should be used."
|
||||
aMetrics->xHeight = 0.5 * emHeight;
|
||||
}
|
||||
aMetrics->aveCharWidth = 0.0; // updated below
|
||||
}
|
||||
// aveCharWidth is used for the width of text input elements so be
|
||||
// liberal rather than conservative in the estimate.
|
||||
if (os2 && os2->xAvgCharWidth) {
|
||||
// Round to pixels as this is compared with maxAdvance to guess
|
||||
// whether this is a fixed width font.
|
||||
gfxFloat avgCharWidth =
|
||||
ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
|
||||
aMetrics->aveCharWidth =
|
||||
PR_MAX(aMetrics->aveCharWidth, avgCharWidth);
|
||||
}
|
||||
aMetrics->aveCharWidth =
|
||||
PR_MAX(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
|
||||
if (aMetrics->aveCharWidth == 0.0) {
|
||||
aMetrics->aveCharWidth = aMetrics->spaceWidth;
|
||||
}
|
||||
if (aMetrics->zeroOrAveCharWidth == 0.0) {
|
||||
aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
|
||||
}
|
||||
// Apparently hinting can mean that max_advance is not always accurate.
|
||||
aMetrics->maxAdvance =
|
||||
PR_MAX(aMetrics->maxAdvance, aMetrics->aveCharWidth);
|
||||
|
||||
// gfxFont::Metrics::underlineOffset is the position of the top of the
|
||||
// underline.
|
||||
//
|
||||
// FT_FaceRec documentation describes underline_position as "the
|
||||
// center of the underlining stem". This was the original definition
|
||||
// of the PostScript metric, but in the PostScript table of OpenType
|
||||
// fonts the metric is "the top of the underline"
|
||||
// (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
|
||||
// (up to version 2.3.7) doesn't make any adjustment.
|
||||
//
|
||||
// Therefore get the underline position directly from the table
|
||||
// ourselves when this table exists. Use FreeType's metrics for
|
||||
// other (including older PostScript) fonts.
|
||||
if (mFace->underline_position && mFace->underline_thickness) {
|
||||
aMetrics->underlineSize = mFace->underline_thickness * yScale;
|
||||
TT_Postscript *post = static_cast<TT_Postscript*>
|
||||
(FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
|
||||
if (post && post->underlinePosition) {
|
||||
aMetrics->underlineOffset = post->underlinePosition * yScale;
|
||||
} else {
|
||||
aMetrics->underlineOffset = mFace->underline_position * yScale
|
||||
+ 0.5 * aMetrics->underlineSize;
|
||||
}
|
||||
} else { // No underline info.
|
||||
// Imitate Pango.
|
||||
aMetrics->underlineSize = emHeight / 14.0;
|
||||
aMetrics->underlineOffset = -aMetrics->underlineSize;
|
||||
}
|
||||
|
||||
if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition) {
|
||||
aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
|
||||
aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
|
||||
} else { // No strikeout info.
|
||||
aMetrics->strikeoutSize = aMetrics->underlineSize;
|
||||
// Use OpenType spec's suggested position for Roman font.
|
||||
aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
|
||||
+ 0.5 * aMetrics->strikeoutSize;
|
||||
}
|
||||
SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
|
||||
|
||||
if (os2 && os2->ySuperscriptYOffset) {
|
||||
gfxFloat val = ScaleRoundDesignUnits(os2->ySuperscriptYOffset,
|
||||
ftMetrics.y_scale);
|
||||
aMetrics->superscriptOffset = PR_MAX(1.0, val);
|
||||
} else {
|
||||
aMetrics->superscriptOffset = aMetrics->xHeight;
|
||||
}
|
||||
|
||||
if (os2 && os2->ySubscriptYOffset) {
|
||||
gfxFloat val = ScaleRoundDesignUnits(os2->ySubscriptYOffset,
|
||||
ftMetrics.y_scale);
|
||||
// some fonts have the incorrect sign.
|
||||
val = fabs(val);
|
||||
aMetrics->subscriptOffset = PR_MAX(1.0, val);
|
||||
} else {
|
||||
aMetrics->subscriptOffset = aMetrics->xHeight;
|
||||
}
|
||||
|
||||
aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
|
||||
|
||||
// Make the line height an integer number of pixels so that lines will be
|
||||
// equally spaced (rather than just being snapped to pixels, some up and
|
||||
// some down). Layout calculates line height from the emHeight +
|
||||
// internalLeading + externalLeading, but first each of these is rounded
|
||||
// to layout units. To ensure that the result is an integer number of
|
||||
// pixels, round each of the components to pixels.
|
||||
aMetrics->emHeight = NS_floor(emHeight + 0.5);
|
||||
|
||||
// maxHeight will normally be an integer, but round anyway in case
|
||||
// FreeType is configured differently.
|
||||
aMetrics->internalLeading =
|
||||
NS_floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
|
||||
|
||||
// Text input boxes currently don't work well with lineHeight
|
||||
// significantly less than maxHeight (with Verdana, for example).
|
||||
lineHeight = NS_floor(PR_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
|
||||
aMetrics->externalLeading =
|
||||
lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
|
||||
|
||||
// Ensure emAscent + emDescent == emHeight
|
||||
gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
|
||||
aMetrics->emAscent = sum > 0.0 ?
|
||||
aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
|
||||
aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
gfxFT2LockedFace::GetGlyph(PRUint32 aCharCode)
|
||||
{
|
||||
if (NS_UNLIKELY(!mFace))
|
||||
return 0;
|
||||
|
||||
return FT_Get_Char_Index(mFace, aCharCode);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
gfxFT2LockedFace::GetCharExtents(char aChar, cairo_text_extents_t* aExtents)
|
||||
{
|
||||
NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
|
||||
|
||||
if (!mFace)
|
||||
return 0;
|
||||
|
||||
FT_UInt gid = mGfxFont->GetGlyph(aChar);
|
||||
if (gid) {
|
||||
mGfxFont->GetGlyphExtents(gid, aExtents);
|
||||
}
|
||||
|
||||
return gid;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- 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) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Vladimir Vukicevic <vladimir@mozilla.com>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Behdad Esfahbod <behdad@gnome.org>
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
|
||||
* Takuro Ashie <ashie@clear-code.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_FT2UTILS_H
|
||||
#define GFX_FT2UTILS_H
|
||||
|
||||
#include "cairo-ft.h"
|
||||
#include "gfxFT2FontBase.h"
|
||||
|
||||
typedef struct FT_FaceRec_* FT_Face;
|
||||
|
||||
class gfxFT2LockedFace {
|
||||
public:
|
||||
gfxFT2LockedFace(gfxFT2FontBase *aFont) :
|
||||
mGfxFont(aFont),
|
||||
mFace(cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont()))
|
||||
{ }
|
||||
~gfxFT2LockedFace()
|
||||
{
|
||||
if (mFace) {
|
||||
cairo_ft_scaled_font_unlock_face(mGfxFont->CairoScaledFont());
|
||||
}
|
||||
}
|
||||
|
||||
FT_Face get() { return mFace; };
|
||||
|
||||
/**
|
||||
* Get the glyph id for a Unicode character representable by a single
|
||||
* glyph, or return zero if there is no such glyph. This does no caching,
|
||||
* so you probably want gfxFcFont::GetGlyph.
|
||||
*/
|
||||
virtual PRUint32 GetGlyph(PRUint32 aCharCode);
|
||||
|
||||
void GetMetrics(gfxFont::Metrics* aMetrics, PRUint32* aSpaceGlyph);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Get extents for a simple character representable by a single glyph.
|
||||
* The return value is the glyph id of that glyph or zero if no such glyph
|
||||
* exists. aExtents is only set when this returns a non-zero glyph id.
|
||||
*/
|
||||
PRUint32 GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
|
||||
|
||||
nsRefPtr<gfxFT2FontBase> mGfxFont;
|
||||
FT_Face mFace;
|
||||
};
|
||||
|
||||
// Rounding and truncation functions for a FreeType fixed point number
|
||||
// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
|
||||
// part and low 6 bits for the fractional part.
|
||||
#define FLOAT_FROM_26_6(x) ((x) / 64.0)
|
||||
#define FLOAT_FROM_16_16(x) ((x) / 65536.0)
|
||||
#define ROUND_26_6_TO_INT(x) ((x) >= 0 ? ((32 + (x)) >> 6) \
|
||||
: -((32 - (x)) >> 6))
|
||||
|
||||
#endif /* GFX_FT2UTILS_H */
|
|
@ -56,6 +56,8 @@
|
|||
#include "gfxContext.h"
|
||||
#include "gfxPlatformGtk.h"
|
||||
#include "gfxPangoFonts.h"
|
||||
#include "gfxFT2FontBase.h"
|
||||
#include "gfxFT2Utils.h"
|
||||
#include "gfxFontconfigUtils.h"
|
||||
#include "gfxUserFontSet.h"
|
||||
|
||||
|
@ -126,21 +128,6 @@ public:
|
|||
#define FC_FULLNAME "fullname"
|
||||
#endif
|
||||
|
||||
// Rounding and truncation functions for a FreeType fixed point number
|
||||
// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
|
||||
// part and low 6 bits for the fractional part.
|
||||
#define FLOAT_FROM_26_6(x) ((x) / 64.0)
|
||||
#define FLOAT_FROM_16_16(x) ((x) / 65536.0)
|
||||
#define ROUND_26_6_TO_INT(x) ((x) >= 0 ? ((32 + (x)) >> 6) \
|
||||
: -((32 - (x)) >> 6))
|
||||
// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
|
||||
static inline FT_Long
|
||||
ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
|
||||
{
|
||||
FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
|
||||
return ROUND_26_6_TO_INT(fixed26dot6);
|
||||
}
|
||||
|
||||
static PRFuncPtr
|
||||
FindFunctionSymbol(const char *name)
|
||||
{
|
||||
|
@ -519,84 +506,46 @@ gfxDownloadedFcFontEntry::GetPangoCoverage()
|
|||
* cairo_scaled_font created from an FcPattern.
|
||||
*/
|
||||
|
||||
class gfxFcFont : public gfxFont {
|
||||
class gfxFcFont : public gfxFT2FontBase {
|
||||
public:
|
||||
virtual ~gfxFcFont ();
|
||||
static already_AddRefed<gfxFcFont> GetOrMakeFont(FcPattern *aPattern);
|
||||
|
||||
virtual const gfxFont::Metrics& GetMetrics();
|
||||
|
||||
virtual nsString GetUniqueName();
|
||||
|
||||
// Get the glyphID of a space
|
||||
virtual PRUint32 GetSpaceGlyph() {
|
||||
NS_ASSERTION(GetStyle()->size != 0,
|
||||
"forgot to short-circuit a text run with zero-sized font?");
|
||||
GetMetrics();
|
||||
return mSpaceGlyph;
|
||||
}
|
||||
|
||||
cairo_scaled_font_t *CairoScaledFont() { return mCairoFont; }
|
||||
PRUint32 GetGlyph(PRUint32 aCharCode);
|
||||
void GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents);
|
||||
|
||||
protected:
|
||||
cairo_scaled_font_t *mCairoFont;
|
||||
|
||||
PRUint32 mSpaceGlyph;
|
||||
Metrics mMetrics;
|
||||
PRPackedBool mHasMetrics;
|
||||
|
||||
gfxFcFont(cairo_scaled_font_t *aCairoFont,
|
||||
gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle);
|
||||
|
||||
virtual PRBool SetupCairoFont(gfxContext *aContext);
|
||||
|
||||
// key for locating a gfxFcFont corresponding to a cairo_scaled_font
|
||||
static cairo_user_data_key_t sGfxFontKey;
|
||||
};
|
||||
|
||||
class LockedFTFace {
|
||||
class gfxFcLockedFace : public gfxFT2LockedFace {
|
||||
public:
|
||||
LockedFTFace(gfxFcFont *aFont)
|
||||
: mGfxFont(aFont),
|
||||
mFace(cairo_ft_scaled_font_lock_face(aFont->CairoScaledFont()))
|
||||
{
|
||||
}
|
||||
|
||||
~LockedFTFace()
|
||||
{
|
||||
if (mFace) {
|
||||
cairo_ft_scaled_font_unlock_face(mGfxFont->CairoScaledFont());
|
||||
}
|
||||
}
|
||||
|
||||
FT_Face get()
|
||||
{
|
||||
return mFace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the glyph id for a Unicode character representable by a single
|
||||
* glyph, or return zero if there is no such glyph. This does no caching,
|
||||
* so you probably want gfxFcFont::GetGlyph.
|
||||
*/
|
||||
PRUint32 GetGlyph(PRUint32 aCharCode);
|
||||
|
||||
void GetMetrics(gfxFont::Metrics* aMetrics, PRUint32* aSpaceGlyph);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Get extents for a simple character representable by a single glyph.
|
||||
* The return value is the glyph id of that glyph or zero if no such glyph
|
||||
* exists. aExtents is only set when this returns a non-zero glyph id.
|
||||
*/
|
||||
PRUint32 GetCharExtents(char aChar, cairo_text_extents_t* aExtents);
|
||||
|
||||
nsRefPtr<gfxFcFont> mGfxFont;
|
||||
FT_Face mFace;
|
||||
gfxFcLockedFace(gfxFT2FontBase *aFont) : gfxFT2LockedFace(aFont) {};
|
||||
~gfxFcLockedFace() {};
|
||||
virtual PRUint32 GetGlyph(PRUint32 aCharCode);
|
||||
};
|
||||
|
||||
PRUint32
|
||||
gfxFcLockedFace::GetGlyph(PRUint32 aCharCode)
|
||||
{
|
||||
if (NS_UNLIKELY(!mFace))
|
||||
return 0;
|
||||
|
||||
// FcFreeTypeCharIndex will search starting from the most recently
|
||||
// selected charmap. This can cause non-determistic behavior when more
|
||||
// than one charmap supports a character but with different glyphs, as
|
||||
// with older versions of MS Gothic, for example. Always prefer a Unicode
|
||||
// charmap, if there is one. (FcFreeTypeCharIndex usually does the
|
||||
// appropriate Unicode conversion, but some fonts have non-Roman glyphs
|
||||
// for FT_ENCODING_APPLE_ROMAN characters.)
|
||||
if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
|
||||
FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
|
||||
}
|
||||
|
||||
return FcFreeTypeCharIndex(mFace, aCharCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* gfxPangoFcFont:
|
||||
*
|
||||
|
@ -2107,18 +2056,14 @@ cairo_user_data_key_t gfxFcFont::sGfxFontKey;
|
|||
gfxFcFont::gfxFcFont(cairo_scaled_font_t *aCairoFont,
|
||||
gfxFontEntry *aFontEntry,
|
||||
const gfxFontStyle *aFontStyle)
|
||||
: gfxFont(aFontEntry, aFontStyle),
|
||||
mCairoFont(aCairoFont),
|
||||
mHasMetrics(PR_FALSE)
|
||||
: gfxFT2FontBase(aCairoFont, aFontEntry, aFontStyle)
|
||||
{
|
||||
cairo_scaled_font_reference(mCairoFont);
|
||||
cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, this, NULL);
|
||||
cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, this, NULL);
|
||||
}
|
||||
|
||||
gfxFcFont::~gfxFcFont()
|
||||
{
|
||||
cairo_scaled_font_set_user_data(mCairoFont, &sGfxFontKey, NULL, NULL);
|
||||
cairo_scaled_font_destroy(mCairoFont);
|
||||
cairo_scaled_font_set_user_data(mScaledFont, &sGfxFontKey, NULL, NULL);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
|
@ -2211,7 +2156,7 @@ GetFTLibrary()
|
|||
if (!font)
|
||||
return NULL;
|
||||
|
||||
LockedFTFace face(font);
|
||||
gfxFcLockedFace face(font);
|
||||
if (!face.get())
|
||||
return NULL;
|
||||
|
||||
|
@ -2402,400 +2347,6 @@ gfxPangoFontGroup::GetBaseFontSet()
|
|||
return fontSet;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
gfxFcFont::GetGlyph(PRUint32 aCharCode)
|
||||
{
|
||||
// FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching
|
||||
// through all the postscript glyph names in the font. Therefore use a
|
||||
// lightweight cache, which is stored on the cairo_font_face_t.
|
||||
|
||||
cairo_font_face_t *face =
|
||||
cairo_scaled_font_get_font_face(CairoScaledFont());
|
||||
|
||||
if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
// This cache algorithm and size is based on what is done in
|
||||
// cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I
|
||||
// think the concept is that adjacent characters probably come mostly from
|
||||
// one Unicode block. This assumption is probably not so valid with
|
||||
// scripts with large character sets as used for East Asian languages.
|
||||
|
||||
struct CmapCacheSlot {
|
||||
PRUint32 mCharCode;
|
||||
PRUint32 mGlyphIndex;
|
||||
};
|
||||
const PRUint32 kNumSlots = 256;
|
||||
static cairo_user_data_key_t sCmapCacheKey;
|
||||
|
||||
CmapCacheSlot *slots = static_cast<CmapCacheSlot*>
|
||||
(cairo_font_face_get_user_data(face, &sCmapCacheKey));
|
||||
|
||||
if (!slots) {
|
||||
// cairo's caches can keep some cairo_font_faces alive past our last
|
||||
// destroy, so the destroy function (free) for the cache must be
|
||||
// callable from cairo without any assumptions about what other
|
||||
// modules have not been shutdown.
|
||||
slots = static_cast<CmapCacheSlot*>
|
||||
(calloc(kNumSlots, sizeof(CmapCacheSlot)));
|
||||
if (!slots)
|
||||
return 0;
|
||||
|
||||
cairo_status_t status =
|
||||
cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free);
|
||||
if (status != CAIRO_STATUS_SUCCESS) { // OOM
|
||||
free(slots);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invalidate slot 0 by setting its char code to something that would
|
||||
// never end up in slot 0. All other slots are already invalid
|
||||
// because they have mCharCode = 0 and a glyph for char code 0 will
|
||||
// always be in the slot 0.
|
||||
slots[0].mCharCode = 1;
|
||||
}
|
||||
|
||||
CmapCacheSlot *slot = &slots[aCharCode % kNumSlots];
|
||||
if (slot->mCharCode != aCharCode) {
|
||||
slot->mCharCode = aCharCode;
|
||||
slot->mGlyphIndex = LockedFTFace(this).GetGlyph(aCharCode);
|
||||
}
|
||||
|
||||
return slot->mGlyphIndex;
|
||||
}
|
||||
|
||||
void
|
||||
gfxFcFont::GetGlyphExtents(PRUint32 aGlyph, cairo_text_extents_t* aExtents)
|
||||
{
|
||||
NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
|
||||
|
||||
cairo_glyph_t glyphs[1];
|
||||
glyphs[0].index = aGlyph;
|
||||
glyphs[0].x = 0.0;
|
||||
glyphs[0].y = 0.0;
|
||||
// cairo does some caching for us here but perhaps a small gain could be
|
||||
// made by caching more. It is usually only the advance that is needed,
|
||||
// so caching only the advance could allow many requests to be cached with
|
||||
// little memory use. Ideally this cache would be merged with
|
||||
// gfxGlyphExtents.
|
||||
cairo_scaled_font_glyph_extents(CairoScaledFont(), glyphs, 1, aExtents);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
LockedFTFace::GetGlyph(PRUint32 aCharCode)
|
||||
{
|
||||
if (NS_UNLIKELY(!mFace))
|
||||
return 0;
|
||||
|
||||
// FcFreeTypeCharIndex will search starting from the most recently
|
||||
// selected charmap. This can cause non-determistic behavior when more
|
||||
// than one charmap supports a character but with different glyphs, as
|
||||
// with older versions of MS Gothic, for example. Always prefer a Unicode
|
||||
// charmap, if there is one. (FcFreeTypeCharIndex usually does the
|
||||
// appropriate Unicode conversion, but some fonts have non-Roman glyphs
|
||||
// for FT_ENCODING_APPLE_ROMAN characters.)
|
||||
if (!mFace->charmap || mFace->charmap->encoding != FT_ENCODING_UNICODE) {
|
||||
FT_Select_Charmap(mFace, FT_ENCODING_UNICODE);
|
||||
}
|
||||
|
||||
return FcFreeTypeCharIndex(mFace, aCharCode);
|
||||
}
|
||||
|
||||
PRUint32
|
||||
LockedFTFace::GetCharExtents(char aChar,
|
||||
cairo_text_extents_t* aExtents)
|
||||
{
|
||||
NS_PRECONDITION(aExtents != NULL, "aExtents must not be NULL");
|
||||
|
||||
if (!mFace)
|
||||
return 0;
|
||||
|
||||
FT_UInt gid = mGfxFont->GetGlyph(aChar);
|
||||
if (gid) {
|
||||
mGfxFont->GetGlyphExtents(gid, aExtents);
|
||||
}
|
||||
|
||||
return gid;
|
||||
}
|
||||
|
||||
// Snap a line to pixels while keeping the center and size of the line as
|
||||
// close to the original position as possible.
|
||||
//
|
||||
// Pango does similar snapping for underline and strikethrough when fonts are
|
||||
// hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
|
||||
// top and size of lines. Optimizing the distance between the line and
|
||||
// baseline is probably good for the gap between text and underline, but
|
||||
// optimizing the center of the line is better for positioning strikethough.
|
||||
static void
|
||||
SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
|
||||
{
|
||||
gfxFloat snappedSize = PR_MAX(NS_floor(aSize + 0.5), 1.0);
|
||||
// Correct offset for change in size
|
||||
gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
|
||||
// Snap offset
|
||||
aOffset = NS_floor(offset + 0.5);
|
||||
aSize = snappedSize;
|
||||
}
|
||||
|
||||
void
|
||||
LockedFTFace::GetMetrics(gfxFont::Metrics* aMetrics, PRUint32* aSpaceGlyph)
|
||||
{
|
||||
NS_PRECONDITION(aMetrics != NULL, "aMetrics must not be NULL");
|
||||
NS_PRECONDITION(aSpaceGlyph != NULL, "aSpaceGlyph must not be NULL");
|
||||
|
||||
if (NS_UNLIKELY(!mFace)) {
|
||||
// No face. This unfortunate situation might happen if the font
|
||||
// file is (re)moved at the wrong time.
|
||||
aMetrics->emHeight = mGfxFont->GetStyle()->size;
|
||||
aMetrics->emAscent = 0.8 * aMetrics->emHeight;
|
||||
aMetrics->emDescent = 0.2 * aMetrics->emHeight;
|
||||
aMetrics->maxAscent = aMetrics->emAscent;
|
||||
aMetrics->maxDescent = aMetrics->maxDescent;
|
||||
aMetrics->maxHeight = aMetrics->emHeight;
|
||||
aMetrics->internalLeading = 0.0;
|
||||
aMetrics->externalLeading = 0.2 * aMetrics->emHeight;
|
||||
aSpaceGlyph = 0;
|
||||
aMetrics->spaceWidth = 0.5 * aMetrics->emHeight;
|
||||
aMetrics->maxAdvance = aMetrics->spaceWidth;
|
||||
aMetrics->aveCharWidth = aMetrics->spaceWidth;
|
||||
aMetrics->zeroOrAveCharWidth = aMetrics->spaceWidth;
|
||||
aMetrics->xHeight = 0.5 * aMetrics->emHeight;
|
||||
aMetrics->underlineSize = aMetrics->emHeight / 14.0;
|
||||
aMetrics->underlineOffset = -aMetrics->underlineSize;
|
||||
aMetrics->strikeoutOffset = 0.25 * aMetrics->emHeight;
|
||||
aMetrics->strikeoutSize = aMetrics->underlineSize;
|
||||
aMetrics->superscriptOffset = aMetrics->xHeight;
|
||||
aMetrics->subscriptOffset = aMetrics->xHeight;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const FT_Size_Metrics& ftMetrics = mFace->size->metrics;
|
||||
|
||||
gfxFloat emHeight;
|
||||
// Scale for vertical design metric conversion: pixels per design unit.
|
||||
gfxFloat yScale;
|
||||
if (FT_IS_SCALABLE(mFace)) {
|
||||
// Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
|
||||
// have subpixel accuracy.
|
||||
//
|
||||
// FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
|
||||
// (fractional) value is a factor that converts vertical metrics from
|
||||
// design units to units of 1/64 pixels, so that the result may be
|
||||
// interpreted as pixels in 26.6 fixed point format.
|
||||
yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
|
||||
emHeight = mFace->units_per_EM * yScale;
|
||||
} else { // Not scalable.
|
||||
// FT_Size_Metrics doc says x_scale is "only relevant for scalable
|
||||
// font formats".
|
||||
gfxFloat emUnit = mFace->units_per_EM;
|
||||
emHeight = ftMetrics.y_ppem;
|
||||
yScale = emHeight / emUnit;
|
||||
}
|
||||
|
||||
TT_OS2 *os2 =
|
||||
static_cast<TT_OS2*>(FT_Get_Sfnt_Table(mFace, ft_sfnt_os2));
|
||||
|
||||
aMetrics->maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
|
||||
aMetrics->maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
|
||||
aMetrics->maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
|
||||
|
||||
gfxFloat lineHeight;
|
||||
if (os2 && os2->sTypoAscender) {
|
||||
aMetrics->emAscent = os2->sTypoAscender * yScale;
|
||||
aMetrics->emDescent = -os2->sTypoDescender * yScale;
|
||||
FT_Short typoHeight =
|
||||
os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
|
||||
lineHeight = typoHeight * yScale;
|
||||
|
||||
// maxAscent/maxDescent get used for frame heights, and some fonts
|
||||
// don't have the HHEA table ascent/descent set (bug 279032).
|
||||
if (aMetrics->emAscent > aMetrics->maxAscent)
|
||||
aMetrics->maxAscent = aMetrics->emAscent;
|
||||
if (aMetrics->emDescent > aMetrics->maxDescent)
|
||||
aMetrics->maxDescent = aMetrics->emDescent;
|
||||
} else {
|
||||
aMetrics->emAscent = aMetrics->maxAscent;
|
||||
aMetrics->emDescent = aMetrics->maxDescent;
|
||||
lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
|
||||
}
|
||||
|
||||
cairo_text_extents_t extents;
|
||||
*aSpaceGlyph = GetCharExtents(' ', &extents);
|
||||
if (*aSpaceGlyph) {
|
||||
aMetrics->spaceWidth = extents.x_advance;
|
||||
} else {
|
||||
aMetrics->spaceWidth = aMetrics->maxAdvance; // guess
|
||||
}
|
||||
|
||||
aMetrics->zeroOrAveCharWidth = 0.0;
|
||||
if (GetCharExtents('0', &extents)) {
|
||||
aMetrics->zeroOrAveCharWidth = extents.x_advance;
|
||||
}
|
||||
|
||||
// Prefering a measured x over sxHeight because sxHeight doesn't consider
|
||||
// hinting, but maybe the x extents are not quite right in some fancy
|
||||
// script fonts. CSS 2.1 suggests possibly using the height of an "o",
|
||||
// which would have a more consistent glyph across fonts.
|
||||
if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
|
||||
aMetrics->xHeight = -extents.y_bearing;
|
||||
aMetrics->aveCharWidth = extents.x_advance;
|
||||
} else {
|
||||
if (os2 && os2->sxHeight) {
|
||||
aMetrics->xHeight = os2->sxHeight * yScale;
|
||||
} else {
|
||||
// CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
|
||||
// impossible or impractical to determine the x-height, a value of
|
||||
// 0.5em should be used."
|
||||
aMetrics->xHeight = 0.5 * emHeight;
|
||||
}
|
||||
aMetrics->aveCharWidth = 0.0; // updated below
|
||||
}
|
||||
// aveCharWidth is used for the width of text input elements so be
|
||||
// liberal rather than conservative in the estimate.
|
||||
if (os2 && os2->xAvgCharWidth) {
|
||||
// Round to pixels as this is compared with maxAdvance to guess
|
||||
// whether this is a fixed width font.
|
||||
gfxFloat avgCharWidth =
|
||||
ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
|
||||
aMetrics->aveCharWidth =
|
||||
PR_MAX(aMetrics->aveCharWidth, avgCharWidth);
|
||||
}
|
||||
aMetrics->aveCharWidth =
|
||||
PR_MAX(aMetrics->aveCharWidth, aMetrics->zeroOrAveCharWidth);
|
||||
if (aMetrics->aveCharWidth == 0.0) {
|
||||
aMetrics->aveCharWidth = aMetrics->spaceWidth;
|
||||
}
|
||||
if (aMetrics->zeroOrAveCharWidth == 0.0) {
|
||||
aMetrics->zeroOrAveCharWidth = aMetrics->aveCharWidth;
|
||||
}
|
||||
// Apparently hinting can mean that max_advance is not always accurate.
|
||||
aMetrics->maxAdvance =
|
||||
PR_MAX(aMetrics->maxAdvance, aMetrics->aveCharWidth);
|
||||
|
||||
// gfxFont::Metrics::underlineOffset is the position of the top of the
|
||||
// underline.
|
||||
//
|
||||
// FT_FaceRec documentation describes underline_position as "the
|
||||
// center of the underlining stem". This was the original definition
|
||||
// of the PostScript metric, but in the PostScript table of OpenType
|
||||
// fonts the metric is "the top of the underline"
|
||||
// (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
|
||||
// (up to version 2.3.7) doesn't make any adjustment.
|
||||
//
|
||||
// Therefore get the underline position directly from the table
|
||||
// ourselves when this table exists. Use FreeType's metrics for
|
||||
// other (including older PostScript) fonts.
|
||||
if (mFace->underline_position && mFace->underline_thickness) {
|
||||
aMetrics->underlineSize = mFace->underline_thickness * yScale;
|
||||
TT_Postscript *post = static_cast<TT_Postscript*>
|
||||
(FT_Get_Sfnt_Table(mFace, ft_sfnt_post));
|
||||
if (post && post->underlinePosition) {
|
||||
aMetrics->underlineOffset = post->underlinePosition * yScale;
|
||||
} else {
|
||||
aMetrics->underlineOffset = mFace->underline_position * yScale
|
||||
+ 0.5 * aMetrics->underlineSize;
|
||||
}
|
||||
} else { // No underline info.
|
||||
// Imitate Pango.
|
||||
aMetrics->underlineSize = emHeight / 14.0;
|
||||
aMetrics->underlineOffset = -aMetrics->underlineSize;
|
||||
}
|
||||
|
||||
if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition) {
|
||||
aMetrics->strikeoutSize = os2->yStrikeoutSize * yScale;
|
||||
aMetrics->strikeoutOffset = os2->yStrikeoutPosition * yScale;
|
||||
} else { // No strikeout info.
|
||||
aMetrics->strikeoutSize = aMetrics->underlineSize;
|
||||
// Use OpenType spec's suggested position for Roman font.
|
||||
aMetrics->strikeoutOffset = emHeight * 409.0 / 2048.0
|
||||
+ 0.5 * aMetrics->strikeoutSize;
|
||||
}
|
||||
SnapLineToPixels(aMetrics->strikeoutOffset, aMetrics->strikeoutSize);
|
||||
|
||||
if (os2 && os2->ySuperscriptYOffset) {
|
||||
gfxFloat val = ScaleRoundDesignUnits(os2->ySuperscriptYOffset,
|
||||
ftMetrics.y_scale);
|
||||
aMetrics->superscriptOffset = PR_MAX(1.0, val);
|
||||
} else {
|
||||
aMetrics->superscriptOffset = aMetrics->xHeight;
|
||||
}
|
||||
|
||||
if (os2 && os2->ySubscriptYOffset) {
|
||||
gfxFloat val = ScaleRoundDesignUnits(os2->ySubscriptYOffset,
|
||||
ftMetrics.y_scale);
|
||||
// some fonts have the incorrect sign.
|
||||
val = fabs(val);
|
||||
aMetrics->subscriptOffset = PR_MAX(1.0, val);
|
||||
} else {
|
||||
aMetrics->subscriptOffset = aMetrics->xHeight;
|
||||
}
|
||||
|
||||
aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
|
||||
|
||||
// Make the line height an integer number of pixels so that lines will be
|
||||
// equally spaced (rather than just being snapped to pixels, some up and
|
||||
// some down). Layout calculates line height from the emHeight +
|
||||
// internalLeading + externalLeading, but first each of these is rounded
|
||||
// to layout units. To ensure that the result is an integer number of
|
||||
// pixels, round each of the components to pixels.
|
||||
aMetrics->emHeight = NS_floor(emHeight + 0.5);
|
||||
|
||||
// maxHeight will normally be an integer, but round anyway in case
|
||||
// FreeType is configured differently.
|
||||
aMetrics->internalLeading =
|
||||
NS_floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5);
|
||||
|
||||
// Text input boxes currently don't work well with lineHeight
|
||||
// significantly less than maxHeight (with Verdana, for example).
|
||||
lineHeight = NS_floor(PR_MAX(lineHeight, aMetrics->maxHeight) + 0.5);
|
||||
aMetrics->externalLeading =
|
||||
lineHeight - aMetrics->internalLeading - aMetrics->emHeight;
|
||||
|
||||
// Ensure emAscent + emDescent == emHeight
|
||||
gfxFloat sum = aMetrics->emAscent + aMetrics->emDescent;
|
||||
aMetrics->emAscent = sum > 0.0 ?
|
||||
aMetrics->emAscent * aMetrics->emHeight / sum : 0.0;
|
||||
aMetrics->emDescent = aMetrics->emHeight - aMetrics->emAscent;
|
||||
}
|
||||
|
||||
const gfxFont::Metrics&
|
||||
gfxFcFont::GetMetrics()
|
||||
{
|
||||
if (mHasMetrics)
|
||||
return mMetrics;
|
||||
|
||||
if (NS_UNLIKELY(GetStyle()->size <= 0.0)) {
|
||||
new(&mMetrics) gfxFont::Metrics(); // zero initialize
|
||||
mSpaceGlyph = 0;
|
||||
} else {
|
||||
LockedFTFace(this).GetMetrics(&mMetrics, &mSpaceGlyph);
|
||||
}
|
||||
|
||||
SanitizeMetrics(&mMetrics, PR_FALSE);
|
||||
|
||||
#if 0
|
||||
// printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
|
||||
// printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
|
||||
|
||||
fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
|
||||
fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
|
||||
fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
|
||||
fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
|
||||
fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
|
||||
fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f suOff: %f suSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset);
|
||||
#endif
|
||||
|
||||
mHasMetrics = PR_TRUE;
|
||||
return mMetrics;
|
||||
}
|
||||
|
||||
nsString
|
||||
gfxFcFont::GetUniqueName()
|
||||
{
|
||||
return GetName();
|
||||
}
|
||||
|
||||
/**
|
||||
** gfxTextRun
|
||||
*
|
||||
|
@ -3113,47 +2664,6 @@ CreateScaledFont(FcPattern *aPattern)
|
|||
return scaledFont;
|
||||
}
|
||||
|
||||
PRBool
|
||||
gfxFcFont::SetupCairoFont(gfxContext *aContext)
|
||||
{
|
||||
cairo_t *cr = aContext->GetCairo();
|
||||
|
||||
// The scaled font ctm is not relevant right here because
|
||||
// cairo_set_scaled_font does not record the scaled font itself, but
|
||||
// merely the font_face, font_matrix, font_options. The scaled_font used
|
||||
// for the target can be different from the scaled_font passed to
|
||||
// cairo_set_scaled_font. (Unfortunately we have measured only for an
|
||||
// identity ctm.)
|
||||
cairo_scaled_font_t *cairoFont = CairoScaledFont();
|
||||
|
||||
if (cairo_scaled_font_status(cairoFont) != 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;
|
||||
}
|
||||
// Thoughts on which font_options to set on the context:
|
||||
//
|
||||
// cairoFont has been created for screen rendering.
|
||||
//
|
||||
// When the context is being used for screen rendering, we should set
|
||||
// font_options such that the same scaled_font gets used (when the ctm is
|
||||
// the same). The use of explicit font_options recorded in
|
||||
// CreateScaledFont ensures that this will happen.
|
||||
//
|
||||
// XXXkt: For pdf and ps surfaces, I don't know whether it's better to
|
||||
// remove surface-specific options, or try to draw with the same
|
||||
// scaled_font that was used to measure. As the same font_face is being
|
||||
// used, its font_options will often override some values anyway (unless
|
||||
// perhaps we remove those from the FcPattern at face creation).
|
||||
//
|
||||
// I can't see any significant difference in printing, irrespective of
|
||||
// what is set here. It's too late to change things here as measuring has
|
||||
// already taken place. We should really be measuring with a different
|
||||
// font for pdf and ps surfaces (bug 403513).
|
||||
cairo_set_scaled_font(cr, cairoFont);
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8Length,
|
||||
PRUint32 aUTF16Offset, PangoAnalysis *aAnalysis)
|
||||
|
|
Загрузка…
Ссылка в новой задаче