LCOV - code coverage report
Current view: directory - layout/generic - nsTextRunTransformations.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 181 0 0.0 %
Date: 2012-06-02 Functions: 11 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       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/
       9                 :  *
      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.
      14                 :  *
      15                 :  * The Original Code is Novell code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is Novell Corporation.
      18                 :  * Portions created by the Initial Developer are Copyright (C) 2006
      19                 :  * the Initial Developer. All Rights Reserved.
      20                 :  *
      21                 :  * Contributor(s):
      22                 :  *   robert@ocallahan.org
      23                 :  *
      24                 :  * Alternatively, the contents of this file may be used under the terms of
      25                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      26                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      27                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      28                 :  * of those above. If you wish to allow use of your version of this file only
      29                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      30                 :  * use your version of this file under the terms of the MPL, indicate your
      31                 :  * decision by deleting the provisions above and replace them with the notice
      32                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      33                 :  * the provisions above, a recipient may use your version of this file under
      34                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      35                 :  *
      36                 :  * ***** END LICENSE BLOCK ***** */
      37                 : 
      38                 : #include "nsTextRunTransformations.h"
      39                 : 
      40                 : #include "nsTextFrameUtils.h"
      41                 : #include "gfxSkipChars.h"
      42                 : 
      43                 : #include "nsStyleConsts.h"
      44                 : #include "nsStyleContext.h"
      45                 : #include "gfxContext.h"
      46                 : #include "nsContentUtils.h"
      47                 : #include "nsUnicharUtils.h"
      48                 : 
      49                 : #define SZLIG 0x00DF
      50                 : 
      51                 : nsTransformedTextRun *
      52               0 : nsTransformedTextRun::Create(const gfxTextRunFactory::Parameters* aParams,
      53                 :                              nsTransformingTextRunFactory* aFactory,
      54                 :                              gfxFontGroup* aFontGroup,
      55                 :                              const PRUnichar* aString, PRUint32 aLength,
      56                 :                              const PRUint32 aFlags, nsStyleContext** aStyles,
      57                 :                              bool aOwnsFactory)
      58                 : {
      59               0 :   NS_ASSERTION(!(aFlags & gfxTextRunFactory::TEXT_IS_8BIT),
      60                 :                "didn't expect text to be marked as 8-bit here");
      61                 : 
      62               0 :   void *storage = AllocateStorageForTextRun(sizeof(nsTransformedTextRun), aLength);
      63               0 :   if (!storage) {
      64               0 :     return nsnull;
      65                 :   }
      66                 : 
      67                 :   return new (storage) nsTransformedTextRun(aParams, aFactory, aFontGroup,
      68                 :                                             aString, aLength,
      69               0 :                                             aFlags, aStyles, aOwnsFactory);
      70                 : }
      71                 : 
      72                 : void
      73               0 : nsTransformedTextRun::SetCapitalization(PRUint32 aStart, PRUint32 aLength,
      74                 :                                         bool* aCapitalization,
      75                 :                                         gfxContext* aRefContext)
      76                 : {
      77               0 :   if (mCapitalize.IsEmpty()) {
      78               0 :     if (!mCapitalize.AppendElements(GetLength()))
      79               0 :       return;
      80               0 :     memset(mCapitalize.Elements(), 0, GetLength()*sizeof(bool));
      81                 :   }
      82               0 :   memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(bool));
      83               0 :   mNeedsRebuild = true;
      84                 : }
      85                 : 
      86                 : bool
      87               0 : nsTransformedTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
      88                 :                                              PRUint8* aBreakBefore,
      89                 :                                              gfxContext* aRefContext)
      90                 : {
      91                 :   bool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
      92               0 :       aBreakBefore, aRefContext);
      93               0 :   if (changed) {
      94               0 :     mNeedsRebuild = true;
      95                 :   }
      96               0 :   return changed;
      97                 : }
      98                 : 
      99                 : size_t
     100               0 : nsTransformedTextRun::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
     101                 : {
     102               0 :   size_t total = gfxTextRun::SizeOfExcludingThis(aMallocSizeOf);
     103               0 :   total += mStyles.SizeOfExcludingThis(aMallocSizeOf);
     104               0 :   total += mCapitalize.SizeOfExcludingThis(aMallocSizeOf);
     105               0 :   if (mOwnsFactory) {
     106               0 :     total += aMallocSizeOf(mFactory);
     107                 :   }
     108               0 :   return total;
     109                 : }
     110                 : 
     111                 : size_t
     112               0 : nsTransformedTextRun::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
     113                 : {
     114               0 :   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
     115                 : }
     116                 : 
     117                 : nsTransformedTextRun*
     118               0 : nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
     119                 :                                           const gfxTextRunFactory::Parameters* aParams,
     120                 :                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
     121                 :                                           nsStyleContext** aStyles, bool aOwnsFactory)
     122                 : {
     123                 :   return nsTransformedTextRun::Create(aParams, this, aFontGroup,
     124               0 :                                       aString, aLength, aFlags, aStyles, aOwnsFactory);
     125                 : }
     126                 : 
     127                 : nsTransformedTextRun*
     128               0 : nsTransformingTextRunFactory::MakeTextRun(const PRUint8* aString, PRUint32 aLength,
     129                 :                                           const gfxTextRunFactory::Parameters* aParams,
     130                 :                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
     131                 :                                           nsStyleContext** aStyles, bool aOwnsFactory)
     132                 : {
     133                 :   // We'll only have a Unicode code path to minimize the amount of code needed
     134                 :   // for these rarely used features
     135               0 :   NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString), aLength);
     136                 :   return MakeTextRun(unicodeString.get(), aLength, aParams, aFontGroup,
     137                 :                      aFlags & ~(gfxFontGroup::TEXT_IS_PERSISTENT | gfxFontGroup::TEXT_IS_8BIT),
     138               0 :                      aStyles, aOwnsFactory);
     139                 : }
     140                 : 
     141                 : /**
     142                 :  * Copy a given textrun, but merge certain characters into a single logical
     143                 :  * character. Glyphs for a character are added to the glyph list for the previous
     144                 :  * character and then the merged character is eliminated. Visually the results
     145                 :  * are identical.
     146                 :  * 
     147                 :  * This is used for text-transform:uppercase when we encounter a SZLIG,
     148                 :  * whose uppercase form is "SS".
     149                 :  * 
     150                 :  * This function is unable to merge characters when they occur in different
     151                 :  * glyph runs. It's hard to see how this could happen, but if it does, we just
     152                 :  * discard the characters-to-merge.
     153                 :  * 
     154                 :  * For simplicity, this produces a textrun containing all DetailedGlyphs,
     155                 :  * no simple glyphs. So don't call it unless you really have merging to do.
     156                 :  * 
     157                 :  * @param aCharsToMerge when aCharsToMerge[i] is true, this character is
     158                 :  * merged into the previous character
     159                 :  */
     160                 : static void
     161               0 : MergeCharactersInTextRun(gfxTextRun* aDest, gfxTextRun* aSrc,
     162                 :                          bool* aCharsToMerge)
     163                 : {
     164               0 :   aDest->ResetGlyphRuns();
     165                 : 
     166               0 :   gfxTextRun::GlyphRunIterator iter(aSrc, 0, aSrc->GetLength());
     167               0 :   PRUint32 offset = 0;
     168               0 :   nsAutoTArray<gfxTextRun::DetailedGlyph,2> glyphs;
     169               0 :   while (iter.NextRun()) {
     170               0 :     gfxTextRun::GlyphRun* run = iter.GetGlyphRun();
     171                 :     nsresult rv = aDest->AddGlyphRun(run->mFont, run->mMatchType,
     172               0 :                                      offset, false);
     173               0 :     if (NS_FAILED(rv))
     174                 :       return;
     175                 : 
     176               0 :     bool anyMissing = false;
     177               0 :     PRUint32 mergeRunStart = iter.GetStringStart();
     178                 :     PRUint32 k;
     179               0 :     for (k = iter.GetStringStart(); k < iter.GetStringEnd(); ++k) {
     180               0 :       gfxTextRun::CompressedGlyph g = aSrc->GetCharacterGlyphs()[k];
     181               0 :       if (g.IsSimpleGlyph()) {
     182               0 :         if (!anyMissing) {
     183                 :           gfxTextRun::DetailedGlyph details;
     184               0 :           details.mGlyphID = g.GetSimpleGlyph();
     185               0 :           details.mAdvance = g.GetSimpleAdvance();
     186               0 :           details.mXOffset = 0;
     187               0 :           details.mYOffset = 0;
     188               0 :           glyphs.AppendElement(details);
     189                 :         }
     190                 :       } else {
     191               0 :         if (g.IsMissing()) {
     192               0 :           anyMissing = true;
     193               0 :           glyphs.Clear();
     194                 :         }
     195               0 :         if (g.GetGlyphCount() > 0) {
     196               0 :           glyphs.AppendElements(aSrc->GetDetailedGlyphs(k), g.GetGlyphCount());
     197                 :         }
     198                 :       }
     199                 : 
     200                 :       // We could teach this method to handle merging of characters that aren't
     201                 :       // cluster starts or ligature group starts, but this is really only used
     202                 :       // to merge S's (uppercase &szlig;), so it's not worth it.
     203                 : 
     204               0 :       if (k + 1 < iter.GetStringEnd() && aCharsToMerge[k + 1]) {
     205               0 :         NS_ASSERTION(g.IsClusterStart() && g.IsLigatureGroupStart(),
     206                 :                      "Don't know how to merge this stuff");
     207               0 :         continue;
     208                 :       }
     209                 : 
     210               0 :       NS_ASSERTION(mergeRunStart == k ||
     211                 :                    (g.IsClusterStart() && g.IsLigatureGroupStart()),
     212                 :                    "Don't know how to merge this stuff");
     213                 : 
     214                 :       // If the start of the merge run is actually a character that should
     215                 :       // have been merged with the previous character (this can happen
     216                 :       // if there's a font change in the middle of a szlig, for example),
     217                 :       // just discard the entire merge run. See comment at start of this
     218                 :       // function.
     219               0 :       if (!aCharsToMerge[mergeRunStart]) {
     220               0 :         if (anyMissing) {
     221               0 :           g.SetMissing(glyphs.Length());
     222                 :         } else {
     223               0 :           g.SetComplex(true, true, glyphs.Length());
     224                 :         }
     225               0 :         aDest->SetGlyphs(offset, g, glyphs.Elements());
     226               0 :         ++offset;
     227                 :       }
     228                 : 
     229               0 :       glyphs.Clear();
     230               0 :       anyMissing = false;
     231               0 :       mergeRunStart = k + 1;
     232                 :     }
     233               0 :     NS_ASSERTION(glyphs.Length() == 0,
     234                 :                  "Leftover glyphs, don't request merging of the last character with its next!");  
     235                 :   }
     236               0 :   NS_ASSERTION(offset == aDest->GetLength(), "Bad offset calculations");
     237                 : }
     238                 : 
     239                 : static gfxTextRunFactory::Parameters
     240               0 : GetParametersForInner(nsTransformedTextRun* aTextRun, PRUint32* aFlags,
     241                 :     gfxContext* aRefContext)
     242                 : {
     243                 :   gfxTextRunFactory::Parameters params =
     244                 :     { aRefContext, nsnull, nsnull,
     245               0 :       nsnull, 0, aTextRun->GetAppUnitsPerDevUnit()
     246               0 :     };
     247               0 :   *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
     248                 :   return params;
     249                 : }
     250                 : 
     251                 : void
     252               0 : nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
     253                 :     gfxContext* aRefContext)
     254                 : {
     255               0 :   gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
     256               0 :   gfxFontStyle fontStyle = *fontGroup->GetStyle();
     257               0 :   fontStyle.size *= 0.8;
     258               0 :   nsRefPtr<gfxFontGroup> smallFont = fontGroup->Copy(&fontStyle);
     259               0 :   if (!smallFont)
     260                 :     return;
     261                 : 
     262                 :   PRUint32 flags;
     263                 :   gfxTextRunFactory::Parameters innerParams =
     264               0 :       GetParametersForInner(aTextRun, &flags, aRefContext);
     265                 : 
     266               0 :   PRUint32 length = aTextRun->GetLength();
     267               0 :   const PRUnichar* str = aTextRun->mString.BeginReading();
     268               0 :   nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
     269                 :   // Create a textrun so we can check cluster-start properties
     270               0 :   nsAutoPtr<gfxTextRun> inner(fontGroup->MakeTextRun(str, length, &innerParams, flags));
     271               0 :   if (!inner.get())
     272                 :     return;
     273                 : 
     274               0 :   nsCaseTransformTextRunFactory uppercaseFactory(nsnull, true);
     275                 : 
     276               0 :   aTextRun->ResetGlyphRuns();
     277                 : 
     278               0 :   PRUint32 runStart = 0;
     279               0 :   bool runIsLowercase = false;
     280               0 :   nsAutoTArray<nsStyleContext*,50> styleArray;
     281               0 :   nsAutoTArray<PRUint8,50> canBreakBeforeArray;
     282                 : 
     283                 :   PRUint32 i;
     284               0 :   for (i = 0; i <= length; ++i) {
     285               0 :     bool isLowercase = false;
     286               0 :     if (i < length) {
     287                 :       // Characters that aren't the start of a cluster are ignored here. They
     288                 :       // get added to whatever lowercase/non-lowercase run we're in.
     289               0 :       if (!inner->IsClusterStart(i)) {
     290               0 :         isLowercase = runIsLowercase;
     291                 :       } else {
     292               0 :         if (styles[i]->GetStyleFont()->mFont.variant == NS_STYLE_FONT_VARIANT_SMALL_CAPS) {
     293               0 :           PRUnichar ch = str[i];
     294                 :           PRUnichar ch2;
     295               0 :           ch2 = ToUpperCase(ch);
     296               0 :           isLowercase = ch != ch2 || ch == SZLIG;
     297                 :         } else {
     298                 :           // Don't transform the character! I.e., pretend that it's not lowercase
     299                 :         }
     300                 :       }
     301                 :     }
     302                 : 
     303               0 :     if ((i == length || runIsLowercase != isLowercase) && runStart < i) {
     304               0 :       nsAutoPtr<nsTransformedTextRun> transformedChild;
     305               0 :       nsAutoPtr<gfxTextRun> cachedChild;
     306                 :       gfxTextRun* child;
     307                 : 
     308               0 :       if (runIsLowercase) {
     309                 :         transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
     310               0 :             &innerParams, smallFont, flags, styleArray.Elements(), false);
     311               0 :         child = transformedChild;
     312                 :       } else {
     313                 :         cachedChild =
     314                 :           fontGroup->MakeTextRun(str + runStart, i - runStart,
     315               0 :                                  &innerParams, flags);
     316               0 :         child = cachedChild.get();
     317                 :       }
     318               0 :       if (!child)
     319                 :         return;
     320                 :       // Copy potential linebreaks into child so they're preserved
     321                 :       // (and also child will be shaped appropriately)
     322               0 :       NS_ASSERTION(canBreakBeforeArray.Length() == i - runStart,
     323                 :                    "lost some break-before values?");
     324                 :       child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
     325               0 :           canBreakBeforeArray.Elements(), aRefContext);
     326               0 :       if (transformedChild) {
     327               0 :         transformedChild->FinishSettingProperties(aRefContext);
     328                 :       }
     329               0 :       aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart);
     330                 : 
     331               0 :       runStart = i;
     332               0 :       styleArray.Clear();
     333               0 :       canBreakBeforeArray.Clear();
     334                 :     }
     335                 : 
     336               0 :     if (i < length) {
     337               0 :       runIsLowercase = isLowercase;
     338               0 :       styleArray.AppendElement(styles[i]);
     339               0 :       canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
     340                 :     }
     341                 :   }
     342                 : }
     343                 : 
     344                 : void
     345               0 : nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
     346                 :     gfxContext* aRefContext)
     347                 : {
     348               0 :   PRUint32 length = aTextRun->GetLength();
     349               0 :   const PRUnichar* str = aTextRun->mString.BeginReading();
     350               0 :   nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
     351                 : 
     352               0 :   nsAutoString convertedString;
     353               0 :   nsAutoTArray<bool,50> charsToMergeArray;
     354               0 :   nsAutoTArray<nsStyleContext*,50> styleArray;
     355               0 :   nsAutoTArray<PRUint8,50> canBreakBeforeArray;
     356               0 :   PRUint32 extraCharsCount = 0;
     357                 : 
     358                 :   PRUint32 i;
     359               0 :   for (i = 0; i < length; ++i) {
     360               0 :     PRUnichar ch = str[i];
     361                 : 
     362               0 :     charsToMergeArray.AppendElement(false);
     363               0 :     styleArray.AppendElement(styles[i]);
     364               0 :     canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
     365                 : 
     366                 :     PRUint8 style = mAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE
     367               0 :       : styles[i]->GetStyleText()->mTextTransform;
     368               0 :     bool extraChar = false;
     369                 : 
     370               0 :     switch (style) {
     371                 :     case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
     372               0 :       ch = ToLowerCase(ch);
     373               0 :       break;
     374                 :     case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
     375               0 :       if (ch == SZLIG) {
     376               0 :         convertedString.Append('S');
     377               0 :         extraChar = true;
     378               0 :         ch = 'S';
     379                 :       } else {
     380               0 :         ch = ToUpperCase(ch);
     381                 :       }
     382               0 :       break;
     383                 :     case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
     384               0 :       if (i < aTextRun->mCapitalize.Length() && aTextRun->mCapitalize[i]) {
     385               0 :         if (ch == SZLIG) {
     386               0 :           convertedString.Append('S');
     387               0 :           extraChar = true;
     388               0 :           ch = 'S';
     389                 :         } else {
     390               0 :           ch = ToTitleCase(ch);
     391                 :         }
     392                 :       }
     393               0 :       break;
     394                 :     default:
     395               0 :       break;
     396                 :     }
     397                 : 
     398               0 :     convertedString.Append(ch);
     399               0 :     if (extraChar) {
     400               0 :       ++extraCharsCount;
     401               0 :       charsToMergeArray.AppendElement(true);
     402               0 :       styleArray.AppendElement(styles[i]);
     403               0 :       canBreakBeforeArray.AppendElement(false);
     404                 :     }
     405                 :   }
     406                 : 
     407                 :   PRUint32 flags;
     408                 :   gfxTextRunFactory::Parameters innerParams =
     409               0 :       GetParametersForInner(aTextRun, &flags, aRefContext);
     410               0 :   gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
     411                 : 
     412               0 :   nsAutoPtr<nsTransformedTextRun> transformedChild;
     413               0 :   nsAutoPtr<gfxTextRun> cachedChild;
     414                 :   gfxTextRun* child;
     415                 : 
     416               0 :   if (mInnerTransformingTextRunFactory) {
     417                 :     transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
     418                 :         convertedString.BeginReading(), convertedString.Length(),
     419               0 :         &innerParams, fontGroup, flags, styleArray.Elements(), false);
     420               0 :     child = transformedChild.get();
     421                 :   } else {
     422                 :     cachedChild = fontGroup->MakeTextRun(
     423                 :         convertedString.BeginReading(), convertedString.Length(),
     424               0 :         &innerParams, flags);
     425               0 :     child = cachedChild.get();
     426                 :   }
     427               0 :   if (!child)
     428                 :     return;
     429                 :   // Copy potential linebreaks into child so they're preserved
     430                 :   // (and also child will be shaped appropriately)
     431               0 :   NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
     432                 :                "Dropped characters or break-before values somewhere!");
     433                 :   child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
     434               0 :       canBreakBeforeArray.Elements(), aRefContext);
     435               0 :   if (transformedChild) {
     436               0 :     transformedChild->FinishSettingProperties(aRefContext);
     437                 :   }
     438                 : 
     439               0 :   if (extraCharsCount > 0) {
     440                 :     // Now merge multiple characters into one multi-glyph character as required
     441               0 :     MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements());
     442                 :   } else {
     443                 :     // No merging to do, so just copy; this produces a more optimized textrun.
     444                 :     // We can't steal the data because the child may be cached and stealing
     445                 :     // the data would break the cache.
     446               0 :     aTextRun->ResetGlyphRuns();
     447               0 :     aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0);
     448                 :   }
     449                 : }

Generated by: LCOV version 1.7