2007-03-22 10:30:00 -07:00
|
|
|
/* -*- 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>
|
2008-08-05 21:34:06 -07:00
|
|
|
* John Daggett <jdaggett@mozilla.com>
|
2007-03-22 10:30:00 -07:00
|
|
|
*
|
|
|
|
* 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 ***** */
|
|
|
|
|
2009-04-04 02:43:42 -07:00
|
|
|
#include "nsIPrefService.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#include "nsReadableUtils.h"
|
2007-04-03 20:32:43 -07:00
|
|
|
#include "nsExpirationTracker.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "gfxFont.h"
|
|
|
|
#include "gfxPlatform.h"
|
|
|
|
|
|
|
|
#include "prtypes.h"
|
|
|
|
#include "gfxTypes.h"
|
|
|
|
#include "gfxContext.h"
|
2007-03-26 20:24:49 -07:00
|
|
|
#include "gfxFontMissingGlyphs.h"
|
2008-09-30 20:01:53 -07:00
|
|
|
#include "gfxUserFontSet.h"
|
2007-05-08 15:46:14 -07:00
|
|
|
#include "nsMathUtils.h"
|
2008-09-30 20:01:53 -07:00
|
|
|
#include "nsBidiUtils.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "cairo.h"
|
|
|
|
#include "gfxFontTest.h"
|
|
|
|
|
|
|
|
#include "nsCRT.h"
|
|
|
|
|
2007-04-03 20:32:43 -07:00
|
|
|
gfxFontCache *gfxFontCache::gGlobalCache = nsnull;
|
|
|
|
|
2007-07-01 18:24:56 -07:00
|
|
|
#ifdef DEBUG_roc
|
|
|
|
#define DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
2007-07-01 18:12:45 -07:00
|
|
|
static PRUint32 gTextRunStorageHighWaterMark = 0;
|
|
|
|
static PRUint32 gTextRunStorage = 0;
|
2007-10-01 18:47:40 -07:00
|
|
|
static PRUint32 gFontCount = 0;
|
|
|
|
static PRUint32 gGlyphExtentsCount = 0;
|
|
|
|
static PRUint32 gGlyphExtentsWidthsTotalSize = 0;
|
|
|
|
static PRUint32 gGlyphExtentsSetupEagerSimple = 0;
|
|
|
|
static PRUint32 gGlyphExtentsSetupEagerTight = 0;
|
|
|
|
static PRUint32 gGlyphExtentsSetupLazyTight = 0;
|
|
|
|
static PRUint32 gGlyphExtentsSetupFallBackToTight = 0;
|
2007-07-01 18:12:45 -07:00
|
|
|
#endif
|
|
|
|
|
2008-09-30 20:01:53 -07:00
|
|
|
gfxFontEntry::~gfxFontEntry()
|
|
|
|
{
|
|
|
|
if (mUserFontData)
|
|
|
|
delete mUserFontData;
|
2008-08-05 21:34:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRBool gfxFontEntry::TestCharacterMap(PRUint32 aCh) {
|
|
|
|
if (!mCmapInitialized) ReadCMAP();
|
|
|
|
return mCharacterMap.test(aCh);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gfxFontEntry *gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, PRBool& aNeedsBold)
|
|
|
|
{
|
|
|
|
gfxFontEntry *weightList[10] = { 0 };
|
|
|
|
|
|
|
|
aNeedsBold = PR_FALSE;
|
|
|
|
|
2008-09-30 20:01:53 -07:00
|
|
|
PRBool foundWeights = FindWeightsForStyle(weightList, aFontStyle);
|
|
|
|
if (!foundWeights)
|
|
|
|
return nsnull;
|
2008-08-05 21:34:06 -07:00
|
|
|
|
|
|
|
PRInt8 baseWeight, weightDistance;
|
|
|
|
aFontStyle.ComputeWeightAndOffset(&baseWeight, &weightDistance);
|
|
|
|
|
|
|
|
// 500 isn't quite bold so we want to treat it as 400 if we don't
|
|
|
|
// have a 500 weight
|
|
|
|
if (baseWeight == 5 && weightDistance == 0) {
|
|
|
|
// If we have a 500 weight then use it
|
|
|
|
if (weightList[5])
|
|
|
|
return weightList[5];
|
|
|
|
|
|
|
|
// Otherwise treat as 400
|
|
|
|
baseWeight = 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt8 matchBaseWeight = 0;
|
|
|
|
PRInt8 direction = (baseWeight > 5) ? 1 : -1;
|
|
|
|
for (PRInt8 i = baseWeight; ; i += direction) {
|
|
|
|
if (weightList[i]) {
|
|
|
|
matchBaseWeight = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we've reached one side without finding a font,
|
|
|
|
// go the other direction until we find a match
|
|
|
|
if (i == 1 || i == 9)
|
|
|
|
direction = -direction;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontEntry *matchFE;
|
|
|
|
const PRInt8 absDistance = abs(weightDistance);
|
|
|
|
direction = (weightDistance >= 0) ? 1 : -1;
|
2009-07-23 14:22:24 -07:00
|
|
|
PRInt8 i, wghtSteps = 0;
|
|
|
|
|
|
|
|
// account for synthetic bold in lighter case
|
|
|
|
// if lighter is applied with an inherited bold weight,
|
|
|
|
// and no actual bold faces exist, synthetic bold is used
|
|
|
|
// so the matched weight above is actually one step down already
|
|
|
|
if (weightDistance < 0 && baseWeight > 5 && matchBaseWeight < 6) {
|
|
|
|
wghtSteps = 1; // if no faces [600, 900] then synthetic bold at 700
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = matchBaseWeight; i < 10 && i > 0; i += direction) {
|
2008-08-05 21:34:06 -07:00
|
|
|
if (weightList[i]) {
|
|
|
|
matchFE = weightList[i];
|
2009-07-23 14:22:24 -07:00
|
|
|
wghtSteps++;
|
2008-08-05 21:34:06 -07:00
|
|
|
}
|
2009-07-23 14:22:24 -07:00
|
|
|
if (wghtSteps > absDistance)
|
2008-08-05 21:34:06 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-07-23 14:22:24 -07:00
|
|
|
if (weightDistance > 0 && wghtSteps <= absDistance) {
|
2008-08-05 21:34:06 -07:00
|
|
|
aNeedsBold = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!matchFE)
|
|
|
|
matchFE = weightList[matchBaseWeight];
|
|
|
|
|
|
|
|
NS_ASSERTION(matchFE, "we should always be able to return something here");
|
|
|
|
return matchFE;
|
|
|
|
}
|
|
|
|
|
2007-04-03 20:32:43 -07:00
|
|
|
nsresult
|
|
|
|
gfxFontCache::Init()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(!gGlobalCache, "Where did this come from?");
|
|
|
|
gGlobalCache = new gfxFontCache();
|
|
|
|
return gGlobalCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontCache::Shutdown()
|
|
|
|
{
|
|
|
|
delete gGlobalCache;
|
|
|
|
gGlobalCache = nsnull;
|
2007-07-01 18:12:45 -07:00
|
|
|
|
2007-07-01 18:24:56 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
2007-07-01 18:12:45 -07:00
|
|
|
printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
|
2007-10-01 18:47:40 -07:00
|
|
|
printf("Total number of fonts=%d\n", gFontCount);
|
|
|
|
printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
|
|
|
|
int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
|
|
|
|
printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
|
|
|
|
printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
|
|
|
|
printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
|
|
|
|
printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
|
|
|
|
printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
|
2007-07-01 18:12:45 -07:00
|
|
|
#endif
|
2007-04-03 20:32:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
|
|
|
|
{
|
|
|
|
return aKey->mString.Equals(mFont->GetName()) &&
|
|
|
|
aKey->mStyle->Equals(*mFont->GetStyle());
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<gfxFont>
|
|
|
|
gfxFontCache::Lookup(const nsAString &aName,
|
|
|
|
const gfxFontStyle *aStyle)
|
|
|
|
{
|
|
|
|
Key key(aName, aStyle);
|
|
|
|
HashEntry *entry = mFonts.GetEntry(key);
|
|
|
|
if (!entry)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
gfxFont *font = entry->mFont;
|
|
|
|
NS_ADDREF(font);
|
|
|
|
return font;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontCache::AddNew(gfxFont *aFont)
|
|
|
|
{
|
|
|
|
Key key(aFont->GetName(), aFont->GetStyle());
|
|
|
|
HashEntry *entry = mFonts.PutEntry(key);
|
|
|
|
if (!entry)
|
|
|
|
return;
|
2008-01-21 19:11:46 -08:00
|
|
|
gfxFont *oldFont = entry->mFont;
|
2007-04-03 20:32:43 -07:00
|
|
|
entry->mFont = aFont;
|
2008-01-21 19:11:46 -08:00
|
|
|
// If someone's asked us to replace an existing font entry, then that's a
|
|
|
|
// bit weird, but let it happen, and expire the old font if it's not used.
|
|
|
|
if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
|
|
|
|
// if oldFont == aFont, recount should be > 0,
|
|
|
|
// so we shouldn't be here.
|
|
|
|
NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
|
|
|
|
NotifyExpired(oldFont);
|
|
|
|
}
|
2007-04-03 20:32:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontCache::NotifyReleased(gfxFont *aFont)
|
|
|
|
{
|
|
|
|
nsresult rv = AddObject(aFont);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// We couldn't track it for some reason. Kill it now.
|
|
|
|
DestroyFont(aFont);
|
|
|
|
}
|
|
|
|
// Note that we might have fonts that aren't in the hashtable, perhaps because
|
|
|
|
// of OOM adding to the hashtable or because someone did an AddNew where
|
|
|
|
// we already had a font. These fonts are added to the expiration tracker
|
|
|
|
// anyway, even though Lookup can't resurrect them. Eventually they will
|
|
|
|
// expire and be deleted.
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontCache::NotifyExpired(gfxFont *aFont)
|
|
|
|
{
|
|
|
|
RemoveObject(aFont);
|
|
|
|
DestroyFont(aFont);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontCache::DestroyFont(gfxFont *aFont)
|
|
|
|
{
|
|
|
|
Key key(aFont->GetName(), aFont->GetStyle());
|
2008-01-21 19:11:46 -08:00
|
|
|
HashEntry *entry = mFonts.GetEntry(key);
|
|
|
|
if (entry && entry->mFont == aFont)
|
|
|
|
mFonts.RemoveEntry(key);
|
|
|
|
NS_ASSERTION(aFont->GetRefCount() == 0,
|
|
|
|
"Destroying with non-zero ref count!");
|
2007-04-03 20:32:43 -07:00
|
|
|
delete aFont;
|
|
|
|
}
|
|
|
|
|
2008-08-12 02:34:52 -07:00
|
|
|
void
|
|
|
|
gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, PRBool aOtherIsOnLeft)
|
|
|
|
{
|
|
|
|
mAscent = PR_MAX(mAscent, aOther.mAscent);
|
|
|
|
mDescent = PR_MAX(mDescent, aOther.mDescent);
|
|
|
|
if (aOtherIsOnLeft) {
|
|
|
|
mBoundingBox =
|
|
|
|
(mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
|
|
|
|
} else {
|
|
|
|
mBoundingBox =
|
|
|
|
mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
|
|
|
|
}
|
|
|
|
mAdvanceWidth += aOther.mAdvanceWidth;
|
|
|
|
}
|
|
|
|
|
2008-08-05 21:34:06 -07:00
|
|
|
gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle) :
|
2008-09-15 21:38:20 -07:00
|
|
|
mFontEntry(aFontEntry), mIsValid(PR_TRUE), mStyle(*aFontStyle), mSyntheticBoldOffset(0)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-10-01 18:47:40 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
++gFontCount;
|
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
gfxFont::~gfxFont()
|
|
|
|
{
|
|
|
|
PRUint32 i;
|
|
|
|
// We destroy the contents of mGlyphExtentsArray explicitly instead of
|
|
|
|
// using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
|
|
|
|
// of classes that lack a proper copy constructor
|
|
|
|
for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
|
|
|
|
delete mGlyphExtentsArray[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/**
|
|
|
|
* A helper function in case we need to do any rounding or other
|
|
|
|
* processing here.
|
|
|
|
*/
|
2008-09-03 19:38:32 -07:00
|
|
|
#define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
|
|
|
|
(double(aAppUnits)*double(aDevUnitsPerAppUnit))
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-17 16:13:48 -07:00
|
|
|
struct GlyphBuffer {
|
|
|
|
#define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t))
|
|
|
|
cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE];
|
|
|
|
unsigned int mNumGlyphs;
|
|
|
|
|
|
|
|
GlyphBuffer()
|
|
|
|
: mNumGlyphs(0) { }
|
|
|
|
|
|
|
|
cairo_glyph_t *AppendGlyph() {
|
|
|
|
return &mGlyphBuffer[mNumGlyphs++];
|
|
|
|
}
|
|
|
|
|
2008-09-03 19:38:32 -07:00
|
|
|
void Flush(cairo_t *aCR, PRBool aDrawToPath, PRBool aReverse,
|
|
|
|
PRBool aFinish = PR_FALSE) {
|
|
|
|
// Ensure there's enough room for at least two glyphs in the
|
|
|
|
// buffer (because we may allocate two glyphs between flushes)
|
|
|
|
if (!aFinish && mNumGlyphs + 2 <= GLYPH_BUFFER_SIZE)
|
2007-07-17 16:13:48 -07:00
|
|
|
return;
|
|
|
|
|
2008-09-03 19:38:32 -07:00
|
|
|
if (aReverse) {
|
|
|
|
for (PRUint32 i = 0; i < mNumGlyphs/2; ++i) {
|
|
|
|
cairo_glyph_t tmp = mGlyphBuffer[i];
|
|
|
|
mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i];
|
|
|
|
mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aDrawToPath)
|
|
|
|
cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
|
2007-07-17 16:13:48 -07:00
|
|
|
else
|
2008-09-03 19:38:32 -07:00
|
|
|
cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
|
2007-07-17 16:13:48 -07:00
|
|
|
|
|
|
|
mNumGlyphs = 0;
|
|
|
|
}
|
|
|
|
#undef GLYPH_BUFFER_SIZE
|
|
|
|
};
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
|
|
|
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aPt,
|
|
|
|
Spacing *aSpacing)
|
|
|
|
{
|
|
|
|
if (aStart >= aEnd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
|
|
|
|
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
|
|
|
|
const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
|
|
|
|
PRBool isRTL = aTextRun->IsRightToLeft();
|
|
|
|
double direction = aTextRun->GetDirection();
|
2008-09-03 19:38:32 -07:00
|
|
|
// double-strike in direction of run
|
|
|
|
double synBoldDevUnitOffsetAppUnits =
|
|
|
|
direction * (double) mSyntheticBoldOffset * appUnitsPerDevUnit;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 i;
|
|
|
|
// Current position in appunits
|
|
|
|
double x = aPt->x;
|
|
|
|
double y = aPt->y;
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
PRBool success = SetupCairoFont(aContext);
|
2007-08-06 05:30:13 -07:00
|
|
|
if (NS_UNLIKELY(!success))
|
|
|
|
return;
|
2007-07-17 16:13:48 -07:00
|
|
|
|
|
|
|
GlyphBuffer glyphs;
|
|
|
|
cairo_glyph_t *glyph;
|
2007-09-23 19:19:14 -07:00
|
|
|
cairo_t *cr = aContext->GetCairo();
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aSpacing) {
|
|
|
|
x += direction*aSpacing[0].mBefore;
|
|
|
|
}
|
|
|
|
for (i = aStart; i < aEnd; ++i) {
|
|
|
|
const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
|
|
|
|
if (glyphData->IsSimpleGlyph()) {
|
2007-07-17 16:13:48 -07:00
|
|
|
glyph = glyphs.AppendGlyph();
|
2007-03-22 10:30:00 -07:00
|
|
|
glyph->index = glyphData->GetSimpleGlyph();
|
|
|
|
double advance = glyphData->GetSimpleAdvance();
|
|
|
|
// Perhaps we should put a scale in the cairo context instead of
|
|
|
|
// doing this scaling here...
|
|
|
|
// Multiplying by the reciprocal may introduce tiny error here,
|
|
|
|
// but we assume cairo is going to round coordinates at some stage
|
|
|
|
// and this is faster
|
2008-09-03 19:38:32 -07:00
|
|
|
double glyphX;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (isRTL) {
|
|
|
|
x -= advance;
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphX = x;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphX = x;
|
2007-03-22 10:30:00 -07:00
|
|
|
x += advance;
|
|
|
|
}
|
2008-09-03 19:38:32 -07:00
|
|
|
glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
|
|
|
|
glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
|
2008-03-19 17:02:21 -07:00
|
|
|
|
|
|
|
// synthetic bolding by drawing with a one-pixel offset
|
|
|
|
if (mSyntheticBoldOffset) {
|
|
|
|
cairo_glyph_t *doubleglyph;
|
|
|
|
doubleglyph = glyphs.AppendGlyph();
|
|
|
|
doubleglyph->index = glyph->index;
|
2008-09-03 19:38:32 -07:00
|
|
|
doubleglyph->x =
|
|
|
|
ToDeviceUnits(glyphX + synBoldDevUnitOffsetAppUnits,
|
|
|
|
devUnitsPerAppUnit);
|
2008-03-19 17:02:21 -07:00
|
|
|
doubleglyph->y = glyph->y;
|
|
|
|
}
|
|
|
|
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphs.Flush(cr, aDrawToPath, isRTL);
|
2007-11-08 22:27:23 -08:00
|
|
|
} else {
|
|
|
|
PRUint32 j;
|
|
|
|
PRUint32 glyphCount = glyphData->GetGlyphCount();
|
2007-11-07 23:41:06 -08:00
|
|
|
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
|
2007-11-08 22:27:23 -08:00
|
|
|
for (j = 0; j < glyphCount; ++j, ++details) {
|
2007-11-07 23:41:06 -08:00
|
|
|
double advance = details->mAdvance;
|
2007-11-08 22:27:23 -08:00
|
|
|
if (glyphData->IsMissing()) {
|
|
|
|
if (!aDrawToPath) {
|
2008-09-03 19:38:32 -07:00
|
|
|
double glyphX = x;
|
2007-11-08 22:27:23 -08:00
|
|
|
if (isRTL) {
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphX -= advance;
|
2007-11-08 22:27:23 -08:00
|
|
|
}
|
2008-09-03 19:38:32 -07:00
|
|
|
gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
|
|
|
|
ToDeviceUnits(y, devUnitsPerAppUnit));
|
|
|
|
gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
|
2007-11-08 22:27:23 -08:00
|
|
|
gfxFloat height = GetMetrics().maxAscent;
|
|
|
|
gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
|
|
|
|
gfxFontMissingGlyphs::DrawMissingGlyph(aContext, glyphRect, details->mGlyphID);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
glyph = glyphs.AppendGlyph();
|
|
|
|
glyph->index = details->mGlyphID;
|
2008-09-03 19:38:32 -07:00
|
|
|
double glyphX = x + details->mXOffset;
|
2007-03-26 20:24:49 -07:00
|
|
|
if (isRTL) {
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphX -= advance;
|
2007-03-26 20:24:49 -07:00
|
|
|
}
|
2008-09-03 19:38:32 -07:00
|
|
|
glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
|
|
|
|
glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
|
2008-03-19 17:02:21 -07:00
|
|
|
|
|
|
|
// synthetic bolding by drawing with a one-pixel offset
|
|
|
|
if (mSyntheticBoldOffset) {
|
|
|
|
cairo_glyph_t *doubleglyph;
|
|
|
|
doubleglyph = glyphs.AppendGlyph();
|
|
|
|
doubleglyph->index = glyph->index;
|
2008-09-03 19:38:32 -07:00
|
|
|
doubleglyph->x =
|
|
|
|
ToDeviceUnits(glyphX + synBoldDevUnitOffsetAppUnits,
|
|
|
|
devUnitsPerAppUnit);
|
2008-03-19 17:02:21 -07:00
|
|
|
doubleglyph->y = glyph->y;
|
|
|
|
}
|
|
|
|
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphs.Flush(cr, aDrawToPath, isRTL);
|
2007-03-26 20:24:49 -07:00
|
|
|
}
|
|
|
|
x += direction*advance;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-11-08 22:27:23 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aSpacing) {
|
|
|
|
double space = aSpacing[i - aStart].mAfter;
|
|
|
|
if (i + 1 < aEnd) {
|
|
|
|
space += aSpacing[i + 1 - aStart].mBefore;
|
|
|
|
}
|
|
|
|
x += direction*space;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gfxFontTestStore::CurrentStore()) {
|
2007-07-17 16:13:48 -07:00
|
|
|
/* This assumes that the tests won't have anything that results
|
|
|
|
* in more than GLYPH_BUFFER_SIZE glyphs. Do this before we
|
|
|
|
* flush, since that'll blow away the num_glyphs.
|
|
|
|
*/
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxFontTestStore::CurrentStore()->AddItem(GetUniqueName(),
|
2007-07-17 16:13:48 -07:00
|
|
|
glyphs.mGlyphBuffer, glyphs.mNumGlyphs);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-07-17 16:13:48 -07:00
|
|
|
|
|
|
|
// draw any remaining glyphs
|
2008-09-03 19:38:32 -07:00
|
|
|
glyphs.Flush(cr, aDrawToPath, isRTL, PR_TRUE);
|
2007-07-17 16:13:48 -07:00
|
|
|
|
|
|
|
*aPt = gfxPoint(x, y);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
static PRInt32
|
|
|
|
GetAdvanceForGlyphs(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd)
|
|
|
|
{
|
|
|
|
const gfxTextRun::CompressedGlyph *glyphData = aTextRun->GetCharacterGlyphs() + aStart;
|
|
|
|
PRInt32 advance = 0;
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = aStart; i < aEnd; ++i, ++glyphData) {
|
|
|
|
if (glyphData->IsSimpleGlyph()) {
|
|
|
|
advance += glyphData->GetSimpleAdvance();
|
|
|
|
} else {
|
|
|
|
PRUint32 glyphCount = glyphData->GetGlyphCount();
|
|
|
|
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
|
|
|
|
PRUint32 j;
|
|
|
|
for (j = 0; j < glyphCount; ++j, ++details) {
|
|
|
|
advance += details->mAdvance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return advance;
|
|
|
|
}
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
static void
|
2008-08-08 01:24:22 -07:00
|
|
|
UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
|
2007-09-23 19:19:14 -07:00
|
|
|
{
|
2008-08-08 01:24:22 -07:00
|
|
|
*aDestMin = PR_MIN(*aDestMin, aX);
|
|
|
|
*aDestMax = PR_MAX(*aDestMax, aX);
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
|
|
|
|
2009-06-24 02:04:20 -07:00
|
|
|
// We get precise glyph extents if the textrun creator requested them, or
|
|
|
|
// if the font is a user font --- in which case the author may be relying
|
|
|
|
// on overflowing glyphs.
|
|
|
|
static PRBool
|
|
|
|
NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
|
|
|
|
{
|
|
|
|
return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
|
|
|
|
aFont->GetFontEntry()->IsUserFont();
|
|
|
|
}
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
static PRBool
|
|
|
|
NeedsGlyphExtents(gfxTextRun *aTextRun)
|
|
|
|
{
|
2009-06-24 02:04:20 -07:00
|
|
|
if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
|
|
|
|
return PR_TRUE;
|
|
|
|
PRUint32 numRuns;
|
|
|
|
const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
|
|
|
|
for (PRUint32 i = 0; i < numRuns; ++i) {
|
|
|
|
if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxFont::RunMetrics
|
|
|
|
gfxFont::Measure(gfxTextRun *aTextRun,
|
|
|
|
PRUint32 aStart, PRUint32 aEnd,
|
2009-02-24 00:32:58 -08:00
|
|
|
BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
|
2007-03-22 10:30:00 -07:00
|
|
|
Spacing *aSpacing)
|
|
|
|
{
|
2007-09-23 19:19:14 -07:00
|
|
|
const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
|
|
|
|
// Current position in appunits
|
|
|
|
const gfxFont::Metrics& fontMetrics = GetMetrics();
|
|
|
|
|
|
|
|
RunMetrics metrics;
|
|
|
|
metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
|
|
|
|
metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
|
2007-11-30 22:52:47 -08:00
|
|
|
if (aStart == aEnd) {
|
2008-08-08 01:24:22 -07:00
|
|
|
// exit now before we look at aSpacing[0], which is undefined
|
|
|
|
metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
|
|
|
|
return metrics;
|
2007-11-30 22:52:47 -08:00
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
|
2008-08-08 01:24:22 -07:00
|
|
|
gfxFloat advanceMin = 0, advanceMax = 0;
|
2007-11-30 22:52:47 -08:00
|
|
|
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
|
|
|
|
PRBool isRTL = aTextRun->IsRightToLeft();
|
|
|
|
double direction = aTextRun->GetDirection();
|
2009-06-24 02:04:20 -07:00
|
|
|
PRBool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
|
2007-09-23 19:19:14 -07:00
|
|
|
gfxGlyphExtents *extents =
|
2009-02-24 00:32:58 -08:00
|
|
|
(aBoundingBoxType == LOOSE_INK_EXTENTS &&
|
2009-06-24 02:04:20 -07:00
|
|
|
!needsGlyphExtents &&
|
2009-02-24 00:32:58 -08:00
|
|
|
!aTextRun->HasDetailedGlyphs()) ? nsnull
|
2007-09-23 19:19:14 -07:00
|
|
|
: GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
|
|
|
|
double x = 0;
|
|
|
|
if (aSpacing) {
|
|
|
|
x += direction*aSpacing[0].mBefore;
|
|
|
|
}
|
|
|
|
PRUint32 i;
|
2007-03-22 10:30:00 -07:00
|
|
|
for (i = aStart; i < aEnd; ++i) {
|
2007-09-23 19:19:14 -07:00
|
|
|
const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
|
|
|
|
if (glyphData->IsSimpleGlyph()) {
|
|
|
|
double advance = glyphData->GetSimpleAdvance();
|
|
|
|
// Only get the real glyph horizontal extent if we were asked
|
|
|
|
// for the tight bounding box or we're in quality mode
|
2009-06-24 02:04:20 -07:00
|
|
|
if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
|
|
|
|
extents) {
|
2007-09-23 19:19:14 -07:00
|
|
|
PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
|
|
|
|
PRUint16 extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
|
2009-02-24 00:32:58 -08:00
|
|
|
if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
|
|
|
|
aBoundingBoxType == LOOSE_INK_EXTENTS) {
|
2008-08-08 01:24:22 -07:00
|
|
|
UnionRange(x, &advanceMin, &advanceMax);
|
|
|
|
UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
|
2007-09-23 19:19:14 -07:00
|
|
|
} else {
|
2007-12-20 23:09:50 -08:00
|
|
|
gfxRect glyphRect;
|
|
|
|
if (!extents->GetTightGlyphExtentsAppUnits(this,
|
|
|
|
aRefContext, glyphIndex, &glyphRect)) {
|
|
|
|
glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
|
|
|
|
advance, metrics.mBoundingBox.Height());
|
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
if (isRTL) {
|
|
|
|
glyphRect.pos.x -= advance;
|
|
|
|
}
|
|
|
|
glyphRect.pos.x += x;
|
|
|
|
metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
|
2007-09-22 05:43:52 -07:00
|
|
|
}
|
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
x += direction*advance;
|
2007-11-08 22:27:23 -08:00
|
|
|
} else {
|
|
|
|
PRUint32 glyphCount = glyphData->GetGlyphCount();
|
2007-09-23 19:19:14 -07:00
|
|
|
const gfxTextRun::DetailedGlyph *details = aTextRun->GetDetailedGlyphs(i);
|
2007-11-08 22:27:23 -08:00
|
|
|
PRUint32 j;
|
|
|
|
for (j = 0; j < glyphCount; ++j, ++details) {
|
2007-09-23 19:19:14 -07:00
|
|
|
PRUint32 glyphIndex = details->mGlyphID;
|
|
|
|
gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
|
|
|
|
double advance = details->mAdvance;
|
2007-12-20 23:09:50 -08:00
|
|
|
gfxRect glyphRect;
|
|
|
|
if (glyphData->IsMissing() || !extents ||
|
|
|
|
!extents->GetTightGlyphExtentsAppUnits(this,
|
|
|
|
aRefContext, glyphIndex, &glyphRect)) {
|
|
|
|
// We might have failed to get glyph extents due to
|
|
|
|
// OOM or something
|
2008-08-08 01:24:22 -07:00
|
|
|
glyphRect = gfxRect(0, -metrics.mAscent,
|
|
|
|
advance, metrics.mAscent + metrics.mDescent);
|
2007-12-20 23:09:50 -08:00
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
if (isRTL) {
|
|
|
|
glyphRect.pos.x -= advance;
|
|
|
|
}
|
|
|
|
glyphRect.pos.x += x;
|
|
|
|
metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
|
|
|
|
x += direction*advance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Every other glyph type is ignored
|
|
|
|
if (aSpacing) {
|
|
|
|
double space = aSpacing[i - aStart].mAfter;
|
|
|
|
if (i + 1 < aEnd) {
|
|
|
|
space += aSpacing[i + 1 - aStart].mBefore;
|
|
|
|
}
|
|
|
|
x += direction*space;
|
2007-09-22 05:43:52 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-02-24 00:32:58 -08:00
|
|
|
if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
|
2008-08-08 01:24:22 -07:00
|
|
|
UnionRange(x, &advanceMin, &advanceMax);
|
|
|
|
gfxRect fontBox(advanceMin, -metrics.mAscent,
|
|
|
|
advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
|
|
|
|
metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
|
2007-09-22 05:43:52 -07:00
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
if (isRTL) {
|
|
|
|
metrics.mBoundingBox.pos.x -= x;
|
|
|
|
}
|
|
|
|
|
|
|
|
metrics.mAdvanceWidth = x*direction;
|
2007-09-22 06:28:16 -07:00
|
|
|
return metrics;
|
2007-09-22 05:43:52 -07:00
|
|
|
}
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
gfxGlyphExtents *
|
|
|
|
gfxFont::GetOrCreateGlyphExtents(PRUint32 aAppUnitsPerDevUnit) {
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
|
|
|
|
if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
|
|
|
|
return mGlyphExtentsArray[i];
|
|
|
|
}
|
|
|
|
gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
|
|
|
|
if (glyphExtents) {
|
|
|
|
mGlyphExtentsArray.AppendElement(glyphExtents);
|
|
|
|
// Initialize the extents of a space glyph, assuming that spaces don't
|
|
|
|
// render anything!
|
|
|
|
glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
|
|
|
|
}
|
|
|
|
return glyphExtents;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeedTight,
|
|
|
|
gfxGlyphExtents *aExtents)
|
|
|
|
{
|
2008-04-08 06:12:04 -07:00
|
|
|
gfxMatrix matrix = aContext->CurrentMatrix();
|
|
|
|
aContext->IdentityMatrix();
|
2007-09-23 19:19:14 -07:00
|
|
|
cairo_glyph_t glyph;
|
|
|
|
glyph.index = aGlyphID;
|
|
|
|
glyph.x = 0;
|
|
|
|
glyph.y = 0;
|
|
|
|
cairo_text_extents_t extents;
|
|
|
|
cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
|
2008-04-08 06:12:04 -07:00
|
|
|
aContext->SetMatrix(matrix);
|
2007-09-23 19:19:14 -07:00
|
|
|
|
|
|
|
const Metrics& fontMetrics = GetMetrics();
|
|
|
|
PRUint32 appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
|
|
|
|
if (!aNeedTight && extents.x_bearing >= 0 &&
|
|
|
|
extents.y_bearing >= -fontMetrics.maxAscent &&
|
|
|
|
extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
|
|
|
|
PRUint32 appUnitsWidth =
|
|
|
|
PRUint32(NS_ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
|
|
|
|
if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
|
|
|
|
aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, PRUint16(appUnitsWidth));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2007-10-01 18:47:40 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
if (!aNeedTight) {
|
|
|
|
++gGlyphExtentsSetupFallBackToTight;
|
|
|
|
}
|
|
|
|
#endif
|
2007-09-23 19:19:14 -07:00
|
|
|
|
|
|
|
double d2a = appUnitsPerDevUnit;
|
|
|
|
gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
|
|
|
|
extents.width*d2a, extents.height*d2a);
|
|
|
|
aExtents->SetTightGlyphExtents(aGlyphID, bounds);
|
|
|
|
}
|
|
|
|
|
2008-02-15 13:31:38 -08:00
|
|
|
void
|
2008-03-12 19:36:58 -07:00
|
|
|
gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont)
|
2008-02-15 13:31:38 -08:00
|
|
|
{
|
2008-03-29 14:25:15 -07:00
|
|
|
// Even if this font size is zero, this font is created with non-zero size.
|
|
|
|
// However, for layout and others, we should return the metrics of zero size font.
|
|
|
|
if (mStyle.size == 0) {
|
|
|
|
memset(aMetrics, 0, sizeof(gfxFont::Metrics));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-02-15 13:31:38 -08:00
|
|
|
// MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
|
|
|
|
// If the values are not suitable, we should use x-height instead of them.
|
|
|
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
|
|
|
|
if (aMetrics->superscriptOffset == 0 ||
|
|
|
|
aMetrics->superscriptOffset >= aMetrics->maxAscent) {
|
|
|
|
aMetrics->superscriptOffset = aMetrics->xHeight;
|
|
|
|
}
|
|
|
|
// And also checking the case of sub script offset. The old gfx for win has checked this too.
|
|
|
|
if (aMetrics->subscriptOffset == 0 ||
|
|
|
|
aMetrics->subscriptOffset >= aMetrics->maxAscent) {
|
|
|
|
aMetrics->subscriptOffset = aMetrics->xHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
aMetrics->underlineSize = PR_MAX(1.0, aMetrics->underlineSize);
|
|
|
|
aMetrics->strikeoutSize = PR_MAX(1.0, aMetrics->strikeoutSize);
|
|
|
|
|
|
|
|
aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -1.0);
|
|
|
|
|
2008-04-12 01:54:18 -07:00
|
|
|
if (aMetrics->maxAscent < 1.0) {
|
|
|
|
// We cannot draw strikeout line and overline in the ascent...
|
|
|
|
aMetrics->underlineSize = 0;
|
|
|
|
aMetrics->underlineOffset = 0;
|
|
|
|
aMetrics->strikeoutSize = 0;
|
|
|
|
aMetrics->strikeoutOffset = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-12 19:36:58 -07:00
|
|
|
/**
|
|
|
|
* Some CJK fonts have bad underline offset. Therefore, if this is such font,
|
|
|
|
* we need to lower the underline offset to bottom of *em* descent.
|
|
|
|
* However, if this is system font, we should not do this for the rendering compatibility with
|
|
|
|
* another application's UI on the platform.
|
2008-04-12 01:54:18 -07:00
|
|
|
* XXX Should not use this hack if the font size is too small?
|
|
|
|
* Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
|
2008-03-12 19:36:58 -07:00
|
|
|
*/
|
|
|
|
if (!mStyle.systemFont && aIsBadUnderlineFont) {
|
|
|
|
// First, we need 2 pixels between baseline and underline at least. Because many CJK characters
|
|
|
|
// put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
|
|
|
|
aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -2.0);
|
|
|
|
|
|
|
|
// Next, we put the underline to bottom of below of the descent space.
|
|
|
|
if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
|
|
|
|
aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -aMetrics->emDescent);
|
|
|
|
} else {
|
|
|
|
aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset,
|
|
|
|
aMetrics->underlineSize - aMetrics->emDescent);
|
|
|
|
}
|
|
|
|
}
|
2008-02-15 13:31:38 -08:00
|
|
|
// If underline positioned is too far from the text, descent position is preferred so that underline
|
|
|
|
// will stay within the boundary.
|
2008-03-12 19:36:58 -07:00
|
|
|
else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
|
2008-04-12 01:54:18 -07:00
|
|
|
if (aMetrics->underlineSize > aMetrics->maxDescent)
|
|
|
|
aMetrics->underlineSize = PR_MAX(aMetrics->maxDescent, 1.0);
|
|
|
|
// The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
|
2008-02-15 13:31:38 -08:00
|
|
|
aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
|
2008-04-12 01:54:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// If strikeout line is overflowed from the ascent, the line should be resized and moved for
|
|
|
|
// that being in the ascent space.
|
|
|
|
// Note that the strikeoutOffset is *middle* of the strikeout line position.
|
|
|
|
gfxFloat halfOfStrikeoutSize = NS_floor(aMetrics->strikeoutSize / 2.0 + 0.5);
|
|
|
|
if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
|
|
|
|
if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
|
|
|
|
aMetrics->strikeoutSize = PR_MAX(aMetrics->maxAscent, 1.0);
|
|
|
|
halfOfStrikeoutSize = NS_floor(aMetrics->strikeoutSize / 2.0 + 0.5);
|
|
|
|
}
|
|
|
|
gfxFloat ascent = NS_floor(aMetrics->maxAscent + 0.5);
|
|
|
|
aMetrics->strikeoutOffset = PR_MAX(halfOfStrikeoutSize, ascent / 2.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If overline is larger than the ascent, the line should be resized.
|
|
|
|
if (aMetrics->underlineSize > aMetrics->maxAscent) {
|
|
|
|
aMetrics->underlineSize = aMetrics->maxAscent;
|
2008-03-12 19:36:58 -07:00
|
|
|
}
|
2008-02-15 13:31:38 -08:00
|
|
|
}
|
|
|
|
|
2008-08-05 21:34:06 -07:00
|
|
|
|
|
|
|
|
2007-10-01 18:47:40 -07:00
|
|
|
gfxGlyphExtents::~gfxGlyphExtents()
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
gGlyphExtentsWidthsTotalSize += mContainedGlyphWidths.ComputeSize();
|
|
|
|
gGlyphExtentsCount++;
|
|
|
|
#endif
|
|
|
|
MOZ_COUNT_DTOR(gfxGlyphExtents);
|
|
|
|
}
|
|
|
|
|
2007-12-20 23:09:50 -08:00
|
|
|
PRBool
|
2007-09-23 19:19:14 -07:00
|
|
|
gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
|
2007-12-20 23:09:50 -08:00
|
|
|
gfxContext *aContext, PRUint32 aGlyphID, gfxRect *aExtents)
|
2007-09-23 19:19:14 -07:00
|
|
|
{
|
|
|
|
HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
|
|
|
if (!entry) {
|
2007-12-20 23:09:50 -08:00
|
|
|
if (!aContext) {
|
|
|
|
NS_WARNING("Could not get glyph extents (no aContext)");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
aFont->SetupCairoFont(aContext);
|
2007-10-01 18:47:40 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
++gGlyphExtentsSetupLazyTight;
|
|
|
|
#endif
|
2007-09-23 19:19:14 -07:00
|
|
|
aFont->SetupGlyphExtents(aContext, aGlyphID, PR_TRUE, this);
|
|
|
|
entry = mTightGlyphExtents.GetEntry(aGlyphID);
|
|
|
|
if (!entry) {
|
|
|
|
NS_WARNING("Could not get glyph extents");
|
2007-12-20 23:09:50 -08:00
|
|
|
return PR_FALSE;
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
|
|
|
}
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2007-12-20 23:09:50 -08:00
|
|
|
*aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
|
|
|
|
return PR_TRUE;
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
|
|
|
|
2007-10-01 18:47:40 -07:00
|
|
|
gfxGlyphExtents::GlyphWidths::~GlyphWidths()
|
|
|
|
{
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = 0; i < mBlocks.Length(); ++i) {
|
|
|
|
PtrBits bits = mBlocks[i];
|
|
|
|
if (bits && !(bits & 0x1)) {
|
|
|
|
delete[] reinterpret_cast<PRUint16 *>(bits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
PRUint32
|
|
|
|
gfxGlyphExtents::GlyphWidths::ComputeSize()
|
|
|
|
{
|
|
|
|
PRUint32 i;
|
|
|
|
PRUint32 size = mBlocks.Capacity()*sizeof(PtrBits);
|
|
|
|
for (i = 0; i < mBlocks.Length(); ++i) {
|
|
|
|
PtrBits bits = mBlocks[i];
|
|
|
|
if (bits && !(bits & 0x1)) {
|
|
|
|
size += BLOCK_SIZE*sizeof(PRUint16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
void
|
2007-10-01 18:47:40 -07:00
|
|
|
gfxGlyphExtents::GlyphWidths::Set(PRUint32 aGlyphID, PRUint16 aWidth)
|
2007-09-23 19:19:14 -07:00
|
|
|
{
|
2007-10-01 18:47:40 -07:00
|
|
|
PRUint32 block = aGlyphID >> BLOCK_SIZE_BITS;
|
|
|
|
PRUint32 len = mBlocks.Length();
|
|
|
|
if (block >= len) {
|
|
|
|
PtrBits *elems = mBlocks.AppendElements(block + 1 - len);
|
2007-09-23 19:19:14 -07:00
|
|
|
if (!elems)
|
|
|
|
return;
|
2007-10-01 18:47:40 -07:00
|
|
|
memset(elems, 0, sizeof(PtrBits)*(block + 1 - len));
|
|
|
|
}
|
|
|
|
|
|
|
|
PtrBits bits = mBlocks[block];
|
|
|
|
PRUint32 glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
|
|
|
|
if (!bits) {
|
|
|
|
mBlocks[block] = MakeSingle(glyphOffset, aWidth);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint16 *newBlock;
|
|
|
|
if (bits & 0x1) {
|
|
|
|
// Expand the block to a real block. We could avoid this by checking
|
|
|
|
// glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
|
|
|
|
newBlock = new PRUint16[BLOCK_SIZE];
|
|
|
|
if (!newBlock)
|
|
|
|
return;
|
2007-10-01 15:11:23 -07:00
|
|
|
PRUint32 i;
|
2007-10-01 18:47:40 -07:00
|
|
|
for (i = 0; i < BLOCK_SIZE; ++i) {
|
|
|
|
newBlock[i] = INVALID_WIDTH;
|
2007-10-01 15:11:23 -07:00
|
|
|
}
|
2007-10-01 18:47:40 -07:00
|
|
|
newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
|
|
|
|
mBlocks[block] = reinterpret_cast<PtrBits>(newBlock);
|
|
|
|
} else {
|
|
|
|
newBlock = reinterpret_cast<PRUint16 *>(bits);
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
2007-10-01 18:47:40 -07:00
|
|
|
newBlock[glyphOffset] = aWidth;
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxGlyphExtents::SetTightGlyphExtents(PRUint32 aGlyphID, const gfxRect& aExtentsAppUnits)
|
|
|
|
{
|
|
|
|
HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
|
|
|
|
if (!entry)
|
|
|
|
return;
|
|
|
|
entry->x = aExtentsAppUnits.pos.x;
|
|
|
|
entry->y = aExtentsAppUnits.pos.y;
|
|
|
|
entry->width = aExtentsAppUnits.size.width;
|
|
|
|
entry->height = aExtentsAppUnits.size.height;
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:01:53 -07:00
|
|
|
gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet)
|
2008-04-12 01:54:18 -07:00
|
|
|
: mFamilies(aFamilies), mStyle(*aStyle), mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-09-30 20:01:53 -07:00
|
|
|
mUserFontSet = nsnull;
|
|
|
|
SetUserFontSet(aUserFontSet);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFontGroup::~gfxFontGroup() {
|
|
|
|
mFonts.Clear();
|
|
|
|
SetUserFontSet(nsnull);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-09-30 20:01:53 -07:00
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxFontGroup::IsInvalidChar(PRUnichar ch) {
|
|
|
|
if (ch >= 32) {
|
|
|
|
return ch == 0x0085/*NEL*/ ||
|
|
|
|
((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
|
|
|
|
(ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/ ||
|
|
|
|
IS_BIDI_CONTROL_CHAR(ch)));
|
|
|
|
}
|
|
|
|
// We could just blacklist all control characters, but it seems better
|
|
|
|
// to only blacklist the ones we know cause problems for native font
|
|
|
|
// engines.
|
|
|
|
return ch == 0x0B || ch == '\t' || ch == '\r' || ch == '\n' || ch == '\f' ||
|
|
|
|
(ch >= 0x1c && ch <= 0x1f);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxFontGroup::ForEachFont(FontCreationCallback fc,
|
|
|
|
void *closure)
|
|
|
|
{
|
|
|
|
return ForEachFontInternal(mFamilies, mStyle.langGroup,
|
2008-01-26 23:37:29 -08:00
|
|
|
PR_TRUE, PR_TRUE, fc, closure);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxFontGroup::ForEachFont(const nsAString& aFamilies,
|
|
|
|
const nsACString& aLangGroup,
|
|
|
|
FontCreationCallback fc,
|
|
|
|
void *closure)
|
|
|
|
{
|
|
|
|
return ForEachFontInternal(aFamilies, aLangGroup,
|
2008-01-26 23:37:29 -08:00
|
|
|
PR_FALSE, PR_TRUE, fc, closure);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ResolveData {
|
|
|
|
ResolveData(gfxFontGroup::FontCreationCallback aCallback,
|
|
|
|
nsACString& aGenericFamily,
|
|
|
|
void *aClosure) :
|
|
|
|
mCallback(aCallback),
|
|
|
|
mGenericFamily(aGenericFamily),
|
|
|
|
mClosure(aClosure) {
|
2007-04-23 07:21:53 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxFontGroup::FontCreationCallback mCallback;
|
|
|
|
nsCString mGenericFamily;
|
|
|
|
void *mClosure;
|
|
|
|
};
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies,
|
|
|
|
const nsACString& aLangGroup,
|
|
|
|
PRBool aResolveGeneric,
|
2008-01-26 23:37:29 -08:00
|
|
|
PRBool aResolveFontName,
|
2007-03-22 10:30:00 -07:00
|
|
|
FontCreationCallback fc,
|
|
|
|
void *closure)
|
|
|
|
{
|
|
|
|
const PRUnichar kSingleQuote = PRUnichar('\'');
|
|
|
|
const PRUnichar kDoubleQuote = PRUnichar('\"');
|
|
|
|
const PRUnichar kComma = PRUnichar(',');
|
|
|
|
|
2009-04-04 02:43:42 -07:00
|
|
|
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsPromiseFlatString families(aFamilies);
|
|
|
|
const PRUnichar *p, *p_end;
|
|
|
|
families.BeginReading(p);
|
|
|
|
families.EndReading(p_end);
|
|
|
|
nsAutoString family;
|
|
|
|
nsCAutoString lcFamily;
|
|
|
|
nsAutoString genericFamily;
|
2009-04-04 02:43:42 -07:00
|
|
|
nsXPIDLCString value;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCAutoString lang(aLangGroup);
|
|
|
|
if (lang.IsEmpty())
|
|
|
|
lang.Assign("x-unicode"); // XXX or should use "x-user-def"?
|
|
|
|
|
|
|
|
while (p < p_end) {
|
|
|
|
while (nsCRT::IsAsciiSpace(*p))
|
|
|
|
if (++p == p_end)
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
PRBool generic;
|
|
|
|
if (*p == kSingleQuote || *p == kDoubleQuote) {
|
|
|
|
// quoted font family
|
|
|
|
PRUnichar quoteMark = *p;
|
|
|
|
if (++p == p_end)
|
|
|
|
return PR_TRUE;
|
|
|
|
const PRUnichar *nameStart = p;
|
|
|
|
|
|
|
|
// XXX What about CSS character escapes?
|
|
|
|
while (*p != quoteMark)
|
|
|
|
if (++p == p_end)
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
family = Substring(nameStart, p);
|
|
|
|
generic = PR_FALSE;
|
|
|
|
genericFamily.SetIsVoid(PR_TRUE);
|
|
|
|
|
|
|
|
while (++p != p_end && *p != kComma)
|
|
|
|
/* nothing */ ;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// unquoted font family
|
|
|
|
const PRUnichar *nameStart = p;
|
|
|
|
while (++p != p_end && *p != kComma)
|
|
|
|
/* nothing */ ;
|
|
|
|
|
|
|
|
family = Substring(nameStart, p);
|
|
|
|
family.CompressWhitespace(PR_FALSE, PR_TRUE);
|
|
|
|
|
|
|
|
if (aResolveGeneric &&
|
|
|
|
(family.LowerCaseEqualsLiteral("serif") ||
|
|
|
|
family.LowerCaseEqualsLiteral("sans-serif") ||
|
|
|
|
family.LowerCaseEqualsLiteral("monospace") ||
|
|
|
|
family.LowerCaseEqualsLiteral("cursive") ||
|
|
|
|
family.LowerCaseEqualsLiteral("fantasy")))
|
|
|
|
{
|
|
|
|
generic = PR_TRUE;
|
|
|
|
|
|
|
|
ToLowerCase(NS_LossyConvertUTF16toASCII(family), lcFamily);
|
|
|
|
|
|
|
|
nsCAutoString prefName("font.name.");
|
|
|
|
prefName.Append(lcFamily);
|
|
|
|
prefName.AppendLiteral(".");
|
|
|
|
prefName.Append(lang);
|
|
|
|
|
|
|
|
// prefs file always uses (must use) UTF-8 so that we can use
|
|
|
|
// |GetCharPref| and treat the result as a UTF-8 string.
|
2009-04-04 02:43:42 -07:00
|
|
|
nsresult rv = prefs->GetCharPref(prefName.get(), getter_Copies(value));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
CopyASCIItoUTF16(lcFamily, genericFamily);
|
2009-04-04 02:43:42 -07:00
|
|
|
CopyUTF8toUTF16(value, family);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
generic = PR_FALSE;
|
|
|
|
genericFamily.SetIsVoid(PR_TRUE);
|
|
|
|
}
|
|
|
|
}
|
2008-04-20 04:34:04 -07:00
|
|
|
|
|
|
|
if (generic) {
|
|
|
|
ForEachFontInternal(family, lang, PR_FALSE, aResolveFontName, fc, closure);
|
|
|
|
} else if (!family.IsEmpty()) {
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_LossyConvertUTF16toASCII gf(genericFamily);
|
2008-01-26 23:37:29 -08:00
|
|
|
if (aResolveFontName) {
|
|
|
|
ResolveData data(fc, gf, closure);
|
2008-11-26 10:13:02 -08:00
|
|
|
PRBool aborted = PR_FALSE, needsBold;
|
2008-09-30 20:01:53 -07:00
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
if (mUserFontSet && mUserFontSet->FindFontEntry(family, mStyle, needsBold)) {
|
|
|
|
gfxFontGroup::FontResolverProc(family, &data);
|
|
|
|
rv = NS_OK;
|
|
|
|
} else {
|
|
|
|
gfxPlatform *pf = gfxPlatform::GetPlatform();
|
|
|
|
rv = pf->ResolveFontName(family,
|
2008-01-26 23:37:29 -08:00
|
|
|
gfxFontGroup::FontResolverProc,
|
|
|
|
&data, aborted);
|
2008-09-30 20:01:53 -07:00
|
|
|
}
|
2008-01-26 23:37:29 -08:00
|
|
|
if (NS_FAILED(rv) || aborted)
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!fc(family, gf, closure))
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (generic && aResolveGeneric) {
|
|
|
|
nsCAutoString prefName("font.name-list.");
|
|
|
|
prefName.Append(lcFamily);
|
|
|
|
prefName.AppendLiteral(".");
|
|
|
|
prefName.Append(aLangGroup);
|
2009-04-04 02:43:42 -07:00
|
|
|
nsresult rv = prefs->GetCharPref(prefName.get(), getter_Copies(value));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2009-04-04 02:43:42 -07:00
|
|
|
ForEachFontInternal(NS_ConvertUTF8toUTF16(value),
|
|
|
|
lang, PR_FALSE, aResolveFontName,
|
2008-01-26 23:37:29 -08:00
|
|
|
fc, closure);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++p; // may advance past p_end
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxFontGroup::FontResolverProc(const nsAString& aName, void *aClosure)
|
|
|
|
{
|
|
|
|
ResolveData *data = reinterpret_cast<ResolveData*>(aClosure);
|
|
|
|
return (data->mCallback)(aName, data->mGenericFamily, data->mClosure);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxTextRun *
|
2007-05-08 15:46:14 -07:00
|
|
|
gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, PRUint32 aFlags)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-05-08 15:46:14 -07:00
|
|
|
aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
|
2007-11-15 17:43:47 -08:00
|
|
|
return gfxTextRun::Create(aParams, nsnull, 0, this, aFlags);
|
2007-05-08 15:46:14 -07:00
|
|
|
}
|
2007-05-04 21:36:13 -07:00
|
|
|
|
2007-05-08 15:46:14 -07:00
|
|
|
gfxTextRun *
|
|
|
|
gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
|
2007-03-22 10:30:00 -07:00
|
|
|
static const PRUint8 space = ' ';
|
|
|
|
|
2007-05-08 15:46:14 -07:00
|
|
|
nsAutoPtr<gfxTextRun> textRun;
|
2007-11-15 17:43:47 -08:00
|
|
|
textRun = gfxTextRun::Create(aParams, &space, 1, this, aFlags);
|
|
|
|
if (!textRun)
|
2007-03-22 10:30:00 -07:00
|
|
|
return nsnull;
|
2007-06-12 13:56:04 -07:00
|
|
|
|
|
|
|
gfxFont *font = GetFontAt(0);
|
2008-01-13 23:05:19 -08:00
|
|
|
if (NS_UNLIKELY(GetStyle()->size == 0)) {
|
|
|
|
// Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
|
|
|
|
// them, and always create at least size 1 fonts, i.e. they still
|
|
|
|
// render something for size 0 fonts.
|
|
|
|
textRun->AddGlyphRun(font, 0);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
textRun->SetSpaceGlyph(font, aParams->mContext, 0);
|
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
// Note that the gfxGlyphExtents glyph bounds storage for the font will
|
|
|
|
// always contain an entry for the font's space glyph, so we don't have
|
|
|
|
// to call FetchGlyphExtents here.
|
2007-05-08 15:46:14 -07:00
|
|
|
return textRun.forget();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-08-05 21:34:06 -07:00
|
|
|
|
|
|
|
|
|
|
|
already_AddRefed<gfxFont>
|
|
|
|
gfxFontGroup::FindFontForChar(PRUint32 aCh, PRUint32 aPrevCh, PRUint32 aNextCh, gfxFont *aPrevMatchedFont)
|
|
|
|
{
|
|
|
|
nsRefPtr<gfxFont> selectedFont;
|
|
|
|
|
|
|
|
// if this character or the next one is a joiner use the
|
|
|
|
// same font as the previous range if we can
|
|
|
|
if (gfxFontUtils::IsJoiner(aCh) || gfxFontUtils::IsJoiner(aPrevCh) || gfxFontUtils::IsJoiner(aNextCh)) {
|
|
|
|
if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
|
|
|
|
selectedFont = aPrevMatchedFont;
|
|
|
|
return selectedFont.forget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1. check fonts in the font group
|
|
|
|
for (PRUint32 i = 0; i < FontListLength(); i++) {
|
|
|
|
nsRefPtr<gfxFont> font = GetFontAt(i);
|
|
|
|
if (font->HasCharacter(aCh))
|
|
|
|
return font.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// if character is in Private Use Area, don't do matching against pref or system fonts
|
|
|
|
if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// 2. search pref fonts
|
|
|
|
if ((selectedFont = WhichPrefFontSupportsChar(aCh))) {
|
|
|
|
return selectedFont.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. use fallback fonts
|
|
|
|
// -- before searching for something else check the font used for the previous character
|
|
|
|
if (!selectedFont && aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
|
|
|
|
selectedFont = aPrevMatchedFont;
|
|
|
|
return selectedFont.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -- otherwise look for other stuff
|
|
|
|
if (!selectedFont) {
|
|
|
|
selectedFont = WhichSystemFontSupportsChar(aCh);
|
|
|
|
return selectedFont.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges, const PRUnichar *aString, PRUint32 begin, PRUint32 end)
|
|
|
|
{
|
|
|
|
const PRUnichar *str = aString + begin;
|
|
|
|
PRUint32 len = end - begin;
|
|
|
|
|
|
|
|
aRanges.Clear();
|
|
|
|
|
2009-04-09 18:32:24 -07:00
|
|
|
if (len == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-08-05 21:34:06 -07:00
|
|
|
PRUint32 prevCh = 0;
|
|
|
|
for (PRUint32 i = 0; i < len; i++) {
|
|
|
|
|
|
|
|
const PRUint32 origI = i; // save off in case we increase for surrogate
|
|
|
|
|
|
|
|
// set up current ch
|
|
|
|
PRUint32 ch = str[i];
|
|
|
|
if ((i+1 < len) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(str[i+1])) {
|
|
|
|
i++;
|
|
|
|
ch = SURROGATE_TO_UCS4(ch, str[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set up next ch
|
|
|
|
PRUint32 nextCh = 0;
|
|
|
|
if (i+1 < len) {
|
|
|
|
nextCh = str[i+1];
|
|
|
|
if ((i+2 < len) && NS_IS_HIGH_SURROGATE(nextCh) && NS_IS_LOW_SURROGATE(str[i+2]))
|
|
|
|
nextCh = SURROGATE_TO_UCS4(nextCh, str[i+2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// find the font for this char
|
2008-08-18 01:31:53 -07:00
|
|
|
nsRefPtr<gfxFont> font = FindFontForChar(ch, prevCh, nextCh, (aRanges.Length() == 0) ? nsnull : aRanges[aRanges.Length() - 1].font.get());
|
2008-08-05 21:34:06 -07:00
|
|
|
|
|
|
|
prevCh = ch;
|
|
|
|
|
|
|
|
if (aRanges.Length() == 0) {
|
|
|
|
// first char ==> make a new range
|
|
|
|
gfxTextRange r(0,1);
|
|
|
|
r.font = font;
|
|
|
|
aRanges.AppendElement(r);
|
|
|
|
} else {
|
|
|
|
// if font has changed, make a new range
|
|
|
|
gfxTextRange& prevRange = aRanges[aRanges.Length() - 1];
|
|
|
|
if (prevRange.font != font) {
|
|
|
|
// close out the previous range
|
|
|
|
prevRange.end = origI;
|
|
|
|
|
|
|
|
gfxTextRange r(origI, i+1);
|
|
|
|
r.font = font;
|
|
|
|
aRanges.AppendElement(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
aRanges[aRanges.Length()-1].end = len;
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:01:53 -07:00
|
|
|
gfxUserFontSet*
|
|
|
|
gfxFontGroup::GetUserFontSet()
|
|
|
|
{
|
|
|
|
return mUserFontSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
|
|
|
|
{
|
|
|
|
NS_IF_RELEASE(mUserFontSet);
|
|
|
|
mUserFontSet = aUserFontSet;
|
|
|
|
NS_IF_ADDREF(mUserFontSet);
|
|
|
|
mCurrGeneration = GetGeneration();
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint64
|
|
|
|
gfxFontGroup::GetGeneration()
|
|
|
|
{
|
|
|
|
if (!mUserFontSet)
|
|
|
|
return 0;
|
|
|
|
return mUserFontSet->GetGeneration();
|
|
|
|
}
|
|
|
|
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2008-03-12 03:52:58 -07:00
|
|
|
#define DEFAULT_PIXEL_FONT_SIZE 16.0f
|
|
|
|
|
|
|
|
gfxFontStyle::gfxFontStyle() :
|
2008-12-16 19:17:50 -08:00
|
|
|
style(FONT_STYLE_NORMAL), systemFont(PR_TRUE), printerFont(PR_FALSE),
|
2009-01-29 12:39:18 -08:00
|
|
|
familyNameQuirks(PR_FALSE), weight(FONT_WEIGHT_NORMAL),
|
|
|
|
stretch(NS_FONT_STRETCH_NORMAL), size(DEFAULT_PIXEL_FONT_SIZE),
|
2008-03-12 03:52:58 -07:00
|
|
|
langGroup(NS_LITERAL_CSTRING("x-western")), sizeAdjust(0.0f)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-01-29 12:39:18 -08:00
|
|
|
gfxFontStyle::gfxFontStyle(PRUint8 aStyle, PRUint16 aWeight, PRInt16 aStretch,
|
|
|
|
gfxFloat aSize, const nsACString& aLangGroup,
|
2007-03-22 10:30:00 -07:00
|
|
|
float aSizeAdjust, PRPackedBool aSystemFont,
|
2008-12-16 19:17:50 -08:00
|
|
|
PRPackedBool aFamilyNameQuirks,
|
|
|
|
PRPackedBool aPrinterFont):
|
|
|
|
style(aStyle), systemFont(aSystemFont), printerFont(aPrinterFont),
|
2009-01-29 12:39:18 -08:00
|
|
|
familyNameQuirks(aFamilyNameQuirks), weight(aWeight), stretch(aStretch),
|
2007-05-30 01:32:50 -07:00
|
|
|
size(aSize), langGroup(aLangGroup), sizeAdjust(aSizeAdjust)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (weight > 900)
|
|
|
|
weight = 900;
|
|
|
|
if (weight < 100)
|
|
|
|
weight = 100;
|
|
|
|
|
2007-06-08 01:22:03 -07:00
|
|
|
if (size >= FONT_MAX_SIZE) {
|
|
|
|
size = FONT_MAX_SIZE;
|
2007-05-30 01:32:50 -07:00
|
|
|
sizeAdjust = 0.0;
|
|
|
|
} else if (size < 0.0) {
|
|
|
|
NS_WARNING("negative font size");
|
|
|
|
size = 0.0;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (langGroup.IsEmpty()) {
|
|
|
|
NS_WARNING("empty langgroup");
|
|
|
|
langGroup.Assign("x-western");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-03 20:32:43 -07:00
|
|
|
gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
|
2008-12-16 19:17:50 -08:00
|
|
|
style(aStyle.style), systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
|
2007-04-03 20:32:43 -07:00
|
|
|
familyNameQuirks(aStyle.familyNameQuirks), weight(aStyle.weight),
|
2009-01-29 12:39:18 -08:00
|
|
|
stretch(aStyle.stretch), size(aStyle.size), langGroup(aStyle.langGroup),
|
2007-05-20 19:18:04 -07:00
|
|
|
sizeAdjust(aStyle.sizeAdjust)
|
2007-04-03 20:32:43 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
gfxFontStyle::ComputeWeightAndOffset(PRInt8 *outBaseWeight, PRInt8 *outOffset) const
|
|
|
|
{
|
|
|
|
PRInt8 baseWeight = (weight + 50) / 100;
|
|
|
|
PRInt8 offset = weight - baseWeight * 100;
|
|
|
|
|
|
|
|
if (baseWeight < 0)
|
|
|
|
baseWeight = 0;
|
|
|
|
if (baseWeight > 9)
|
|
|
|
baseWeight = 9;
|
|
|
|
|
|
|
|
if (outBaseWeight)
|
|
|
|
*outBaseWeight = baseWeight;
|
|
|
|
if (outOffset)
|
|
|
|
*outOffset = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxTextRun::GlyphRunIterator::NextRun() {
|
|
|
|
if (mNextIndex >= mTextRun->mGlyphRuns.Length())
|
|
|
|
return PR_FALSE;
|
|
|
|
mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
|
|
|
|
if (mGlyphRun->mCharacterOffset >= mEndOffset)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
mStringStart = PR_MAX(mStartOffset, mGlyphRun->mCharacterOffset);
|
|
|
|
PRUint32 last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
|
|
|
|
? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->mCharacterCount;
|
|
|
|
mStringEnd = PR_MIN(mEndOffset, last);
|
|
|
|
|
|
|
|
++mNextIndex;
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2007-07-01 18:24:56 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
2007-07-01 18:12:45 -07:00
|
|
|
static void
|
|
|
|
AccountStorageForTextRun(gfxTextRun *aTextRun, PRInt32 aSign)
|
|
|
|
{
|
|
|
|
// Ignores detailed glyphs... we don't know when those have been constructed
|
|
|
|
// Also ignores gfxSkipChars dynamic storage (which won't be anything
|
|
|
|
// for preformatted text)
|
|
|
|
// Also ignores GlyphRun array, again because it hasn't been constructed
|
|
|
|
// by the time this gets called. If there's only one glyphrun that's stored
|
|
|
|
// directly in the textrun anyway so no additional overhead.
|
|
|
|
PRInt32 bytesPerChar = sizeof(gfxTextRun::CompressedGlyph);
|
|
|
|
if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_PERSISTENT) {
|
|
|
|
bytesPerChar += (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) ? 1 : 2;
|
|
|
|
}
|
|
|
|
PRInt32 bytes = sizeof(gfxTextRun) + aTextRun->GetLength()*bytesPerChar;
|
|
|
|
gTextRunStorage += bytes*aSign;
|
|
|
|
gTextRunStorageHighWaterMark = PR_MAX(gTextRunStorageHighWaterMark, gTextRunStorage);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-11-15 17:43:47 -08:00
|
|
|
gfxTextRun *
|
|
|
|
gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams, const void *aText,
|
|
|
|
PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
return new (aLength, aFlags)
|
|
|
|
gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags, sizeof(gfxTextRun));
|
|
|
|
}
|
|
|
|
|
|
|
|
void *
|
|
|
|
gfxTextRun::operator new(size_t aSize, PRUint32 aLength, PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aSize % sizeof(CompressedGlyph) == 0, "Alignment broken!");
|
|
|
|
aSize += sizeof(CompressedGlyph)*aLength;
|
|
|
|
if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
|
|
|
|
NS_ASSERTION(aSize % 2 == 0, "Alignment broken!");
|
|
|
|
aSize += ((aFlags & gfxTextRunFactory::TEXT_IS_8BIT) ? 1 : 2)*aLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new PRUint8[aSize];
|
|
|
|
}
|
|
|
|
|
|
|
|
void gfxTextRun::operator delete(void *p)
|
|
|
|
{
|
|
|
|
delete[] static_cast<PRUint8*>(p);
|
|
|
|
}
|
|
|
|
|
2007-05-08 15:46:14 -07:00
|
|
|
gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
|
2007-11-15 17:43:47 -08:00
|
|
|
PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags,
|
|
|
|
PRUint32 aObjectSize)
|
2007-03-22 10:30:00 -07:00
|
|
|
: mUserData(aParams->mUserData),
|
2007-05-08 15:46:14 -07:00
|
|
|
mFontGroup(aFontGroup),
|
2007-03-22 10:30:00 -07:00
|
|
|
mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
|
2007-05-11 22:51:15 -07:00
|
|
|
mFlags(aFlags), mCharacterCount(aLength), mHashCode(0)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-06 05:30:13 -07:00
|
|
|
NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
|
2007-06-10 16:07:29 -07:00
|
|
|
MOZ_COUNT_CTOR(gfxTextRun);
|
2007-05-08 15:46:14 -07:00
|
|
|
NS_ADDREF(mFontGroup);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aParams->mSkipChars) {
|
|
|
|
mSkipChars.TakeFrom(aParams->mSkipChars);
|
|
|
|
}
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2007-11-15 17:43:47 -08:00
|
|
|
mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>
|
|
|
|
(reinterpret_cast<PRUint8*>(this) + aObjectSize);
|
|
|
|
memset(mCharacterGlyphs, 0, sizeof(CompressedGlyph)*aLength);
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2007-05-08 15:46:14 -07:00
|
|
|
if (mFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
|
2007-07-08 00:08:04 -07:00
|
|
|
mText.mSingle = static_cast<const PRUint8 *>(aText);
|
2007-05-14 20:56:47 -07:00
|
|
|
if (!(mFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
|
2007-11-15 17:43:47 -08:00
|
|
|
PRUint8 *newText = reinterpret_cast<PRUint8*>(mCharacterGlyphs + aLength);
|
|
|
|
memcpy(newText, aText, aLength);
|
2007-05-14 20:56:47 -07:00
|
|
|
mText.mSingle = newText;
|
|
|
|
}
|
2007-05-08 15:46:14 -07:00
|
|
|
} else {
|
2007-07-08 00:08:04 -07:00
|
|
|
mText.mDouble = static_cast<const PRUnichar *>(aText);
|
2007-05-14 20:56:47 -07:00
|
|
|
if (!(mFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
|
2007-11-15 17:43:47 -08:00
|
|
|
PRUnichar *newText = reinterpret_cast<PRUnichar*>(mCharacterGlyphs + aLength);
|
|
|
|
memcpy(newText, aText, aLength*sizeof(PRUnichar));
|
2007-05-14 20:56:47 -07:00
|
|
|
mText.mDouble = newText;
|
|
|
|
}
|
2007-05-08 15:46:14 -07:00
|
|
|
}
|
2007-07-01 18:24:56 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
2007-07-01 18:12:45 -07:00
|
|
|
AccountStorageForTextRun(this, 1);
|
|
|
|
#endif
|
2008-09-30 20:01:53 -07:00
|
|
|
|
|
|
|
mUserFontSetGeneration = mFontGroup->GetGeneration();
|
2007-05-08 15:46:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxTextRun::~gfxTextRun()
|
|
|
|
{
|
2007-07-01 18:24:56 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
2007-07-01 18:12:45 -07:00
|
|
|
AccountStorageForTextRun(this, -1);
|
2009-01-08 16:23:28 -08:00
|
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Make it easy to detect a dead text run
|
|
|
|
mFlags = 0xFFFFFFFF;
|
2007-07-01 18:12:45 -07:00
|
|
|
#endif
|
2007-05-08 15:46:14 -07:00
|
|
|
NS_RELEASE(mFontGroup);
|
2007-06-10 16:07:29 -07:00
|
|
|
MOZ_COUNT_DTOR(gfxTextRun);
|
2007-05-08 15:46:14 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxTextRun *
|
|
|
|
gfxTextRun::Clone(const gfxTextRunFactory::Parameters *aParams, const void *aText,
|
|
|
|
PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
if (!mCharacterGlyphs)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsAutoPtr<gfxTextRun> textRun;
|
2007-11-15 17:43:47 -08:00
|
|
|
textRun = gfxTextRun::Create(aParams, aText, aLength, aFontGroup, aFlags);
|
|
|
|
if (!textRun)
|
2007-05-08 15:46:14 -07:00
|
|
|
return nsnull;
|
|
|
|
|
2007-06-12 13:56:04 -07:00
|
|
|
textRun->CopyGlyphDataFrom(this, 0, mCharacterCount, 0, PR_FALSE);
|
2007-05-08 15:46:14 -07:00
|
|
|
return textRun.forget();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
2007-06-26 21:22:21 -07:00
|
|
|
PRPackedBool *aBreakBefore,
|
|
|
|
gfxContext *aRefContext)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Overflow");
|
|
|
|
|
|
|
|
if (!mCharacterGlyphs)
|
|
|
|
return PR_TRUE;
|
|
|
|
PRUint32 changed = 0;
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = 0; i < aLength; ++i) {
|
2007-08-09 18:35:32 -07:00
|
|
|
PRBool canBreak = aBreakBefore[i];
|
|
|
|
if (canBreak && !mCharacterGlyphs[aStart + i].IsClusterStart()) {
|
|
|
|
// This can happen ... there is no guarantee that our linebreaking rules
|
|
|
|
// align with the platform's idea of what constitutes a cluster.
|
|
|
|
NS_WARNING("Break suggested inside cluster!");
|
|
|
|
canBreak = PR_FALSE;
|
|
|
|
}
|
|
|
|
changed |= mCharacterGlyphs[aStart + i].SetCanBreakBefore(canBreak);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return changed != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxTextRun::LigatureData
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
|
|
|
|
PropertyProvider *aProvider)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-09 18:35:32 -07:00
|
|
|
NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
|
|
|
|
NS_ASSERTION(aPartEnd <= mCharacterCount, "Character length overflow");
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
LigatureData result;
|
|
|
|
CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
2007-08-09 18:35:32 -07:00
|
|
|
|
|
|
|
PRUint32 i;
|
2007-11-08 22:27:23 -08:00
|
|
|
for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
|
2007-08-09 18:35:32 -07:00
|
|
|
NS_ASSERTION(i > 0, "Ligature at the start of the run??");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-08-09 18:35:32 -07:00
|
|
|
result.mLigatureStart = i;
|
2007-11-08 22:27:23 -08:00
|
|
|
for (i = aPartStart + 1; i < mCharacterCount && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
|
2007-08-09 18:35:32 -07:00
|
|
|
}
|
|
|
|
result.mLigatureEnd = i;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
PRInt32 ligatureWidth =
|
|
|
|
GetAdvanceForGlyphs(this, result.mLigatureStart, result.mLigatureEnd);
|
2007-03-22 10:30:00 -07:00
|
|
|
// Count the number of started clusters we have seen
|
2007-08-09 18:35:32 -07:00
|
|
|
PRUint32 totalClusterCount = 0;
|
|
|
|
PRUint32 partClusterIndex = 0;
|
|
|
|
PRUint32 partClusterCount = 0;
|
|
|
|
for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
|
2008-04-22 19:06:22 -07:00
|
|
|
// Treat the first character of the ligature as the start of a
|
|
|
|
// cluster for our purposes of allocating ligature width to its
|
|
|
|
// characters.
|
|
|
|
if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
|
2007-08-09 18:35:32 -07:00
|
|
|
++totalClusterCount;
|
|
|
|
if (i < aPartStart) {
|
|
|
|
++partClusterIndex;
|
|
|
|
} else if (i < aPartEnd) {
|
|
|
|
++partClusterCount;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2009-03-29 17:31:51 -07:00
|
|
|
NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
|
2007-08-09 18:35:32 -07:00
|
|
|
result.mPartAdvance = ligatureWidth*partClusterIndex/totalClusterCount;
|
|
|
|
result.mPartWidth = ligatureWidth*partClusterCount/totalClusterCount;
|
2008-04-22 19:06:22 -07:00
|
|
|
|
|
|
|
if (partClusterCount == 0) {
|
|
|
|
// nothing to draw
|
|
|
|
result.mClipBeforePart = result.mClipAfterPart = PR_TRUE;
|
|
|
|
} else {
|
|
|
|
// Determine whether we should clip before or after this part when
|
|
|
|
// drawing its slice of the ligature.
|
|
|
|
// We need to clip before the part if any cluster is drawn before
|
|
|
|
// this part.
|
|
|
|
result.mClipBeforePart = partClusterIndex > 0;
|
|
|
|
// We need to clip after the part if any cluster is drawn after
|
|
|
|
// this part.
|
|
|
|
result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxFont::Spacing spacing;
|
|
|
|
if (aPartStart == result.mLigatureStart) {
|
|
|
|
aProvider->GetSpacing(aPartStart, 1, &spacing);
|
|
|
|
result.mPartWidth += spacing.mBefore;
|
|
|
|
}
|
|
|
|
if (aPartEnd == result.mLigatureEnd) {
|
|
|
|
aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
|
|
|
|
result.mPartWidth += spacing.mAfter;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxFloat
|
|
|
|
gfxTextRun::ComputePartialLigatureWidth(PRUint32 aPartStart, PRUint32 aPartEnd,
|
|
|
|
PropertyProvider *aProvider)
|
|
|
|
{
|
|
|
|
if (aPartStart >= aPartEnd)
|
|
|
|
return 0;
|
|
|
|
LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
|
|
|
|
return data.mPartWidth;
|
|
|
|
}
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
static void
|
|
|
|
GetAdjustedSpacing(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
|
|
|
gfxTextRun::PropertyProvider *aProvider,
|
|
|
|
gfxTextRun::PropertyProvider::Spacing *aSpacing)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (aStart >= aEnd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
|
|
|
|
|
2007-11-07 23:41:06 -08:00
|
|
|
#ifdef DEBUG
|
|
|
|
// Check to see if we have spacing inside ligatures
|
2007-11-08 22:27:23 -08:00
|
|
|
|
|
|
|
const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
|
|
|
|
PRUint32 i;
|
|
|
|
|
2007-05-30 22:01:56 -07:00
|
|
|
for (i = aStart; i < aEnd; ++i) {
|
2007-11-08 22:27:23 -08:00
|
|
|
if (!charGlyphs[i].IsLigatureGroupStart()) {
|
2007-05-30 22:01:56 -07:00
|
|
|
NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
|
|
|
|
"Before-spacing inside a ligature!");
|
|
|
|
NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
|
|
|
|
"After-spacing inside a ligature!");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-05-30 22:01:56 -07:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
gfxTextRun::GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd,
|
|
|
|
PropertyProvider *aProvider,
|
2007-08-09 18:35:32 -07:00
|
|
|
PRUint32 aSpacingStart, PRUint32 aSpacingEnd,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsTArray<PropertyProvider::Spacing> *aSpacing)
|
|
|
|
{
|
2007-05-11 22:53:29 -07:00
|
|
|
if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
|
2007-03-22 10:30:00 -07:00
|
|
|
return PR_FALSE;
|
|
|
|
if (!aSpacing->AppendElements(aEnd - aStart))
|
|
|
|
return PR_FALSE;
|
2007-08-09 18:35:32 -07:00
|
|
|
memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
|
2007-11-08 22:27:23 -08:00
|
|
|
GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
|
2007-08-09 18:35:32 -07:00
|
|
|
aSpacing->Elements() + aSpacingStart - aStart);
|
|
|
|
memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
|
2007-03-22 10:30:00 -07:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd)
|
|
|
|
{
|
|
|
|
if (*aStart >= *aEnd)
|
|
|
|
return;
|
|
|
|
|
|
|
|
CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
|
2007-08-09 18:35:32 -07:00
|
|
|
++(*aStart);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-08-09 18:35:32 -07:00
|
|
|
if (*aEnd < mCharacterCount) {
|
2007-11-08 22:27:23 -08:00
|
|
|
while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
|
2007-08-09 18:35:32 -07:00
|
|
|
--(*aEnd);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
|
|
|
|
PRBool aDrawToPath, gfxPoint *aPt,
|
|
|
|
PRUint32 aStart, PRUint32 aEnd,
|
2007-08-09 18:35:32 -07:00
|
|
|
PropertyProvider *aProvider,
|
|
|
|
PRUint32 aSpacingStart, PRUint32 aSpacingEnd)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
|
2007-08-09 18:35:32 -07:00
|
|
|
PRBool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
|
|
|
|
aSpacingStart, aSpacingEnd, &spacingBuffer);
|
2007-03-22 10:30:00 -07:00
|
|
|
aFont->Draw(this, aStart, aEnd, aContext, aDrawToPath, aPt,
|
|
|
|
haveSpacing ? spacingBuffer.Elements() : nsnull);
|
|
|
|
}
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
static void
|
|
|
|
ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
|
|
|
|
gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-04-22 19:06:22 -07:00
|
|
|
if (aLigature->mClipBeforePart) {
|
2007-08-09 18:35:32 -07:00
|
|
|
if (aTextRun->IsRightToLeft()) {
|
|
|
|
*aRight = PR_MIN(*aRight, aXOrigin);
|
|
|
|
} else {
|
|
|
|
*aLeft = PR_MAX(*aLeft, aXOrigin);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2008-04-22 19:06:22 -07:00
|
|
|
if (aLigature->mClipAfterPart) {
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
|
|
|
|
if (aTextRun->IsRightToLeft()) {
|
|
|
|
*aLeft = PR_MAX(*aLeft, endEdge);
|
|
|
|
} else {
|
|
|
|
*aRight = PR_MIN(*aRight, endEdge);
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, PRUint32 aStart,
|
|
|
|
PRUint32 aEnd,
|
2007-03-22 10:30:00 -07:00
|
|
|
const gfxRect *aDirtyRect, gfxPoint *aPt,
|
|
|
|
PropertyProvider *aProvider)
|
|
|
|
{
|
2007-08-09 18:35:32 -07:00
|
|
|
if (aStart >= aEnd)
|
|
|
|
return;
|
|
|
|
if (!aDirtyRect) {
|
|
|
|
NS_ERROR("Cannot draw partial ligatures without a dirty rect");
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
2007-08-09 18:35:32 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Draw partial ligature. We hack this by clipping the ligature.
|
2007-08-09 18:35:32 -07:00
|
|
|
LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxFloat left = aDirtyRect->X();
|
|
|
|
gfxFloat right = aDirtyRect->XMost();
|
2007-08-09 18:35:32 -07:00
|
|
|
ClipPartialLigature(this, &left, &right, aPt->x, &data);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
aCtx->Save();
|
2007-05-29 02:47:54 -07:00
|
|
|
aCtx->NewPath();
|
2007-03-22 10:30:00 -07:00
|
|
|
// use division here to ensure that when the rect is aligned on multiples
|
2007-05-29 02:47:54 -07:00
|
|
|
// of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
|
|
|
|
// Also, make sure we snap the rectangle to device pixels.
|
|
|
|
aCtx->Rectangle(gfxRect(left/mAppUnitsPerDevUnit,
|
|
|
|
aDirtyRect->Y()/mAppUnitsPerDevUnit,
|
|
|
|
(right - left)/mAppUnitsPerDevUnit,
|
|
|
|
aDirtyRect->Height()/mAppUnitsPerDevUnit), PR_TRUE);
|
|
|
|
aCtx->Clip();
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxFloat direction = GetDirection();
|
|
|
|
gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
|
|
|
|
DrawGlyphs(aFont, aCtx, PR_FALSE, &pt, data.mLigatureStart,
|
|
|
|
data.mLigatureEnd, aProvider, aStart, aEnd);
|
2007-03-22 10:30:00 -07:00
|
|
|
aCtx->Restore();
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
aPt->x += direction*data.mPartWidth;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
// returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
|
|
|
|
static PRBool
|
|
|
|
HasSyntheticBold(gfxTextRun *aRun, PRUint32 aStart, PRUint32 aLength)
|
|
|
|
{
|
|
|
|
gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
|
|
|
|
while (iter.NextRun()) {
|
|
|
|
gfxFont *font = iter.GetGlyphRun()->mFont;
|
|
|
|
if (font && font->IsSyntheticBold()) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
|
|
|
|
// if true, color is set on output
|
|
|
|
static PRBool
|
|
|
|
HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
|
|
|
|
{
|
2008-04-28 14:27:05 -07:00
|
|
|
if (aContext->GetDeviceColor(aCurrentColor)) {
|
2008-03-19 17:02:21 -07:00
|
|
|
if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper class for double-buffering drawing with non-opaque color
|
|
|
|
struct BufferAlphaColor {
|
|
|
|
BufferAlphaColor(gfxContext *aContext)
|
|
|
|
: mContext(aContext)
|
|
|
|
{
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
}
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
~BufferAlphaColor() {}
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, PRUint32 appsPerDevUnit)
|
|
|
|
{
|
|
|
|
mContext->Save();
|
|
|
|
mContext->NewPath();
|
|
|
|
mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
|
|
|
|
aBounds.Y() / appsPerDevUnit,
|
|
|
|
aBounds.Width() / appsPerDevUnit,
|
|
|
|
aBounds.Height() / appsPerDevUnit), PR_TRUE);
|
|
|
|
mContext->Clip();
|
|
|
|
mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
|
|
|
|
mContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
|
|
|
|
mAlpha = aAlphaColor.a;
|
|
|
|
}
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
void PopAlpha()
|
|
|
|
{
|
|
|
|
// pop the text, using the color alpha as the opacity
|
|
|
|
mContext->PopGroupToSource();
|
|
|
|
mContext->SetOperator(gfxContext::OPERATOR_OVER);
|
|
|
|
mContext->Paint(mAlpha);
|
|
|
|
mContext->Restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxContext *mContext;
|
|
|
|
gfxFloat mAlpha;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::AdjustAdvancesForSyntheticBold(PRUint32 aStart, PRUint32 aLength)
|
|
|
|
{
|
|
|
|
const PRUint32 appUnitsPerDevUnit = GetAppUnitsPerDevUnit();
|
|
|
|
PRBool isRTL = IsRightToLeft();
|
|
|
|
|
|
|
|
GlyphRunIterator iter(this, aStart, aLength);
|
|
|
|
while (iter.NextRun()) {
|
|
|
|
gfxFont *font = iter.GetGlyphRun()->mFont;
|
|
|
|
if (font->IsSyntheticBold()) {
|
|
|
|
PRUint32 synAppUnitOffset = font->GetSyntheticBoldOffset() * appUnitsPerDevUnit;
|
|
|
|
PRUint32 start = iter.GetStringStart();
|
|
|
|
PRUint32 end = iter.GetStringEnd();
|
|
|
|
PRUint32 i;
|
|
|
|
|
|
|
|
// iterate over glyphs, start to end
|
|
|
|
for (i = start; i < end; ++i) {
|
|
|
|
gfxTextRun::CompressedGlyph *glyphData = &mCharacterGlyphs[i];
|
|
|
|
|
|
|
|
if (glyphData->IsSimpleGlyph()) {
|
|
|
|
// simple glyphs ==> just add the advance
|
|
|
|
PRUint32 advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
|
|
|
|
if (CompressedGlyph::IsSimpleAdvance(advance)) {
|
|
|
|
glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
|
|
|
|
} else {
|
|
|
|
// rare case, tested by making this the default
|
|
|
|
PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
|
|
|
|
glyphData->SetComplex(PR_TRUE, PR_TRUE, 1);
|
|
|
|
DetailedGlyph detail = {glyphIndex, advance, 0, 0};
|
|
|
|
SetGlyphs(i, *glyphData, &detail);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// complex glyphs ==> add offset at cluster/ligature boundaries
|
|
|
|
PRUint32 detailedLength = glyphData->GetGlyphCount();
|
|
|
|
if (detailedLength && mDetailedGlyphs) {
|
|
|
|
gfxTextRun::DetailedGlyph *details = mDetailedGlyphs[i].get();
|
|
|
|
if (!details) continue;
|
|
|
|
if (isRTL)
|
|
|
|
details[0].mAdvance += synAppUnitOffset;
|
|
|
|
else
|
|
|
|
details[detailedLength - 1].mAdvance += synAppUnitOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt,
|
|
|
|
PRUint32 aStart, PRUint32 aLength, const gfxRect *aDirtyRect,
|
|
|
|
PropertyProvider *aProvider, gfxFloat *aAdvanceWidth)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
|
|
|
|
|
|
|
|
gfxFloat direction = GetDirection();
|
|
|
|
gfxPoint pt = aPt;
|
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
// synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha
|
|
|
|
BufferAlphaColor syntheticBoldBuffer(aContext);
|
|
|
|
gfxRGBA currentColor;
|
|
|
|
PRBool needToRestore = PR_FALSE;
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
if (HasNonOpaqueColor(aContext, currentColor) && HasSyntheticBold(this, aStart, aLength)) {
|
|
|
|
needToRestore = PR_TRUE;
|
|
|
|
// measure text, use the bounding box
|
2009-02-24 00:32:58 -08:00
|
|
|
gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
|
|
|
|
aContext, aProvider);
|
2008-03-19 17:02:21 -07:00
|
|
|
metrics.mBoundingBox.MoveBy(aPt);
|
|
|
|
syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit());
|
|
|
|
}
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
GlyphRunIterator iter(this, aStart, aLength);
|
|
|
|
while (iter.NextRun()) {
|
|
|
|
gfxFont *font = iter.GetGlyphRun()->mFont;
|
|
|
|
PRUint32 start = iter.GetStringStart();
|
|
|
|
PRUint32 end = iter.GetStringEnd();
|
|
|
|
PRUint32 ligatureRunStart = start;
|
|
|
|
PRUint32 ligatureRunEnd = end;
|
|
|
|
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
2008-03-19 17:02:21 -07:00
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
DrawPartialLigature(font, aContext, start, ligatureRunStart, aDirtyRect, &pt, aProvider);
|
2007-03-22 10:30:00 -07:00
|
|
|
DrawGlyphs(font, aContext, PR_FALSE, &pt, ligatureRunStart,
|
2007-08-09 18:35:32 -07:00
|
|
|
ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd);
|
|
|
|
DrawPartialLigature(font, aContext, ligatureRunEnd, end, aDirtyRect, &pt, aProvider);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-03-19 17:02:21 -07:00
|
|
|
// composite result when synthetic bolding used
|
|
|
|
if (needToRestore) {
|
|
|
|
syntheticBoldBuffer.PopAlpha();
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aAdvanceWidth) {
|
|
|
|
*aAdvanceWidth = (pt.x - aPt.x)*direction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::DrawToPath(gfxContext *aContext, gfxPoint aPt,
|
|
|
|
PRUint32 aStart, PRUint32 aLength,
|
|
|
|
PropertyProvider *aProvider, gfxFloat *aAdvanceWidth)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
|
|
|
|
|
|
|
|
gfxFloat direction = GetDirection();
|
|
|
|
gfxPoint pt = aPt;
|
|
|
|
|
|
|
|
GlyphRunIterator iter(this, aStart, aLength);
|
|
|
|
while (iter.NextRun()) {
|
|
|
|
gfxFont *font = iter.GetGlyphRun()->mFont;
|
|
|
|
PRUint32 start = iter.GetStringStart();
|
|
|
|
PRUint32 end = iter.GetStringEnd();
|
2007-08-09 18:35:32 -07:00
|
|
|
PRUint32 ligatureRunStart = start;
|
|
|
|
PRUint32 ligatureRunEnd = end;
|
|
|
|
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
|
|
|
NS_ASSERTION(ligatureRunStart == start,
|
2007-03-22 10:30:00 -07:00
|
|
|
"Can't draw path starting inside ligature");
|
2007-08-09 18:35:32 -07:00
|
|
|
NS_ASSERTION(ligatureRunEnd == end,
|
2007-03-22 10:30:00 -07:00
|
|
|
"Can't end drawing path inside ligature");
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
DrawGlyphs(font, aContext, PR_TRUE, &pt, ligatureRunStart, ligatureRunEnd, aProvider,
|
|
|
|
ligatureRunStart, ligatureRunEnd);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aAdvanceWidth) {
|
|
|
|
*aAdvanceWidth = (pt.x - aPt.x)*direction;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
|
|
|
|
PRUint32 aStart, PRUint32 aEnd,
|
2009-02-24 00:32:58 -08:00
|
|
|
gfxFont::BoundingBoxType aBoundingBoxType,
|
|
|
|
gfxContext *aRefContext,
|
2007-09-23 19:19:14 -07:00
|
|
|
PropertyProvider *aProvider,
|
2007-08-09 18:35:32 -07:00
|
|
|
PRUint32 aSpacingStart, PRUint32 aSpacingEnd,
|
2007-03-22 10:30:00 -07:00
|
|
|
Metrics *aMetrics)
|
|
|
|
{
|
|
|
|
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
|
2007-08-09 18:35:32 -07:00
|
|
|
PRBool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
|
|
|
|
aSpacingStart, aSpacingEnd, &spacingBuffer);
|
2009-02-24 00:32:58 -08:00
|
|
|
Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
|
2007-03-22 10:30:00 -07:00
|
|
|
haveSpacing ? spacingBuffer.Elements() : nsnull);
|
2008-08-12 02:34:52 -07:00
|
|
|
aMetrics->CombineWith(metrics, IsRightToLeft());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
|
2009-02-24 00:32:58 -08:00
|
|
|
PRUint32 aStart, PRUint32 aEnd,
|
|
|
|
gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
|
2007-08-09 18:35:32 -07:00
|
|
|
PropertyProvider *aProvider, Metrics *aMetrics)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-09 18:35:32 -07:00
|
|
|
if (aStart >= aEnd)
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Measure partial ligature. We hack this by clipping the metrics in the
|
|
|
|
// same way we clip the drawing.
|
2007-08-09 18:35:32 -07:00
|
|
|
LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// First measure the complete ligature
|
|
|
|
Metrics metrics;
|
2007-08-09 18:35:32 -07:00
|
|
|
AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
|
2009-02-24 00:32:58 -08:00
|
|
|
aBoundingBoxType, aRefContext,
|
|
|
|
aProvider, aStart, aEnd, &metrics);
|
2008-08-05 21:34:06 -07:00
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
// Clip the bounding box to the ligature part
|
|
|
|
gfxFloat bboxLeft = metrics.mBoundingBox.X();
|
|
|
|
gfxFloat bboxRight = metrics.mBoundingBox.XMost();
|
|
|
|
// Where we are going to start "drawing" relative to our left baseline origin
|
|
|
|
gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
|
|
|
|
ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
|
|
|
|
metrics.mBoundingBox.pos.x = bboxLeft;
|
|
|
|
metrics.mBoundingBox.size.width = bboxRight - bboxLeft;
|
|
|
|
|
|
|
|
// mBoundingBox is now relative to the left baseline origin for the entire
|
|
|
|
// ligature. Shift it left.
|
|
|
|
metrics.mBoundingBox.pos.x -=
|
|
|
|
IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
|
|
|
|
: data.mPartAdvance;
|
|
|
|
metrics.mAdvanceWidth = data.mPartWidth;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-08-12 02:34:52 -07:00
|
|
|
aMetrics->CombineWith(metrics, IsRightToLeft());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxTextRun::Metrics
|
|
|
|
gfxTextRun::MeasureText(PRUint32 aStart, PRUint32 aLength,
|
2009-02-24 00:32:58 -08:00
|
|
|
gfxFont::BoundingBoxType aBoundingBoxType,
|
|
|
|
gfxContext *aRefContext,
|
2007-03-22 10:30:00 -07:00
|
|
|
PropertyProvider *aProvider)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
|
|
|
|
|
|
|
|
Metrics accumulatedMetrics;
|
|
|
|
GlyphRunIterator iter(this, aStart, aLength);
|
|
|
|
while (iter.NextRun()) {
|
|
|
|
gfxFont *font = iter.GetGlyphRun()->mFont;
|
2007-08-09 18:35:32 -07:00
|
|
|
PRUint32 start = iter.GetStringStart();
|
|
|
|
PRUint32 end = iter.GetStringEnd();
|
|
|
|
PRUint32 ligatureRunStart = start;
|
|
|
|
PRUint32 ligatureRunEnd = end;
|
2007-03-22 10:30:00 -07:00
|
|
|
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
|
2009-02-24 00:32:58 -08:00
|
|
|
aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// XXX This sucks. We have to get glyph extents just so we can detect
|
2009-02-24 00:32:58 -08:00
|
|
|
// glyphs outside the font box, even when aBoundingBoxType is LOOSE,
|
2007-03-22 10:30:00 -07:00
|
|
|
// even though in almost all cases we could get correct results just
|
|
|
|
// by getting some ascent/descent from the font and using our stored
|
|
|
|
// advance widths.
|
|
|
|
AccumulateMetricsForRun(font,
|
2009-02-24 00:32:58 -08:00
|
|
|
ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
|
2007-09-23 19:19:14 -07:00
|
|
|
aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
|
2007-03-22 10:30:00 -07:00
|
|
|
&accumulatedMetrics);
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
|
2009-02-24 00:32:58 -08:00
|
|
|
aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return accumulatedMetrics;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MEASUREMENT_BUFFER_SIZE 100
|
|
|
|
|
|
|
|
PRUint32
|
|
|
|
gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
|
|
|
|
PRBool aLineBreakBefore, gfxFloat aWidth,
|
|
|
|
PropertyProvider *aProvider,
|
|
|
|
PRBool aSuppressInitialBreak,
|
2007-05-22 16:40:07 -07:00
|
|
|
gfxFloat *aTrimWhitespace,
|
2009-02-24 00:32:58 -08:00
|
|
|
Metrics *aMetrics,
|
|
|
|
gfxFont::BoundingBoxType aBoundingBoxType,
|
2007-09-23 19:19:14 -07:00
|
|
|
gfxContext *aRefContext,
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool *aUsedHyphenation,
|
2008-07-24 00:16:18 -07:00
|
|
|
PRUint32 *aLastBreak,
|
|
|
|
PRBool aCanWordWrap,
|
|
|
|
gfxBreakPriority *aBreakPriority)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
aMaxLength = PR_MIN(aMaxLength, mCharacterCount - aStart);
|
|
|
|
|
|
|
|
NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range");
|
|
|
|
|
|
|
|
PRUint32 bufferStart = aStart;
|
|
|
|
PRUint32 bufferLength = PR_MIN(aMaxLength, MEASUREMENT_BUFFER_SIZE);
|
|
|
|
PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
|
2007-05-11 22:53:29 -07:00
|
|
|
PRBool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (haveSpacing) {
|
2007-11-08 22:27:23 -08:00
|
|
|
GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
|
2007-03-22 10:30:00 -07:00
|
|
|
spacingBuffer);
|
|
|
|
}
|
|
|
|
PRPackedBool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
|
|
|
|
PRBool haveHyphenation = (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0;
|
|
|
|
if (haveHyphenation) {
|
2007-04-03 19:26:02 -07:00
|
|
|
aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
|
2007-03-22 10:30:00 -07:00
|
|
|
hyphenBuffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFloat width = 0;
|
|
|
|
gfxFloat advance = 0;
|
2007-05-30 22:04:24 -07:00
|
|
|
// The number of space characters that can be trimmed
|
2007-05-22 16:40:07 -07:00
|
|
|
PRUint32 trimmableChars = 0;
|
2007-05-30 22:04:24 -07:00
|
|
|
// The amount of space removed by ignoring trimmableChars
|
|
|
|
gfxFloat trimmableAdvance = 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRInt32 lastBreak = -1;
|
2007-05-30 22:04:24 -07:00
|
|
|
PRInt32 lastBreakTrimmableChars = -1;
|
|
|
|
gfxFloat lastBreakTrimmableAdvance = -1;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool aborted = PR_FALSE;
|
|
|
|
PRUint32 end = aStart + aMaxLength;
|
|
|
|
PRBool lastBreakUsedHyphenation = PR_FALSE;
|
|
|
|
|
|
|
|
PRUint32 ligatureRunStart = aStart;
|
|
|
|
PRUint32 ligatureRunEnd = end;
|
|
|
|
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
|
|
|
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = aStart; i < end; ++i) {
|
|
|
|
if (i >= bufferStart + bufferLength) {
|
|
|
|
// Fetch more spacing and hyphenation data
|
|
|
|
bufferStart = i;
|
|
|
|
bufferLength = PR_MIN(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
|
|
|
|
if (haveSpacing) {
|
2007-11-08 22:27:23 -08:00
|
|
|
GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
|
2007-03-22 10:30:00 -07:00
|
|
|
spacingBuffer);
|
|
|
|
}
|
|
|
|
if (haveHyphenation) {
|
2007-04-03 19:26:02 -07:00
|
|
|
aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
|
2007-03-22 10:30:00 -07:00
|
|
|
hyphenBuffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-27 09:43:07 -07:00
|
|
|
// There can't be a word-wrap break opportunity at the beginning of the
|
|
|
|
// line: if the width is too small for even one character to fit, it
|
|
|
|
// could be the first and last break opportunity on the line, and that
|
|
|
|
// would trigger an infinite loop.
|
|
|
|
if (!aSuppressInitialBreak || i > aStart) {
|
|
|
|
PRBool lineBreakHere = mCharacterGlyphs[i].CanBreakBefore();
|
|
|
|
PRBool hyphenation = haveHyphenation && hyphenBuffer[i - bufferStart];
|
|
|
|
PRBool wordWrapping = aCanWordWrap && *aBreakPriority <= eWordWrapBreak;
|
|
|
|
|
|
|
|
if (lineBreakHere || hyphenation || wordWrapping) {
|
|
|
|
gfxFloat hyphenatedAdvance = advance;
|
|
|
|
if (!lineBreakHere && !wordWrapping) {
|
|
|
|
hyphenatedAdvance += aProvider->GetHyphenWidth();
|
|
|
|
}
|
2007-05-22 16:40:07 -07:00
|
|
|
|
2008-07-27 09:43:07 -07:00
|
|
|
if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
|
|
|
|
// We can break here.
|
|
|
|
lastBreak = i;
|
|
|
|
lastBreakTrimmableChars = trimmableChars;
|
|
|
|
lastBreakTrimmableAdvance = trimmableAdvance;
|
|
|
|
lastBreakUsedHyphenation = !lineBreakHere && !wordWrapping;
|
|
|
|
*aBreakPriority = hyphenation || lineBreakHere ?
|
|
|
|
eNormalBreak : eWordWrapBreak;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-07-27 09:43:07 -07:00
|
|
|
width += advance;
|
|
|
|
advance = 0;
|
|
|
|
if (width - trimmableAdvance > aWidth) {
|
|
|
|
// No more text fits. Abort
|
|
|
|
aborted = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
gfxFloat charAdvance;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (i >= ligatureRunStart && i < ligatureRunEnd) {
|
2007-11-08 22:27:23 -08:00
|
|
|
charAdvance = GetAdvanceForGlyphs(this, i, i + 1);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (haveSpacing) {
|
|
|
|
PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
|
2007-05-22 16:40:07 -07:00
|
|
|
charAdvance += space->mBefore + space->mAfter;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
} else {
|
2007-11-08 22:27:23 -08:00
|
|
|
charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
|
2007-05-22 16:40:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
advance += charAdvance;
|
|
|
|
if (aTrimWhitespace) {
|
|
|
|
if (GetChar(i) == ' ') {
|
|
|
|
++trimmableChars;
|
|
|
|
trimmableAdvance += charAdvance;
|
|
|
|
} else {
|
|
|
|
trimmableAdvance = 0;
|
|
|
|
trimmableChars = 0;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aborted) {
|
|
|
|
width += advance;
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are three possibilities:
|
|
|
|
// 1) all the text fit (width <= aWidth)
|
|
|
|
// 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
|
|
|
|
// 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
|
|
|
|
PRUint32 charsFit;
|
2007-07-01 18:12:45 -07:00
|
|
|
PRBool usedHyphenation = PR_FALSE;
|
2007-05-22 16:40:07 -07:00
|
|
|
if (width - trimmableAdvance <= aWidth) {
|
2007-03-22 10:30:00 -07:00
|
|
|
charsFit = aMaxLength;
|
|
|
|
} else if (lastBreak >= 0) {
|
|
|
|
charsFit = lastBreak - aStart;
|
2007-05-30 22:04:24 -07:00
|
|
|
trimmableChars = lastBreakTrimmableChars;
|
|
|
|
trimmableAdvance = lastBreakTrimmableAdvance;
|
2007-07-01 18:12:45 -07:00
|
|
|
usedHyphenation = lastBreakUsedHyphenation;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
charsFit = aMaxLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aMetrics) {
|
2007-09-23 19:19:14 -07:00
|
|
|
*aMetrics = MeasureText(aStart, charsFit - trimmableChars,
|
2009-02-24 00:32:58 -08:00
|
|
|
aBoundingBoxType, aRefContext, aProvider);
|
2007-05-22 16:40:07 -07:00
|
|
|
}
|
|
|
|
if (aTrimWhitespace) {
|
|
|
|
*aTrimWhitespace = trimmableAdvance;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (aUsedHyphenation) {
|
2007-07-01 18:12:45 -07:00
|
|
|
*aUsedHyphenation = usedHyphenation;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (aLastBreak && charsFit == aMaxLength) {
|
|
|
|
if (lastBreak < 0) {
|
|
|
|
*aLastBreak = PR_UINT32_MAX;
|
|
|
|
} else {
|
|
|
|
*aLastBreak = lastBreak - aStart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return charsFit;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxFloat
|
|
|
|
gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
|
|
|
|
PropertyProvider *aProvider)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
|
|
|
|
|
2007-05-30 22:01:56 -07:00
|
|
|
PRUint32 ligatureRunStart = aStart;
|
|
|
|
PRUint32 ligatureRunEnd = aStart + aLength;
|
|
|
|
ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
|
|
|
|
|
2007-08-09 18:35:32 -07:00
|
|
|
gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
|
|
|
|
ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-05-30 22:01:56 -07:00
|
|
|
// Account for all remaining spacing here. This is more efficient than
|
|
|
|
// processing it along with the glyphs.
|
2007-05-11 22:53:29 -07:00
|
|
|
if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 i;
|
|
|
|
nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
|
|
|
|
if (spacingBuffer.AppendElements(aLength)) {
|
2007-11-08 22:27:23 -08:00
|
|
|
GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
|
2007-03-22 10:30:00 -07:00
|
|
|
spacingBuffer.Elements());
|
2007-05-30 22:01:56 -07:00
|
|
|
for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
|
2007-03-22 10:30:00 -07:00
|
|
|
PropertyProvider::Spacing *space = &spacingBuffer[i];
|
|
|
|
result += space->mBefore + space->mAfter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
return result + GetAdvanceForGlyphs(this, ligatureRunStart, ligatureRunEnd);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 16:07:18 -07:00
|
|
|
PRBool
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxTextRun::SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
|
|
|
|
PRBool aLineBreakBefore, PRBool aLineBreakAfter,
|
2007-06-26 21:22:21 -07:00
|
|
|
gfxFloat *aAdvanceWidthDelta,
|
|
|
|
gfxContext *aRefContext)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// Do nothing because our shaping does not currently take linebreaks into
|
|
|
|
// account. There is no change in advance width.
|
|
|
|
if (aAdvanceWidthDelta) {
|
|
|
|
*aAdvanceWidthDelta = 0;
|
|
|
|
}
|
2007-03-22 16:07:18 -07:00
|
|
|
return PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32
|
|
|
|
gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun");
|
|
|
|
if (aOffset == mCharacterCount)
|
|
|
|
return mGlyphRuns.Length();
|
|
|
|
|
|
|
|
PRUint32 start = 0;
|
|
|
|
PRUint32 end = mGlyphRuns.Length();
|
|
|
|
while (end - start > 1) {
|
|
|
|
PRUint32 mid = (start + end)/2;
|
|
|
|
if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
|
|
|
|
start = mid;
|
|
|
|
} else {
|
|
|
|
end = mid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
|
|
|
|
"Hmm, something went wrong, aOffset should have been found");
|
|
|
|
return start;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2007-07-18 07:22:06 -07:00
|
|
|
gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint32 aUTF16Offset, PRBool aForceNewRun)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-06-12 13:56:04 -07:00
|
|
|
PRUint32 numGlyphRuns = mGlyphRuns.Length();
|
2007-07-18 07:22:06 -07:00
|
|
|
if (!aForceNewRun &&
|
|
|
|
numGlyphRuns > 0)
|
|
|
|
{
|
2007-06-12 13:56:04 -07:00
|
|
|
GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
|
|
|
|
|
|
|
|
NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
|
2007-07-18 07:22:06 -07:00
|
|
|
"Glyph runs out of order (and run not forced)");
|
2007-06-12 13:56:04 -07:00
|
|
|
|
|
|
|
if (lastGlyphRun->mFont == aFont)
|
|
|
|
return NS_OK;
|
|
|
|
if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
|
|
|
|
lastGlyphRun->mFont = aFont;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-18 07:22:06 -07:00
|
|
|
NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
|
|
|
|
"First run doesn't cover the first character (and run not forced)?");
|
2007-06-12 13:56:04 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
GlyphRun *glyphRun = mGlyphRuns.AppendElement();
|
|
|
|
if (!glyphRun)
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
glyphRun->mFont = aFont;
|
|
|
|
glyphRun->mCharacterOffset = aUTF16Offset;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-07-18 07:22:06 -07:00
|
|
|
void
|
|
|
|
gfxTextRun::SortGlyphRuns()
|
|
|
|
{
|
2007-08-26 16:44:00 -07:00
|
|
|
if (mGlyphRuns.Length() <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsTArray<GlyphRun> runs(mGlyphRuns);
|
2007-07-18 07:22:06 -07:00
|
|
|
GlyphRunOffsetComparator comp;
|
2007-08-26 16:44:00 -07:00
|
|
|
runs.Sort(comp);
|
|
|
|
|
|
|
|
// Now copy back, coalescing adjacent glyph runs that have the same font
|
|
|
|
mGlyphRuns.Clear();
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = 0; i < runs.Length(); ++i) {
|
|
|
|
// a GlyphRun with the same font as the previous GlyphRun can just
|
|
|
|
// be skipped; the last GlyphRun will cover its character range.
|
|
|
|
if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
|
|
|
|
mGlyphRuns.AppendElement(runs[i]);
|
2008-10-29 10:24:06 -07:00
|
|
|
// If two fonts have the same character offset, Sort() will have
|
|
|
|
// randomized the order.
|
|
|
|
NS_ASSERTION(i == 0 ||
|
|
|
|
runs[i].mCharacterOffset !=
|
|
|
|
runs[i - 1].mCharacterOffset,
|
|
|
|
"Two fonts for the same run, glyph indices may not match the font");
|
2007-08-26 16:44:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-07-18 07:22:06 -07:00
|
|
|
}
|
|
|
|
|
2009-03-29 17:31:51 -07:00
|
|
|
void
|
|
|
|
gfxTextRun::SanitizeGlyphRuns()
|
|
|
|
{
|
|
|
|
if (mGlyphRuns.Length() <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If any glyph run starts with ligature-continuation characters, we need to advance it
|
|
|
|
// to the first "real" character to avoid drawing partial ligature glyphs from wrong font
|
|
|
|
// (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
|
|
|
|
// it appear as if a ligature has been formed)
|
|
|
|
PRInt32 i;
|
|
|
|
for (i = mGlyphRuns.Length() - 1; i >= 0; --i) {
|
|
|
|
GlyphRun& run = mGlyphRuns[i];
|
|
|
|
while (mCharacterGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
|
|
|
|
run.mCharacterOffset < mCharacterCount) {
|
|
|
|
run.mCharacterOffset++;
|
|
|
|
}
|
|
|
|
// if the run has become empty, eliminate it
|
|
|
|
if ((i < mGlyphRuns.Length() - 1 &&
|
|
|
|
run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
|
|
|
|
(i == mGlyphRuns.Length() - 1 &&
|
|
|
|
run.mCharacterOffset == mCharacterCount)) {
|
|
|
|
mGlyphRuns.RemoveElementAt(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32
|
|
|
|
gfxTextRun::CountMissingGlyphs()
|
|
|
|
{
|
|
|
|
PRUint32 i;
|
|
|
|
PRUint32 count = 0;
|
|
|
|
for (i = 0; i < mCharacterCount; ++i) {
|
|
|
|
if (mCharacterGlyphs[i].IsMissing()) {
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2007-03-26 20:24:49 -07:00
|
|
|
gfxTextRun::DetailedGlyph *
|
|
|
|
gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-01-09 15:33:34 -08:00
|
|
|
NS_ASSERTION(aIndex < mCharacterCount, "Index out of range");
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mCharacterGlyphs)
|
2007-03-26 20:24:49 -07:00
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!mDetailedGlyphs) {
|
|
|
|
mDetailedGlyphs = new nsAutoArrayPtr<DetailedGlyph>[mCharacterCount];
|
|
|
|
if (!mDetailedGlyphs) {
|
2007-11-08 22:27:23 -08:00
|
|
|
mCharacterGlyphs[aIndex].SetMissing(0);
|
2007-03-26 20:24:49 -07:00
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
DetailedGlyph *details = new DetailedGlyph[aCount];
|
|
|
|
if (!details) {
|
2007-11-08 22:27:23 -08:00
|
|
|
mCharacterGlyphs[aIndex].SetMissing(0);
|
2007-03-26 20:24:49 -07:00
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
mDetailedGlyphs[aIndex] = details;
|
2007-03-26 20:24:49 -07:00
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-11-08 22:27:23 -08:00
|
|
|
gfxTextRun::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
|
|
|
|
const DetailedGlyph *aGlyphs)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
|
|
|
|
NS_ASSERTION(aIndex > 0 ||
|
|
|
|
(aGlyph.IsClusterStart() && aGlyph.IsLigatureGroupStart()),
|
|
|
|
"First character must be the start of a cluster and can't be a ligature continuation!");
|
|
|
|
|
|
|
|
PRUint32 glyphCount = aGlyph.GetGlyphCount();
|
|
|
|
if (glyphCount > 0) {
|
|
|
|
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
|
|
|
|
if (!details)
|
|
|
|
return;
|
|
|
|
memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
|
|
|
|
}
|
|
|
|
mCharacterGlyphs[aIndex] = aGlyph;
|
2007-11-07 23:41:06 -08:00
|
|
|
}
|
2007-11-08 22:27:23 -08:00
|
|
|
|
2007-03-26 20:24:49 -07:00
|
|
|
void
|
2007-09-20 03:16:15 -07:00
|
|
|
gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
|
2007-03-26 20:24:49 -07:00
|
|
|
{
|
|
|
|
DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
|
|
|
|
if (!details)
|
|
|
|
return;
|
|
|
|
|
|
|
|
details->mGlyphID = aChar;
|
|
|
|
GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)];
|
|
|
|
gfxFloat width = PR_MAX(glyphRun->mFont->GetMetrics().aveCharWidth,
|
2007-09-20 03:16:15 -07:00
|
|
|
gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
|
2007-03-26 20:24:49 -07:00
|
|
|
details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit());
|
|
|
|
details->mXOffset = 0;
|
|
|
|
details->mYOffset = 0;
|
2007-11-08 22:27:23 -08:00
|
|
|
mCharacterGlyphs[aIndex].SetMissing(1);
|
2007-03-26 20:24:49 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-12 13:56:04 -07:00
|
|
|
static void
|
|
|
|
ClearCharacters(gfxTextRun::CompressedGlyph *aGlyphs, PRUint32 aLength)
|
|
|
|
{
|
2007-11-08 22:27:23 -08:00
|
|
|
memset(aGlyphs, 0, sizeof(gfxTextRun::CompressedGlyph)*aLength);
|
2007-06-12 13:56:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
|
|
|
|
PRUint32 aLength, PRUint32 aDest,
|
|
|
|
PRBool aStealData)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
|
|
|
|
"Source substring out of range");
|
|
|
|
NS_ASSERTION(aDest + aLength <= GetLength(),
|
|
|
|
"Destination substring out of range");
|
|
|
|
|
|
|
|
PRUint32 i;
|
2007-11-08 22:27:23 -08:00
|
|
|
// Copy base character data
|
2007-06-12 13:56:04 -07:00
|
|
|
for (i = 0; i < aLength; ++i) {
|
|
|
|
CompressedGlyph g = aSource->mCharacterGlyphs[i + aStart];
|
2007-07-16 15:50:35 -07:00
|
|
|
g.SetCanBreakBefore(mCharacterGlyphs[i + aDest].CanBreakBefore());
|
2007-06-12 13:56:04 -07:00
|
|
|
mCharacterGlyphs[i + aDest] = g;
|
|
|
|
if (aStealData) {
|
2007-11-08 22:27:23 -08:00
|
|
|
aSource->mCharacterGlyphs[i + aStart].SetMissing(0);
|
2007-06-12 13:56:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
// Copy detailed glyphs
|
2007-06-12 13:56:04 -07:00
|
|
|
if (aSource->mDetailedGlyphs) {
|
|
|
|
for (i = 0; i < aLength; ++i) {
|
|
|
|
DetailedGlyph *details = aSource->mDetailedGlyphs[i + aStart];
|
|
|
|
if (details) {
|
|
|
|
if (aStealData) {
|
|
|
|
if (!mDetailedGlyphs) {
|
|
|
|
mDetailedGlyphs = new nsAutoArrayPtr<DetailedGlyph>[mCharacterCount];
|
|
|
|
if (!mDetailedGlyphs) {
|
|
|
|
ClearCharacters(&mCharacterGlyphs[aDest], aLength);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mDetailedGlyphs[i + aDest] = details;
|
|
|
|
aSource->mDetailedGlyphs[i + aStart].forget();
|
|
|
|
} else {
|
2007-11-08 22:27:23 -08:00
|
|
|
PRUint32 glyphCount = mCharacterGlyphs[i + aDest].GetGlyphCount();
|
2007-06-12 13:56:04 -07:00
|
|
|
DetailedGlyph *dest = AllocateDetailedGlyphs(i + aDest, glyphCount);
|
|
|
|
if (!dest) {
|
|
|
|
ClearCharacters(&mCharacterGlyphs[aDest], aLength);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(dest, details, sizeof(DetailedGlyph)*glyphCount);
|
|
|
|
}
|
|
|
|
} else if (mDetailedGlyphs) {
|
|
|
|
mDetailedGlyphs[i + aDest] = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (mDetailedGlyphs) {
|
|
|
|
for (i = 0; i < aLength; ++i) {
|
|
|
|
mDetailedGlyphs[i + aDest] = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-08 22:27:23 -08:00
|
|
|
// Copy glyph runs
|
2007-06-12 13:56:04 -07:00
|
|
|
GlyphRunIterator iter(aSource, aStart, aLength);
|
2007-08-26 16:44:00 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
gfxFont *lastFont = nsnull;
|
|
|
|
#endif
|
2007-06-12 13:56:04 -07:00
|
|
|
while (iter.NextRun()) {
|
|
|
|
gfxFont *font = iter.GetGlyphRun()->mFont;
|
2007-08-26 16:44:00 -07:00
|
|
|
NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
|
|
|
|
#ifdef DEBUG
|
|
|
|
lastFont = font;
|
2008-09-15 06:46:07 -07:00
|
|
|
PRUint32 end = iter.GetStringEnd();
|
2007-08-26 16:44:00 -07:00
|
|
|
#endif
|
2007-06-12 13:56:04 -07:00
|
|
|
PRUint32 start = iter.GetStringStart();
|
2009-03-29 17:31:51 -07:00
|
|
|
// These assertions are probably not needed; it's possible for us to assign
|
|
|
|
// different fonts to a base character and a following diacritic.
|
|
|
|
// View http://www.alanwood.net/unicode/cyrillic.html on OS X 10.5 for an example.
|
2007-06-12 13:56:04 -07:00
|
|
|
NS_ASSERTION(aSource->IsClusterStart(start),
|
|
|
|
"Started word in the middle of a cluster...");
|
|
|
|
NS_ASSERTION(end == aSource->GetLength() || aSource->IsClusterStart(end),
|
|
|
|
"Ended word in the middle of a cluster...");
|
|
|
|
|
|
|
|
nsresult rv = AddGlyphRun(font, start - aStart + aDest);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, PRUint32 aCharIndex)
|
|
|
|
{
|
|
|
|
PRUint32 spaceGlyph = aFont->GetSpaceGlyph();
|
|
|
|
float spaceWidth = aFont->GetMetrics().spaceWidth;
|
|
|
|
PRUint32 spaceWidthAppUnits = NS_lroundf(spaceWidth*mAppUnitsPerDevUnit);
|
|
|
|
if (!spaceGlyph ||
|
|
|
|
!CompressedGlyph::IsSimpleGlyphID(spaceGlyph) ||
|
|
|
|
!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
|
|
|
|
gfxTextRunFactory::Parameters params = {
|
|
|
|
aContext, nsnull, nsnull, nsnull, 0, mAppUnitsPerDevUnit
|
|
|
|
};
|
|
|
|
static const PRUint8 space = ' ';
|
|
|
|
nsAutoPtr<gfxTextRun> textRun;
|
|
|
|
textRun = mFontGroup->MakeTextRun(&space, 1, ¶ms,
|
|
|
|
gfxTextRunFactory::TEXT_IS_8BIT | gfxTextRunFactory::TEXT_IS_ASCII |
|
|
|
|
gfxTextRunFactory::TEXT_IS_PERSISTENT);
|
|
|
|
if (!textRun || !textRun->mCharacterGlyphs)
|
|
|
|
return;
|
|
|
|
CopyGlyphDataFrom(textRun, 0, 1, aCharIndex, PR_TRUE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddGlyphRun(aFont, aCharIndex);
|
|
|
|
CompressedGlyph g;
|
|
|
|
g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
2007-11-08 22:27:23 -08:00
|
|
|
SetSimpleGlyph(aCharIndex, g);
|
2007-06-12 13:56:04 -07:00
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
|
|
|
|
void
|
|
|
|
gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
|
|
|
|
{
|
2009-06-24 02:04:20 -07:00
|
|
|
PRBool needsGlyphExtents = NeedsGlyphExtents(this);
|
|
|
|
if (!needsGlyphExtents && !mDetailedGlyphs)
|
2007-09-23 19:19:14 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
PRUint32 i;
|
|
|
|
CompressedGlyph *charGlyphs = mCharacterGlyphs;
|
|
|
|
for (i = 0; i < mGlyphRuns.Length(); ++i) {
|
|
|
|
gfxFont *font = mGlyphRuns[i].mFont;
|
|
|
|
PRUint32 start = mGlyphRuns[i].mCharacterOffset;
|
|
|
|
PRUint32 end = i + 1 < mGlyphRuns.Length()
|
|
|
|
? mGlyphRuns[i + 1].mCharacterOffset : GetLength();
|
|
|
|
PRBool fontIsSetup = PR_FALSE;
|
|
|
|
PRUint32 j;
|
|
|
|
gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
|
|
|
|
|
|
|
|
for (j = start; j < end; ++j) {
|
|
|
|
const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
|
|
|
|
if (glyphData->IsSimpleGlyph()) {
|
|
|
|
// If we're in speed mode, don't set up glyph extents here; we'll
|
|
|
|
// just return "optimistic" glyph bounds later
|
2009-06-24 02:04:20 -07:00
|
|
|
if (needsGlyphExtents) {
|
2007-09-23 19:19:14 -07:00
|
|
|
PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
|
|
|
|
if (!extents->IsGlyphKnown(glyphIndex)) {
|
|
|
|
if (!fontIsSetup) {
|
|
|
|
font->SetupCairoFont(aRefContext);
|
2007-11-08 22:27:23 -08:00
|
|
|
fontIsSetup = PR_TRUE;
|
2007-09-23 19:19:14 -07:00
|
|
|
}
|
2007-10-01 18:47:40 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
++gGlyphExtentsSetupEagerSimple;
|
|
|
|
#endif
|
2007-09-23 19:19:14 -07:00
|
|
|
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_FALSE, extents);
|
|
|
|
}
|
|
|
|
}
|
2007-12-20 23:09:50 -08:00
|
|
|
} else if (!glyphData->IsMissing()) {
|
2007-11-08 22:27:23 -08:00
|
|
|
PRUint32 k;
|
|
|
|
PRUint32 glyphCount = glyphData->GetGlyphCount();
|
2007-11-07 23:41:06 -08:00
|
|
|
const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
|
2007-11-08 22:27:23 -08:00
|
|
|
for (k = 0; k < glyphCount; ++k, ++details) {
|
2007-09-23 19:19:14 -07:00
|
|
|
PRUint32 glyphIndex = details->mGlyphID;
|
|
|
|
if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
|
|
|
|
if (!fontIsSetup) {
|
|
|
|
font->SetupCairoFont(aRefContext);
|
|
|
|
fontIsSetup = PR_TRUE;
|
|
|
|
}
|
2007-10-01 18:47:40 -07:00
|
|
|
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
|
|
|
++gGlyphExtentsSetupEagerTight;
|
|
|
|
#endif
|
2007-09-23 19:19:14 -07:00
|
|
|
font->SetupGlyphExtents(aRefContext, glyphIndex, PR_TRUE, extents);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-20 17:23:50 -08:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void
|
|
|
|
gfxTextRun::Dump(FILE* aOutput) {
|
|
|
|
if (!aOutput) {
|
|
|
|
aOutput = stdout;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 i;
|
|
|
|
fputc('"', aOutput);
|
|
|
|
for (i = 0; i < mCharacterCount; ++i) {
|
|
|
|
PRUnichar ch = GetChar(i);
|
|
|
|
if (ch >= 32 && ch < 128) {
|
|
|
|
fputc(ch, aOutput);
|
|
|
|
} else {
|
|
|
|
fprintf(aOutput, "\\u%4x", ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fputs("\" [", aOutput);
|
|
|
|
for (i = 0; i < mGlyphRuns.Length(); ++i) {
|
|
|
|
if (i > 0) {
|
|
|
|
fputc(',', aOutput);
|
|
|
|
}
|
|
|
|
gfxFont* font = mGlyphRuns[i].mFont;
|
|
|
|
const gfxFontStyle* style = font->GetStyle();
|
|
|
|
NS_ConvertUTF16toUTF8 fontName(font->GetName());
|
|
|
|
fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
|
|
|
|
fontName.get(), style->size,
|
|
|
|
style->weight, style->style, style->langGroup.get());
|
|
|
|
}
|
|
|
|
fputc(']', aOutput);
|
|
|
|
}
|
|
|
|
#endif
|