gfx/thebes/gfxUniscribeShaper.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "gfxTypes.h"
     8 #include "gfxContext.h"
     9 #include "gfxUniscribeShaper.h"
    10 #include "gfxWindowsPlatform.h"
    12 #include "gfxFontTest.h"
    14 #include "cairo.h"
    15 #include "cairo-win32.h"
    17 #include <windows.h>
    19 #include "nsTArray.h"
    21 #include "prinit.h"
    23 /**********************************************************************
    24  *
    25  * class gfxUniscribeShaper
    26  *
    27  **********************************************************************/
    29 #define ESTIMATE_MAX_GLYPHS(L) (((3 * (L)) >> 1) + 16)
    31 class UniscribeItem
    32 {
    33 public:
    34     UniscribeItem(gfxContext *aContext, HDC aDC,
    35                   gfxUniscribeShaper *aShaper,
    36                   const char16_t *aString, uint32_t aLength,
    37                   SCRIPT_ITEM *aItem, uint32_t aIVS) :
    38         mContext(aContext), mDC(aDC),
    39         mShaper(aShaper),
    40         mItemString(aString), mItemLength(aLength), 
    41         mAlternativeString(nullptr), mScriptItem(aItem),
    42         mScript(aItem->a.eScript),
    43         mNumGlyphs(0), mMaxGlyphs(ESTIMATE_MAX_GLYPHS(aLength)),
    44         mFontSelected(false), mIVS(aIVS)
    45     {
    46         // See bug 394751 for details.
    47         NS_ASSERTION(mMaxGlyphs < 65535,
    48                      "UniscribeItem is too big, ScriptShape() will fail!");
    49     }
    51     ~UniscribeItem() {
    52         free(mAlternativeString);
    53     }
    55     bool AllocateBuffers() {
    56         return (mGlyphs.SetLength(mMaxGlyphs) &&
    57                 mClusters.SetLength(mItemLength + 1) &&
    58                 mAttr.SetLength(mMaxGlyphs));
    59     }
    61     /* possible return values:
    62      * S_OK - things succeeded
    63      * GDI_ERROR - things failed to shape.  Might want to try again after calling DisableShaping()
    64      */
    66     HRESULT Shape() {
    67         HRESULT rv;
    68         HDC shapeDC = nullptr;
    70         char16ptr_t str = mAlternativeString ? mAlternativeString : mItemString;
    72         mScriptItem->a.fLogicalOrder = true; 
    73         SCRIPT_ANALYSIS sa = mScriptItem->a;
    75         while (true) {
    77             rv = ScriptShape(shapeDC, mShaper->ScriptCache(),
    78                              str, mItemLength,
    79                              mMaxGlyphs, &sa,
    80                              mGlyphs.Elements(), mClusters.Elements(),
    81                              mAttr.Elements(), &mNumGlyphs);
    83             if (rv == E_OUTOFMEMORY) {
    84                 mMaxGlyphs *= 2;
    85                 if (!mGlyphs.SetLength(mMaxGlyphs) ||
    86                     !mAttr.SetLength(mMaxGlyphs)) {
    87                     return E_OUTOFMEMORY;
    88                 }
    89                 continue;
    90             }
    92             // Uniscribe can't do shaping with some fonts, so it sets the 
    93             // fNoGlyphIndex flag in the SCRIPT_ANALYSIS structure to indicate
    94             // this.  This occurs with CFF fonts loaded with 
    95             // AddFontMemResourceEx but it's not clear what the other cases
    96             // are. We return an error so our caller can try fallback shaping.
    97             // see http://msdn.microsoft.com/en-us/library/ms776520(VS.85).aspx
    99             if (sa.fNoGlyphIndex) {
   100                 return GDI_ERROR;
   101             }
   103             if (rv == E_PENDING) {
   104                 if (shapeDC == mDC) {
   105                     // we already tried this once, something failed, give up
   106                     return E_PENDING;
   107                 }
   109                 SelectFont();
   111                 shapeDC = mDC;
   112                 continue;
   113             }
   115             // http://msdn.microsoft.com/en-us/library/dd368564(VS.85).aspx:
   116             // Uniscribe will return this if "the font corresponding to the
   117             // DC does not support the script required by the run...".
   118             // In this case, we'll set the script code to SCRIPT_UNDEFINED
   119             // and try again, so that we'll at least get glyphs even though
   120             // they won't necessarily have proper shaping.
   121             // (We probably shouldn't have selected this font at all,
   122             // but it's too late to fix that here.)
   123             if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
   124                 sa.eScript = SCRIPT_UNDEFINED;
   125                 NS_WARNING("Uniscribe says font does not support script needed");
   126                 continue;
   127             }
   129             // Prior to Windows 7, Uniscribe didn't support Ideographic Variation
   130             // Selectors. Replace the UVS glyph manually.
   131             if (mIVS) {
   132                 uint32_t lastChar = str[mItemLength - 1];
   133                 if (NS_IS_LOW_SURROGATE(lastChar)
   134                     && NS_IS_HIGH_SURROGATE(str[mItemLength - 2])) {
   135                     lastChar = SURROGATE_TO_UCS4(str[mItemLength - 2], lastChar);
   136                 }
   137                 uint16_t glyphId = mShaper->GetFont()->GetUVSGlyph(lastChar, mIVS);
   138                 if (glyphId) {
   139                     mGlyphs[mNumGlyphs - 1] = glyphId;
   140                 }
   141             }
   143             return rv;
   144         }
   145     }
   147     bool ShapingEnabled() {
   148         return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
   149     }
   150     void DisableShaping() {
   151         mScriptItem->a.eScript = SCRIPT_UNDEFINED;
   152         // Note: If we disable the shaping by using SCRIPT_UNDEFINED and
   153         // the string has the surrogate pair, ScriptShape API is
   154         // *sometimes* crashed. Therefore, we should replace the surrogate
   155         // pair to U+FFFD. See bug 341500.
   156         GenerateAlternativeString();
   157     }
   158     void EnableShaping() {
   159         mScriptItem->a.eScript = mScript;
   160         if (mAlternativeString) {
   161             free(mAlternativeString);
   162             mAlternativeString = nullptr;
   163         }
   164     }
   166     bool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, uint32_t aGlyphIndex) {
   167         return (mGlyphs[aGlyphIndex] == aSFP->wgDefault);
   168     }
   171     HRESULT Place() {
   172         HRESULT rv;
   173         HDC placeDC = nullptr;
   175         if (!mOffsets.SetLength(mNumGlyphs) ||
   176             !mAdvances.SetLength(mNumGlyphs)) {
   177             return E_OUTOFMEMORY;
   178         }
   180         SCRIPT_ANALYSIS sa = mScriptItem->a;
   182         while (true) {
   183             rv = ScriptPlace(placeDC, mShaper->ScriptCache(),
   184                              mGlyphs.Elements(), mNumGlyphs,
   185                              mAttr.Elements(), &sa,
   186                              mAdvances.Elements(), mOffsets.Elements(), nullptr);
   188             if (rv == E_PENDING) {
   189                 SelectFont();
   190                 placeDC = mDC;
   191                 continue;
   192             }
   194             if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
   195                 sa.eScript = SCRIPT_UNDEFINED;
   196                 continue;
   197             }
   199             break;
   200         }
   202         return rv;
   203     }
   205     void ScriptFontProperties(SCRIPT_FONTPROPERTIES *sfp) {
   206         HRESULT rv;
   208         memset(sfp, 0, sizeof(SCRIPT_FONTPROPERTIES));
   209         sfp->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
   210         rv = ScriptGetFontProperties(nullptr, mShaper->ScriptCache(),
   211                                      sfp);
   212         if (rv == E_PENDING) {
   213             SelectFont();
   214             rv = ScriptGetFontProperties(mDC, mShaper->ScriptCache(),
   215                                          sfp);
   216         }
   217     }
   219     void SaveGlyphs(gfxShapedText *aShapedText, uint32_t aOffset) {
   220         uint32_t offsetInRun = mScriptItem->iCharPos;
   222         // XXX We should store this in the item and only fetch it once
   223         SCRIPT_FONTPROPERTIES sfp;
   224         ScriptFontProperties(&sfp);
   226         uint32_t offset = 0;
   227         nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
   228         gfxShapedText::CompressedGlyph g;
   229         gfxShapedText::CompressedGlyph *charGlyphs =
   230             aShapedText->GetCharacterGlyphs();
   231         const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
   232         while (offset < mItemLength) {
   233             uint32_t runOffset = aOffset + offsetInRun + offset;
   234             bool atClusterStart = charGlyphs[runOffset].IsClusterStart();
   235             if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
   236                 gfxShapedText::CompressedGlyph &g = charGlyphs[runOffset];
   237                 NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
   238                 g.SetComplex(atClusterStart, false, 0);
   239             } else {
   240                 // Count glyphs for this character
   241                 uint32_t k = mClusters[offset];
   242                 uint32_t glyphCount = mNumGlyphs - k;
   243                 uint32_t nextClusterOffset;
   244                 bool missing = IsGlyphMissing(&sfp, k);
   245                 for (nextClusterOffset = offset + 1; nextClusterOffset < mItemLength; ++nextClusterOffset) {
   246                     if (mClusters[nextClusterOffset] > k) {
   247                         glyphCount = mClusters[nextClusterOffset] - k;
   248                         break;
   249                     }
   250                 }
   251                 uint32_t j;
   252                 for (j = 1; j < glyphCount; ++j) {
   253                     if (IsGlyphMissing(&sfp, k + j)) {
   254                         missing = true;
   255                     }
   256                 }
   257                 int32_t advance = mAdvances[k]*appUnitsPerDevUnit;
   258                 WORD glyph = mGlyphs[k];
   259                 NS_ASSERTION(!gfxFontGroup::IsInvalidChar(mItemString[offset]),
   260                              "invalid character detected");
   261                 if (missing) {
   262                     if (NS_IS_HIGH_SURROGATE(mItemString[offset]) &&
   263                         offset + 1 < mItemLength &&
   264                         NS_IS_LOW_SURROGATE(mItemString[offset + 1])) {
   265                         aShapedText->SetMissingGlyph(runOffset,
   266                                                      SURROGATE_TO_UCS4(mItemString[offset],
   267                                                                        mItemString[offset + 1]),
   268                                                      mShaper->GetFont());
   269                     } else {
   270                         aShapedText->SetMissingGlyph(runOffset, mItemString[offset],
   271                                                      mShaper->GetFont());
   272                     }
   273                 } else if (glyphCount == 1 && advance >= 0 &&
   274                     mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
   275                     gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
   276                     gfxShapedText::CompressedGlyph::IsSimpleGlyphID(glyph) &&
   277                     atClusterStart)
   278                 {
   279                     charGlyphs[runOffset].SetSimpleGlyph(advance, glyph);
   280                 } else {
   281                     if (detailedGlyphs.Length() < glyphCount) {
   282                         if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
   283                             return;
   284                     }
   285                     uint32_t i;
   286                     for (i = 0; i < glyphCount; ++i) {
   287                         gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
   288                         details->mGlyphID = mGlyphs[k + i];
   289                         details->mAdvance = mAdvances[k + i] * appUnitsPerDevUnit;
   290                         details->mXOffset = float(mOffsets[k + i].du) * appUnitsPerDevUnit *
   291                             aShapedText->GetDirection();
   292                         details->mYOffset = - float(mOffsets[k + i].dv) * appUnitsPerDevUnit;
   293                     }
   294                     aShapedText->SetGlyphs(runOffset,
   295                                            g.SetComplex(atClusterStart, true,
   296                                                         glyphCount),
   297                                            detailedGlyphs.Elements());
   298                 }
   299             }
   300             ++offset;
   301         }
   302     }
   304     void SelectFont() {
   305         if (mFontSelected)
   306             return;
   308         cairo_t *cr = mContext->GetCairo();
   310         cairo_set_font_face(cr, mShaper->GetFont()->CairoFontFace());
   311         cairo_set_font_size(cr, mShaper->GetFont()->GetAdjustedSize());
   312         cairo_scaled_font_t *scaledFont = mShaper->GetFont()->CairoScaledFont();
   313         cairo_win32_scaled_font_select_font(scaledFont, mDC);
   315         mFontSelected = true;
   316     }
   318 private:
   320     void GenerateAlternativeString() {
   321         if (mAlternativeString)
   322             free(mAlternativeString);
   323         mAlternativeString = (char16_t *)malloc(mItemLength * sizeof(char16_t));
   324         if (!mAlternativeString)
   325             return;
   326         memcpy((void *)mAlternativeString, (const void *)mItemString,
   327                mItemLength * sizeof(char16_t));
   328         for (uint32_t i = 0; i < mItemLength; i++) {
   329             if (NS_IS_HIGH_SURROGATE(mItemString[i]) || NS_IS_LOW_SURROGATE(mItemString[i]))
   330                 mAlternativeString[i] = char16_t(0xFFFD);
   331         }
   332     }
   334 private:
   335     nsRefPtr<gfxContext> mContext;
   336     HDC mDC;
   337     gfxUniscribeShaper *mShaper;
   339     SCRIPT_ITEM *mScriptItem;
   340     WORD mScript;
   342 public:
   343     // these point to the full string/length of the item
   344     const char16_t *mItemString;
   345     const uint32_t mItemLength;
   347 private:
   348     char16_t *mAlternativeString;
   350 #define AVERAGE_ITEM_LENGTH 40
   352     AutoFallibleTArray<WORD, uint32_t(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mGlyphs;
   353     AutoFallibleTArray<WORD, AVERAGE_ITEM_LENGTH + 1> mClusters;
   354     AutoFallibleTArray<SCRIPT_VISATTR, uint32_t(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mAttr;
   356     AutoFallibleTArray<GOFFSET, 2 * AVERAGE_ITEM_LENGTH> mOffsets;
   357     AutoFallibleTArray<int, 2 * AVERAGE_ITEM_LENGTH> mAdvances;
   359 #undef AVERAGE_ITEM_LENGTH
   361     int mMaxGlyphs;
   362     int mNumGlyphs;
   363     uint32_t mIVS;
   365     bool mFontSelected;
   366 };
   368 class Uniscribe
   369 {
   370 public:
   371     Uniscribe(const char16_t *aString,
   372               gfxShapedText *aShapedText,
   373               uint32_t aOffset, uint32_t aLength):
   374         mString(aString), mShapedText(aShapedText),
   375         mOffset(aOffset), mLength(aLength)
   376     {
   377     }
   379     void Init() {
   380         memset(&mControl, 0, sizeof(SCRIPT_CONTROL));
   381         memset(&mState, 0, sizeof(SCRIPT_STATE));
   382         // Lock the direction. Don't allow the itemizer to change directions
   383         // based on character type.
   384         mState.uBidiLevel = mShapedText->IsRightToLeft() ? 1 : 0;
   385         mState.fOverrideDirection = true;
   386     }
   388 public:
   389     int Itemize() {
   390         HRESULT rv;
   392         int maxItems = 5;
   394         Init();
   396         // Allocate space for one more item than expected, to handle a rare
   397         // overflow in ScriptItemize (pre XP SP2). See bug 366643.
   398         if (!mItems.SetLength(maxItems + 1)) {
   399             return 0;
   400         }
   401         while ((rv = ScriptItemize(mString, mLength,
   402                                    maxItems, &mControl, &mState,
   403                                    mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
   404             maxItems *= 2;
   405             if (!mItems.SetLength(maxItems + 1)) {
   406                 return 0;
   407             }
   408             Init();
   409         }
   411         return mNumItems;
   412     }
   414     SCRIPT_ITEM *ScriptItem(uint32_t i) {
   415         NS_ASSERTION(i <= (uint32_t)mNumItems, "Trying to get out of bounds item");
   416         return &mItems[i];
   417     }
   419 private:
   420     char16ptr_t      mString;
   421     gfxShapedText   *mShapedText;
   422     uint32_t         mOffset;
   423     uint32_t         mLength;
   425     SCRIPT_CONTROL mControl;
   426     SCRIPT_STATE   mState;
   427     FallibleTArray<SCRIPT_ITEM> mItems;
   428     int mNumItems;
   429 };
   432 bool
   433 gfxUniscribeShaper::ShapeText(gfxContext      *aContext,
   434                               const char16_t *aText,
   435                               uint32_t         aOffset,
   436                               uint32_t         aLength,
   437                               int32_t          aScript,
   438                               gfxShapedText   *aShapedText)
   439 {
   440     DCFromContext aDC(aContext);
   442     bool result = true;
   443     HRESULT rv;
   445     Uniscribe us(aText, aShapedText, aOffset, aLength);
   447     /* itemize the string */
   448     int numItems = us.Itemize();
   450     uint32_t length = aLength;
   451     SaveDC(aDC);
   452     uint32_t ivs = 0;
   453     for (int i = 0; i < numItems; ++i) {
   454         int iCharPos = us.ScriptItem(i)->iCharPos;
   455         int iCharPosNext = us.ScriptItem(i+1)->iCharPos;
   457         if (ivs) {
   458             iCharPos += 2;
   459             if (iCharPos >= iCharPosNext) {
   460                 ivs = 0;
   461                 continue;
   462             }
   463         }
   465         if (i+1 < numItems && iCharPosNext <= length - 2
   466             && aText[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
   467             && uint32_t(aText[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
   468             <= L_SURROGATE(kUnicodeVS256) - L_SURROGATE(kUnicodeVS17)) {
   470             ivs = SURROGATE_TO_UCS4(aText[iCharPosNext],
   471                                     aText[iCharPosNext + 1]);
   472         } else {
   473             ivs = 0;
   474         }
   476         UniscribeItem item(aContext, aDC, this,
   477                            aText + iCharPos,
   478                            iCharPosNext - iCharPos,
   479                            us.ScriptItem(i), ivs);
   480         if (!item.AllocateBuffers()) {
   481             result = false;
   482             break;
   483         }
   485         if (!item.ShapingEnabled()) {
   486             item.EnableShaping();
   487         }
   489         rv = item.Shape();
   490         if (FAILED(rv)) {
   491             // we know we have the glyphs to display this font already
   492             // so Uniscribe just doesn't know how to shape the script.
   493             // Render the glyphs without shaping.
   494             item.DisableShaping();
   495             rv = item.Shape();
   496         }
   497 #ifdef DEBUG
   498         if (FAILED(rv)) {
   499             NS_WARNING("Uniscribe failed to shape with font");
   500         }
   501 #endif
   503         if (SUCCEEDED(rv)) {
   504             rv = item.Place();
   505 #ifdef DEBUG
   506             if (FAILED(rv)) {
   507                 // crap fonts may fail when placing (e.g. funky free fonts)
   508                 NS_WARNING("Uniscribe failed to place with font");
   509             }
   510 #endif
   511         }
   513         if (FAILED(rv)) {
   514             // Uniscribe doesn't like this font for some reason.
   515             // Returning FALSE will make the gfxGDIFont retry with the
   516             // "dumb" GDI shaper, unless useUniscribeOnly was set.
   517             result = false;
   518             break;
   519         }
   521         item.SaveGlyphs(aShapedText, aOffset);
   522     }
   524     RestoreDC(aDC, -1);
   526     return result;
   527 }

mercurial