1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Foundation code.
17 * The Initial Developer of the Original Code is Mozilla Foundation.
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
22 * Stuart Parmenter <stuart@mozilla.com>
23 * Masayuki Nakano <masayuki@d-toybox.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
42 //#define FORCE_UNISCRIBE 1
48 #include "gfxContext.h"
49 #include "gfxWindowsFonts.h"
50 #include "gfxWindowsSurface.h"
51 #include "gfxWindowsPlatform.h"
53 #ifdef MOZ_ENABLE_GLITZ
54 #include "gfxGlitzSurface.h"
57 #include "gfxFontTest.h"
60 #include "cairo-win32.h"
64 #include "nsUnicodeRange.h"
65 #include "nsUnicharUtils.h"
67 #include "nsIPrefBranch.h"
68 #include "nsIPrefService.h"
69 #include "nsServiceManagerUtils.h"
76 static PRLogModuleInfo
*gFontLog
= PR_NewLogModule("winfonts");
78 #define ROUND(x) floor((x) + 0.5)
81 GetDCFromSurface(gfxASurface
*aSurface
)
83 if (aSurface
->GetType() != gfxASurface::SurfaceTypeWin32
) {
84 NS_ERROR("GetDCFromSurface: Context target is not win32!");
87 return NS_STATIC_CAST(gfxWindowsSurface
*, aSurface
)->GetDC();
90 /**********************************************************************
92 * class gfxWindowsFont
94 **********************************************************************/
96 gfxWindowsFont::gfxWindowsFont(const nsAString
& aName
, const gfxFontStyle
*aFontStyle
)
97 : gfxFont(aName
, aFontStyle
),
98 mFont(nsnull
), mAdjustedSize(0), mScriptCache(nsnull
),
99 mFontFace(nsnull
), mScaledFont(nsnull
),
102 // XXX we should work to get this passed in rather than having to find it again.
103 mFontEntry
= gfxWindowsPlatform::GetPlatform()->FindFontEntry(aName
);
104 NS_ASSERTION(mFontEntry
, "Unable to find font entry for font. Something is whack.");
106 mFont
= MakeHFONT(); // create the HFONT, compute metrics, etc
107 NS_ASSERTION(mFont
, "Failed to make HFONT");
110 gfxWindowsFont::~gfxWindowsFont()
113 cairo_font_face_destroy(mFontFace
);
116 cairo_scaled_font_destroy(mScaledFont
);
121 ScriptFreeCache(&mScriptCache
);
126 const gfxFont::Metrics
&
127 gfxWindowsFont::GetMetrics()
136 gfxWindowsFont::CairoFontFace()
139 mFontFace
= cairo_win32_font_face_create_for_logfontw_hfont(&mLogFont
, mFont
);
141 NS_ASSERTION(mFontFace
, "Failed to make font face");
146 cairo_scaled_font_t
*
147 gfxWindowsFont::CairoScaledFont()
150 cairo_matrix_t sizeMatrix
;
151 cairo_matrix_t identityMatrix
;
153 cairo_matrix_init_scale(&sizeMatrix
, mAdjustedSize
, mAdjustedSize
);
154 cairo_matrix_init_identity(&identityMatrix
);
156 cairo_font_options_t
*fontOptions
= cairo_font_options_create();
157 mScaledFont
= cairo_scaled_font_create(CairoFontFace(), &sizeMatrix
,
158 &identityMatrix
, fontOptions
);
159 cairo_font_options_destroy(fontOptions
);
162 NS_ASSERTION(mScaledFont
, "Failed to make scaled font");
168 gfxWindowsFont::MakeHFONT()
173 PRInt8 baseWeight
, weightDistance
;
174 GetStyle()->ComputeWeightAndOffset(&baseWeight
, &weightDistance
);
178 PRUint32 chosenWeight
= 0;
180 PRUint8 direction
= (weightDistance
>= 0) ? 1 : -1;
182 for (PRUint8 i
= baseWeight
, k
= 0; i
< 10 && i
>= 1; i
+=direction
) {
183 if (mFontEntry
->mWeightTable
.HasWeight(i
)) {
185 chosenWeight
= i
* 100;
186 } else if (mFontEntry
->mWeightTable
.TriedWeight(i
)) {
189 const PRUint32 tryWeight
= i
* 100;
192 dc
= GetDC((HWND
)nsnull
);
194 FillLogFont(GetStyle()->size
, tryWeight
);
195 mFont
= CreateFontIndirectW(&mLogFont
);
196 HGDIOBJ oldFont
= SelectObject(dc
, mFont
);
198 GetTextMetrics(dc
, &metrics
);
200 PRBool hasWeight
= (metrics
.tmWeight
== tryWeight
);
201 mFontEntry
->mWeightTable
.SetWeight(i
, hasWeight
);
203 chosenWeight
= i
* 100;
207 SelectObject(dc
, oldFont
);
208 if (k
<= abs(weightDistance
)) {
214 if (k
> abs(weightDistance
)) {
215 chosenWeight
= i
* 100;
220 if (chosenWeight
== 0)
221 chosenWeight
= baseWeight
* 100;
223 mAdjustedSize
= GetStyle()->size
;
224 if (GetStyle()->sizeAdjust
> 0) {
226 FillLogFont(mAdjustedSize
, chosenWeight
);
227 mFont
= CreateFontIndirectW(&mLogFont
);
230 Metrics
*oldMetrics
= mMetrics
;
232 gfxFloat aspect
= mMetrics
->xHeight
/ mMetrics
->emHeight
;
233 mAdjustedSize
= GetStyle()->GetAdjustedSize(aspect
);
235 if (mMetrics
!= oldMetrics
) {
237 mMetrics
= oldMetrics
;
244 FillLogFont(mAdjustedSize
, chosenWeight
);
245 mFont
= CreateFontIndirectW(&mLogFont
);
249 ReleaseDC((HWND
)nsnull
, dc
);
255 gfxWindowsFont::ComputeMetrics()
258 mMetrics
= new gfxFont::Metrics
;
260 NS_WARNING("Calling ComputeMetrics multiple times");
262 HDC dc
= GetDC((HWND
)nsnull
);
264 HGDIOBJ oldFont
= SelectObject(dc
, mFont
);
267 OUTLINETEXTMETRIC oMetrics
;
268 TEXTMETRIC
& metrics
= oMetrics
.otmTextMetrics
;
270 if (0 < GetOutlineTextMetrics(dc
, sizeof(oMetrics
), &oMetrics
)) {
271 mMetrics
->superscriptOffset
= (double)oMetrics
.otmptSuperscriptOffset
.y
;
272 mMetrics
->subscriptOffset
= (double)oMetrics
.otmptSubscriptOffset
.y
;
273 mMetrics
->strikeoutSize
= PR_MAX(1, (double)oMetrics
.otmsStrikeoutSize
);
274 mMetrics
->strikeoutOffset
= (double)oMetrics
.otmsStrikeoutPosition
;
275 mMetrics
->underlineSize
= PR_MAX(1, (double)oMetrics
.otmsUnderscoreSize
);
276 mMetrics
->underlineOffset
= (double)oMetrics
.otmsUnderscorePosition
;
278 const MAT2 kIdentityMatrix
= { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
280 DWORD len
= GetGlyphOutlineW(dc
, PRUnichar('x'), GGO_METRICS
, &gm
, 0, nsnull
, &kIdentityMatrix
);
281 if (len
== GDI_ERROR
|| gm
.gmptGlyphOrigin
.y
<= 0) {
282 // 56% of ascent, best guess for true type
283 mMetrics
->xHeight
= ROUND((double)metrics
.tmAscent
* 0.56);
285 mMetrics
->xHeight
= gm
.gmptGlyphOrigin
.y
;
287 // The MS (P)Gothic and MS (P)Mincho are not having suitable values
288 // in them super script offset. If the values are not suitable,
289 // we should use x-height instead of them.
290 // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
291 if (mMetrics
->superscriptOffset
== 0 ||
292 mMetrics
->superscriptOffset
>= metrics
.tmAscent
) {
293 mMetrics
->superscriptOffset
= mMetrics
->xHeight
;
295 // And also checking the case of sub script offset.
296 // The old gfx has checked this too.
297 if (mMetrics
->subscriptOffset
== 0 ||
298 mMetrics
->subscriptOffset
>= metrics
.tmAscent
) {
299 mMetrics
->subscriptOffset
= mMetrics
->xHeight
;
302 // Make a best-effort guess at extended metrics
303 // this is based on general typographic guidelines
304 GetTextMetrics(dc
, &metrics
);
306 mMetrics
->xHeight
= ROUND((float)metrics
.tmAscent
* 0.56f
); // 56% of ascent, best guess for non-true type
307 mMetrics
->superscriptOffset
= mMetrics
->xHeight
;
308 mMetrics
->subscriptOffset
= mMetrics
->xHeight
;
309 mMetrics
->strikeoutSize
= 1;
310 mMetrics
->strikeoutOffset
= ROUND(mMetrics
->xHeight
/ 2.0f
); // 50% of xHeight
311 mMetrics
->underlineSize
= 1;
312 mMetrics
->underlineOffset
= -ROUND((float)metrics
.tmDescent
* 0.30f
); // 30% of descent
315 mMetrics
->internalLeading
= metrics
.tmInternalLeading
;
316 mMetrics
->externalLeading
= metrics
.tmExternalLeading
;
317 mMetrics
->emHeight
= (metrics
.tmHeight
- metrics
.tmInternalLeading
);
318 mMetrics
->emAscent
= (metrics
.tmAscent
- metrics
.tmInternalLeading
);
319 mMetrics
->emDescent
= metrics
.tmDescent
;
320 mMetrics
->maxHeight
= metrics
.tmHeight
;
321 mMetrics
->maxAscent
= metrics
.tmAscent
;
322 mMetrics
->maxDescent
= metrics
.tmDescent
;
323 mMetrics
->maxAdvance
= metrics
.tmMaxCharWidth
;
324 mMetrics
->aveCharWidth
= PR_MAX(1, metrics
.tmAveCharWidth
);
326 // Cache the width of a single space.
328 GetTextExtentPoint32(dc
, " ", 1, &size
);
329 mMetrics
->spaceWidth
= ROUND(size
.cx
);
332 if (metrics
.tmPitchAndFamily
& TMPF_TRUETYPE
) {
334 DWORD ret
= GetGlyphIndicesA(dc
, " ", 1, &glyph
,
335 GGI_MARK_NONEXISTING_GLYPHS
);
336 if (ret
!= GDI_ERROR
&& glyph
!= 0xFFFF) {
341 SelectObject(dc
, oldFont
);
343 ReleaseDC((HWND
)nsnull
, dc
);
347 gfxWindowsFont::FillLogFont(gfxFloat aSize
, PRInt16 aWeight
)
349 #define CLIP_TURNOFF_FONTASSOCIATION 0x40
351 mLogFont
.lfHeight
= (LONG
)-ROUND(aSize
);
353 if (mLogFont
.lfHeight
== 0)
354 mLogFont
.lfHeight
= -1;
356 // Fill in logFont structure
357 mLogFont
.lfWidth
= 0;
358 mLogFont
.lfEscapement
= 0;
359 mLogFont
.lfOrientation
= 0;
360 mLogFont
.lfUnderline
= FALSE
;
361 mLogFont
.lfStrikeOut
= FALSE
;
362 mLogFont
.lfCharSet
= DEFAULT_CHARSET
;
364 mLogFont
.lfOutPrecision
= OUT_TT_PRECIS
;
366 mLogFont
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
368 mLogFont
.lfClipPrecision
= CLIP_TURNOFF_FONTASSOCIATION
;
369 mLogFont
.lfQuality
= DEFAULT_QUALITY
;
370 mLogFont
.lfPitchAndFamily
= DEFAULT_PITCH
| FF_DONTCARE
;
371 mLogFont
.lfItalic
= (GetStyle()->style
& (FONT_STYLE_ITALIC
| FONT_STYLE_OBLIQUE
)) ? TRUE
: FALSE
;
372 mLogFont
.lfWeight
= aWeight
;
374 int len
= PR_MIN(mName
.Length(), LF_FACESIZE
- 1);
375 memcpy(mLogFont
.lfFaceName
, nsPromiseFlatString(mName
).get(), len
* 2);
376 mLogFont
.lfFaceName
[len
] = '\0';
381 gfxWindowsFont::GetUniqueName()
385 // start with the family name
386 uniqueName
.Assign(mName
);
388 // append the weight code
389 if (mLogFont
.lfWeight
!= 400) {
390 uniqueName
.AppendLiteral(":");
391 uniqueName
.AppendInt((PRInt32
)mLogFont
.lfWeight
);
395 if (mLogFont
.lfItalic
)
396 uniqueName
.AppendLiteral(":Italic");
398 if (mLogFont
.lfUnderline
)
399 uniqueName
.AppendLiteral(":Underline");
401 if (mLogFont
.lfStrikeOut
)
402 uniqueName
.AppendLiteral(":StrikeOut");
408 gfxWindowsFont::Draw(gfxTextRun
*aTextRun
, PRUint32 aStart
, PRUint32 aEnd
,
409 gfxContext
*aContext
, PRBool aDrawToPath
, gfxPoint
*aBaselineOrigin
,
412 // XXX stuart may want us to do something faster here
413 gfxFont::Draw(aTextRun
, aStart
, aEnd
, aContext
, aDrawToPath
, aBaselineOrigin
,
418 gfxWindowsFont::SetupCairoFont(cairo_t
*aCR
)
420 cairo_set_scaled_font(aCR
, CairoScaledFont());
423 /**********************************************************************
425 * class gfxWindowsFontGroup
427 **********************************************************************/
430 * Look up the font in the gfxFont cache. If we don't find it, create one.
431 * In either case, add a ref, append it to the aFonts array, and return it ---
432 * except for OOM in which case we do nothing and return null.
434 static already_AddRefed
<gfxWindowsFont
>
435 GetOrMakeFont(FontEntry
*aFontEntry
, const gfxFontStyle
*aStyle
)
437 nsRefPtr
<gfxFont
> font
= gfxFontCache::GetCache()->Lookup(aFontEntry
->mName
, aStyle
);
439 font
= new gfxWindowsFont(aFontEntry
->mName
, aStyle
);
442 gfxFontCache::GetCache()->AddNew(font
);
446 return static_cast<gfxWindowsFont
*>(f
);
450 AddFontEntryToArray(const nsAString
& aName
,
451 const nsACString
& aGenericName
,
454 if (!aName
.IsEmpty()) {
455 nsTArray
<nsRefPtr
<FontEntry
> > *list
= NS_STATIC_CAST(nsTArray
<nsRefPtr
<FontEntry
> >*, closure
);
457 nsRefPtr
<FontEntry
> fe
= gfxWindowsPlatform::GetPlatform()->FindFontEntry(aName
);
458 if (list
->IndexOf(fe
) == list
->NoIndex
)
459 list
->AppendElement(fe
);
465 gfxWindowsFontGroup::gfxWindowsFontGroup(const nsAString
& aFamilies
, const gfxFontStyle
*aStyle
)
466 : gfxFontGroup(aFamilies
, aStyle
)
468 ForEachFont(AddFontEntryToArray
, &mFontEntries
);
470 if (mFonts
.Length() == 0) {
471 // Should append default GUI font if there are no available fonts.
472 HGDIOBJ hGDI
= ::GetStockObject(DEFAULT_GUI_FONT
);
475 !::GetObjectW(hGDI
, sizeof(logFont
), &logFont
)) {
476 NS_ERROR("Failed to create font group");
479 nsRefPtr
<FontEntry
> fe
= gfxWindowsPlatform::GetPlatform()->FindFontEntry(nsDependentString(logFont
.lfFaceName
));
480 mFontEntries
.AppendElement(fe
);
483 mFonts
.AppendElements(mFontEntries
.Length());
486 gfxWindowsFontGroup::~gfxWindowsFontGroup()
491 gfxWindowsFontGroup::GetFontAt(PRInt32 i
)
494 nsRefPtr
<gfxWindowsFont
> font
= GetOrMakeFont(mFontEntries
[i
], &mStyle
);
498 return NS_STATIC_CAST(gfxWindowsFont
*, mFonts
[i
].get());
502 gfxWindowsFontGroup::Copy(const gfxFontStyle
*aStyle
)
504 return new gfxWindowsFontGroup(mFamilies
, aStyle
);
508 gfxWindowsFontGroup::MakeTextRun(const PRUnichar
*aString
, PRUint32 aLength
,
509 const Parameters
*aParams
, PRUint32 aFlags
)
511 // XXX comment out the assertion for now since it fires too much
512 // NS_ASSERTION(!(mFlags & TEXT_NEED_BOUNDING_BOX),
513 // "Glyph extents not yet supported");
515 gfxTextRun
*textRun
= new gfxTextRun(aParams
, aString
, aLength
, this, aFlags
);
518 NS_ASSERTION(aParams
->mContext
, "MakeTextRun called without a gfxContext");
520 textRun
->RecordSurrogates(aString
);
522 #ifdef FORCE_UNISCRIBE
523 const PRBool isComplex
= PR_TRUE
;
525 const PRBool isComplex
= ScriptIsComplex(aString
, aLength
, SIC_COMPLEX
) == S_OK
||
526 textRun
->IsRightToLeft();
529 InitTextRunUniscribe(aParams
->mContext
, textRun
, aString
, aLength
);
531 InitTextRunGDI(aParams
->mContext
, textRun
, aString
, aLength
);
537 gfxWindowsFontGroup::MakeTextRun(const PRUint8
*aString
, PRUint32 aLength
,
538 const Parameters
*aParams
, PRUint32 aFlags
)
540 NS_ASSERTION(aFlags
& TEXT_IS_8BIT
, "should be marked 8bit");
542 gfxTextRun
*textRun
= new gfxTextRun(aParams
, aString
, aLength
, this, aFlags
);
545 NS_ASSERTION(aParams
->mContext
, "MakeTextRun called without a gfxContext");
547 #ifdef FORCE_UNISCRIBE
548 const PRBool isComplex
= PR_TRUE
;
550 const PRBool isComplex
= textRun
->IsRightToLeft();
553 /* We can only call GDI "A" functions if this is a true 7bit ASCII string,
554 because they interpret code points from 0x80-0xFF as if they were
555 in the system code page. */
556 if (!isComplex
&& (aFlags
& TEXT_IS_ASCII
)) {
557 InitTextRunGDI(aParams
->mContext
, textRun
,
558 reinterpret_cast<const char*>(aString
), aLength
);
561 nsDependentCSubstring
cString(reinterpret_cast<const char*>(aString
),
562 reinterpret_cast<const char*>(aString
+ aLength
));
564 AppendASCIItoUTF16(cString
, utf16
);
566 InitTextRunUniscribe(aParams
->mContext
, textRun
, utf16
.get(), aLength
);
568 InitTextRunGDI(aParams
->mContext
, textRun
, utf16
.get(), aLength
);
576 * Set the font in the DC for aContext's surface. Return the DC if we're
577 * successful. If it's not a win32 surface or something goes wrong or if
578 * the font is not a Truetype font (hence GetGlyphIndices may be buggy) then
579 * we're not successful and return 0.
582 SetupContextFont(gfxContext
*aContext
, gfxWindowsFont
*aFont
)
584 nsRefPtr
<gfxASurface
> surf
= aContext
->CurrentSurface();
585 HDC dc
= GetDCFromSurface(surf
);
589 HFONT hfont
= aFont
->GetHFONT();
592 SelectObject(dc
, hfont
);
594 /* GetGlyphIndices is buggy for bitmap and vector fonts,
595 so send them to uniscribe */
597 GetTextMetrics(dc
, &metrics
);
598 if ((metrics
.tmPitchAndFamily
& (TMPF_TRUETYPE
)) == 0)
605 IsAnyGlyphMissing(WCHAR
*aGlyphs
, PRUint32 aLength
)
608 for (i
= 0; i
< aLength
; ++i
) {
609 if (aGlyphs
[i
] == 0xFFFF)
616 SetupTextRunFromGlyphs(gfxTextRun
*aRun
, WCHAR
*aGlyphs
, HDC aDC
,
617 gfxWindowsFont
*aFont
)
619 PRUint32 length
= aRun
->GetLength();
620 if (IsAnyGlyphMissing(aGlyphs
, length
))
624 nsAutoTArray
<int,500> partialWidthArray
;
625 if (!partialWidthArray
.AppendElements(length
))
627 BOOL success
= GetTextExtentExPointI(aDC
,
632 partialWidthArray
.Elements(),
637 aRun
->AddGlyphRun(aFont
, 0);
639 gfxTextRun::CompressedGlyph g
;
641 PRInt32 lastWidth
= 0;
642 PRUint32 appUnitsPerDevPixel
= aRun
->GetAppUnitsPerDevUnit();
643 for (i
= 0; i
< length
; ++i
) {
644 PRInt32 advancePixels
= partialWidthArray
[i
] - lastWidth
;
645 lastWidth
= partialWidthArray
[i
];
646 PRInt32 advanceAppUnits
= advancePixels
*appUnitsPerDevPixel
;
647 WCHAR glyph
= aGlyphs
[i
];
648 if (gfxFontGroup::IsInvisibleChar(aRun
->GetChar(i
))) {
649 aRun
->SetCharacterGlyph(i
, g
.SetMissing());
650 } else if (advanceAppUnits
>= 0 &&
651 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advanceAppUnits
) &&
652 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph
)) {
653 aRun
->SetCharacterGlyph(i
, g
.SetSimpleGlyph(advanceAppUnits
, glyph
));
655 gfxTextRun::DetailedGlyph details
;
656 details
.mIsLastGlyph
= PR_TRUE
;
657 details
.mGlyphID
= glyph
;
658 details
.mAdvance
= advanceAppUnits
;
659 details
.mXOffset
= 0;
660 details
.mYOffset
= 0;
661 aRun
->SetDetailedGlyphs(i
, &details
, 1);
668 gfxWindowsFontGroup::InitTextRunGDI(gfxContext
*aContext
, gfxTextRun
*aRun
,
669 const char *aString
, PRUint32 aLength
)
671 nsRefPtr
<gfxWindowsFont
> font
= GetFontAt(0);
672 HDC dc
= SetupContextFont(aContext
, font
);
674 nsAutoTArray
<WCHAR
,500> glyphArray
;
675 if (!glyphArray
.AppendElements(aLength
))
678 DWORD ret
= GetGlyphIndicesA(dc
, aString
, aLength
, (WORD
*) glyphArray
.Elements(),
679 GGI_MARK_NONEXISTING_GLYPHS
);
680 if (ret
!= GDI_ERROR
&&
681 SetupTextRunFromGlyphs(aRun
, glyphArray
.Elements(), dc
, font
))
685 nsDependentCSubstring
cString(aString
, aString
+ aLength
);
687 AppendASCIItoUTF16(cString
, utf16
);
688 InitTextRunUniscribe(aContext
, aRun
, utf16
.get(), aLength
);
692 gfxWindowsFontGroup::InitTextRunGDI(gfxContext
*aContext
, gfxTextRun
*aRun
,
693 const PRUnichar
*aString
, PRUint32 aLength
)
695 nsRefPtr
<gfxWindowsFont
> font
= GetFontAt(0);
696 HDC dc
= SetupContextFont(aContext
, font
);
698 nsAutoTArray
<WCHAR
,500> glyphArray
;
699 if (!glyphArray
.AppendElements(aLength
))
702 DWORD ret
= GetGlyphIndicesW(dc
, aString
, aLength
, (WORD
*) glyphArray
.Elements(),
703 GGI_MARK_NONEXISTING_GLYPHS
);
704 if (ret
!= GDI_ERROR
&&
705 SetupTextRunFromGlyphs(aRun
, glyphArray
.Elements(), dc
, font
))
709 InitTextRunUniscribe(aContext
, aRun
, aString
, aLength
);
716 /* we map primary language id's to this to look up language codes */
717 struct ScriptPropertyEntry
{
719 const char *langCode
;
722 static const struct ScriptPropertyEntry gScriptToText
[] =
725 { "LANG_ARABIC", "ara" },
726 { "LANG_BULGARIAN", "bul" },
727 { "LANG_CATALAN", "cat" },
728 { "LANG_CHINESE", "zh-CN" }, //XXX right lang code?
729 { "LANG_CZECH", "cze" }, // cze/ces
730 { "LANG_DANISH", "dan" },
731 { "LANG_GERMAN", "ger" }, // ger/deu
732 { "LANG_GREEK", "el" }, // gre/ell
733 { "LANG_ENGLISH", "x-western" },
734 { "LANG_SPANISH", "spa" },
735 { "LANG_FINNISH", "fin" },
736 { "LANG_FRENCH", "fre" }, // fre/fra
737 { "LANG_HEBREW", "he" }, // heb
738 { "LANG_HUNGARIAN", "hun" },
739 { "LANG_ICELANDIC", "ice" }, // ice/isl
740 { "LANG_ITALIAN", "ita" },
741 { "LANG_JAPANESE", "ja" }, // jpn
742 { "LANG_KOREAN", "ko" }, // kor
743 { "LANG_DUTCH", "dut" }, // dut/nld
744 { "LANG_NORWEGIAN", "nor" },
745 { "LANG_POLISH", "pol" },
746 { "LANG_PORTUGUESE", "por" },
748 { "LANG_ROMANIAN", "rum" }, // rum/ron
749 { "LANG_RUSSIAN", "rus" },
750 { "LANG_SERBIAN", "scc" }, // scc/srp
751 { "LANG_SLOVAK", "slo" }, // slo/slk
752 { "LANG_ALBANIAN", "alb" }, // alb/sqi
753 { "LANG_SWEDISH", "swe" },
754 { "LANG_THAI", "th" }, // tha
755 { "LANG_TURKISH", "tr" }, // tur
756 { "LANG_URDU", "urd" },
757 { "LANG_INDONESIAN", "ind" },
758 { "LANG_UKRAINIAN", "ukr" },
759 { "LANG_BELARUSIAN", "bel" },
760 { "LANG_SLOVENIAN", "slv" },
761 { "LANG_ESTONIAN", "est" },
762 { "LANG_LATVIAN", "lav" },
763 { "LANG_LITHUANIAN", "lit" },
765 { "LANG_FARSI", "per" }, // per/fas
766 { "LANG_VIETNAMESE", "vie" },
767 { "LANG_ARMENIAN", "x-armn" }, // arm/hye
768 { "LANG_AZERI", "aze" },
769 { "LANG_BASQUE", "baq" }, // baq/eus
771 { "LANG_MACEDONIAN", "mac" }, // mac/mkd
778 { "LANG_AFRIKAANS", "afr" },
779 { "LANG_GEORGIAN", "x-geor" }, // geo
780 { "LANG_FAEROESE", "fao" },
781 { "LANG_HINDI", "x-devanagari" }, // hin
786 { "LANG_MALAY", "may" }, // may/msa
787 { "LANG_KAZAK", "kaz" }, // listed as kazakh?
788 { "LANG_KYRGYZ", "kis" },
789 { "LANG_SWAHILI", "swa" },
791 { "LANG_UZBEK", "uzb" },
792 { "LANG_TATAR", "tat" },
793 { "LANG_BENGALI", "x-beng" }, // ben
794 { "LANG_PUNJABI", "x-guru" }, // pan -- XXX x-guru is for Gurmukhi which isn't just Punjabi
795 { "LANG_GUJARATI", "x-gujr" }, // guj
796 { "LANG_ORIYA", "ori" },
797 { "LANG_TAMIL", "x-tamil" }, // tam
798 { "LANG_TELUGU", "tel" },
799 { "LANG_KANNADA", "kan" },
800 { "LANG_MALAYALAM", "x-mlym" }, // mal
801 { "LANG_ASSAMESE", "asm" },
802 { "LANG_MARATHI", "mar" },
803 { "LANG_SANSKRIT", "san" },
804 { "LANG_MONGOLIAN", "mon" },
805 { "TIBETAN", "tib" }, // tib/bod
807 { "KHMER", "x-khmr" }, // khm
809 { "MYANMAR", "bur" }, // bur/mya
810 { "LANG_GALICIAN", "glg" },
811 { "LANG_KONKANI", "kok" },
812 { "LANG_MANIPURI", "mni" },
813 { "LANG_SINDHI", "x-devanagari" }, // snd
814 { "LANG_SYRIAC", "syr" },
815 { "SINHALESE", "sin" },
816 { "CHEROKEE", "chr" },
817 { "INUKTITUT", "x-cans" }, // iku
818 { "ETHIOPIC", "x-ethi" }, // amh -- this is both Amharic and Tigrinya
820 { "LANG_KASHMIRI", "x-devanagari" }, // kas
821 { "LANG_NEPALI", "x-devanagari" }, // nep
825 { "LANG_DIVEHI", "div" }
828 static const char *sCJKLangGroup
[] = {
836 #define COUNT_OF_CJK_LANG_GROUP 5
837 #define CJK_LANG_JA sCJKLangGroup[0]
838 #define CJK_LANG_KO sCJKLangGroup[1]
839 #define CJK_LANG_ZH_CN sCJKLangGroup[2]
840 #define CJK_LANG_ZH_HK sCJKLangGroup[3]
841 #define CJK_LANG_ZH_TW sCJKLangGroup[4]
843 #define STATIC_STRING_LENGTH 100
846 * XXX We could use a bunch of nsAutoTArrays to avoid memory allocation
847 * for non-huge strings.
852 UniscribeItem(gfxContext
*aContext
, HDC aDC
,
853 const PRUnichar
*aString
, PRUint32 aLength
,
855 gfxWindowsFontGroup
*aGroup
) :
856 mContext(aContext
), mDC(aDC
), mRangeString(nsnull
), mRangeLength(0),
857 mItemString(aString
), mItemLength(aLength
),
858 mAlternativeString(nsnull
), mScriptItem(aItem
),
859 mScript(aItem
->a
.eScript
), mGroup(aGroup
),
860 mGlyphs(nsnull
), mClusters(nsnull
), mAttr(nsnull
),
861 mNumGlyphs(0), mMaxGlyphs((int)(1.5 * aLength
) + 16),
862 mOffsets(nsnull
), mAdvances(nsnull
),
863 mFontSelected(PR_FALSE
)
865 mGlyphs
= (WORD
*)malloc(mMaxGlyphs
* sizeof(WORD
));
866 mClusters
= (WORD
*)malloc((mItemLength
+ 1) * sizeof(WORD
));
867 mAttr
= (SCRIPT_VISATTR
*)malloc(mMaxGlyphs
* sizeof(SCRIPT_VISATTR
));
876 free(mAlternativeString
);
879 /* possible return values:
880 * S_OK - things succeeded
881 * GDI_ERROR - things failed to shape. Might want to try again after calling DisableShaping()
886 HDC shapeDC
= nsnull
;
888 const PRUnichar
*str
= mAlternativeString
? mAlternativeString
: mRangeString
;
891 mScriptItem
->a
.fLogicalOrder
= PR_TRUE
;
893 rv
= ScriptShape(shapeDC
, mCurrentFont
->ScriptCache(),
895 mMaxGlyphs
, &mScriptItem
->a
,
899 if (rv
== E_OUTOFMEMORY
) {
901 mGlyphs
= (WORD
*)realloc(mGlyphs
, mMaxGlyphs
* sizeof(WORD
));
902 mAttr
= (SCRIPT_VISATTR
*)realloc(mAttr
, mMaxGlyphs
* sizeof(SCRIPT_VISATTR
));
906 if (rv
== E_PENDING
) {
913 if (rv
== USP_E_SCRIPT_NOT_IN_FONT
) {
914 ScriptGetCMap(mDC
, mCurrentFont
->ScriptCache(), str
, mRangeString
, 0, mGlyphs
);
915 PRUnichar foo
[LF_FACESIZE
+1];
916 GetTextFaceW(mDC
, LF_FACESIZE
, foo
);
926 PRBool
ShapingEnabled() {
927 return (mScriptItem
->a
.eScript
!= SCRIPT_UNDEFINED
);
929 void DisableShaping() {
930 mScriptItem
->a
.eScript
= SCRIPT_UNDEFINED
;
931 // Note: If we disable the shaping by using SCRIPT_UNDEFINED and
932 // the string has the surrogate pair, ScriptShape API is
933 // *sometimes* crashed. Therefore, we should replace the surrogate
934 // pair to U+FFFD. See bug 341500.
935 GenerateAlternativeString();
937 void EnableShaping() {
938 mScriptItem
->a
.eScript
= mScript
;
939 if (mAlternativeString
) {
940 free(mAlternativeString
);
941 mAlternativeString
= nsnull
;
945 PRBool
IsGlyphMissing(SCRIPT_FONTPROPERTIES
*aSFP
, PRUint32 aGlyphIndex
) {
946 if (mGlyphs
[aGlyphIndex
] == aSFP
->wgDefault
)
954 mOffsets
= (GOFFSET
*)malloc(mNumGlyphs
* sizeof(GOFFSET
));
955 mAdvances
= (int *)malloc(mNumGlyphs
* sizeof(int));
957 HDC placeDC
= nsnull
;
960 rv
= ScriptPlace(placeDC
, mCurrentFont
->ScriptCache(),
962 mAttr
, &mScriptItem
->a
,
963 mAdvances
, mOffsets
, NULL
);
965 if (rv
== E_PENDING
) {
977 const SCRIPT_PROPERTIES
*ScriptProperties() {
978 /* we can use this to figure out in some cases the language of the item */
979 static const SCRIPT_PROPERTIES
**gScriptProperties
;
980 static int gMaxScript
= -1;
982 if (gMaxScript
== -1) {
983 ScriptGetProperties(&gScriptProperties
, &gMaxScript
);
985 return gScriptProperties
[mScript
];
988 void ScriptFontProperties(SCRIPT_FONTPROPERTIES
*sfp
) {
991 memset(sfp
, 0, sizeof(SCRIPT_FONTPROPERTIES
));
992 sfp
->cBytes
= sizeof(SCRIPT_FONTPROPERTIES
);
993 rv
= ScriptGetFontProperties(NULL
, mCurrentFont
->ScriptCache(),
995 if (rv
== E_PENDING
) {
997 rv
= ScriptGetFontProperties(mDC
, mCurrentFont
->ScriptCache(),
1002 void SetupClusterBoundaries(gfxTextRun
*aRun
, PRUint32 aOffsetInRun
) {
1003 if (aRun
->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT
)
1006 nsAutoTArray
<SCRIPT_LOGATTR
,STATIC_STRING_LENGTH
> logAttr
;
1007 if (!logAttr
.AppendElements(mRangeLength
))
1009 HRESULT rv
= ScriptBreak(mRangeString
, mRangeLength
,
1010 &mScriptItem
->a
, logAttr
.Elements());
1013 gfxTextRun::CompressedGlyph g
;
1014 // The first character is never inside a cluster. Windows might tell us
1015 // that it should be, but we have no before-character to cluster
1016 // it with so we just can't cluster it. So skip it here.
1017 for (PRUint32 i
= 1; i
< mRangeLength
; ++i
) {
1018 if (!logAttr
[i
].fCharStop
) {
1019 aRun
->SetCharacterGlyph(i
+ aOffsetInRun
, g
.SetClusterContinuation());
1024 void SaveGlyphs(gfxTextRun
*aRun
) {
1025 PRUint32 offsetInRun
= mScriptItem
->iCharPos
+ (mRangeString
- mItemString
);
1026 SetupClusterBoundaries(aRun
, offsetInRun
);
1028 aRun
->AddGlyphRun(GetCurrentFont(), offsetInRun
);
1030 // XXX We should store this in the item and only fetch it once
1031 SCRIPT_FONTPROPERTIES sfp
;
1032 ScriptFontProperties(&sfp
);
1034 PRUint32 offset
= 0;
1035 nsAutoTArray
<gfxTextRun::DetailedGlyph
,1> detailedGlyphs
;
1036 gfxTextRun::CompressedGlyph g
;
1037 const PRUint32 appUnitsPerDevUnit
= aRun
->GetAppUnitsPerDevUnit();
1038 while (offset
< mRangeLength
) {
1039 PRUint32 runOffset
= offsetInRun
+ offset
;
1040 if (offset
> 0 && mClusters
[offset
] == mClusters
[offset
- 1]) {
1041 if (!aRun
->GetCharacterGlyphs()[runOffset
].IsClusterContinuation()) {
1042 // No glyphs for character 'index', it must be a ligature continuation
1043 aRun
->SetCharacterGlyph(runOffset
, g
.SetLigatureContinuation());
1046 // Count glyphs for this character
1047 PRUint32 k
= mClusters
[offset
];
1048 PRUint32 glyphCount
= mNumGlyphs
- k
;
1049 PRUint32 nextClusterOffset
;
1050 PRBool missing
= IsGlyphMissing(&sfp
, k
);
1051 for (nextClusterOffset
= offset
+ 1; nextClusterOffset
< mRangeLength
; ++nextClusterOffset
) {
1052 if (mClusters
[nextClusterOffset
] > k
) {
1053 glyphCount
= mClusters
[nextClusterOffset
] - k
;
1058 for (j
= 1; j
< glyphCount
; ++j
) {
1059 if (IsGlyphMissing(&sfp
, k
+ j
)) {
1063 PRInt32 advance
= mAdvances
[k
]*appUnitsPerDevUnit
;
1064 WORD glyph
= mGlyphs
[k
];
1065 if (gfxFontGroup::IsInvisibleChar(mRangeString
[offset
])) {
1066 aRun
->SetCharacterGlyph(runOffset
, g
.SetMissing());
1067 } else if (missing
) {
1068 aRun
->SetMissingGlyph(runOffset
, mRangeString
[offset
]);
1069 } else if (glyphCount
== 1 && advance
>= 0 &&
1070 mOffsets
[k
].dv
== 0 && mOffsets
[k
].du
== 0 &&
1071 gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance
) &&
1072 gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph
)) {
1073 aRun
->SetCharacterGlyph(runOffset
, g
.SetSimpleGlyph(advance
, glyph
));
1075 if (detailedGlyphs
.Length() < glyphCount
) {
1076 if (!detailedGlyphs
.AppendElements(glyphCount
- detailedGlyphs
.Length()))
1080 for (i
= 0; i
< glyphCount
; ++i
) {
1081 gfxTextRun::DetailedGlyph
*details
= &detailedGlyphs
[i
];
1082 details
->mIsLastGlyph
= i
== glyphCount
- 1;
1083 details
->mGlyphID
= mGlyphs
[k
+ i
];
1084 details
->mAdvance
= mAdvances
[k
+ i
]*appUnitsPerDevUnit
;
1085 details
->mXOffset
= float(mOffsets
[k
+ i
].du
)*appUnitsPerDevUnit
*aRun
->GetDirection();
1086 details
->mYOffset
= float(mOffsets
[k
+ i
].dv
)*appUnitsPerDevUnit
;
1088 aRun
->SetDetailedGlyphs(runOffset
, detailedGlyphs
.Elements(), glyphCount
);
1095 void SetCurrentFont(gfxWindowsFont
*aFont
) {
1096 if (mCurrentFont
!= aFont
) {
1097 mCurrentFont
= aFont
;
1098 cairo_win32_scaled_font_done_font(mCurrentFont
->CairoScaledFont());
1099 mFontSelected
= PR_FALSE
;
1103 gfxWindowsFont
*GetCurrentFont() {
1104 return mCurrentFont
;
1111 cairo_t
*cr
= mContext
->GetCairo();
1113 cairo_set_font_face(cr
, mCurrentFont
->CairoFontFace());
1114 cairo_set_font_size(cr
, mCurrentFont
->GetAdjustedSize());
1116 cairo_win32_scaled_font_select_font(mCurrentFont
->CairoScaledFont(), mDC
);
1118 mFontSelected
= PR_TRUE
;
1122 TextRange(PRUint32 aStart
, PRUint32 aEnd
) : start(aStart
), end(aEnd
) { }
1123 PRUint32
Length() const { return end
- start
; }
1124 nsRefPtr
<FontEntry
> font
;
1125 PRUint32 start
, end
;
1128 void SetRange(PRUint32 i
) {
1129 nsRefPtr
<FontEntry
> fe
;
1130 if (mRanges
[i
].font
)
1131 fe
= mRanges
[i
].font
;
1133 fe
= mGroup
->GetFontEntryAt(0);
1135 nsRefPtr
<gfxWindowsFont
> font
= GetOrMakeFont(fe
, mGroup
->GetStyle());
1136 SetCurrentFont(font
);
1138 mRangeString
= mItemString
+ mRanges
[i
].start
;
1139 mRangeLength
= mRanges
[i
].Length();
1142 static inline FontEntry
*WhichFontSupportsChar(const nsTArray
<nsRefPtr
<FontEntry
> >& fonts
, PRUint32 ch
) {
1143 for (PRUint32 i
= 0; i
< fonts
.Length(); i
++) {
1144 nsRefPtr
<FontEntry
> fe
= fonts
[i
];
1145 if (fe
->mCharacterMap
.test(ch
))
1152 static inline bool IsJoiner(PRUint32 ch
) {
1153 return (ch
== 0x200C ||
1158 inline FontEntry
*FindFontForChar(PRUint32 ch
, PRUint32 prevCh
, PRUint32 nextCh
, FontEntry
*aFont
) {
1159 nsRefPtr
<FontEntry
> selectedFont
;
1161 // if this character or the next one is a joiner use the
1162 // same font as the previous range if we can
1163 if (IsJoiner(ch
) || IsJoiner(prevCh
) || IsJoiner(nextCh
)) {
1164 if (aFont
&& aFont
->mCharacterMap
.test(ch
))
1168 // check the list of fonts
1169 selectedFont
= WhichFontSupportsChar(mGroup
->GetFontList(), ch
);
1171 // otherwise search prefs
1172 if (!selectedFont
) {
1173 /* first check with the script properties to see what they think */
1174 const SCRIPT_PROPERTIES
*sp
= ScriptProperties();
1175 if (!sp
->fAmbiguousCharSet
) {
1176 WORD primaryId
= PRIMARYLANGID(sp
->langid
);
1177 const char *langGroup
= gScriptToText
[primaryId
].langCode
;
1179 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" - Trying to find fonts for: %s (%s)", langGroup
, gScriptToText
[primaryId
].value
));
1181 nsTArray
<nsRefPtr
<FontEntry
> > fonts
;
1182 this->GetPrefFonts(langGroup
, fonts
);
1183 selectedFont
= WhichFontSupportsChar(fonts
, ch
);
1188 if (!selectedFont
) {
1189 PRUint32 unicodeRange
= FindCharUnicodeRange(ch
);
1191 /* special case CJK */
1192 if (unicodeRange
== kRangeSetCJK
) {
1193 if (PR_LOG_TEST(gFontLog
, PR_LOG_DEBUG
))
1194 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" - Trying to find fonts for: CJK"));
1196 nsTArray
<nsRefPtr
<FontEntry
> > fonts
;
1197 this->GetCJKPrefFonts(fonts
);
1198 selectedFont
= WhichFontSupportsChar(fonts
, ch
);
1200 const char *langGroup
= LangGroupFromUnicodeRange(unicodeRange
);
1202 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" - Trying to find fonts for: %s", langGroup
));
1204 nsTArray
<nsRefPtr
<FontEntry
> > fonts
;
1205 this->GetPrefFonts(langGroup
, fonts
);
1206 selectedFont
= WhichFontSupportsChar(fonts
, ch
);
1211 // before searching for something else check the font used for the previous character
1212 if (!selectedFont
&& aFont
&& aFont
->mCharacterMap
.test(ch
))
1213 selectedFont
= aFont
;
1215 // otherwise look for other stuff
1216 if (!selectedFont
) {
1217 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" - Looking for best match"));
1219 nsRefPtr
<gfxWindowsFont
> refFont
= mGroup
->GetFontAt(0);
1220 gfxWindowsPlatform
*platform
= gfxWindowsPlatform::GetPlatform();
1224 str
[0] = H_SURROGATE(ch
);
1225 str
[1] = L_SURROGATE(ch
);
1231 selectedFont
= platform
->FindFontForString(str
, len
, refFont
);
1234 return selectedFont
;
1237 PRUint32
ComputeRanges() {
1238 if (mItemLength
== 0)
1241 PR_LOG(gFontLog
, PR_LOG_DEBUG
, ("Computing ranges for string: (len = %d)", mItemLength
));
1243 PRUint32 prevCh
= 0;
1244 for (PRUint32 i
= 0; i
< mItemLength
; i
++) {
1245 const PRUint32 origI
= i
; // save off incase we increase for surrogate
1246 PRUint32 ch
= mItemString
[i
];
1247 if ((i
+1 < mItemLength
) && NS_IS_HIGH_SURROGATE(ch
) && NS_IS_LOW_SURROGATE(mItemString
[i
+1])) {
1249 ch
= SURROGATE_TO_UCS4(ch
, mItemString
[i
]);
1252 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" 0x%04x - ", ch
));
1253 PRUint32 nextCh
= 0;
1254 if (i
+1 < mItemLength
) {
1255 nextCh
= mItemString
[i
+1];
1256 if ((i
+2 < mItemLength
) && NS_IS_HIGH_SURROGATE(ch
) && NS_IS_LOW_SURROGATE(mItemString
[i
+2]))
1257 nextCh
= SURROGATE_TO_UCS4(nextCh
, mItemString
[i
+2]);
1259 nsRefPtr
<FontEntry
> fe
= FindFontForChar(ch
,
1262 (mRanges
.Length() == 0) ? nsnull
: mRanges
[mRanges
.Length() - 1].font
);
1266 if (mRanges
.Length() == 0) {
1269 mRanges
.AppendElement(r
);
1271 TextRange
& prevRange
= mRanges
[mRanges
.Length() - 1];
1272 if (prevRange
.font
!= fe
) {
1273 // close out the previous range
1274 prevRange
.end
= origI
;
1276 TextRange
r(i
, i
+1);
1278 mRanges
.AppendElement(r
);
1281 if (PR_LOG_TEST(gFontLog
, PR_LOG_DEBUG
)) {
1283 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" - Using %s", NS_LossyConvertUTF16toASCII(fe
->mName
).get()));
1285 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" - Unable to find font"));
1288 mRanges
[mRanges
.Length()-1].end
= mItemLength
;
1290 PRUint32 nranges
= mRanges
.Length();
1291 PR_LOG(gFontLog
, PR_LOG_DEBUG
, (" Found %d ranges", nranges
));
1296 static PRInt32
GetCJKLangGroupIndex(const char *aLangGroup
) {
1298 for (i
= 0; i
< COUNT_OF_CJK_LANG_GROUP
; i
++) {
1299 if (!PL_strcasecmp(aLangGroup
, sCJKLangGroup
[i
]))
1305 void GetPrefFonts(const char *aLangGroup
, nsTArray
<nsRefPtr
<FontEntry
> >& array
) {
1306 NS_ASSERTION(aLangGroup
, "aLangGroup is null");
1307 gfxPlatform
*platform
= gfxPlatform::GetPlatform();
1309 platform
->GetPrefFonts(aLangGroup
, fonts
);
1310 if (fonts
.IsEmpty())
1312 gfxFontGroup::ForEachFont(fonts
, nsDependentCString(aLangGroup
),
1313 AddFontEntryToArray
, &array
);
1316 void GetCJKPrefFonts(nsTArray
<nsRefPtr
<FontEntry
> >& array
) {
1317 nsCOMPtr
<nsIPrefService
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
1321 nsCOMPtr
<nsIPrefBranch
> prefBranch
;
1322 prefs
->GetBranch(0, getter_AddRefs(prefBranch
));
1326 // Add by the order of accept languages.
1327 nsXPIDLCString list
;
1328 nsresult rv
= prefBranch
->GetCharPref("intl.accept_languages", getter_Copies(list
));
1329 if (NS_SUCCEEDED(rv
) && !list
.IsEmpty()) {
1330 const char kComma
= ',';
1331 const char *p
, *p_end
;
1332 list
.BeginReading(p
);
1333 list
.EndReading(p_end
);
1335 while (nsCRT::IsAsciiSpace(*p
)) {
1341 const char *start
= p
;
1342 while (++p
!= p_end
&& *p
!= kComma
)
1344 nsCAutoString
lang(Substring(start
, p
));
1345 lang
.CompressWhitespace(PR_FALSE
, PR_TRUE
);
1346 PRInt32 index
= GetCJKLangGroupIndex(lang
.get());
1348 GetPrefFonts(sCJKLangGroup
[index
], array
);
1353 // Add the system locale
1354 switch (::GetACP()) {
1355 case 932: GetPrefFonts(CJK_LANG_JA
, array
); break;
1356 case 936: GetPrefFonts(CJK_LANG_ZH_CN
, array
); break;
1357 case 949: GetPrefFonts(CJK_LANG_KO
, array
); break;
1358 // XXX Don't we need to append CJK_LANG_ZH_HK if the codepage is 950?
1359 case 950: GetPrefFonts(CJK_LANG_ZH_TW
, array
); break;
1363 GetPrefFonts(CJK_LANG_JA
, array
);
1364 GetPrefFonts(CJK_LANG_KO
, array
);
1365 GetPrefFonts(CJK_LANG_ZH_CN
, array
);
1366 GetPrefFonts(CJK_LANG_ZH_HK
, array
);
1367 GetPrefFonts(CJK_LANG_ZH_TW
, array
);
1370 void GenerateAlternativeString() {
1371 if (mAlternativeString
)
1372 free(mAlternativeString
);
1373 mAlternativeString
= (PRUnichar
*)malloc(mRangeLength
* sizeof(PRUnichar
));
1374 memcpy((void *)mAlternativeString
, (const void *)mRangeString
,
1375 mRangeLength
* sizeof(PRUnichar
));
1376 for (PRUint32 i
= 0; i
< mRangeLength
; i
++) {
1377 if (NS_IS_HIGH_SURROGATE(mRangeString
[i
]) || NS_IS_LOW_SURROGATE(mRangeString
[i
]))
1378 mAlternativeString
[i
] = PRUnichar(0xFFFD);
1383 nsRefPtr
<gfxContext
> mContext
;
1386 SCRIPT_ITEM
*mScriptItem
;
1389 // these point to the current range
1390 const PRUnichar
*mRangeString
;
1391 PRUint32 mRangeLength
;
1393 // these point to the full string/length of the item
1394 const PRUnichar
*mItemString
;
1395 const PRUint32 mItemLength
;
1397 PRUnichar
*mAlternativeString
;
1399 gfxWindowsFontGroup
*mGroup
;
1403 SCRIPT_VISATTR
*mAttr
;
1411 nsTArray
< nsRefPtr
<gfxWindowsFont
> > mFonts
;
1413 nsRefPtr
<gfxWindowsFont
> mCurrentFont
;
1415 PRPackedBool mFontSelected
;
1417 nsTArray
<TextRange
> mRanges
;
1423 Uniscribe(gfxContext
*aContext
, HDC aDC
, const PRUnichar
*aString
, PRUint32 aLength
, PRBool aIsRTL
) :
1424 mContext(aContext
), mDC(aDC
), mString(aString
), mLength(aLength
), mIsRTL(aIsRTL
),
1433 memset(&mControl
, 0, sizeof(SCRIPT_CONTROL
));
1434 memset(&mState
, 0, sizeof(SCRIPT_STATE
));
1435 // Lock the direction. Don't allow the itemizer to change directions
1436 // based on character type.
1437 mState
.uBidiLevel
= mIsRTL
;
1438 mState
.fOverrideDirection
= PR_TRUE
;
1447 // Allocate space for one more item than expected, to handle a rare
1448 // overflow in ScriptItemize (pre XP SP2). See bug 366643.
1449 mItems
= (SCRIPT_ITEM
*)malloc((maxItems
+ 1) * sizeof(SCRIPT_ITEM
));
1450 while ((rv
= ScriptItemize(mString
, mLength
, maxItems
, &mControl
, &mState
,
1451 mItems
, &mNumItems
)) == E_OUTOFMEMORY
) {
1453 mItems
= (SCRIPT_ITEM
*)realloc(mItems
, (maxItems
+ 1) * sizeof(SCRIPT_ITEM
));
1460 PRUint32
ItemsLength() {
1464 // XXX Why do we dynamically allocate this? We could just fill in an object
1466 UniscribeItem
*GetItem(PRUint32 i
, gfxWindowsFontGroup
*aGroup
) {
1467 NS_ASSERTION(i
< (PRUint32
)mNumItems
, "Trying to get out of bounds item");
1469 UniscribeItem
*item
= new UniscribeItem(mContext
, mDC
,
1470 mString
+ mItems
[i
].iCharPos
,
1471 mItems
[i
+1].iCharPos
- mItems
[i
].iCharPos
,
1479 nsRefPtr
<gfxContext
> mContext
;
1481 const PRUnichar
*mString
;
1482 const PRUint32 mLength
;
1483 const PRBool mIsRTL
;
1485 SCRIPT_CONTROL mControl
;
1486 SCRIPT_STATE mState
;
1487 SCRIPT_ITEM
*mItems
;
1492 gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext
*aContext
, gfxTextRun
*aRun
, const PRUnichar
*aString
,
1495 nsRefPtr
<gfxASurface
> surf
= aContext
->CurrentSurface();
1496 HDC aDC
= GetDCFromSurface(surf
);
1497 NS_ASSERTION(aDC
, "No DC");
1499 const PRBool isRTL
= aRun
->IsRightToLeft();
1503 Uniscribe
us(aContext
, aDC
, aString
, aLength
, isRTL
);
1505 /* itemize the string */
1506 int numItems
= us
.Itemize();
1508 for (int i
= 0; i
< numItems
; ++i
) {
1511 UniscribeItem
*item
= us
.GetItem(i
, this);
1513 PRUint32 nranges
= item
->ComputeRanges();
1515 for (PRUint32 j
= 0; j
< nranges
; ++j
) {
1519 if (!item
->ShapingEnabled())
1520 item
->EnableShaping();
1522 while (FAILED(item
->Shape())) {
1523 PR_LOG(gFontLog
, PR_LOG_DEBUG
, ("shaping failed"));
1524 // we know we have the glyphs to display this font already
1525 // so Uniscribe just doesn't know how to shape the script.
1526 // Render the glyphs without shaping.
1527 item
->DisableShaping();
1530 NS_ASSERTION(SUCCEEDED(rv
), "Failed to shape -- we should never hit this");
1533 NS_ASSERTION(SUCCEEDED(rv
), "Failed to place -- this is pretty bad.");
1535 item
->SaveGlyphs(aRun
);