minor cleanup to make the code more readable. bug 384363. r=vlad
[mozilla-central.git] / gfx / thebes / src / gfxWindowsFonts.cpp
blob11b2dcc77248c4759055658b89ff3b464a96182b
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
13 * License.
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.
21 * Contributor(s):
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 ***** */
38 #ifdef DEBUG_smontagu
39 #define DEBUG_pavlov
40 #endif
42 //#define FORCE_UNISCRIBE 1
43 #define FORCE_PR_LOG
45 #include "prtypes.h"
46 #include "gfxTypes.h"
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"
55 #endif
57 #include "gfxFontTest.h"
59 #include "cairo.h"
60 #include "cairo-win32.h"
62 #include <windows.h>
64 #include "nsUnicodeRange.h"
65 #include "nsUnicharUtils.h"
67 #include "nsIPrefBranch.h"
68 #include "nsIPrefService.h"
69 #include "nsServiceManagerUtils.h"
71 #include "nsCRT.h"
73 #include <math.h>
75 #include "prlog.h"
76 static PRLogModuleInfo *gFontLog = PR_NewLogModule("winfonts");
78 #define ROUND(x) floor((x) + 0.5)
80 inline HDC
81 GetDCFromSurface(gfxASurface *aSurface)
83 if (aSurface->GetType() != gfxASurface::SurfaceTypeWin32) {
84 NS_ERROR("GetDCFromSurface: Context target is not win32!");
85 return nsnull;
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),
100 mMetrics(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()
112 if (mFontFace)
113 cairo_font_face_destroy(mFontFace);
115 if (mScaledFont)
116 cairo_scaled_font_destroy(mScaledFont);
118 if (mFont)
119 DeleteObject(mFont);
121 ScriptFreeCache(&mScriptCache);
123 delete mMetrics;
126 const gfxFont::Metrics&
127 gfxWindowsFont::GetMetrics()
129 if (!mMetrics)
130 ComputeMetrics();
132 return *mMetrics;
135 cairo_font_face_t *
136 gfxWindowsFont::CairoFontFace()
138 if (!mFontFace)
139 mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&mLogFont, mFont);
141 NS_ASSERTION(mFontFace, "Failed to make font face");
143 return mFontFace;
146 cairo_scaled_font_t *
147 gfxWindowsFont::CairoScaledFont()
149 if (!mScaledFont) {
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");
164 return mScaledFont;
167 HFONT
168 gfxWindowsFont::MakeHFONT()
170 if (mFont)
171 return mFont;
173 PRInt8 baseWeight, weightDistance;
174 GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
176 HDC dc = nsnull;
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)) {
184 k++;
185 chosenWeight = i * 100;
186 } else if (mFontEntry->mWeightTable.TriedWeight(i)) {
187 continue;
188 } else {
189 const PRUint32 tryWeight = i * 100;
191 if (!dc)
192 dc = GetDC((HWND)nsnull);
194 FillLogFont(GetStyle()->size, tryWeight);
195 mFont = CreateFontIndirectW(&mLogFont);
196 HGDIOBJ oldFont = SelectObject(dc, mFont);
197 TEXTMETRIC metrics;
198 GetTextMetrics(dc, &metrics);
200 PRBool hasWeight = (metrics.tmWeight == tryWeight);
201 mFontEntry->mWeightTable.SetWeight(i, hasWeight);
202 if (hasWeight) {
203 chosenWeight = i * 100;
204 k++;
207 SelectObject(dc, oldFont);
208 if (k <= abs(weightDistance)) {
209 DeleteObject(mFont);
210 mFont = nsnull;
214 if (k > abs(weightDistance)) {
215 chosenWeight = i * 100;
216 break;
220 if (chosenWeight == 0)
221 chosenWeight = baseWeight * 100;
223 mAdjustedSize = GetStyle()->size;
224 if (GetStyle()->sizeAdjust > 0) {
225 if (!mFont) {
226 FillLogFont(mAdjustedSize, chosenWeight);
227 mFont = CreateFontIndirectW(&mLogFont);
230 Metrics *oldMetrics = mMetrics;
231 ComputeMetrics();
232 gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight;
233 mAdjustedSize = GetStyle()->GetAdjustedSize(aspect);
235 if (mMetrics != oldMetrics) {
236 delete mMetrics;
237 mMetrics = oldMetrics;
239 DeleteObject(mFont);
240 mFont = nsnull;
243 if (!mFont) {
244 FillLogFont(mAdjustedSize, chosenWeight);
245 mFont = CreateFontIndirectW(&mLogFont);
248 if (dc)
249 ReleaseDC((HWND)nsnull, dc);
251 return mFont;
254 void
255 gfxWindowsFont::ComputeMetrics()
257 if (!mMetrics)
258 mMetrics = new gfxFont::Metrics;
259 else
260 NS_WARNING("Calling ComputeMetrics multiple times");
262 HDC dc = GetDC((HWND)nsnull);
264 HGDIOBJ oldFont = SelectObject(dc, mFont);
266 // Get font metrics
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} };
279 GLYPHMETRICS gm;
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);
284 } else {
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;
301 } else {
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.
327 SIZE size;
328 GetTextExtentPoint32(dc, " ", 1, &size);
329 mMetrics->spaceWidth = ROUND(size.cx);
331 mSpaceGlyph = 0;
332 if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) {
333 WORD glyph;
334 DWORD ret = GetGlyphIndicesA(dc, " ", 1, &glyph,
335 GGI_MARK_NONEXISTING_GLYPHS);
336 if (ret != GDI_ERROR && glyph != 0xFFFF) {
337 mSpaceGlyph = glyph;
341 SelectObject(dc, oldFont);
343 ReleaseDC((HWND)nsnull, dc);
346 void
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;
363 #ifndef WINCE
364 mLogFont.lfOutPrecision = OUT_TT_PRECIS;
365 #else
366 mLogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
367 #endif
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';
380 nsString
381 gfxWindowsFont::GetUniqueName()
383 nsString uniqueName;
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);
394 // append italic?
395 if (mLogFont.lfItalic)
396 uniqueName.AppendLiteral(":Italic");
398 if (mLogFont.lfUnderline)
399 uniqueName.AppendLiteral(":Underline");
401 if (mLogFont.lfStrikeOut)
402 uniqueName.AppendLiteral(":StrikeOut");
404 return uniqueName;
407 void
408 gfxWindowsFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
409 gfxContext *aContext, PRBool aDrawToPath, gfxPoint *aBaselineOrigin,
410 Spacing *aSpacing)
412 // XXX stuart may want us to do something faster here
413 gfxFont::Draw(aTextRun, aStart, aEnd, aContext, aDrawToPath, aBaselineOrigin,
414 aSpacing);
417 void
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);
438 if (!font) {
439 font = new gfxWindowsFont(aFontEntry->mName, aStyle);
440 if (!font)
441 return nsnull;
442 gfxFontCache::GetCache()->AddNew(font);
444 gfxFont *f = nsnull;
445 font.swap(f);
446 return static_cast<gfxWindowsFont *>(f);
449 static PRBool
450 AddFontEntryToArray(const nsAString& aName,
451 const nsACString& aGenericName,
452 void *closure)
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);
462 return PR_TRUE;
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);
473 LOGFONTW logFont;
474 if (!hGDI ||
475 !::GetObjectW(hGDI, sizeof(logFont), &logFont)) {
476 NS_ERROR("Failed to create font group");
477 return;
479 nsRefPtr<FontEntry> fe = gfxWindowsPlatform::GetPlatform()->FindFontEntry(nsDependentString(logFont.lfFaceName));
480 mFontEntries.AppendElement(fe);
483 mFonts.AppendElements(mFontEntries.Length());
486 gfxWindowsFontGroup::~gfxWindowsFontGroup()
490 gfxWindowsFont *
491 gfxWindowsFontGroup::GetFontAt(PRInt32 i)
493 if (!mFonts[i]) {
494 nsRefPtr<gfxWindowsFont> font = GetOrMakeFont(mFontEntries[i], &mStyle);
495 mFonts[i] = font;
498 return NS_STATIC_CAST(gfxWindowsFont*, mFonts[i].get());
501 gfxFontGroup *
502 gfxWindowsFontGroup::Copy(const gfxFontStyle *aStyle)
504 return new gfxWindowsFontGroup(mFamilies, aStyle);
507 gfxTextRun *
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);
516 if (!textRun)
517 return nsnull;
518 NS_ASSERTION(aParams->mContext, "MakeTextRun called without a gfxContext");
520 textRun->RecordSurrogates(aString);
522 #ifdef FORCE_UNISCRIBE
523 const PRBool isComplex = PR_TRUE;
524 #else
525 const PRBool isComplex = ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK ||
526 textRun->IsRightToLeft();
527 #endif
528 if (isComplex)
529 InitTextRunUniscribe(aParams->mContext, textRun, aString, aLength);
530 else
531 InitTextRunGDI(aParams->mContext, textRun, aString, aLength);
533 return textRun;
536 gfxTextRun *
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);
543 if (!textRun)
544 return nsnull;
545 NS_ASSERTION(aParams->mContext, "MakeTextRun called without a gfxContext");
547 #ifdef FORCE_UNISCRIBE
548 const PRBool isComplex = PR_TRUE;
549 #else
550 const PRBool isComplex = textRun->IsRightToLeft();
551 #endif
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);
560 else {
561 nsDependentCSubstring cString(reinterpret_cast<const char*>(aString),
562 reinterpret_cast<const char*>(aString + aLength));
563 nsAutoString utf16;
564 AppendASCIItoUTF16(cString, utf16);
565 if (isComplex) {
566 InitTextRunUniscribe(aParams->mContext, textRun, utf16.get(), aLength);
567 } else {
568 InitTextRunGDI(aParams->mContext, textRun, utf16.get(), aLength);
572 return textRun;
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.
581 static HDC
582 SetupContextFont(gfxContext *aContext, gfxWindowsFont *aFont)
584 nsRefPtr<gfxASurface> surf = aContext->CurrentSurface();
585 HDC dc = GetDCFromSurface(surf);
586 if (!dc)
587 return 0;
589 HFONT hfont = aFont->GetHFONT();
590 if (!hfont)
591 return 0;
592 SelectObject(dc, hfont);
594 /* GetGlyphIndices is buggy for bitmap and vector fonts,
595 so send them to uniscribe */
596 TEXTMETRIC metrics;
597 GetTextMetrics(dc, &metrics);
598 if ((metrics.tmPitchAndFamily & (TMPF_TRUETYPE)) == 0)
599 return 0;
601 return dc;
604 static PRBool
605 IsAnyGlyphMissing(WCHAR *aGlyphs, PRUint32 aLength)
607 PRUint32 i;
608 for (i = 0; i < aLength; ++i) {
609 if (aGlyphs[i] == 0xFFFF)
610 return PR_TRUE;
612 return PR_FALSE;
615 static PRBool
616 SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC,
617 gfxWindowsFont *aFont)
619 PRUint32 length = aRun->GetLength();
620 if (IsAnyGlyphMissing(aGlyphs, length))
621 return PR_FALSE;
623 SIZE size;
624 nsAutoTArray<int,500> partialWidthArray;
625 if (!partialWidthArray.AppendElements(length))
626 return PR_FALSE;
627 BOOL success = GetTextExtentExPointI(aDC,
628 (WORD*) aGlyphs,
629 length,
630 INT_MAX,
631 NULL,
632 partialWidthArray.Elements(),
633 &size);
634 if (!success)
635 return PR_FALSE;
637 aRun->AddGlyphRun(aFont, 0);
639 gfxTextRun::CompressedGlyph g;
640 PRUint32 i;
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));
654 } else {
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);
664 return PR_TRUE;
667 void
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);
673 if (dc) {
674 nsAutoTArray<WCHAR,500> glyphArray;
675 if (!glyphArray.AppendElements(aLength))
676 return;
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))
682 return;
685 nsDependentCSubstring cString(aString, aString + aLength);
686 nsAutoString utf16;
687 AppendASCIItoUTF16(cString, utf16);
688 InitTextRunUniscribe(aContext, aRun, utf16.get(), aLength);
691 void
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);
697 if (dc) {
698 nsAutoTArray<WCHAR,500> glyphArray;
699 if (!glyphArray.AppendElements(aLength))
700 return;
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))
706 return;
709 InitTextRunUniscribe(aContext, aRun, aString, aLength);
712 /*******************
713 * Uniscribe
714 *******************/
716 /* we map primary language id's to this to look up language codes */
717 struct ScriptPropertyEntry {
718 const char *value;
719 const char *langCode;
722 static const struct ScriptPropertyEntry gScriptToText[] =
724 { nsnull, nsnull },
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" },
747 { nsnull, nsnull },
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" },
764 { nsnull, nsnull },
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
770 { nsnull, nsnull },
771 { "LANG_MACEDONIAN", "mac" }, // mac/mkd
772 { nsnull, nsnull },
773 { nsnull, nsnull },
774 { nsnull, nsnull },
775 { nsnull, nsnull },
776 { nsnull, nsnull },
777 { nsnull, nsnull },
778 { "LANG_AFRIKAANS", "afr" },
779 { "LANG_GEORGIAN", "x-geor" }, // geo
780 { "LANG_FAEROESE", "fao" },
781 { "LANG_HINDI", "x-devanagari" }, // hin
782 { nsnull, nsnull },
783 { nsnull, nsnull },
784 { nsnull, nsnull },
785 { nsnull, nsnull },
786 { "LANG_MALAY", "may" }, // may/msa
787 { "LANG_KAZAK", "kaz" }, // listed as kazakh?
788 { "LANG_KYRGYZ", "kis" },
789 { "LANG_SWAHILI", "swa" },
790 { nsnull, nsnull },
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
806 { nsnull, nsnull },
807 { "KHMER", "x-khmr" }, // khm
808 { "LAO", "lao" },
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
819 { nsnull, nsnull },
820 { "LANG_KASHMIRI", "x-devanagari" }, // kas
821 { "LANG_NEPALI", "x-devanagari" }, // nep
822 { nsnull, nsnull },
823 { nsnull, nsnull },
824 { nsnull, nsnull },
825 { "LANG_DIVEHI", "div" }
828 static const char *sCJKLangGroup[] = {
829 "ja",
830 "ko",
831 "zh-CN",
832 "zh-HK",
833 "zh-TW"
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.
849 class UniscribeItem
851 public:
852 UniscribeItem(gfxContext *aContext, HDC aDC,
853 const PRUnichar *aString, PRUint32 aLength,
854 SCRIPT_ITEM *aItem,
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));
870 ~UniscribeItem() {
871 free(mGlyphs);
872 free(mClusters);
873 free(mAttr);
874 free(mOffsets);
875 free(mAdvances);
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()
883 HRESULT Shape() {
884 HRESULT rv;
886 HDC shapeDC = nsnull;
888 const PRUnichar *str = mAlternativeString ? mAlternativeString : mRangeString;
890 while (PR_TRUE) {
891 mScriptItem->a.fLogicalOrder = PR_TRUE;
893 rv = ScriptShape(shapeDC, mCurrentFont->ScriptCache(),
894 str, mRangeLength,
895 mMaxGlyphs, &mScriptItem->a,
896 mGlyphs, mClusters,
897 mAttr, &mNumGlyphs);
899 if (rv == E_OUTOFMEMORY) {
900 mMaxGlyphs *= 2;
901 mGlyphs = (WORD *)realloc(mGlyphs, mMaxGlyphs * sizeof(WORD));
902 mAttr = (SCRIPT_VISATTR *)realloc(mAttr, mMaxGlyphs * sizeof(SCRIPT_VISATTR));
903 continue;
906 if (rv == E_PENDING) {
907 SelectFont();
909 shapeDC = mDC;
910 continue;
912 #ifdef DEBUG_pavlov
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);
917 printf("bah\n");
919 else if (FAILED(rv))
920 printf("%d\n", rv);
921 #endif
922 return rv;
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)
947 return PR_TRUE;
948 return PR_FALSE;
951 HRESULT Place() {
952 HRESULT rv;
954 mOffsets = (GOFFSET *)malloc(mNumGlyphs * sizeof(GOFFSET));
955 mAdvances = (int *)malloc(mNumGlyphs * sizeof(int));
957 HDC placeDC = nsnull;
959 while (PR_TRUE) {
960 rv = ScriptPlace(placeDC, mCurrentFont->ScriptCache(),
961 mGlyphs, mNumGlyphs,
962 mAttr, &mScriptItem->a,
963 mAdvances, mOffsets, NULL);
965 if (rv == E_PENDING) {
966 SelectFont();
967 placeDC = mDC;
968 continue;
971 break;
974 return rv;
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) {
989 HRESULT rv;
991 memset(sfp, 0, sizeof(SCRIPT_FONTPROPERTIES));
992 sfp->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
993 rv = ScriptGetFontProperties(NULL, mCurrentFont->ScriptCache(),
994 sfp);
995 if (rv == E_PENDING) {
996 SelectFont();
997 rv = ScriptGetFontProperties(mDC, mCurrentFont->ScriptCache(),
998 sfp);
1002 void SetupClusterBoundaries(gfxTextRun *aRun, PRUint32 aOffsetInRun) {
1003 if (aRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT)
1004 return;
1006 nsAutoTArray<SCRIPT_LOGATTR,STATIC_STRING_LENGTH> logAttr;
1007 if (!logAttr.AppendElements(mRangeLength))
1008 return;
1009 HRESULT rv = ScriptBreak(mRangeString, mRangeLength,
1010 &mScriptItem->a, logAttr.Elements());
1011 if (FAILED(rv))
1012 return;
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());
1045 } else {
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;
1054 break;
1057 PRUint32 j;
1058 for (j = 1; j < glyphCount; ++j) {
1059 if (IsGlyphMissing(&sfp, k + j)) {
1060 missing = PR_TRUE;
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));
1074 } else {
1075 if (detailedGlyphs.Length() < glyphCount) {
1076 if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
1077 return;
1079 PRUint32 i;
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);
1091 ++offset;
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;
1107 void SelectFont() {
1108 if (mFontSelected)
1109 return;
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;
1121 struct TextRange {
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;
1132 else
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))
1146 return fe;
1148 return nsnull;
1152 static inline bool IsJoiner(PRUint32 ch) {
1153 return (ch == 0x200C ||
1154 ch == 0x200D ||
1155 ch == 0x2060);
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))
1165 return aFont;
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;
1178 if (langGroup) {
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);
1187 // maybe it is cjk?
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);
1199 } else {
1200 const char *langGroup = LangGroupFromUnicodeRange(unicodeRange);
1201 if (langGroup) {
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();
1221 PRUnichar str[2];
1222 PRUint32 len;
1223 if (ch > 0xFFFF) {
1224 str[0] = H_SURROGATE(ch);
1225 str[1] = L_SURROGATE(ch);
1226 len = 2;
1227 } else {
1228 str[0] = ch;
1229 len = 1;
1231 selectedFont = platform->FindFontForString(str, len, refFont);
1234 return selectedFont;
1237 PRUint32 ComputeRanges() {
1238 if (mItemLength == 0)
1239 return 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])) {
1248 i++;
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,
1260 prevCh,
1261 nextCh,
1262 (mRanges.Length() == 0) ? nsnull : mRanges[mRanges.Length() - 1].font);
1264 prevCh = ch;
1266 if (mRanges.Length() == 0) {
1267 TextRange r(0,1);
1268 r.font = fe;
1269 mRanges.AppendElement(r);
1270 } else {
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);
1277 r.font = fe;
1278 mRanges.AppendElement(r);
1281 if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG)) {
1282 if (fe)
1283 PR_LOG(gFontLog, PR_LOG_DEBUG, (" - Using %s", NS_LossyConvertUTF16toASCII(fe->mName).get()));
1284 else
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));
1292 return nranges;
1295 private:
1296 static PRInt32 GetCJKLangGroupIndex(const char *aLangGroup) {
1297 PRInt32 i;
1298 for (i = 0; i < COUNT_OF_CJK_LANG_GROUP; i++) {
1299 if (!PL_strcasecmp(aLangGroup, sCJKLangGroup[i]))
1300 return i;
1302 return -1;
1305 void GetPrefFonts(const char *aLangGroup, nsTArray<nsRefPtr<FontEntry> >& array) {
1306 NS_ASSERTION(aLangGroup, "aLangGroup is null");
1307 gfxPlatform *platform = gfxPlatform::GetPlatform();
1308 nsString fonts;
1309 platform->GetPrefFonts(aLangGroup, fonts);
1310 if (fonts.IsEmpty())
1311 return;
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);
1318 if (!prefs)
1319 return;
1321 nsCOMPtr<nsIPrefBranch> prefBranch;
1322 prefs->GetBranch(0, getter_AddRefs(prefBranch));
1323 if (!prefBranch)
1324 return;
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);
1334 while (p < p_end) {
1335 while (nsCRT::IsAsciiSpace(*p)) {
1336 if (++p == p_end)
1337 break;
1339 if (p == p_end)
1340 break;
1341 const char *start = p;
1342 while (++p != p_end && *p != kComma)
1343 /* nothing */ ;
1344 nsCAutoString lang(Substring(start, p));
1345 lang.CompressWhitespace(PR_FALSE, PR_TRUE);
1346 PRInt32 index = GetCJKLangGroupIndex(lang.get());
1347 if (index >= 0)
1348 GetPrefFonts(sCJKLangGroup[index], array);
1349 p++;
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;
1362 // last resort...
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);
1382 private:
1383 nsRefPtr<gfxContext> mContext;
1384 HDC mDC;
1386 SCRIPT_ITEM *mScriptItem;
1387 WORD mScript;
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;
1401 WORD *mGlyphs;
1402 WORD *mClusters;
1403 SCRIPT_VISATTR *mAttr;
1405 int mMaxGlyphs;
1406 int mNumGlyphs;
1408 GOFFSET *mOffsets;
1409 int *mAdvances;
1411 nsTArray< nsRefPtr<gfxWindowsFont> > mFonts;
1413 nsRefPtr<gfxWindowsFont> mCurrentFont;
1415 PRPackedBool mFontSelected;
1417 nsTArray<TextRange> mRanges;
1420 class Uniscribe
1422 public:
1423 Uniscribe(gfxContext *aContext, HDC aDC, const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) :
1424 mContext(aContext), mDC(aDC), mString(aString), mLength(aLength), mIsRTL(aIsRTL),
1425 mItems(nsnull) {
1427 ~Uniscribe() {
1428 if (mItems)
1429 free(mItems);
1432 void Init() {
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;
1441 int Itemize() {
1442 HRESULT rv;
1444 int maxItems = 5;
1446 Init();
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) {
1452 maxItems *= 2;
1453 mItems = (SCRIPT_ITEM *)realloc(mItems, (maxItems + 1) * sizeof(SCRIPT_ITEM));
1454 Init();
1457 return mNumItems;
1460 PRUint32 ItemsLength() {
1461 return mNumItems;
1464 // XXX Why do we dynamically allocate this? We could just fill in an object
1465 // on the stack.
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,
1472 &mItems[i],
1473 aGroup);
1475 return item;
1478 private:
1479 nsRefPtr<gfxContext> mContext;
1480 HDC mDC;
1481 const PRUnichar *mString;
1482 const PRUint32 mLength;
1483 const PRBool mIsRTL;
1485 SCRIPT_CONTROL mControl;
1486 SCRIPT_STATE mState;
1487 SCRIPT_ITEM *mItems;
1488 int mNumItems;
1491 void
1492 gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString,
1493 PRUint32 aLength)
1495 nsRefPtr<gfxASurface> surf = aContext->CurrentSurface();
1496 HDC aDC = GetDCFromSurface(surf);
1497 NS_ASSERTION(aDC, "No DC");
1499 const PRBool isRTL = aRun->IsRightToLeft();
1501 HRESULT rv;
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) {
1509 SaveDC(aDC);
1511 UniscribeItem *item = us.GetItem(i, this);
1513 PRUint32 nranges = item->ComputeRanges();
1515 for (PRUint32 j = 0; j < nranges; ++j) {
1517 item->SetRange(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");
1532 rv = item->Place();
1533 NS_ASSERTION(SUCCEEDED(rv), "Failed to place -- this is pretty bad.");
1535 item->SaveGlyphs(aRun);
1538 delete item;
1540 RestoreDC(aDC, -1);