2007-05-20 19:45:45 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2007-03-22 10:30:00 -07:00
|
|
|
* ***** 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-02-16 18:44:12 -08:00
|
|
|
* Mats Palmgren <mats.palmgren@bredband.net>
|
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 ***** */
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
//#define FORCE_UNISCRIBE 1
|
2007-03-22 10:30:00 -07:00
|
|
|
#define FORCE_PR_LOG
|
|
|
|
|
|
|
|
#include "prtypes.h"
|
|
|
|
#include "gfxTypes.h"
|
|
|
|
|
|
|
|
#include "gfxContext.h"
|
|
|
|
#include "gfxWindowsFonts.h"
|
|
|
|
#include "gfxWindowsSurface.h"
|
|
|
|
#include "gfxWindowsPlatform.h"
|
|
|
|
|
|
|
|
#ifdef MOZ_ENABLE_GLITZ
|
|
|
|
#include "gfxGlitzSurface.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gfxFontTest.h"
|
|
|
|
|
|
|
|
#include "cairo.h"
|
|
|
|
#include "cairo-win32.h"
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
2007-09-18 16:12:06 -07:00
|
|
|
#include "nsTArray.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsUnicodeRange.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
|
|
|
|
#include "nsIPrefBranch.h"
|
|
|
|
#include "nsIPrefService.h"
|
2008-02-09 10:47:15 -08:00
|
|
|
#include "nsIPrefLocalizedString.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
|
|
|
|
#include "nsCRT.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "prlog.h"
|
|
|
|
static PRLogModuleInfo *gFontLog = PR_NewLogModule("winfonts");
|
|
|
|
|
|
|
|
#define ROUND(x) floor((x) + 0.5)
|
|
|
|
|
2007-08-07 15:46:42 -07:00
|
|
|
|
|
|
|
struct DCFromContext {
|
|
|
|
DCFromContext(gfxContext *aContext) {
|
2007-08-14 04:46:26 -07:00
|
|
|
dc = NULL;
|
2007-08-07 15:46:42 -07:00
|
|
|
nsRefPtr<gfxASurface> aSurface = aContext->CurrentSurface();
|
2007-08-14 02:47:37 -07:00
|
|
|
NS_ASSERTION(aSurface, "DCFromContext: null surface");
|
2007-09-24 15:50:44 -07:00
|
|
|
if (aSurface &&
|
|
|
|
(aSurface->GetType() == gfxASurface::SurfaceTypeWin32 ||
|
|
|
|
aSurface->GetType() == gfxASurface::SurfaceTypeWin32Printing))
|
|
|
|
{
|
2007-08-07 15:46:42 -07:00
|
|
|
dc = static_cast<gfxWindowsSurface*>(aSurface.get())->GetDC();
|
|
|
|
needsRelease = PR_FALSE;
|
2007-08-14 02:47:37 -07:00
|
|
|
}
|
|
|
|
if (!dc) {
|
2007-08-07 15:46:42 -07:00
|
|
|
dc = GetDC(NULL);
|
|
|
|
needsRelease = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~DCFromContext() {
|
|
|
|
if (needsRelease)
|
|
|
|
ReleaseDC(NULL, dc);
|
|
|
|
}
|
|
|
|
|
|
|
|
operator HDC () {
|
|
|
|
return dc;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-08-07 15:46:42 -07:00
|
|
|
|
|
|
|
HDC dc;
|
|
|
|
PRBool needsRelease;
|
|
|
|
};
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-03-26 14:32:51 -07:00
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
*
|
|
|
|
* class FontFamily
|
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
static nsresult
|
|
|
|
ReadCMAP(HDC hdc, FontEntry *aFontEntry)
|
|
|
|
{
|
|
|
|
const PRUint32 kCMAP = (('c') | ('m' << 8) | ('a' << 16) | ('p' << 24));
|
|
|
|
|
|
|
|
DWORD len = GetFontData(hdc, kCMAP, 0, nsnull, 0);
|
|
|
|
if (len == GDI_ERROR || len == 0) // not a truetype font --
|
|
|
|
return NS_ERROR_FAILURE; // we'll treat it as a symbol font
|
|
|
|
|
|
|
|
nsAutoTArray<PRUint8,16384> buffer;
|
|
|
|
if (!buffer.AppendElements(len))
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
PRUint8 *buf = buffer.Elements();
|
|
|
|
|
|
|
|
DWORD newLen = GetFontData(hdc, kCMAP, 0, buf, len);
|
|
|
|
NS_ENSURE_TRUE(newLen == len, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
return gfxFontUtils::ReadCMAP(buf, len, aFontEntry->mCharacterMap,
|
|
|
|
aFontEntry->mUnicodeFont, aFontEntry->mSymbolFont);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FamilyAddStyleProcData {
|
|
|
|
HDC dc;
|
|
|
|
FontFamily *ff;
|
|
|
|
};
|
|
|
|
|
|
|
|
int CALLBACK
|
|
|
|
FontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
|
|
|
|
const NEWTEXTMETRICEXW *nmetrics,
|
|
|
|
DWORD fontType, LPARAM data)
|
|
|
|
{
|
|
|
|
const NEWTEXTMETRICW& metrics = nmetrics->ntmTm;
|
|
|
|
LOGFONTW logFont = lpelfe->elfLogFont;
|
|
|
|
|
|
|
|
FamilyAddStyleProcData *faspd = reinterpret_cast<FamilyAddStyleProcData*>(data);
|
|
|
|
FontFamily *ff = faspd->ff;
|
|
|
|
HDC hdc = faspd->dc;
|
|
|
|
|
|
|
|
// Some fonts claim to support things > 900, but we don't so clamp the sizes
|
|
|
|
logFont.lfWeight = PR_MAX(PR_MIN(logFont.lfWeight, 900), 100);
|
|
|
|
|
|
|
|
FontEntry *fe = nsnull;
|
|
|
|
for (PRUint32 i = 0; i < ff->mVariations.Length(); ++i) {
|
|
|
|
fe = ff->mVariations[i];
|
|
|
|
|
|
|
|
// check if we already know about this face
|
|
|
|
if (fe->mWeight == logFont.lfWeight &&
|
|
|
|
fe->mItalic == (logFont.lfItalic == 0xFF)) {
|
|
|
|
// update the charset bit here since this could be different
|
|
|
|
fe->mCharset[metrics.tmCharSet] = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fe = new FontEntry(ff->mName);
|
|
|
|
ff->mVariations.AppendElement(fe);
|
|
|
|
|
|
|
|
fe->mItalic = (logFont.lfItalic == 0xFF);
|
|
|
|
fe->mWeight = logFont.lfWeight;
|
|
|
|
|
|
|
|
if (metrics.ntmFlags & NTM_TYPE1)
|
|
|
|
fe->mIsType1 = fe->mForceGDI = PR_TRUE;
|
|
|
|
|
|
|
|
// fontType == TRUETYPE_FONTTYPE when (metrics.ntmFlags & NTM_TT_OPENTYPE)
|
|
|
|
if (fontType == TRUETYPE_FONTTYPE || metrics.ntmFlags & (NTM_PS_OPENTYPE))
|
|
|
|
fe->mTrueType = PR_TRUE;
|
|
|
|
|
|
|
|
// mark the charset bit
|
|
|
|
fe->mCharset[metrics.tmCharSet] = 1;
|
|
|
|
|
|
|
|
fe->mWindowsFamily = logFont.lfPitchAndFamily & 0xF0;
|
|
|
|
fe->mWindowsPitch = logFont.lfPitchAndFamily & 0x0F;
|
|
|
|
|
|
|
|
if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 &&
|
|
|
|
nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 &&
|
|
|
|
nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 &&
|
|
|
|
nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) {
|
|
|
|
|
|
|
|
// set the unicode ranges
|
|
|
|
PRUint32 x = 0;
|
|
|
|
for (PRUint32 i = 0; i < 4; ++i) {
|
|
|
|
DWORD range = nmetrics->ntmFontSig.fsUsb[i];
|
|
|
|
for (PRUint32 k = 0; k < 32; ++k) {
|
|
|
|
fe->mUnicodeRanges[x++] = (range & (1 << k)) != 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-26 14:43:43 -07:00
|
|
|
fe->mIsBadUnderlineFont = ff->mIsBadUnderlineFont;
|
|
|
|
|
2008-03-26 14:32:51 -07:00
|
|
|
// read in the character map
|
|
|
|
logFont.lfCharSet = DEFAULT_CHARSET;
|
|
|
|
HFONT font = CreateFontIndirectW(&logFont);
|
|
|
|
|
|
|
|
NS_ASSERTION(font, "This font creation should never ever ever fail");
|
|
|
|
if (font) {
|
|
|
|
HFONT oldFont = (HFONT)SelectObject(hdc, font);
|
|
|
|
|
|
|
|
// ReadCMAP may change the values of mUnicodeFont and mSymbolFont
|
|
|
|
if (NS_FAILED(ReadCMAP(hdc, fe))) {
|
|
|
|
// Type1 fonts aren't necessarily Unicode but
|
|
|
|
// this is the best guess we can make here
|
|
|
|
if (fe->mIsType1)
|
|
|
|
fe->mUnicodeFont = PR_TRUE;
|
|
|
|
else
|
|
|
|
fe->mUnicodeFont = PR_FALSE;
|
|
|
|
|
|
|
|
// For fonts where we failed to read the character map,
|
|
|
|
// we should use GDI to slowly determine their cmap lazily
|
|
|
|
fe->mForceGDI = PR_TRUE;
|
|
|
|
|
|
|
|
//printf("%d, %s failed to get cmap\n", aFontEntry->mIsType1, NS_ConvertUTF16toUTF8(aFontEntry->mName).get());
|
|
|
|
}
|
|
|
|
|
|
|
|
SelectObject(hdc, oldFont);
|
|
|
|
DeleteObject(font);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// general cmap reading routines moved to gfxFontUtils.cpp
|
|
|
|
void
|
|
|
|
FontFamily::FindStyleVariations()
|
|
|
|
{
|
|
|
|
mHasStyles = PR_TRUE;
|
|
|
|
|
|
|
|
HDC hdc = GetDC(nsnull);
|
|
|
|
|
|
|
|
LOGFONTW logFont;
|
|
|
|
memset(&logFont, 0, sizeof(LOGFONTW));
|
|
|
|
logFont.lfCharSet = DEFAULT_CHARSET;
|
|
|
|
logFont.lfPitchAndFamily = 0;
|
|
|
|
PRUint32 l = PR_MIN(mName.Length(), LF_FACESIZE - 1);
|
|
|
|
memcpy(logFont.lfFaceName,
|
|
|
|
nsPromiseFlatString(mName).get(),
|
|
|
|
l * sizeof(PRUnichar));
|
|
|
|
logFont.lfFaceName[l] = 0;
|
|
|
|
|
|
|
|
FamilyAddStyleProcData faspd;
|
|
|
|
faspd.dc = hdc;
|
|
|
|
faspd.ff = this;
|
|
|
|
|
|
|
|
EnumFontFamiliesExW(hdc, &logFont, (FONTENUMPROCW)FontFamily::FamilyAddStylesProc, (LPARAM)&faspd, 0);
|
|
|
|
|
|
|
|
ReleaseDC(nsnull, hdc);
|
|
|
|
|
|
|
|
// Look for font families without bold variations and add a FontEntry
|
|
|
|
// with synthetic bold (weight 600) for them.
|
|
|
|
FontEntry *darkestItalic = nsnull;
|
|
|
|
FontEntry *darkestNonItalic = nsnull;
|
|
|
|
PRUint8 highestItalic = 0, highestNonItalic = 0;
|
|
|
|
for (PRUint32 i = 0; i < mVariations.Length(); i++) {
|
|
|
|
FontEntry *fe = mVariations[i];
|
|
|
|
if (fe->mItalic) {
|
|
|
|
if (!darkestItalic || fe->mWeight > darkestItalic->mWeight)
|
|
|
|
darkestItalic = fe;
|
|
|
|
} else {
|
|
|
|
if (!darkestNonItalic || fe->mWeight > darkestNonItalic->mWeight)
|
|
|
|
darkestNonItalic = fe;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (darkestItalic && darkestItalic->mWeight < 600) {
|
|
|
|
FontEntry *newEntry = new FontEntry(*darkestItalic);
|
|
|
|
newEntry->mWeight = 600;
|
|
|
|
mVariations.AppendElement(newEntry);
|
|
|
|
}
|
|
|
|
if (darkestNonItalic && darkestNonItalic->mWeight < 600) {
|
|
|
|
FontEntry *newEntry = new FontEntry(*darkestNonItalic);
|
|
|
|
newEntry->mWeight = 600;
|
|
|
|
mVariations.AppendElement(newEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FontEntry *
|
|
|
|
FontFamily::FindFontEntry(const gfxFontStyle& aFontStyle)
|
|
|
|
{
|
|
|
|
if (!mHasStyles)
|
|
|
|
FindStyleVariations();
|
|
|
|
|
|
|
|
PRUint8 bestMatch = 0;
|
|
|
|
PRBool italic = (aFontStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
|
|
|
|
|
|
|
|
FontEntry *weightList[10] = { 0 };
|
|
|
|
for (PRUint32 j = 0; j < 2; j++) {
|
|
|
|
PRBool matchesSomething = PR_FALSE;
|
|
|
|
// build up an array of weights that match the italicness we're looking for
|
|
|
|
for (PRUint32 i = 0; i < mVariations.Length(); i++) {
|
|
|
|
FontEntry *fe = mVariations[i];
|
|
|
|
const PRUint8 weight = (fe->mWeight / 100);
|
|
|
|
if (fe->mItalic == italic) {
|
|
|
|
weightList[weight] = fe;
|
|
|
|
matchesSomething = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (matchesSomething)
|
|
|
|
break;
|
|
|
|
italic = !italic;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
FontEntry *matchFE;
|
|
|
|
const PRInt8 absDistance = abs(weightDistance);
|
|
|
|
direction = (weightDistance >= 0) ? 1 : -1;
|
|
|
|
for (PRInt8 i = matchBaseWeight, k = 0; i < 10 && i > 0; i += direction) {
|
|
|
|
if (weightList[i]) {
|
|
|
|
matchFE = weightList[i];
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
if (k > absDistance)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!matchFE)
|
|
|
|
matchFE = weightList[matchBaseWeight];
|
|
|
|
|
|
|
|
NS_ASSERTION(matchFE, "we should always be able to return something here");
|
|
|
|
return matchFE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/**********************************************************************
|
|
|
|
*
|
|
|
|
* class gfxWindowsFont
|
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
|
2008-03-19 17:41:19 -07:00
|
|
|
gfxWindowsFont::gfxWindowsFont(const nsAString& aName, const gfxFontStyle *aFontStyle, FontEntry *aFontEntry)
|
2007-03-22 10:30:00 -07:00
|
|
|
: gfxFont(aName, aFontStyle),
|
2007-12-19 13:55:35 -08:00
|
|
|
mFont(nsnull), mAdjustedSize(0.0), mScriptCache(nsnull),
|
2007-03-22 10:30:00 -07:00
|
|
|
mFontFace(nsnull), mScaledFont(nsnull),
|
2008-03-19 17:41:19 -07:00
|
|
|
mMetrics(nsnull), mFontEntry(aFontEntry)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-05-29 17:07:03 -07:00
|
|
|
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-13 16:28:25 -07:00
|
|
|
mFont = MakeHFONT(); // create the HFONT, compute metrics, etc
|
|
|
|
NS_ASSERTION(mFont, "Failed to make HFONT");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-06-13 16:28:25 -07:00
|
|
|
gfxWindowsFont::~gfxWindowsFont()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (mFontFace)
|
|
|
|
cairo_font_face_destroy(mFontFace);
|
|
|
|
|
|
|
|
if (mScaledFont)
|
|
|
|
cairo_scaled_font_destroy(mScaledFont);
|
|
|
|
|
|
|
|
if (mFont)
|
|
|
|
DeleteObject(mFont);
|
|
|
|
|
|
|
|
ScriptFreeCache(&mScriptCache);
|
|
|
|
|
|
|
|
delete mMetrics;
|
|
|
|
}
|
|
|
|
|
|
|
|
const gfxFont::Metrics&
|
|
|
|
gfxWindowsFont::GetMetrics()
|
|
|
|
{
|
|
|
|
if (!mMetrics)
|
|
|
|
ComputeMetrics();
|
|
|
|
|
|
|
|
return *mMetrics;
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_font_face_t *
|
|
|
|
gfxWindowsFont::CairoFontFace()
|
|
|
|
{
|
|
|
|
if (!mFontFace)
|
2007-06-13 16:28:25 -07:00
|
|
|
mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&mLogFont, mFont);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_ASSERTION(mFontFace, "Failed to make font face");
|
|
|
|
|
|
|
|
return mFontFace;
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_scaled_font_t *
|
|
|
|
gfxWindowsFont::CairoScaledFont()
|
|
|
|
{
|
2007-06-13 16:28:25 -07:00
|
|
|
if (!mScaledFont) {
|
|
|
|
cairo_matrix_t sizeMatrix;
|
|
|
|
cairo_matrix_t identityMatrix;
|
|
|
|
|
|
|
|
cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
|
|
|
|
cairo_matrix_init_identity(&identityMatrix);
|
|
|
|
|
|
|
|
cairo_font_options_t *fontOptions = cairo_font_options_create();
|
|
|
|
mScaledFont = cairo_scaled_font_create(CairoFontFace(), &sizeMatrix,
|
|
|
|
&identityMatrix, fontOptions);
|
|
|
|
cairo_font_options_destroy(fontOptions);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-08-25 11:33:45 -07:00
|
|
|
NS_ASSERTION(mAdjustedSize == 0.0 ||
|
|
|
|
cairo_scaled_font_status(mScaledFont) == CAIRO_STATUS_SUCCESS,
|
2007-08-06 05:30:13 -07:00
|
|
|
"Failed to make scaled font");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return mScaledFont;
|
|
|
|
}
|
|
|
|
|
|
|
|
HFONT
|
|
|
|
gfxWindowsFont::MakeHFONT()
|
|
|
|
{
|
|
|
|
if (mFont)
|
|
|
|
return mFont;
|
|
|
|
|
2007-04-03 20:32:43 -07:00
|
|
|
mAdjustedSize = GetStyle()->size;
|
2007-12-19 14:16:46 -08:00
|
|
|
if (GetStyle()->sizeAdjust > 0.0) {
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mFont) {
|
2008-03-18 17:06:55 -07:00
|
|
|
FillLogFont(mAdjustedSize);
|
2007-03-22 10:30:00 -07:00
|
|
|
mFont = CreateFontIndirectW(&mLogFont);
|
|
|
|
}
|
|
|
|
|
|
|
|
Metrics *oldMetrics = mMetrics;
|
|
|
|
ComputeMetrics();
|
|
|
|
gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
|
2007-06-08 01:22:03 -07:00
|
|
|
mAdjustedSize = GetStyle()->GetAdjustedSize(aspect);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (mMetrics != oldMetrics) {
|
|
|
|
delete mMetrics;
|
|
|
|
mMetrics = oldMetrics;
|
|
|
|
}
|
|
|
|
DeleteObject(mFont);
|
|
|
|
mFont = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mFont) {
|
2008-03-18 17:06:55 -07:00
|
|
|
FillLogFont(mAdjustedSize);
|
2007-03-22 10:30:00 -07:00
|
|
|
mFont = CreateFontIndirectW(&mLogFont);
|
|
|
|
}
|
|
|
|
|
|
|
|
return mFont;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxWindowsFont::ComputeMetrics()
|
|
|
|
{
|
|
|
|
if (!mMetrics)
|
|
|
|
mMetrics = new gfxFont::Metrics;
|
2007-06-13 16:28:25 -07:00
|
|
|
else
|
|
|
|
NS_WARNING("Calling ComputeMetrics multiple times");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
HDC dc = GetDC((HWND)nsnull);
|
|
|
|
|
2007-06-13 16:28:25 -07:00
|
|
|
HGDIOBJ oldFont = SelectObject(dc, mFont);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Get font metrics
|
|
|
|
OUTLINETEXTMETRIC oMetrics;
|
|
|
|
TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
|
|
|
|
|
|
|
|
if (0 < GetOutlineTextMetrics(dc, sizeof(oMetrics), &oMetrics)) {
|
|
|
|
mMetrics->superscriptOffset = (double)oMetrics.otmptSuperscriptOffset.y;
|
2008-02-16 18:44:12 -08:00
|
|
|
// Some fonts have wrong sign on their subscript offset, bug 410917.
|
|
|
|
mMetrics->subscriptOffset = fabs((double)oMetrics.otmptSubscriptOffset.y);
|
2008-02-15 13:31:38 -08:00
|
|
|
mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize;
|
2007-03-22 10:30:00 -07:00
|
|
|
mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
|
2008-02-15 13:31:38 -08:00
|
|
|
mMetrics->underlineSize = (double)oMetrics.otmsUnderscoreSize;
|
2007-03-22 10:30:00 -07:00
|
|
|
mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
|
2007-05-29 17:07:03 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
|
|
|
|
GLYPHMETRICS gm;
|
|
|
|
DWORD len = GetGlyphOutlineW(dc, PRUnichar('x'), GGO_METRICS, &gm, 0, nsnull, &kIdentityMatrix);
|
|
|
|
if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) {
|
|
|
|
// 56% of ascent, best guess for true type
|
|
|
|
mMetrics->xHeight = ROUND((double)metrics.tmAscent * 0.56);
|
|
|
|
} else {
|
|
|
|
mMetrics->xHeight = gm.gmptGlyphOrigin.y;
|
|
|
|
}
|
2008-02-15 13:31:38 -08:00
|
|
|
mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
|
|
|
|
gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent;
|
|
|
|
mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight);
|
|
|
|
mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
// Make a best-effort guess at extended metrics
|
|
|
|
// this is based on general typographic guidelines
|
|
|
|
GetTextMetrics(dc, &metrics);
|
|
|
|
|
|
|
|
mMetrics->xHeight = ROUND((float)metrics.tmAscent * 0.56f); // 56% of ascent, best guess for non-true type
|
|
|
|
mMetrics->superscriptOffset = mMetrics->xHeight;
|
|
|
|
mMetrics->subscriptOffset = mMetrics->xHeight;
|
|
|
|
mMetrics->strikeoutSize = 1;
|
|
|
|
mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight / 2.0f); // 50% of xHeight
|
|
|
|
mMetrics->underlineSize = 1;
|
|
|
|
mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent
|
2008-02-15 13:31:38 -08:00
|
|
|
mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading;
|
|
|
|
mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading;
|
|
|
|
mMetrics->emDescent = metrics.tmDescent;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
mMetrics->internalLeading = metrics.tmInternalLeading;
|
|
|
|
mMetrics->externalLeading = metrics.tmExternalLeading;
|
|
|
|
mMetrics->maxHeight = metrics.tmHeight;
|
|
|
|
mMetrics->maxAscent = metrics.tmAscent;
|
|
|
|
mMetrics->maxDescent = metrics.tmDescent;
|
|
|
|
mMetrics->maxAdvance = metrics.tmMaxCharWidth;
|
|
|
|
mMetrics->aveCharWidth = PR_MAX(1, metrics.tmAveCharWidth);
|
|
|
|
|
|
|
|
// Cache the width of a single space.
|
|
|
|
SIZE size;
|
2008-03-11 15:46:26 -07:00
|
|
|
GetTextExtentPoint32(dc, " ", 1, &size);
|
2007-03-22 10:30:00 -07:00
|
|
|
mMetrics->spaceWidth = ROUND(size.cx);
|
|
|
|
|
2007-05-08 15:46:14 -07:00
|
|
|
mSpaceGlyph = 0;
|
|
|
|
if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
|
|
|
|
WORD glyph;
|
2008-03-11 15:46:26 -07:00
|
|
|
DWORD ret = GetGlyphIndicesA(dc, " ", 1, &glyph,
|
2007-05-08 15:46:14 -07:00
|
|
|
GGI_MARK_NONEXISTING_GLYPHS);
|
|
|
|
if (ret != GDI_ERROR && glyph != 0xFFFF) {
|
|
|
|
mSpaceGlyph = glyph;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
SelectObject(dc, oldFont);
|
|
|
|
|
|
|
|
ReleaseDC((HWND)nsnull, dc);
|
2008-02-15 13:31:38 -08:00
|
|
|
|
2008-03-12 19:36:58 -07:00
|
|
|
SanitizeMetrics(mMetrics, mFontEntry->IsBadUnderlineFont());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-03-18 17:06:55 -07:00
|
|
|
gfxWindowsFont::FillLogFont(gfxFloat aSize)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
#define CLIP_TURNOFF_FONTASSOCIATION 0x40
|
|
|
|
|
2007-04-20 17:53:00 -07:00
|
|
|
mLogFont.lfHeight = (LONG)-ROUND(aSize);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (mLogFont.lfHeight == 0)
|
|
|
|
mLogFont.lfHeight = -1;
|
|
|
|
|
|
|
|
// Fill in logFont structure
|
2008-03-18 17:06:55 -07:00
|
|
|
mLogFont.lfWidth = 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
mLogFont.lfEscapement = 0;
|
|
|
|
mLogFont.lfOrientation = 0;
|
2007-05-20 19:57:10 -07:00
|
|
|
mLogFont.lfUnderline = FALSE;
|
|
|
|
mLogFont.lfStrikeOut = FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
mLogFont.lfCharSet = DEFAULT_CHARSET;
|
|
|
|
#ifndef WINCE
|
|
|
|
mLogFont.lfOutPrecision = OUT_TT_PRECIS;
|
|
|
|
#else
|
|
|
|
mLogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
|
|
#endif
|
|
|
|
mLogFont.lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION;
|
|
|
|
mLogFont.lfQuality = DEFAULT_QUALITY;
|
|
|
|
mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
2008-03-18 17:06:55 -07:00
|
|
|
// always force lfItalic if we want it. Font selection code will
|
|
|
|
// do its best to give us an italic font entry, but if no face exists
|
|
|
|
// it may give us a regular one based on weight. Windows should
|
|
|
|
// do fake italic for us in that case.
|
2007-04-03 20:32:43 -07:00
|
|
|
mLogFont.lfItalic = (GetStyle()->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) ? TRUE : FALSE;
|
2008-03-19 22:35:05 -07:00
|
|
|
mLogFont.lfWeight = mFontEntry->mWeight;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
int len = PR_MIN(mName.Length(), LF_FACESIZE - 1);
|
|
|
|
memcpy(mLogFont.lfFaceName, nsPromiseFlatString(mName).get(), len * 2);
|
|
|
|
mLogFont.lfFaceName[len] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsString
|
|
|
|
gfxWindowsFont::GetUniqueName()
|
|
|
|
{
|
|
|
|
nsString uniqueName;
|
|
|
|
|
|
|
|
// start with the family name
|
|
|
|
uniqueName.Assign(mName);
|
|
|
|
|
|
|
|
// append the weight code
|
|
|
|
if (mLogFont.lfWeight != 400) {
|
|
|
|
uniqueName.AppendLiteral(":");
|
|
|
|
uniqueName.AppendInt((PRInt32)mLogFont.lfWeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
// append italic?
|
|
|
|
if (mLogFont.lfItalic)
|
|
|
|
uniqueName.AppendLiteral(":Italic");
|
|
|
|
|
|
|
|
if (mLogFont.lfUnderline)
|
|
|
|
uniqueName.AppendLiteral(":Underline");
|
|
|
|
|
|
|
|
if (mLogFont.lfStrikeOut)
|
|
|
|
uniqueName.AppendLiteral(":StrikeOut");
|
|
|
|
|
|
|
|
return uniqueName;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxWindowsFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
|
|
|
|
gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
|
|
|
|
Spacing *aSpacing)
|
|
|
|
{
|
|
|
|
// XXX stuart may want us to do something faster here
|
|
|
|
gfxFont::Draw(aTextRun, aStart, aEnd, aContext, aDrawToPath, aBaselineOrigin,
|
|
|
|
aSpacing);
|
|
|
|
}
|
|
|
|
|
2007-08-06 05:30:13 -07:00
|
|
|
PRBool
|
2007-09-23 19:19:14 -07:00
|
|
|
gfxWindowsFont::SetupCairoFont(gfxContext *aContext)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-06 05:30:13 -07:00
|
|
|
cairo_scaled_font_t *scaledFont = CairoScaledFont();
|
2007-08-25 11:33:45 -07:00
|
|
|
if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
|
|
|
|
// Don't cairo_set_scaled_font as that would propagate the error to
|
|
|
|
// the cairo_t, precluding any further drawing.
|
|
|
|
return PR_FALSE;
|
2007-08-06 05:30:13 -07:00
|
|
|
}
|
2007-09-23 19:19:14 -07:00
|
|
|
cairo_set_scaled_font(aContext->GetCairo(), scaledFont);
|
2007-08-25 11:33:45 -07:00
|
|
|
return PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
*
|
|
|
|
* class gfxWindowsFontGroup
|
|
|
|
*
|
|
|
|
**********************************************************************/
|
|
|
|
|
2007-04-03 20:32:43 -07:00
|
|
|
/**
|
|
|
|
* Look up the font in the gfxFont cache. If we don't find it, create one.
|
|
|
|
* In either case, add a ref, append it to the aFonts array, and return it ---
|
|
|
|
* except for OOM in which case we do nothing and return null.
|
|
|
|
*/
|
|
|
|
static already_AddRefed<gfxWindowsFont>
|
2007-06-10 18:31:27 -07:00
|
|
|
GetOrMakeFont(FontEntry *aFontEntry, const gfxFontStyle *aStyle)
|
2007-04-03 20:32:43 -07:00
|
|
|
{
|
2008-03-27 16:30:43 -07:00
|
|
|
// because we know the FontEntry has the weight we really want, use it for matching
|
|
|
|
// things in the cache so we don't end up with things like 402 in there.
|
|
|
|
gfxFontStyle style(*aStyle);
|
|
|
|
style.weight = aFontEntry->mWeight;
|
|
|
|
// also pre-round the size if there is no size adjust
|
|
|
|
if (style.sizeAdjust == 0.0)
|
|
|
|
style.size = ROUND(style.size);
|
|
|
|
|
|
|
|
nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(aFontEntry->GetName(), &style);
|
2007-04-03 20:32:43 -07:00
|
|
|
if (!font) {
|
2008-03-27 16:30:43 -07:00
|
|
|
font = new gfxWindowsFont(aFontEntry->GetName(), &style, aFontEntry);
|
2007-04-03 20:32:43 -07:00
|
|
|
if (!font)
|
|
|
|
return nsnull;
|
|
|
|
gfxFontCache::GetCache()->AddNew(font);
|
|
|
|
}
|
|
|
|
gfxFont *f = nsnull;
|
|
|
|
font.swap(f);
|
|
|
|
return static_cast<gfxWindowsFont *>(f);
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
static PRBool
|
2008-03-18 17:06:55 -07:00
|
|
|
AddFontNameToArray(const nsAString& aName,
|
|
|
|
const nsACString& aGenericName,
|
|
|
|
void *closure)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (!aName.IsEmpty()) {
|
2008-03-18 17:06:55 -07:00
|
|
|
nsTArray<nsAutoString> *list = static_cast<nsTArray<nsAutoString> *>(closure);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-03-18 17:06:55 -07:00
|
|
|
if (list->IndexOf(aName) == list->NoIndex)
|
|
|
|
list->AppendElement(aName);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2008-03-18 17:06:55 -07:00
|
|
|
void
|
|
|
|
gfxWindowsFontGroup::GroupFamilyListToArrayList(nsTArray<nsRefPtr<FontEntry> > *list)
|
|
|
|
{
|
|
|
|
nsAutoTArray<nsAutoString, 15> fonts;
|
|
|
|
ForEachFont(AddFontNameToArray, &fonts);
|
|
|
|
|
|
|
|
PRUint32 len = fonts.Length();
|
|
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
2008-03-26 14:32:51 -07:00
|
|
|
nsRefPtr<FontEntry> fe = gfxWindowsPlatform::GetPlatform()->FindFontEntry(fonts[i], mStyle);
|
2008-03-18 17:06:55 -07:00
|
|
|
list->AppendElement(fe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxWindowsFontGroup::FamilyListToArrayList(const nsString& aFamilies,
|
|
|
|
const nsCString& aLangGroup,
|
|
|
|
nsTArray<nsRefPtr<FontEntry> > *list)
|
|
|
|
{
|
|
|
|
nsAutoTArray<nsAutoString, 15> fonts;
|
|
|
|
ForEachFont(aFamilies, aLangGroup, AddFontNameToArray, &fonts);
|
|
|
|
|
|
|
|
PRUint32 len = fonts.Length();
|
|
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
|
|
|
const nsAutoString& str = fonts[i];
|
2008-03-26 14:32:51 -07:00
|
|
|
nsRefPtr<FontEntry> fe = gfxWindowsPlatform::GetPlatform()->FindFontEntry(str, mStyle);
|
2008-03-18 17:06:55 -07:00
|
|
|
list->AppendElement(fe);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxWindowsFontGroup::gfxWindowsFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle)
|
|
|
|
: gfxFontGroup(aFamilies, aStyle)
|
|
|
|
{
|
2008-03-18 17:06:55 -07:00
|
|
|
GroupFamilyListToArrayList(&mFontEntries);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-08-24 16:05:07 -07:00
|
|
|
if (mFontEntries.Length() == 0) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// Should append default GUI font if there are no available fonts.
|
|
|
|
HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
|
|
|
|
LOGFONTW logFont;
|
|
|
|
if (!hGDI ||
|
|
|
|
!::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
|
|
|
|
NS_ERROR("Failed to create font group");
|
|
|
|
return;
|
|
|
|
}
|
2008-03-26 14:32:51 -07:00
|
|
|
nsRefPtr<FontEntry> fe = gfxWindowsPlatform::GetPlatform()->FindFontEntry(nsDependentString(logFont.lfFaceName), *aStyle);
|
2007-06-10 18:31:27 -07:00
|
|
|
mFontEntries.AppendElement(fe);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-06-10 18:31:27 -07:00
|
|
|
|
|
|
|
mFonts.AppendElements(mFontEntries.Length());
|
2008-03-12 19:36:58 -07:00
|
|
|
|
|
|
|
for (PRUint32 i = 0; i < mFontEntries.Length(); ++i) {
|
|
|
|
if (mFontEntries[i]->IsBadUnderlineFont()) {
|
|
|
|
gfxFloat first = GetFontAt(0)->GetMetrics().underlineOffset;
|
|
|
|
gfxFloat bad = GetFontAt(i)->GetMetrics().underlineOffset;
|
|
|
|
mUnderlineOffset = PR_MIN(first, bad);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
gfxWindowsFontGroup::~gfxWindowsFontGroup()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
gfxWindowsFont *
|
|
|
|
gfxWindowsFontGroup::GetFontAt(PRInt32 i)
|
|
|
|
{
|
|
|
|
if (!mFonts[i]) {
|
2007-06-10 18:45:41 -07:00
|
|
|
nsRefPtr<gfxWindowsFont> font = GetOrMakeFont(mFontEntries[i], &mStyle);
|
|
|
|
mFonts[i] = font;
|
2007-06-10 18:31:27 -07:00
|
|
|
}
|
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
return static_cast<gfxWindowsFont*>(mFonts[i].get());
|
2007-06-10 18:31:27 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 16:07:18 -07:00
|
|
|
gfxFontGroup *
|
|
|
|
gfxWindowsFontGroup::Copy(const gfxFontStyle *aStyle)
|
|
|
|
{
|
|
|
|
return new gfxWindowsFontGroup(mFamilies, aStyle);
|
|
|
|
}
|
|
|
|
|
2007-07-26 02:47:43 -07:00
|
|
|
static PRBool
|
|
|
|
CanTakeFastPath(PRUint32 aFlags)
|
|
|
|
{
|
|
|
|
// Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't
|
|
|
|
// We need to always use Uniscribe for RTL text, in case glyph mirroring is required
|
|
|
|
return (aFlags &
|
|
|
|
(gfxTextRunFactory::TEXT_OPTIMIZE_SPEED | gfxTextRunFactory::TEXT_IS_RTL)) ==
|
|
|
|
gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxTextRun *
|
|
|
|
gfxWindowsFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
2007-05-08 15:46:14 -07:00
|
|
|
const Parameters *aParams, PRUint32 aFlags)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-04-19 02:04:10 -07:00
|
|
|
// XXX comment out the assertion for now since it fires too much
|
2007-05-08 15:46:14 -07:00
|
|
|
// NS_ASSERTION(!(mFlags & TEXT_NEED_BOUNDING_BOX),
|
2007-04-19 02:04:10 -07:00
|
|
|
// "Glyph extents not yet supported");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-15 17:51:59 -08:00
|
|
|
gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!textRun)
|
|
|
|
return nsnull;
|
2007-05-06 03:41:27 -07:00
|
|
|
NS_ASSERTION(aParams->mContext, "MakeTextRun called without a gfxContext");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
textRun->RecordSurrogates(aString);
|
|
|
|
|
|
|
|
#ifdef FORCE_UNISCRIBE
|
|
|
|
const PRBool isComplex = PR_TRUE;
|
|
|
|
#else
|
2007-07-26 02:47:43 -07:00
|
|
|
const PRBool isComplex = !CanTakeFastPath(aFlags) ||
|
|
|
|
ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
if (isComplex)
|
|
|
|
InitTextRunUniscribe(aParams->mContext, textRun, aString, aLength);
|
|
|
|
else
|
|
|
|
InitTextRunGDI(aParams->mContext, textRun, aString, aLength);
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
textRun->FetchGlyphExtents(aParams->mContext);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return textRun;
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxTextRun *
|
|
|
|
gfxWindowsFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
2007-05-08 15:46:14 -07:00
|
|
|
const Parameters *aParams, PRUint32 aFlags)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-05-08 15:46:14 -07:00
|
|
|
NS_ASSERTION(aFlags & TEXT_IS_8BIT, "should be marked 8bit");
|
|
|
|
|
2007-11-15 17:51:59 -08:00
|
|
|
gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength, this, aFlags);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!textRun)
|
|
|
|
return nsnull;
|
2007-05-06 03:41:27 -07:00
|
|
|
NS_ASSERTION(aParams->mContext, "MakeTextRun called without a gfxContext");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#ifdef FORCE_UNISCRIBE
|
|
|
|
const PRBool isComplex = PR_TRUE;
|
|
|
|
#else
|
2007-07-26 02:47:43 -07:00
|
|
|
const PRBool isComplex = !CanTakeFastPath(aFlags);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
2007-04-26 07:37:11 -07:00
|
|
|
/* We can only call GDI "A" functions if this is a true 7bit ASCII string,
|
|
|
|
because they interpret code points from 0x80-0xFF as if they were
|
|
|
|
in the system code page. */
|
2007-05-08 15:46:14 -07:00
|
|
|
if (!isComplex && (aFlags & TEXT_IS_ASCII)) {
|
2007-04-26 07:37:11 -07:00
|
|
|
InitTextRunGDI(aParams->mContext, textRun,
|
|
|
|
reinterpret_cast<const char*>(aString), aLength);
|
|
|
|
}
|
|
|
|
else {
|
2007-03-22 10:30:00 -07:00
|
|
|
nsDependentCSubstring cString(reinterpret_cast<const char*>(aString),
|
|
|
|
reinterpret_cast<const char*>(aString + aLength));
|
|
|
|
nsAutoString utf16;
|
|
|
|
AppendASCIItoUTF16(cString, utf16);
|
2007-04-26 07:37:11 -07:00
|
|
|
if (isComplex) {
|
|
|
|
InitTextRunUniscribe(aParams->mContext, textRun, utf16.get(), aLength);
|
|
|
|
} else {
|
|
|
|
InitTextRunGDI(aParams->mContext, textRun, utf16.get(), aLength);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-09-23 19:19:14 -07:00
|
|
|
textRun->FetchGlyphExtents(aParams->mContext);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return textRun;
|
|
|
|
}
|
|
|
|
|
2007-03-26 15:48:42 -07:00
|
|
|
/**
|
2007-08-07 15:46:42 -07:00
|
|
|
* Set the font in the given DC. If something goes wrong or if the
|
|
|
|
* font is not a Truetype font (hence GetGlyphIndices may be buggy)
|
|
|
|
* then we're not successful and return PR_FALSE, otherwise PR_TRUE.
|
2007-03-26 15:48:42 -07:00
|
|
|
*/
|
2007-08-07 15:46:42 -07:00
|
|
|
static PRBool
|
|
|
|
SetupDCFont(HDC dc, gfxWindowsFont *aFont)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-03-26 15:48:42 -07:00
|
|
|
HFONT hfont = aFont->GetHFONT();
|
|
|
|
if (!hfont)
|
2007-08-07 15:46:42 -07:00
|
|
|
return PR_FALSE;
|
2007-03-26 15:48:42 -07:00
|
|
|
SelectObject(dc, hfont);
|
|
|
|
|
2008-03-21 15:19:12 -07:00
|
|
|
// GetGlyphIndices is buggy for bitmap and vector fonts, so send them to uniscribe
|
|
|
|
// Also sent Symbol fonts through Uniscribe as it has special code to deal with them
|
|
|
|
if (!aFont->GetFontEntry()->mTrueType || aFont->GetFontEntry()->mSymbolFont)
|
2007-08-07 15:46:42 -07:00
|
|
|
return PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-08-07 15:46:42 -07:00
|
|
|
return PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-03-26 15:48:42 -07:00
|
|
|
static PRBool
|
|
|
|
IsAnyGlyphMissing(WCHAR *aGlyphs, PRUint32 aLength)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
PRUint32 i;
|
2007-03-26 15:48:42 -07:00
|
|
|
for (i = 0; i < aLength; ++i) {
|
|
|
|
if (aGlyphs[i] == 0xFFFF)
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PRBool
|
|
|
|
SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC,
|
|
|
|
gfxWindowsFont *aFont)
|
|
|
|
{
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 length = aRun->GetLength();
|
2007-03-26 15:48:42 -07:00
|
|
|
if (IsAnyGlyphMissing(aGlyphs, length))
|
|
|
|
return PR_FALSE;
|
2007-05-29 17:07:03 -07:00
|
|
|
|
2007-03-26 15:48:42 -07:00
|
|
|
SIZE size;
|
2007-09-18 16:12:06 -07:00
|
|
|
nsAutoTArray<int,500> partialWidthArray;
|
|
|
|
if (!partialWidthArray.SetLength(length))
|
2007-03-26 15:48:42 -07:00
|
|
|
return PR_FALSE;
|
|
|
|
BOOL success = GetTextExtentExPointI(aDC,
|
2007-04-04 11:42:21 -07:00
|
|
|
(WORD*) aGlyphs,
|
2007-03-26 15:48:42 -07:00
|
|
|
length,
|
|
|
|
INT_MAX,
|
|
|
|
NULL,
|
2007-09-18 16:12:06 -07:00
|
|
|
partialWidthArray.Elements(),
|
2007-03-26 15:48:42 -07:00
|
|
|
&size);
|
|
|
|
if (!success)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
aRun->AddGlyphRun(aFont, 0);
|
|
|
|
|
|
|
|
gfxTextRun::CompressedGlyph g;
|
|
|
|
PRUint32 i;
|
|
|
|
PRInt32 lastWidth = 0;
|
|
|
|
PRUint32 appUnitsPerDevPixel = aRun->GetAppUnitsPerDevUnit();
|
2007-03-22 10:30:00 -07:00
|
|
|
for (i = 0; i < length; ++i) {
|
2007-03-26 15:48:42 -07:00
|
|
|
PRInt32 advancePixels = partialWidthArray[i] - lastWidth;
|
|
|
|
lastWidth = partialWidthArray[i];
|
|
|
|
PRInt32 advanceAppUnits = advancePixels*appUnitsPerDevPixel;
|
2007-03-22 10:30:00 -07:00
|
|
|
WCHAR glyph = aGlyphs[i];
|
2007-07-03 20:39:01 -07:00
|
|
|
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aRun->GetChar(i)),
|
|
|
|
"Invalid character detected!");
|
|
|
|
if (advanceAppUnits >= 0 &&
|
2007-03-22 10:30:00 -07:00
|
|
|
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
|
|
|
|
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
|
2007-11-08 22:27:23 -08:00
|
|
|
aRun->SetSimpleGlyph(i, g.SetSimpleGlyph(advanceAppUnits, glyph));
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
gfxTextRun::DetailedGlyph details;
|
|
|
|
details.mGlyphID = glyph;
|
|
|
|
details.mAdvance = advanceAppUnits;
|
|
|
|
details.mXOffset = 0;
|
|
|
|
details.mYOffset = 0;
|
2007-11-08 22:27:23 -08:00
|
|
|
aRun->SetGlyphs(i, g.SetComplex(PR_TRUE, PR_TRUE, 1), &details);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-03-26 15:48:42 -07:00
|
|
|
return PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxWindowsFontGroup::InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun,
|
|
|
|
const char *aString, PRUint32 aLength)
|
|
|
|
{
|
2007-06-10 18:31:27 -07:00
|
|
|
nsRefPtr<gfxWindowsFont> font = GetFontAt(0);
|
2007-08-07 15:46:42 -07:00
|
|
|
DCFromContext dc(aContext);
|
|
|
|
if (SetupDCFont(dc, font)) {
|
2007-09-18 16:12:06 -07:00
|
|
|
nsAutoTArray<WCHAR,500> glyphArray;
|
|
|
|
if (!glyphArray.SetLength(aLength))
|
2007-03-26 15:48:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-18 16:12:06 -07:00
|
|
|
DWORD ret = GetGlyphIndicesA(dc, aString, aLength, (WORD*) glyphArray.Elements(),
|
2007-03-26 15:48:42 -07:00
|
|
|
GGI_MARK_NONEXISTING_GLYPHS);
|
|
|
|
if (ret != GDI_ERROR &&
|
2007-09-18 16:12:06 -07:00
|
|
|
SetupTextRunFromGlyphs(aRun, glyphArray.Elements(), dc, font))
|
2007-03-26 15:48:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-03-26 15:48:42 -07:00
|
|
|
nsDependentCSubstring cString(aString, aString + aLength);
|
|
|
|
nsAutoString utf16;
|
|
|
|
AppendASCIItoUTF16(cString, utf16);
|
|
|
|
InitTextRunUniscribe(aContext, aRun, utf16.get(), aLength);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxWindowsFontGroup::InitTextRunGDI(gfxContext *aContext, gfxTextRun *aRun,
|
|
|
|
const PRUnichar *aString, PRUint32 aLength)
|
|
|
|
{
|
2007-06-10 18:31:27 -07:00
|
|
|
nsRefPtr<gfxWindowsFont> font = GetFontAt(0);
|
2007-08-07 15:46:42 -07:00
|
|
|
DCFromContext dc(aContext);
|
|
|
|
if (SetupDCFont(dc, font)) {
|
2007-09-18 16:12:06 -07:00
|
|
|
nsAutoTArray<WCHAR,500> glyphArray;
|
|
|
|
if (!glyphArray.SetLength(aLength))
|
2007-03-26 15:48:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-18 16:12:06 -07:00
|
|
|
DWORD ret = GetGlyphIndicesW(dc, aString, aLength, (WORD*) glyphArray.Elements(),
|
2007-03-26 15:48:42 -07:00
|
|
|
GGI_MARK_NONEXISTING_GLYPHS);
|
|
|
|
if (ret != GDI_ERROR &&
|
2007-09-18 16:12:06 -07:00
|
|
|
SetupTextRunFromGlyphs(aRun, glyphArray.Elements(), dc, font))
|
2007-03-26 15:48:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-03-26 15:48:42 -07:00
|
|
|
InitTextRunUniscribe(aContext, aRun, aString, aLength);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*******************
|
|
|
|
* Uniscribe
|
|
|
|
*******************/
|
|
|
|
|
|
|
|
/* we map primary language id's to this to look up language codes */
|
|
|
|
struct ScriptPropertyEntry {
|
|
|
|
const char *value;
|
|
|
|
const char *langCode;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct ScriptPropertyEntry gScriptToText[] =
|
|
|
|
{
|
|
|
|
{ nsnull, nsnull },
|
2008-03-26 14:32:51 -07:00
|
|
|
{ "LANG_ARABIC", "ar" }, // ara
|
2007-03-22 10:30:00 -07:00
|
|
|
{ "LANG_BULGARIAN", "bul" },
|
|
|
|
{ "LANG_CATALAN", "cat" },
|
|
|
|
{ "LANG_CHINESE", "zh-CN" }, //XXX right lang code?
|
|
|
|
{ "LANG_CZECH", "cze" }, // cze/ces
|
|
|
|
{ "LANG_DANISH", "dan" },
|
|
|
|
{ "LANG_GERMAN", "ger" }, // ger/deu
|
|
|
|
{ "LANG_GREEK", "el" }, // gre/ell
|
|
|
|
{ "LANG_ENGLISH", "x-western" },
|
|
|
|
{ "LANG_SPANISH", "spa" },
|
|
|
|
{ "LANG_FINNISH", "fin" },
|
|
|
|
{ "LANG_FRENCH", "fre" }, // fre/fra
|
|
|
|
{ "LANG_HEBREW", "he" }, // heb
|
|
|
|
{ "LANG_HUNGARIAN", "hun" },
|
|
|
|
{ "LANG_ICELANDIC", "ice" }, // ice/isl
|
|
|
|
{ "LANG_ITALIAN", "ita" },
|
|
|
|
{ "LANG_JAPANESE", "ja" }, // jpn
|
|
|
|
{ "LANG_KOREAN", "ko" }, // kor
|
|
|
|
{ "LANG_DUTCH", "dut" }, // dut/nld
|
|
|
|
{ "LANG_NORWEGIAN", "nor" },
|
|
|
|
{ "LANG_POLISH", "pol" },
|
|
|
|
{ "LANG_PORTUGUESE", "por" },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_ROMANIAN", "rum" }, // rum/ron
|
|
|
|
{ "LANG_RUSSIAN", "rus" },
|
|
|
|
{ "LANG_SERBIAN", "scc" }, // scc/srp
|
|
|
|
{ "LANG_SLOVAK", "slo" }, // slo/slk
|
|
|
|
{ "LANG_ALBANIAN", "alb" }, // alb/sqi
|
|
|
|
{ "LANG_SWEDISH", "swe" },
|
|
|
|
{ "LANG_THAI", "th" }, // tha
|
|
|
|
{ "LANG_TURKISH", "tr" }, // tur
|
|
|
|
{ "LANG_URDU", "urd" },
|
|
|
|
{ "LANG_INDONESIAN", "ind" },
|
|
|
|
{ "LANG_UKRAINIAN", "ukr" },
|
|
|
|
{ "LANG_BELARUSIAN", "bel" },
|
|
|
|
{ "LANG_SLOVENIAN", "slv" },
|
|
|
|
{ "LANG_ESTONIAN", "est" },
|
|
|
|
{ "LANG_LATVIAN", "lav" },
|
|
|
|
{ "LANG_LITHUANIAN", "lit" },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_FARSI", "per" }, // per/fas
|
|
|
|
{ "LANG_VIETNAMESE", "vie" },
|
|
|
|
{ "LANG_ARMENIAN", "x-armn" }, // arm/hye
|
|
|
|
{ "LANG_AZERI", "aze" },
|
|
|
|
{ "LANG_BASQUE", "baq" }, // baq/eus
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_MACEDONIAN", "mac" }, // mac/mkd
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_AFRIKAANS", "afr" },
|
|
|
|
{ "LANG_GEORGIAN", "x-geor" }, // geo
|
|
|
|
{ "LANG_FAEROESE", "fao" },
|
|
|
|
{ "LANG_HINDI", "x-devanagari" }, // hin
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_MALAY", "may" }, // may/msa
|
|
|
|
{ "LANG_KAZAK", "kaz" }, // listed as kazakh?
|
|
|
|
{ "LANG_KYRGYZ", "kis" },
|
|
|
|
{ "LANG_SWAHILI", "swa" },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_UZBEK", "uzb" },
|
|
|
|
{ "LANG_TATAR", "tat" },
|
|
|
|
{ "LANG_BENGALI", "x-beng" }, // ben
|
|
|
|
{ "LANG_PUNJABI", "x-guru" }, // pan -- XXX x-guru is for Gurmukhi which isn't just Punjabi
|
|
|
|
{ "LANG_GUJARATI", "x-gujr" }, // guj
|
|
|
|
{ "LANG_ORIYA", "ori" },
|
|
|
|
{ "LANG_TAMIL", "x-tamil" }, // tam
|
|
|
|
{ "LANG_TELUGU", "tel" },
|
|
|
|
{ "LANG_KANNADA", "kan" },
|
|
|
|
{ "LANG_MALAYALAM", "x-mlym" }, // mal
|
|
|
|
{ "LANG_ASSAMESE", "asm" },
|
|
|
|
{ "LANG_MARATHI", "mar" },
|
|
|
|
{ "LANG_SANSKRIT", "san" },
|
|
|
|
{ "LANG_MONGOLIAN", "mon" },
|
|
|
|
{ "TIBETAN", "tib" }, // tib/bod
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "KHMER", "x-khmr" }, // khm
|
|
|
|
{ "LAO", "lao" },
|
|
|
|
{ "MYANMAR", "bur" }, // bur/mya
|
|
|
|
{ "LANG_GALICIAN", "glg" },
|
|
|
|
{ "LANG_KONKANI", "kok" },
|
|
|
|
{ "LANG_MANIPURI", "mni" },
|
|
|
|
{ "LANG_SINDHI", "x-devanagari" }, // snd
|
|
|
|
{ "LANG_SYRIAC", "syr" },
|
|
|
|
{ "SINHALESE", "sin" },
|
|
|
|
{ "CHEROKEE", "chr" },
|
|
|
|
{ "INUKTITUT", "x-cans" }, // iku
|
|
|
|
{ "ETHIOPIC", "x-ethi" }, // amh -- this is both Amharic and Tigrinya
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_KASHMIRI", "x-devanagari" }, // kas
|
|
|
|
{ "LANG_NEPALI", "x-devanagari" }, // nep
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ nsnull, nsnull },
|
|
|
|
{ "LANG_DIVEHI", "div" }
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *sCJKLangGroup[] = {
|
|
|
|
"ja",
|
|
|
|
"ko",
|
|
|
|
"zh-CN",
|
|
|
|
"zh-HK",
|
|
|
|
"zh-TW"
|
|
|
|
};
|
|
|
|
|
|
|
|
#define COUNT_OF_CJK_LANG_GROUP 5
|
|
|
|
#define CJK_LANG_JA sCJKLangGroup[0]
|
|
|
|
#define CJK_LANG_KO sCJKLangGroup[1]
|
|
|
|
#define CJK_LANG_ZH_CN sCJKLangGroup[2]
|
|
|
|
#define CJK_LANG_ZH_HK sCJKLangGroup[3]
|
|
|
|
#define CJK_LANG_ZH_TW sCJKLangGroup[4]
|
|
|
|
|
|
|
|
#define STATIC_STRING_LENGTH 100
|
|
|
|
|
2007-11-06 22:35:38 -08:00
|
|
|
#define ESTIMATE_MAX_GLYPHS(L) (((3 * (L)) >> 1) + 16)
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
class UniscribeItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UniscribeItem(gfxContext *aContext, HDC aDC,
|
|
|
|
const PRUnichar *aString, PRUint32 aLength,
|
|
|
|
SCRIPT_ITEM *aItem,
|
|
|
|
gfxWindowsFontGroup *aGroup) :
|
2007-06-10 18:31:27 -07:00
|
|
|
mContext(aContext), mDC(aDC), mRangeString(nsnull), mRangeLength(0),
|
|
|
|
mItemString(aString), mItemLength(aLength),
|
|
|
|
mAlternativeString(nsnull), mScriptItem(aItem),
|
2007-03-22 10:30:00 -07:00
|
|
|
mScript(aItem->a.eScript), mGroup(aGroup),
|
2007-11-06 22:35:38 -08:00
|
|
|
mNumGlyphs(0), mMaxGlyphs(ESTIMATE_MAX_GLYPHS(aLength)),
|
2007-03-22 10:30:00 -07:00
|
|
|
mFontSelected(PR_FALSE)
|
|
|
|
{
|
2007-11-06 22:35:38 -08:00
|
|
|
NS_ASSERTION(mMaxGlyphs < 65535, "UniscribeItem is too big, ScriptShape() will fail!");
|
2007-09-18 16:12:06 -07:00
|
|
|
mGlyphs.SetLength(mMaxGlyphs);
|
|
|
|
mClusters.SetLength(mItemLength + 1);
|
|
|
|
mAttr.SetLength(mMaxGlyphs);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
~UniscribeItem() {
|
|
|
|
free(mAlternativeString);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* possible return values:
|
2007-05-29 17:07:03 -07:00
|
|
|
* S_OK - things succeeded
|
|
|
|
* GDI_ERROR - things failed to shape. Might want to try again after calling DisableShaping()
|
|
|
|
*/
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-03-12 21:15:24 -07:00
|
|
|
HRESULT ShapeUniscribe() {
|
|
|
|
HRESULT rv;
|
2007-03-22 10:30:00 -07:00
|
|
|
HDC shapeDC = nsnull;
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
const PRUnichar *str = mAlternativeString ? mAlternativeString : mRangeString;
|
2007-05-29 17:07:03 -07:00
|
|
|
|
2007-09-25 08:41:34 -07:00
|
|
|
mScriptItem->a.fLogicalOrder = PR_TRUE;
|
2007-08-26 10:28:56 -07:00
|
|
|
SCRIPT_ANALYSIS sa = mScriptItem->a;
|
|
|
|
/*
|
|
|
|
fLinkBefore and fLinkAfter in the SCRIPT_ANALYSIS structure refer to
|
|
|
|
the whole item, so if the current range begins after the beginning
|
|
|
|
of the item or ends before the end of the item, we need to override
|
|
|
|
them here.
|
|
|
|
This assumes that we won't split an item into ranges between two
|
|
|
|
characters that need to be shaped together.
|
|
|
|
*/
|
|
|
|
if (mRangeString > mItemString)
|
|
|
|
sa.fLinkBefore = PR_FALSE;
|
|
|
|
if (mRangeString + mRangeLength < mItemString + mItemLength)
|
|
|
|
sa.fLinkAfter = PR_FALSE;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
while (PR_TRUE) {
|
2007-04-02 23:32:23 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
rv = ScriptShape(shapeDC, mCurrentFont->ScriptCache(),
|
2007-06-10 18:31:27 -07:00
|
|
|
str, mRangeLength,
|
2007-08-26 10:28:56 -07:00
|
|
|
mMaxGlyphs, &sa,
|
2007-09-18 16:12:06 -07:00
|
|
|
mGlyphs.Elements(), mClusters.Elements(),
|
|
|
|
mAttr.Elements(), &mNumGlyphs);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (rv == E_OUTOFMEMORY) {
|
2007-09-18 16:12:06 -07:00
|
|
|
mGlyphs.SetLength(mMaxGlyphs);
|
|
|
|
mAttr.SetLength(mMaxGlyphs);
|
2007-03-22 10:30:00 -07:00
|
|
|
mMaxGlyphs *= 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rv == E_PENDING) {
|
2007-08-07 15:46:42 -07:00
|
|
|
if (shapeDC == mDC) {
|
|
|
|
// we already tried this once, something failed, give up
|
|
|
|
return GDI_ERROR;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
SelectFont();
|
|
|
|
|
|
|
|
shapeDC = mDC;
|
|
|
|
continue;
|
|
|
|
}
|
2008-03-12 21:15:24 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-12 21:15:24 -07:00
|
|
|
HRESULT ShapeGDI() {
|
|
|
|
SelectFont();
|
|
|
|
|
|
|
|
mNumGlyphs = mRangeLength;
|
|
|
|
GetGlyphIndicesW(mDC, mRangeString, mRangeLength,
|
|
|
|
(WORD*) mGlyphs.Elements(),
|
|
|
|
GGI_MARK_NONEXISTING_GLYPHS);
|
|
|
|
|
2008-03-21 13:22:58 -07:00
|
|
|
for (PRUint32 i = 0; i < mRangeLength; ++i)
|
2008-03-12 21:15:24 -07:00
|
|
|
mClusters[i] = i;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT Shape() {
|
2008-03-21 13:22:58 -07:00
|
|
|
// Skip Uniscribe for fonts that need GDI
|
|
|
|
if (mCurrentFont->GetFontEntry()->mForceGDI)
|
2008-03-12 21:15:24 -07:00
|
|
|
return ShapeGDI();
|
|
|
|
|
|
|
|
return ShapeUniscribe();
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool ShapingEnabled() {
|
|
|
|
return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
|
|
|
|
}
|
|
|
|
void DisableShaping() {
|
|
|
|
mScriptItem->a.eScript = SCRIPT_UNDEFINED;
|
|
|
|
// Note: If we disable the shaping by using SCRIPT_UNDEFINED and
|
|
|
|
// the string has the surrogate pair, ScriptShape API is
|
|
|
|
// *sometimes* crashed. Therefore, we should replace the surrogate
|
|
|
|
// pair to U+FFFD. See bug 341500.
|
|
|
|
GenerateAlternativeString();
|
|
|
|
}
|
2007-06-10 18:31:27 -07:00
|
|
|
void EnableShaping() {
|
|
|
|
mScriptItem->a.eScript = mScript;
|
|
|
|
if (mAlternativeString) {
|
|
|
|
free(mAlternativeString);
|
|
|
|
mAlternativeString = nsnull;
|
2007-05-29 08:09:23 -07:00
|
|
|
}
|
2007-04-02 23:32:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex) {
|
2008-03-14 16:02:32 -07:00
|
|
|
PRBool missing = PR_FALSE;
|
2008-03-21 13:22:58 -07:00
|
|
|
if (GetCurrentFont()->GetFontEntry()->mForceGDI) {
|
|
|
|
// Our GDI path marks missing glyphs as 0xFFFF. So just look for that.
|
2008-03-14 16:02:32 -07:00
|
|
|
if (mGlyphs[aGlyphIndex] == 0xFFFF)
|
|
|
|
missing = PR_TRUE;
|
|
|
|
} else if (mGlyphs[aGlyphIndex] == aSFP->wgDefault) {
|
|
|
|
missing = PR_TRUE;
|
|
|
|
}
|
|
|
|
return missing;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-14 20:50:15 -08:00
|
|
|
HRESULT PlaceUniscribe() {
|
|
|
|
HRESULT rv;
|
2007-03-22 10:30:00 -07:00
|
|
|
HDC placeDC = nsnull;
|
|
|
|
|
|
|
|
while (PR_TRUE) {
|
|
|
|
rv = ScriptPlace(placeDC, mCurrentFont->ScriptCache(),
|
2007-09-18 16:12:06 -07:00
|
|
|
mGlyphs.Elements(), mNumGlyphs,
|
|
|
|
mAttr.Elements(), &mScriptItem->a,
|
|
|
|
mAdvances.Elements(), mOffsets.Elements(), NULL);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (rv == E_PENDING) {
|
|
|
|
SelectFont();
|
|
|
|
placeDC = mDC;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2008-02-14 20:50:15 -08:00
|
|
|
HRESULT PlaceGDI() {
|
|
|
|
SelectFont();
|
|
|
|
|
|
|
|
nsAutoTArray<int,500> partialWidthArray;
|
|
|
|
if (!partialWidthArray.SetLength(mNumGlyphs))
|
|
|
|
return GDI_ERROR;
|
|
|
|
SIZE size;
|
|
|
|
|
|
|
|
GetTextExtentExPointI(mDC,
|
|
|
|
(WORD*) mGlyphs.Elements(),
|
|
|
|
mNumGlyphs,
|
|
|
|
INT_MAX,
|
|
|
|
NULL,
|
|
|
|
partialWidthArray.Elements(),
|
|
|
|
&size);
|
|
|
|
|
|
|
|
PRInt32 lastWidth = 0;
|
|
|
|
|
|
|
|
for (PRUint32 i = 0; i < mNumGlyphs; i++) {
|
|
|
|
mAdvances[i] = partialWidthArray[i] - lastWidth;
|
|
|
|
lastWidth = partialWidthArray[i];
|
|
|
|
mOffsets[i].du = mOffsets[i].dv = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT Place() {
|
|
|
|
mOffsets.SetLength(mNumGlyphs);
|
|
|
|
mAdvances.SetLength(mNumGlyphs);
|
|
|
|
|
|
|
|
PRBool allCJK = PR_TRUE;
|
|
|
|
|
2008-03-21 13:22:58 -07:00
|
|
|
// Some fonts don't get along with Uniscribe so we'll use GDI to
|
|
|
|
// render them.
|
|
|
|
if (!mCurrentFont->GetFontEntry()->mForceGDI) {
|
2008-03-12 21:15:24 -07:00
|
|
|
for (PRUint32 i = 0; i < mRangeLength; i++) {
|
|
|
|
const PRUnichar ch = mRangeString[i];
|
|
|
|
if (ch == ' ' || FindCharUnicodeRange(ch) == kRangeSetCJK)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
allCJK = PR_FALSE;
|
|
|
|
break;
|
|
|
|
}
|
2008-02-14 20:50:15 -08:00
|
|
|
}
|
|
|
|
|
2008-03-21 13:22:58 -07:00
|
|
|
if (allCJK || mCurrentFont->GetFontEntry()->mForceGDI)
|
2008-02-14 20:50:15 -08:00
|
|
|
return PlaceGDI();
|
|
|
|
|
|
|
|
return PlaceUniscribe();
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
const SCRIPT_PROPERTIES *ScriptProperties() {
|
|
|
|
/* we can use this to figure out in some cases the language of the item */
|
|
|
|
static const SCRIPT_PROPERTIES **gScriptProperties;
|
|
|
|
static int gMaxScript = -1;
|
|
|
|
|
|
|
|
if (gMaxScript == -1) {
|
|
|
|
ScriptGetProperties(&gScriptProperties, &gMaxScript);
|
|
|
|
}
|
|
|
|
return gScriptProperties[mScript];
|
|
|
|
}
|
|
|
|
|
|
|
|
void ScriptFontProperties(SCRIPT_FONTPROPERTIES *sfp) {
|
|
|
|
HRESULT rv;
|
|
|
|
|
|
|
|
memset(sfp, 0, sizeof(SCRIPT_FONTPROPERTIES));
|
|
|
|
sfp->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
|
|
|
|
rv = ScriptGetFontProperties(NULL, mCurrentFont->ScriptCache(),
|
|
|
|
sfp);
|
|
|
|
if (rv == E_PENDING) {
|
|
|
|
SelectFont();
|
|
|
|
rv = ScriptGetFontProperties(mDC, mCurrentFont->ScriptCache(),
|
|
|
|
sfp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetupClusterBoundaries(gfxTextRun *aRun, PRUint32 aOffsetInRun) {
|
|
|
|
if (aRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsAutoTArray<SCRIPT_LOGATTR,STATIC_STRING_LENGTH> logAttr;
|
2007-06-10 18:31:27 -07:00
|
|
|
if (!logAttr.AppendElements(mRangeLength))
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
2007-06-10 18:31:27 -07:00
|
|
|
HRESULT rv = ScriptBreak(mRangeString, mRangeLength,
|
|
|
|
&mScriptItem->a, logAttr.Elements());
|
2007-03-22 10:30:00 -07:00
|
|
|
if (FAILED(rv))
|
|
|
|
return;
|
|
|
|
gfxTextRun::CompressedGlyph g;
|
2007-04-02 20:19:46 -07:00
|
|
|
// The first character is never inside a cluster. Windows might tell us
|
|
|
|
// that it should be, but we have no before-character to cluster
|
|
|
|
// it with so we just can't cluster it. So skip it here.
|
2007-06-10 18:31:27 -07:00
|
|
|
for (PRUint32 i = 1; i < mRangeLength; ++i) {
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!logAttr[i].fCharStop) {
|
2007-11-08 22:27:23 -08:00
|
|
|
aRun->SetGlyphs(i + aOffsetInRun, g.SetComplex(PR_FALSE, PR_TRUE, 0), nsnull);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SaveGlyphs(gfxTextRun *aRun) {
|
2007-06-10 18:31:27 -07:00
|
|
|
PRUint32 offsetInRun = mScriptItem->iCharPos + (mRangeString - mItemString);
|
2007-03-22 10:30:00 -07:00
|
|
|
SetupClusterBoundaries(aRun, offsetInRun);
|
|
|
|
|
2007-03-26 20:24:49 -07:00
|
|
|
aRun->AddGlyphRun(GetCurrentFont(), offsetInRun);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// XXX We should store this in the item and only fetch it once
|
|
|
|
SCRIPT_FONTPROPERTIES sfp;
|
|
|
|
ScriptFontProperties(&sfp);
|
|
|
|
|
|
|
|
PRUint32 offset = 0;
|
|
|
|
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
|
|
|
|
gfxTextRun::CompressedGlyph g;
|
|
|
|
const PRUint32 appUnitsPerDevUnit = aRun->GetAppUnitsPerDevUnit();
|
2007-06-10 18:31:27 -07:00
|
|
|
while (offset < mRangeLength) {
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 runOffset = offsetInRun + offset;
|
|
|
|
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
|
2007-11-08 22:27:23 -08:00
|
|
|
g.SetComplex(aRun->IsClusterStart(runOffset), PR_FALSE, 0);
|
|
|
|
aRun->SetGlyphs(runOffset, g, nsnull);
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
// Count glyphs for this character
|
|
|
|
PRUint32 k = mClusters[offset];
|
|
|
|
PRUint32 glyphCount = mNumGlyphs - k;
|
|
|
|
PRUint32 nextClusterOffset;
|
2007-04-02 23:32:23 -07:00
|
|
|
PRBool missing = IsGlyphMissing(&sfp, k);
|
2007-06-10 18:31:27 -07:00
|
|
|
for (nextClusterOffset = offset + 1; nextClusterOffset < mRangeLength; ++nextClusterOffset) {
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mClusters[nextClusterOffset] > k) {
|
|
|
|
glyphCount = mClusters[nextClusterOffset] - k;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PRUint32 j;
|
|
|
|
for (j = 1; j < glyphCount; ++j) {
|
2007-04-02 23:32:23 -07:00
|
|
|
if (IsGlyphMissing(&sfp, k + j)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
missing = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PRInt32 advance = mAdvances[k]*appUnitsPerDevUnit;
|
|
|
|
WORD glyph = mGlyphs[k];
|
2007-07-03 21:07:01 -07:00
|
|
|
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(mRangeString[offset]),
|
|
|
|
"invalid character detected");
|
|
|
|
if (missing) {
|
2007-09-20 03:16:15 -07:00
|
|
|
if (NS_IS_HIGH_SURROGATE(mRangeString[offset]) &&
|
|
|
|
offset + 1 < mRangeLength &&
|
|
|
|
NS_IS_LOW_SURROGATE(mRangeString[offset + 1])) {
|
|
|
|
aRun->SetMissingGlyph(runOffset,
|
|
|
|
SURROGATE_TO_UCS4(mRangeString[offset],
|
|
|
|
mRangeString[offset + 1]));
|
|
|
|
} else {
|
|
|
|
aRun->SetMissingGlyph(runOffset, mRangeString[offset]);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
} else if (glyphCount == 1 && advance >= 0 &&
|
|
|
|
mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
|
|
|
|
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
|
|
|
|
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) {
|
2007-11-08 22:27:23 -08:00
|
|
|
aRun->SetSimpleGlyph(runOffset, g.SetSimpleGlyph(advance, glyph));
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
if (detailedGlyphs.Length() < glyphCount) {
|
|
|
|
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
PRUint32 i;
|
|
|
|
for (i = 0; i < glyphCount; ++i) {
|
|
|
|
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
|
|
|
|
details->mGlyphID = mGlyphs[k + i];
|
|
|
|
details->mAdvance = mAdvances[k + i]*appUnitsPerDevUnit;
|
2007-04-24 23:59:34 -07:00
|
|
|
details->mXOffset = float(mOffsets[k + i].du)*appUnitsPerDevUnit*aRun->GetDirection();
|
2007-03-22 10:30:00 -07:00
|
|
|
details->mYOffset = float(mOffsets[k + i].dv)*appUnitsPerDevUnit;
|
|
|
|
}
|
2007-11-08 22:27:23 -08:00
|
|
|
aRun->SetGlyphs(runOffset,
|
|
|
|
g.SetComplex(PR_TRUE, PR_TRUE, glyphCount), detailedGlyphs.Elements());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
++offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
void SetCurrentFont(gfxWindowsFont *aFont) {
|
|
|
|
if (mCurrentFont != aFont) {
|
|
|
|
mCurrentFont = aFont;
|
2007-08-06 05:30:13 -07:00
|
|
|
cairo_scaled_font_t *scaledFont = mCurrentFont->CairoScaledFont();
|
2007-08-25 11:33:45 -07:00
|
|
|
cairo_win32_scaled_font_done_font(scaledFont);
|
2007-06-10 18:31:27 -07:00
|
|
|
mFontSelected = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxWindowsFont *GetCurrentFont() {
|
|
|
|
return mCurrentFont;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SelectFont() {
|
|
|
|
if (mFontSelected)
|
|
|
|
return;
|
|
|
|
|
|
|
|
cairo_t *cr = mContext->GetCairo();
|
|
|
|
|
|
|
|
cairo_set_font_face(cr, mCurrentFont->CairoFontFace());
|
|
|
|
cairo_set_font_size(cr, mCurrentFont->GetAdjustedSize());
|
2007-08-06 05:30:13 -07:00
|
|
|
cairo_scaled_font_t *scaledFont = mCurrentFont->CairoScaledFont();
|
2007-08-25 11:33:45 -07:00
|
|
|
cairo_win32_scaled_font_select_font(scaledFont, mDC);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
mFontSelected = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TextRange {
|
|
|
|
TextRange(PRUint32 aStart, PRUint32 aEnd) : start(aStart), end(aEnd) { }
|
|
|
|
PRUint32 Length() const { return end - start; }
|
|
|
|
nsRefPtr<FontEntry> font;
|
|
|
|
PRUint32 start, end;
|
|
|
|
};
|
|
|
|
|
|
|
|
void SetRange(PRUint32 i) {
|
|
|
|
nsRefPtr<FontEntry> fe;
|
|
|
|
if (mRanges[i].font)
|
|
|
|
fe = mRanges[i].font;
|
|
|
|
else
|
|
|
|
fe = mGroup->GetFontEntryAt(0);
|
|
|
|
|
|
|
|
nsRefPtr<gfxWindowsFont> font = GetOrMakeFont(fe, mGroup->GetStyle());
|
|
|
|
SetCurrentFont(font);
|
|
|
|
|
|
|
|
mRangeString = mItemString + mRanges[i].start;
|
|
|
|
mRangeLength = mRanges[i].Length();
|
|
|
|
}
|
|
|
|
|
2008-03-14 16:02:32 -07:00
|
|
|
PRBool HasCharacter(FontEntry *aFontEntry, PRUint32 ch) {
|
|
|
|
if (aFontEntry->mCharacterMap.test(ch))
|
|
|
|
return PR_TRUE;
|
|
|
|
|
2008-03-21 13:22:58 -07:00
|
|
|
if (aFontEntry->mForceGDI) {
|
2008-03-14 16:02:32 -07:00
|
|
|
if (ch > 0xFFFF)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
nsRefPtr<gfxWindowsFont> font = GetOrMakeFont(aFontEntry, mGroup->GetStyle());
|
|
|
|
|
|
|
|
HDC dc = GetDC((HWND)nsnull);
|
|
|
|
HFONT hfont = font->GetHFONT();
|
|
|
|
SelectObject(dc, hfont);
|
|
|
|
|
|
|
|
PRUnichar str[1] = { (PRUnichar)ch };
|
|
|
|
WORD glyph[1];
|
|
|
|
|
|
|
|
DWORD ret = GetGlyphIndicesW(dc, str, 1, glyph, GGI_MARK_NONEXISTING_GLYPHS);
|
|
|
|
|
|
|
|
ReleaseDC(NULL, dc);
|
|
|
|
if (ret != GDI_ERROR && glyph[0] != 0xFFFF) {
|
|
|
|
aFontEntry->mCharacterMap.set(ch);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline FontEntry *WhichFontSupportsChar(const nsTArray<nsRefPtr<FontEntry> >& fonts, PRUint32 ch) {
|
2007-06-10 18:31:27 -07:00
|
|
|
for (PRUint32 i = 0; i < fonts.Length(); i++) {
|
|
|
|
nsRefPtr<FontEntry> fe = fonts[i];
|
2008-03-21 15:19:12 -07:00
|
|
|
if (fe->mSymbolFont && !mGroup->GetStyle()->familyNameQuirks)
|
|
|
|
continue;
|
2008-03-14 16:02:32 -07:00
|
|
|
if (HasCharacter(fe, ch))
|
2007-06-10 18:31:27 -07:00
|
|
|
return fe;
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-06-12 11:52:32 -07:00
|
|
|
static inline bool IsJoiner(PRUint32 ch) {
|
|
|
|
return (ch == 0x200C ||
|
|
|
|
ch == 0x200D ||
|
|
|
|
ch == 0x2060);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline FontEntry *FindFontForChar(PRUint32 ch, PRUint32 prevCh, PRUint32 nextCh, FontEntry *aFont) {
|
2007-06-10 18:31:27 -07:00
|
|
|
nsRefPtr<FontEntry> selectedFont;
|
|
|
|
|
2007-06-12 11:52:32 -07:00
|
|
|
// if this character or the next one is a joiner use the
|
|
|
|
// same font as the previous range if we can
|
|
|
|
if (IsJoiner(ch) || IsJoiner(prevCh) || IsJoiner(nextCh)) {
|
2008-03-14 16:02:32 -07:00
|
|
|
if (aFont && HasCharacter(aFont, ch))
|
2007-06-12 11:52:32 -07:00
|
|
|
return aFont;
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
// check the list of fonts
|
|
|
|
selectedFont = WhichFontSupportsChar(mGroup->GetFontList(), ch);
|
|
|
|
|
2007-11-06 20:53:47 -08:00
|
|
|
// don't look in other fonts if the character is in a Private Use Area
|
|
|
|
if ((ch >= 0xE000 && ch <= 0xF8FF) ||
|
|
|
|
(ch >= 0xF0000 && ch <= 0x10FFFD))
|
|
|
|
return selectedFont;
|
|
|
|
|
2008-03-26 14:32:51 -07:00
|
|
|
// check out the style's language group
|
|
|
|
if (!selectedFont) {
|
|
|
|
nsAutoTArray<nsRefPtr<FontEntry>, 5> fonts;
|
|
|
|
this->GetPrefFonts(mGroup->GetStyle()->langGroup.get(), fonts);
|
|
|
|
selectedFont = WhichFontSupportsChar(fonts, ch);
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
// otherwise search prefs
|
|
|
|
if (!selectedFont) {
|
2007-03-22 10:30:00 -07:00
|
|
|
/* first check with the script properties to see what they think */
|
|
|
|
const SCRIPT_PROPERTIES *sp = ScriptProperties();
|
|
|
|
if (!sp->fAmbiguousCharSet) {
|
|
|
|
WORD primaryId = PRIMARYLANGID(sp->langid);
|
|
|
|
const char *langGroup = gScriptToText[primaryId].langCode;
|
|
|
|
if (langGroup) {
|
2007-06-10 18:31:27 -07:00
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: %s (%s)", langGroup, gScriptToText[primaryId].value));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-08-24 12:41:16 -07:00
|
|
|
nsAutoTArray<nsRefPtr<FontEntry>, 5> fonts;
|
2007-06-10 18:31:27 -07:00
|
|
|
this->GetPrefFonts(langGroup, fonts);
|
|
|
|
selectedFont = WhichFontSupportsChar(fonts, ch);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-06-25 14:51:15 -07:00
|
|
|
} else if (ch <= 0xFFFF) {
|
|
|
|
PRUint32 unicodeRange = FindCharUnicodeRange(ch);
|
2007-05-29 17:07:03 -07:00
|
|
|
|
2007-06-25 14:51:15 -07:00
|
|
|
/* special case CJK */
|
|
|
|
if (unicodeRange == kRangeSetCJK) {
|
|
|
|
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
|
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: CJK"));
|
2007-06-10 18:31:27 -07:00
|
|
|
|
2007-08-24 12:41:16 -07:00
|
|
|
nsAutoTArray<nsRefPtr<FontEntry>, 15> fonts;
|
2007-06-25 14:51:15 -07:00
|
|
|
this->GetCJKPrefFonts(fonts);
|
2007-06-10 18:31:27 -07:00
|
|
|
selectedFont = WhichFontSupportsChar(fonts, ch);
|
2007-06-25 14:51:15 -07:00
|
|
|
} else {
|
|
|
|
const char *langGroup = LangGroupFromUnicodeRange(unicodeRange);
|
|
|
|
if (langGroup) {
|
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Trying to find fonts for: %s", langGroup));
|
|
|
|
|
2007-08-24 12:41:16 -07:00
|
|
|
nsAutoTArray<nsRefPtr<FontEntry>, 5> fonts;
|
2007-06-25 14:51:15 -07:00
|
|
|
this->GetPrefFonts(langGroup, fonts);
|
|
|
|
selectedFont = WhichFontSupportsChar(fonts, ch);
|
|
|
|
}
|
2007-06-10 18:31:27 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
// before searching for something else check the font used for the previous character
|
2008-03-14 16:02:32 -07:00
|
|
|
if (!selectedFont && aFont && HasCharacter(aFont, ch))
|
2007-06-10 18:31:27 -07:00
|
|
|
selectedFont = aFont;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
// otherwise look for other stuff
|
|
|
|
if (!selectedFont) {
|
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Looking for best match"));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
nsRefPtr<gfxWindowsFont> refFont = mGroup->GetFontAt(0);
|
|
|
|
gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
|
2008-03-03 18:34:05 -08:00
|
|
|
selectedFont = platform->FindFontForChar(ch, refFont);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
return selectedFont;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
PRUint32 ComputeRanges() {
|
|
|
|
if (mItemLength == 0)
|
|
|
|
return 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, ("Computing ranges for string: (len = %d)", mItemLength));
|
2007-06-12 11:52:32 -07:00
|
|
|
|
|
|
|
PRUint32 prevCh = 0;
|
2007-06-10 18:31:27 -07:00
|
|
|
for (PRUint32 i = 0; i < mItemLength; i++) {
|
|
|
|
const PRUint32 origI = i; // save off incase we increase for surrogate
|
|
|
|
PRUint32 ch = mItemString[i];
|
|
|
|
if ((i+1 < mItemLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(mItemString[i+1])) {
|
|
|
|
i++;
|
|
|
|
ch = SURROGATE_TO_UCS4(ch, mItemString[i]);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" 0x%04x - ", ch));
|
2007-06-12 11:52:32 -07:00
|
|
|
PRUint32 nextCh = 0;
|
|
|
|
if (i+1 < mItemLength) {
|
|
|
|
nextCh = mItemString[i+1];
|
|
|
|
if ((i+2 < mItemLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(mItemString[i+2]))
|
|
|
|
nextCh = SURROGATE_TO_UCS4(nextCh, mItemString[i+2]);
|
|
|
|
}
|
|
|
|
nsRefPtr<FontEntry> fe = FindFontForChar(ch,
|
|
|
|
prevCh,
|
|
|
|
nextCh,
|
|
|
|
(mRanges.Length() == 0) ? nsnull : mRanges[mRanges.Length() - 1].font);
|
|
|
|
|
|
|
|
prevCh = ch;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
if (mRanges.Length() == 0) {
|
|
|
|
TextRange r(0,1);
|
|
|
|
r.font = fe;
|
|
|
|
mRanges.AppendElement(r);
|
|
|
|
} else {
|
|
|
|
TextRange& prevRange = mRanges[mRanges.Length() - 1];
|
|
|
|
if (prevRange.font != fe) {
|
|
|
|
// close out the previous range
|
|
|
|
prevRange.end = origI;
|
|
|
|
|
2008-01-28 09:30:18 -08:00
|
|
|
TextRange r(origI, i+1);
|
2007-06-10 18:31:27 -07:00
|
|
|
r.font = fe;
|
|
|
|
mRanges.AppendElement(r);
|
|
|
|
}
|
|
|
|
}
|
2007-06-12 11:52:32 -07:00
|
|
|
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG)) {
|
|
|
|
if (fe)
|
2008-03-18 17:06:55 -07:00
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Using %s", NS_LossyConvertUTF16toASCII(fe->GetName()).get()));
|
2007-06-12 11:52:32 -07:00
|
|
|
else
|
2008-03-18 17:06:55 -07:00
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Unable to find font"));
|
2007-06-12 11:52:32 -07:00
|
|
|
}
|
2007-06-10 18:31:27 -07:00
|
|
|
}
|
|
|
|
mRanges[mRanges.Length()-1].end = mItemLength;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
PRUint32 nranges = mRanges.Length();
|
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, (" Found %d ranges", nranges));
|
|
|
|
return nranges;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static PRInt32 GetCJKLangGroupIndex(const char *aLangGroup) {
|
|
|
|
PRInt32 i;
|
|
|
|
for (i = 0; i < COUNT_OF_CJK_LANG_GROUP; i++) {
|
|
|
|
if (!PL_strcasecmp(aLangGroup, sCJKLangGroup[i]))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-08-24 17:00:41 -07:00
|
|
|
// this function appends to the array passed in.
|
2007-06-10 18:31:27 -07:00
|
|
|
void GetPrefFonts(const char *aLangGroup, nsTArray<nsRefPtr<FontEntry> >& array) {
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ASSERTION(aLangGroup, "aLangGroup is null");
|
2007-08-24 12:41:16 -07:00
|
|
|
gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
|
|
|
|
nsAutoTArray<nsRefPtr<FontEntry>, 5> fonts;
|
2008-03-18 17:06:55 -07:00
|
|
|
/* this lookup has to depend on weight and style */
|
|
|
|
nsCAutoString key(aLangGroup);
|
|
|
|
key.Append("-");
|
|
|
|
key.AppendInt(mGroup->GetStyle()->style);
|
|
|
|
key.Append("-");
|
|
|
|
key.AppendInt(mGroup->GetStyle()->weight);
|
|
|
|
if (!platform->GetPrefFontEntries(key, &fonts)) {
|
2007-08-24 12:41:16 -07:00
|
|
|
nsString fontString;
|
|
|
|
platform->GetPrefFonts(aLangGroup, fontString);
|
|
|
|
if (fontString.IsEmpty())
|
|
|
|
return;
|
|
|
|
|
2008-03-18 17:06:55 -07:00
|
|
|
mGroup->FamilyListToArrayList(fontString, nsDependentCString(aLangGroup),
|
|
|
|
&fonts);
|
|
|
|
|
|
|
|
platform->SetPrefFontEntries(key, fonts);
|
2007-08-24 12:41:16 -07:00
|
|
|
}
|
|
|
|
array.AppendElements(fonts);
|
2007-06-10 18:31:27 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-08-24 17:00:41 -07:00
|
|
|
// this function assigns to the array passed in.
|
2007-06-10 18:31:27 -07:00
|
|
|
void GetCJKPrefFonts(nsTArray<nsRefPtr<FontEntry> >& array) {
|
2007-08-24 17:00:41 -07:00
|
|
|
gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
|
2008-03-18 17:06:55 -07:00
|
|
|
|
|
|
|
nsCAutoString key("x-internal-cjk-");
|
|
|
|
key.AppendInt(mGroup->GetStyle()->style);
|
|
|
|
key.Append("-");
|
|
|
|
key.AppendInt(mGroup->GetStyle()->weight);
|
|
|
|
|
|
|
|
if (!platform->GetPrefFontEntries(key, &array)) {
|
2007-08-24 17:00:41 -07:00
|
|
|
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
|
|
if (!prefs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefBranch;
|
|
|
|
prefs->GetBranch(0, getter_AddRefs(prefBranch));
|
|
|
|
if (!prefBranch)
|
|
|
|
return;
|
|
|
|
|
2008-02-09 10:47:15 -08:00
|
|
|
// Add the CJK pref fonts from accept languages, the order should be same order
|
|
|
|
nsCAutoString list;
|
|
|
|
nsCOMPtr<nsIPrefLocalizedString> val;
|
|
|
|
nsresult rv = prefBranch->GetComplexValue("intl.accept_languages", NS_GET_IID(nsIPrefLocalizedString),
|
|
|
|
getter_AddRefs(val));
|
|
|
|
if (NS_SUCCEEDED(rv) && val) {
|
|
|
|
nsAutoString temp;
|
|
|
|
val->ToString(getter_Copies(temp));
|
|
|
|
LossyCopyUTF16toASCII(temp, list);
|
|
|
|
}
|
|
|
|
if (!list.IsEmpty()) {
|
2007-08-24 17:00:41 -07:00
|
|
|
const char kComma = ',';
|
|
|
|
const char *p, *p_end;
|
|
|
|
list.BeginReading(p);
|
|
|
|
list.EndReading(p_end);
|
|
|
|
while (p < p_end) {
|
|
|
|
while (nsCRT::IsAsciiSpace(*p)) {
|
|
|
|
if (++p == p_end)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (p == p_end)
|
|
|
|
break;
|
|
|
|
const char *start = p;
|
|
|
|
while (++p != p_end && *p != kComma)
|
|
|
|
/* nothing */ ;
|
|
|
|
nsCAutoString lang(Substring(start, p));
|
|
|
|
lang.CompressWhitespace(PR_FALSE, PR_TRUE);
|
|
|
|
PRInt32 index = GetCJKLangGroupIndex(lang.get());
|
|
|
|
if (index >= 0)
|
|
|
|
GetPrefFonts(sCJKLangGroup[index], array);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the system locale
|
|
|
|
switch (::GetACP()) {
|
|
|
|
case 932: GetPrefFonts(CJK_LANG_JA, array); break;
|
|
|
|
case 936: GetPrefFonts(CJK_LANG_ZH_CN, array); break;
|
|
|
|
case 949: GetPrefFonts(CJK_LANG_KO, array); break;
|
|
|
|
// XXX Don't we need to append CJK_LANG_ZH_HK if the codepage is 950?
|
|
|
|
case 950: GetPrefFonts(CJK_LANG_ZH_TW, array); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// last resort...
|
|
|
|
GetPrefFonts(CJK_LANG_JA, array);
|
|
|
|
GetPrefFonts(CJK_LANG_KO, array);
|
|
|
|
GetPrefFonts(CJK_LANG_ZH_CN, array);
|
|
|
|
GetPrefFonts(CJK_LANG_ZH_HK, array);
|
|
|
|
GetPrefFonts(CJK_LANG_ZH_TW, array);
|
|
|
|
|
2008-03-18 17:06:55 -07:00
|
|
|
platform->SetPrefFontEntries(key, array);
|
2007-08-24 17:00:41 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void GenerateAlternativeString() {
|
|
|
|
if (mAlternativeString)
|
|
|
|
free(mAlternativeString);
|
2007-06-10 18:31:27 -07:00
|
|
|
mAlternativeString = (PRUnichar *)malloc(mRangeLength * sizeof(PRUnichar));
|
2007-10-19 01:10:28 -07:00
|
|
|
if (!mAlternativeString)
|
|
|
|
return;
|
2007-06-10 18:31:27 -07:00
|
|
|
memcpy((void *)mAlternativeString, (const void *)mRangeString,
|
|
|
|
mRangeLength * sizeof(PRUnichar));
|
2007-06-12 11:54:41 -07:00
|
|
|
for (PRUint32 i = 0; i < mRangeLength; i++) {
|
2007-06-10 18:31:27 -07:00
|
|
|
if (NS_IS_HIGH_SURROGATE(mRangeString[i]) || NS_IS_LOW_SURROGATE(mRangeString[i]))
|
2007-03-22 10:30:00 -07:00
|
|
|
mAlternativeString[i] = PRUnichar(0xFFFD);
|
|
|
|
}
|
|
|
|
}
|
2007-05-29 17:07:03 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
private:
|
|
|
|
nsRefPtr<gfxContext> mContext;
|
|
|
|
HDC mDC;
|
|
|
|
|
|
|
|
SCRIPT_ITEM *mScriptItem;
|
|
|
|
WORD mScript;
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
// these point to the current range
|
|
|
|
const PRUnichar *mRangeString;
|
|
|
|
PRUint32 mRangeLength;
|
|
|
|
|
|
|
|
// these point to the full string/length of the item
|
|
|
|
const PRUnichar *mItemString;
|
|
|
|
const PRUint32 mItemLength;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRUnichar *mAlternativeString;
|
|
|
|
|
|
|
|
gfxWindowsFontGroup *mGroup;
|
|
|
|
|
2007-08-28 13:44:12 -07:00
|
|
|
#define AVERAGE_ITEM_LENGTH 40
|
|
|
|
|
2007-11-06 22:35:38 -08:00
|
|
|
nsAutoTArray<WORD, PRUint32(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mGlyphs;
|
2007-09-18 16:12:06 -07:00
|
|
|
nsAutoTArray<WORD, AVERAGE_ITEM_LENGTH + 1> mClusters;
|
2007-11-06 22:35:38 -08:00
|
|
|
nsAutoTArray<SCRIPT_VISATTR, PRUint32(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mAttr;
|
2007-08-28 13:44:12 -07:00
|
|
|
|
2007-09-18 16:12:06 -07:00
|
|
|
nsAutoTArray<GOFFSET, 2 * AVERAGE_ITEM_LENGTH> mOffsets;
|
|
|
|
nsAutoTArray<int, 2 * AVERAGE_ITEM_LENGTH> mAdvances;
|
2007-08-28 13:44:12 -07:00
|
|
|
|
|
|
|
#undef AVERAGE_ITEM_LENGTH
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
int mMaxGlyphs;
|
|
|
|
int mNumGlyphs;
|
|
|
|
|
|
|
|
nsRefPtr<gfxWindowsFont> mCurrentFont;
|
|
|
|
|
|
|
|
PRPackedBool mFontSelected;
|
2007-06-10 18:31:27 -07:00
|
|
|
|
|
|
|
nsTArray<TextRange> mRanges;
|
2007-03-22 10:30:00 -07:00
|
|
|
};
|
|
|
|
|
2007-11-06 22:35:38 -08:00
|
|
|
|
|
|
|
#define MAX_ITEM_LENGTH 32768
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static PRUint32 FindNextItemStart(int aOffset, int aLimit,
|
|
|
|
nsTArray<SCRIPT_LOGATTR> &aLogAttr,
|
|
|
|
const PRUnichar *aString)
|
|
|
|
{
|
|
|
|
if (aOffset + MAX_ITEM_LENGTH >= aLimit) {
|
|
|
|
// The item starting at aOffset can't be longer than the max length,
|
|
|
|
// so starting the next item at aLimit won't cause ScriptShape() to fail.
|
|
|
|
return aLimit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to start the next item before or after a space, since spaces
|
|
|
|
// don't kern or ligate.
|
|
|
|
PRUint32 off;
|
|
|
|
int boundary = -1;
|
|
|
|
for (off = MAX_ITEM_LENGTH; off > 1; --off) {
|
|
|
|
if (aLogAttr[off].fCharStop) {
|
|
|
|
if (off > boundary) {
|
|
|
|
boundary = off;
|
|
|
|
}
|
|
|
|
if (aString[aOffset+off] == ' ' || aString[aOffset+off - 1] == ' ')
|
|
|
|
return aOffset+off;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to start the next item at the last cluster boundary in the range.
|
|
|
|
if (boundary > 0) {
|
|
|
|
return aOffset+boundary;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break
|
|
|
|
// on the size limit. It won't be visually plesaing, but at least it
|
|
|
|
// won't cause ScriptShape() to fail.
|
|
|
|
return aOffset + MAX_ITEM_LENGTH;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
class Uniscribe
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Uniscribe(gfxContext *aContext, HDC aDC, const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) :
|
|
|
|
mContext(aContext), mDC(aDC), mString(aString), mLength(aLength), mIsRTL(aIsRTL),
|
|
|
|
mItems(nsnull) {
|
|
|
|
}
|
2007-04-02 23:32:23 -07:00
|
|
|
~Uniscribe() {
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
void Init() {
|
|
|
|
memset(&mControl, 0, sizeof(SCRIPT_CONTROL));
|
|
|
|
memset(&mState, 0, sizeof(SCRIPT_STATE));
|
|
|
|
// Lock the direction. Don't allow the itemizer to change directions
|
|
|
|
// based on character type.
|
|
|
|
mState.uBidiLevel = mIsRTL;
|
|
|
|
mState.fOverrideDirection = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2007-11-06 22:35:38 -08:00
|
|
|
private:
|
|
|
|
|
|
|
|
// Append mItems[aIndex] to aDest, adding extra items to aDest to ensure
|
|
|
|
// that no item is too long for ScriptShape() to handle. See bug 366643.
|
|
|
|
nsresult CopyItemSplitOversize(int aIndex, nsTArray<SCRIPT_ITEM> &aDest) {
|
|
|
|
aDest.AppendElement(mItems[aIndex]);
|
|
|
|
const int itemLength = mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos;
|
|
|
|
if (ESTIMATE_MAX_GLYPHS(itemLength) > 65535) {
|
|
|
|
// This items length would cause ScriptShape() to fail. We need to
|
|
|
|
// add extra items here so that no item's length could cause the fail.
|
|
|
|
|
|
|
|
// Get cluster boundaries, so we can break cleanly if possible.
|
|
|
|
nsTArray<SCRIPT_LOGATTR> logAttr;
|
|
|
|
if (!logAttr.SetLength(itemLength))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
HRESULT rv= ScriptBreak(mString+mItems[aIndex].iCharPos, itemLength,
|
|
|
|
&mItems[aIndex].a, logAttr.Elements());
|
|
|
|
if (FAILED(rv))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
const int nextItemStart = mItems[aIndex+1].iCharPos;
|
|
|
|
int start = FindNextItemStart(mItems[aIndex].iCharPos,
|
|
|
|
nextItemStart, logAttr, mString);
|
|
|
|
|
|
|
|
while (start < nextItemStart) {
|
|
|
|
SCRIPT_ITEM item = mItems[aIndex];
|
|
|
|
item.iCharPos = start;
|
|
|
|
aDest.AppendElement(item);
|
|
|
|
start = FindNextItemStart(start, nextItemStart, logAttr, mString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
int Itemize() {
|
|
|
|
HRESULT rv;
|
|
|
|
|
|
|
|
int maxItems = 5;
|
|
|
|
|
|
|
|
Init();
|
2007-11-06 22:35:38 -08:00
|
|
|
|
2007-04-25 14:02:50 -07:00
|
|
|
// Allocate space for one more item than expected, to handle a rare
|
|
|
|
// overflow in ScriptItemize (pre XP SP2). See bug 366643.
|
2007-11-06 22:35:38 -08:00
|
|
|
if (!mItems.SetLength(maxItems + 1)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
while ((rv = ScriptItemize(mString, mLength, maxItems, &mControl, &mState,
|
2007-11-06 22:35:38 -08:00
|
|
|
mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
|
2007-03-22 10:30:00 -07:00
|
|
|
maxItems *= 2;
|
2007-11-06 22:35:38 -08:00
|
|
|
if (!mItems.SetLength(maxItems + 1)) {
|
|
|
|
return 0;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
2007-11-06 22:35:38 -08:00
|
|
|
if (ESTIMATE_MAX_GLYPHS(mLength) > 65535) {
|
|
|
|
// Any item of length > 43680 will cause ScriptShape() to fail, as its
|
|
|
|
// mMaxGlyphs value will be greater than 65535 (43680*1.5+16>65535). So we
|
|
|
|
// need to break up items which are longer than that upon cluster boundaries.
|
|
|
|
// See bug 394751 for details.
|
|
|
|
nsTArray<SCRIPT_ITEM> items;
|
|
|
|
for (int i=0; i<mNumItems; i++) {
|
|
|
|
nsresult nrs = CopyItemSplitOversize(i, items);
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(nrs), "CopyItemSplitOversize() failed");
|
|
|
|
}
|
|
|
|
items.AppendElement(mItems[mNumItems]); // copy terminator.
|
|
|
|
|
|
|
|
mItems = items;
|
|
|
|
mNumItems = items.Length() - 1; // Don't count the terminator.
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
return mNumItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 ItemsLength() {
|
|
|
|
return mNumItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX Why do we dynamically allocate this? We could just fill in an object
|
|
|
|
// on the stack.
|
|
|
|
UniscribeItem *GetItem(PRUint32 i, gfxWindowsFontGroup *aGroup) {
|
|
|
|
NS_ASSERTION(i < (PRUint32)mNumItems, "Trying to get out of bounds item");
|
|
|
|
|
|
|
|
UniscribeItem *item = new UniscribeItem(mContext, mDC,
|
|
|
|
mString + mItems[i].iCharPos,
|
|
|
|
mItems[i+1].iCharPos - mItems[i].iCharPos,
|
|
|
|
&mItems[i],
|
|
|
|
aGroup);
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<gfxContext> mContext;
|
|
|
|
HDC mDC;
|
|
|
|
const PRUnichar *mString;
|
|
|
|
const PRUint32 mLength;
|
|
|
|
const PRBool mIsRTL;
|
|
|
|
|
|
|
|
SCRIPT_CONTROL mControl;
|
|
|
|
SCRIPT_STATE mState;
|
2007-11-06 22:35:38 -08:00
|
|
|
nsTArray<SCRIPT_ITEM> mItems;
|
2007-03-22 10:30:00 -07:00
|
|
|
int mNumItems;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString,
|
2007-05-29 17:07:03 -07:00
|
|
|
PRUint32 aLength)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-07 15:46:42 -07:00
|
|
|
DCFromContext aDC(aContext);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
const PRBool isRTL = aRun->IsRightToLeft();
|
|
|
|
|
|
|
|
HRESULT rv;
|
|
|
|
|
|
|
|
Uniscribe us(aContext, aDC, aString, aLength, isRTL);
|
|
|
|
|
|
|
|
/* itemize the string */
|
|
|
|
int numItems = us.Itemize();
|
|
|
|
|
2007-05-29 17:07:03 -07:00
|
|
|
for (int i = 0; i < numItems; ++i) {
|
2007-03-22 10:30:00 -07:00
|
|
|
SaveDC(aDC);
|
|
|
|
|
2008-03-24 21:46:30 -07:00
|
|
|
nsAutoPtr<UniscribeItem> item(us.GetItem(i, this));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
PRUint32 nranges = item->ComputeRanges();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
for (PRUint32 j = 0; j < nranges; ++j) {
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
item->SetRange(j);
|
2007-05-29 17:07:03 -07:00
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
if (!item->ShapingEnabled())
|
|
|
|
item->EnableShaping();
|
|
|
|
|
2007-11-06 22:35:38 -08:00
|
|
|
rv = item->Shape();
|
|
|
|
if (FAILED(rv)) {
|
2007-06-10 18:31:27 -07:00
|
|
|
PR_LOG(gFontLog, PR_LOG_DEBUG, ("shaping failed"));
|
|
|
|
// we know we have the glyphs to display this font already
|
|
|
|
// so Uniscribe just doesn't know how to shape the script.
|
|
|
|
// Render the glyphs without shaping.
|
2007-03-22 10:30:00 -07:00
|
|
|
item->DisableShaping();
|
2007-11-06 22:35:38 -08:00
|
|
|
rv = item->Shape();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-06-10 18:31:27 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ASSERTION(SUCCEEDED(rv), "Failed to shape -- we should never hit this");
|
|
|
|
|
|
|
|
rv = item->Place();
|
2007-06-10 18:31:27 -07:00
|
|
|
NS_ASSERTION(SUCCEEDED(rv), "Failed to place -- this is pretty bad.");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-03-21 13:22:58 -07:00
|
|
|
if (FAILED(rv)) {
|
|
|
|
aRun->ResetGlyphRuns();
|
|
|
|
|
|
|
|
/* Uniscribe doesn't like this font, use GDI instead */
|
|
|
|
item->GetCurrentFont()->GetFontEntry()->mForceGDI = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-06-10 18:31:27 -07:00
|
|
|
item->SaveGlyphs(aRun);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
RestoreDC(aDC, -1);
|
2008-03-21 13:22:58 -07:00
|
|
|
|
|
|
|
if (FAILED(rv)) {
|
|
|
|
i = -1;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|