LCOV - code coverage report
Current view: directory - layout/svg/base/src - nsSVGGlyphFrame.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 752 0 0.0 %
Date: 2012-06-02 Functions: 76 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 the Mozilla SVG project.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Crocodile Clips Ltd..
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2002
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      27                 :  * or 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.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "nsSVGTextFrame.h"
      40                 : #include "mozilla/LookAndFeel.h"
      41                 : #include "nsTextFragment.h"
      42                 : #include "nsBidiPresUtils.h"
      43                 : #include "nsSVGUtils.h"
      44                 : #include "SVGLengthList.h"
      45                 : #include "nsIDOMSVGLength.h"
      46                 : #include "nsIDOMSVGRect.h"
      47                 : #include "DOMSVGPoint.h"
      48                 : #include "nsSVGGlyphFrame.h"
      49                 : #include "nsSVGTextPathFrame.h"
      50                 : #include "nsSVGPathElement.h"
      51                 : #include "nsSVGRect.h"
      52                 : #include "nsDOMError.h"
      53                 : #include "gfxContext.h"
      54                 : #include "gfxMatrix.h"
      55                 : #include "gfxPlatform.h"
      56                 : #include "nsSVGEffects.h"
      57                 : #include "nsSVGPaintServerFrame.h"
      58                 : 
      59                 : using namespace mozilla;
      60                 : 
      61               0 : struct CharacterPosition {
      62                 :   gfxPoint pos;
      63                 :   gfxFloat angle;
      64                 :   bool draw;
      65                 : };
      66                 : 
      67               0 : static gfxContext* MakeTmpCtx() {
      68               0 :   return new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
      69                 : }
      70                 : 
      71                 : /**
      72                 :  * This is a do-it-all helper class. It supports iterating through the
      73                 :  * drawable character clusters of a string. For each cluster, it can set up
      74                 :  * a graphics context with a transform appropriate for drawing the
      75                 :  * character, or a transform appropriate for emitting geometry in the
      76                 :  * text metrics coordinate system (which differs from the drawing
      77                 :  * coordinate system by a scale factor of AppUnitPerCSSPixels). These
      78                 :  * transforms include offsets and rotations of characters along paths, and
      79                 :  * the mPosition of the nsSVGGlyphFrame.
      80                 :  * 
      81                 :  * This helper also creates the textrun as needed. It supports detecting
      82                 :  * the special case when the entire textrun can be drawn or measured
      83                 :  * as a unit, and setting the graphics context transform up for that. It
      84                 :  * takes care of setting up the global transform if requested. It also
      85                 :  * provides direct access to the character path position data for the
      86                 :  * DOM APIs that need that.
      87                 :  * 
      88                 :  * If an error occurs, for example, a canvas TM is not available because
      89                 :  * the element is in a <defs> section, then the CharacterIterator will
      90                 :  * behave as if the frame has no drawable characters.
      91                 :  *
      92                 :  * XXX needs RTL love
      93                 :  * XXX might want to make AdvanceToCharacter constant time (e.g. by
      94                 :  * caching advances and/or the CharacterPosition array across DOM
      95                 :  * API calls) to ensure that calling Get*OfChar (etc) for each character
      96                 :  * in the text is O(N)
      97                 :  */
      98                 : class CharacterIterator
      99               0 : {
     100                 : public:
     101                 :   /**
     102                 :    * Sets up the iterator so that NextCluster will return the first drawable
     103                 :    * cluster.
     104                 :    * @param aForceGlobalTransform passed on to EnsureTextRun (see below)
     105                 :    */
     106                 :   CharacterIterator(nsSVGGlyphFrame *aSource, bool aForceGlobalTransform);
     107                 :   /**
     108                 :    * This matrix will be applied to aContext in the SetupFor methods below,
     109                 :    * before any glyph translation/rotation.
     110                 :    */
     111               0 :   void SetInitialMatrix(gfxContext *aContext) {
     112               0 :     mInitialMatrix = aContext->CurrentMatrix();
     113               0 :     if (mInitialMatrix.IsSingular()) {
     114               0 :       mInError = true;
     115                 :     }
     116               0 :   }
     117                 :   /**
     118                 :    * Try to set up aContext so we can draw the whole textrun at once.
     119                 :    * This applies any global transform requested by SetInitialMatrix,
     120                 :    * then applies the positioning of the text. Returns false if drawing
     121                 :    * the whole textrun at once is impossible due to individual positioning
     122                 :    * and/or rotation of glyphs.
     123                 :    */
     124               0 :   bool SetupForDirectTextRunDrawing(gfxContext *aContext) {
     125               0 :     return SetupForDirectTextRun(aContext, mDrawScale);
     126                 :   }
     127                 :   /**
     128                 :    * Try to set up aContext so we can measure the whole textrun at once.
     129                 :    * This applies any global transform requested by SetInitialMatrix,
     130                 :    * then applies the positioning of the text, then applies a scale
     131                 :    * from appunits to device pixels so drawing in appunits works.
     132                 :    * Returns false if drawing the whole textrun at once is impossible due
     133                 :    * to individual positioning and/or rotation of glyphs.
     134                 :    */
     135               0 :   bool SetupForDirectTextRunMetrics(gfxContext *aContext) {
     136               0 :     return SetupForDirectTextRun(aContext, mMetricsScale);
     137                 :   }
     138                 :   /**
     139                 :    * We are scaling the glyphs up/down to the size we want so we need to
     140                 :    * inverse scale the outline widths of those glyphs so they are invariant
     141                 :    */
     142               0 :   void SetLineWidthAndDashesForDrawing(gfxContext *aContext) {
     143               0 :     aContext->SetLineWidth(aContext->CurrentLineWidth() / mDrawScale);
     144               0 :     AutoFallibleTArray<gfxFloat, 10> dashes;
     145                 :     gfxFloat dashOffset;
     146               0 :     if (aContext->CurrentDash(dashes, &dashOffset)) {
     147               0 :       for (PRUint32 i = 0; i <  dashes.Length(); i++) {
     148               0 :         dashes[i] /= mDrawScale;
     149                 :       }
     150               0 :       aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset / mDrawScale);
     151                 :     }
     152               0 :   }
     153                 : 
     154                 :   /**
     155                 :    * Returns the index of the next cluster in the string that should be drawn,
     156                 :    * or InvalidCluster() (i.e. PRUint32(-1)) if there is no such cluster.
     157                 :    */
     158                 :   PRUint32 NextCluster();
     159                 : 
     160                 :   /**
     161                 :    * Returns the length of the current cluster (usually 1, unless there
     162                 :    * are combining marks)
     163                 :    */
     164                 :   PRUint32 ClusterLength();
     165                 : 
     166                 :   /**
     167                 :    * Repeated calls NextCluster until it returns aIndex (i.e. aIndex is the
     168                 :    * current drawable character). Returns false if that never happens
     169                 :    * (because aIndex is before or equal to the current character, or
     170                 :    * out of bounds, or not drawable).
     171                 :    */
     172                 :   bool AdvanceToCharacter(PRUint32 aIndex);
     173                 : 
     174                 :   /**
     175                 :    * Resets the iterator to the beginning of the string.
     176                 :    */
     177                 :   void Reset() {
     178                 :     // There are two ways mInError can be set
     179                 :     // a) If there was a problem creating the iterator (mCurrentChar == -1)
     180                 :     // b) If we ran off the end of the string (mCurrentChar != -1)
     181                 :     // We can only reset the mInError flag in case b)
     182                 :     if (mCurrentChar != InvalidCluster()) {
     183                 :       mCurrentChar = InvalidCluster();
     184                 :       mInError = false;
     185                 :     }
     186                 :   }
     187                 : 
     188                 :   /**
     189                 :    * Set up aContext for glyph drawing. This applies any global transform
     190                 :    * requested by SetInitialMatrix, then applies any positioning and
     191                 :    * rotation for the current character.
     192                 :    */
     193               0 :   void SetupForDrawing(gfxContext *aContext) {
     194               0 :     return SetupFor(aContext, mDrawScale);
     195                 :   }
     196                 :   /**
     197                 :    * Set up aContext for glyph measuring. This applies any global transform
     198                 :    * requested by SetInitialMatrix, then applies any positioning and
     199                 :    * rotation for the current character, then applies a scale from appunits
     200                 :    * to device pixels so that drawing in appunits sizes works.
     201                 :    */
     202               0 :   void SetupForMetrics(gfxContext *aContext) {
     203               0 :     return SetupFor(aContext, mMetricsScale);
     204                 :   }
     205                 :   /**
     206                 :    * Get the raw position data for the current character.
     207                 :    */
     208                 :   CharacterPosition GetPositionData();
     209                 : 
     210                 :   /**
     211                 :    * "Invalid" cluster index returned to indicate error state
     212                 :    */
     213               0 :   PRUint32 InvalidCluster() {
     214               0 :     return PRUint32(-1);
     215                 :   }
     216                 : 
     217                 : private:
     218                 :   bool SetupForDirectTextRun(gfxContext *aContext, float aScale);
     219                 :   void SetupFor(gfxContext *aContext, float aScale);
     220                 : 
     221                 :   nsSVGGlyphFrame *mSource;
     222                 :   nsAutoTArray<CharacterPosition,80> mPositions;
     223                 :   gfxMatrix mInitialMatrix;
     224                 :   // Textrun advance width from start to mCurrentChar, in appunits
     225                 :   gfxFloat mCurrentAdvance;
     226                 :   PRUint32 mCurrentChar;
     227                 :   float mDrawScale;
     228                 :   float mMetricsScale;
     229                 :   bool mInError;
     230                 : };
     231                 : 
     232                 : //----------------------------------------------------------------------
     233                 : // Implementation
     234                 : 
     235                 : nsIFrame*
     236               0 : NS_NewSVGGlyphFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
     237                 : {
     238               0 :   return new (aPresShell) nsSVGGlyphFrame(aContext);
     239                 : }
     240                 : 
     241               0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGGlyphFrame)
     242                 : 
     243                 : //----------------------------------------------------------------------
     244                 : // nsQueryFrame methods
     245                 : 
     246               0 : NS_QUERYFRAME_HEAD(nsSVGGlyphFrame)
     247               0 :   NS_QUERYFRAME_ENTRY(nsISVGGlyphFragmentNode)
     248               0 :   NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
     249               0 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGGlyphFrameBase)
     250                 : 
     251                 : //----------------------------------------------------------------------
     252                 : // nsIFrame methods
     253                 : 
     254                 : NS_IMETHODIMP
     255               0 : nsSVGGlyphFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
     256                 : {
     257               0 :   ClearTextRun();
     258               0 :   NotifyGlyphMetricsChange();
     259               0 :   if (IsTextEmpty()) {
     260                 :     // That's it for this frame. Leave no trace we were here
     261               0 :     nsSVGUtils::UpdateGraphic(this);
     262                 :   }
     263                 : 
     264               0 :   return NS_OK;
     265                 : }
     266                 : 
     267                 : // Usable font size range in devpixels / user-units
     268                 : #define CLAMP_MIN_SIZE 8
     269                 : #define CLAMP_MAX_SIZE 200
     270                 : #define PRECISE_SIZE   200
     271                 : 
     272                 : /* virtual */ void
     273               0 : nsSVGGlyphFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
     274                 : {
     275               0 :   nsSVGGlyphFrameBase::DidSetStyleContext(aOldStyleContext);
     276                 : 
     277               0 :   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     278               0 :     ClearTextRun();
     279               0 :     NotifyGlyphMetricsChange();
     280                 :   }
     281               0 : }
     282                 : 
     283                 : NS_IMETHODIMP
     284               0 : nsSVGGlyphFrame::IsSelectable(bool* aIsSelectable,
     285                 :                               PRUint8* aSelectStyle) const
     286                 : {
     287               0 :   nsresult rv = nsSVGGlyphFrameBase::IsSelectable(aIsSelectable, aSelectStyle);
     288                 : #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
     289                 :   printf("nsSVGGlyphFrame(%p)::IsSelectable()=(%d,%d)\n", this, *aIsSelectable, aSelectStyle);
     290                 : #endif
     291               0 :   return rv;
     292                 : }
     293                 : 
     294                 : NS_IMETHODIMP
     295               0 : nsSVGGlyphFrame::Init(nsIContent* aContent,
     296                 :                       nsIFrame* aParent,
     297                 :                       nsIFrame* aPrevInFlow)
     298                 : {
     299                 : #ifdef DEBUG
     300               0 :   NS_ASSERTION(aParent, "null parent");
     301                 : 
     302               0 :   nsIFrame* ancestorFrame = nsSVGUtils::GetFirstNonAAncestorFrame(aParent);
     303               0 :   NS_ASSERTION(ancestorFrame, "Must have ancestor");
     304                 : 
     305               0 :   nsSVGTextContainerFrame *metrics = do_QueryFrame(ancestorFrame);
     306               0 :   NS_ASSERTION(metrics,
     307                 :                "trying to construct an SVGGlyphFrame for an invalid container");
     308                 : 
     309               0 :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     310                 :                "trying to construct an SVGGlyphFrame for wrong content element");
     311                 : #endif /* DEBUG */
     312                 : 
     313               0 :   return nsSVGGlyphFrameBase::Init(aContent, aParent, aPrevInFlow);
     314                 : }
     315                 : 
     316                 : nsIAtom *
     317               0 : nsSVGGlyphFrame::GetType() const
     318                 : {
     319               0 :   return nsGkAtoms::svgGlyphFrame;
     320                 : }
     321                 : 
     322                 : //----------------------------------------------------------------------
     323                 : // nsISVGChildFrame methods
     324                 : 
     325                 : NS_IMETHODIMP
     326               0 : nsSVGGlyphFrame::PaintSVG(nsRenderingContext *aContext,
     327                 :                           const nsIntRect *aDirtyRect)
     328                 : {
     329               0 :   if (!GetStyleVisibility()->IsVisible())
     330               0 :     return NS_OK;
     331                 : 
     332               0 :   gfxContext *gfx = aContext->ThebesContext();
     333               0 :   PRUint16 renderMode = SVGAutoRenderState::GetRenderMode(aContext);
     334                 : 
     335               0 :   switch (GetStyleSVG()->mTextRendering) {
     336                 :   case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
     337               0 :     gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
     338               0 :     break;
     339                 :   default:
     340               0 :     gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
     341               0 :     break;
     342                 :   }
     343                 : 
     344               0 :   if (renderMode != SVGAutoRenderState::NORMAL) {
     345                 : 
     346               0 :     gfxMatrix matrix = gfx->CurrentMatrix();
     347               0 :     SetupGlobalTransform(gfx);
     348                 : 
     349               0 :     CharacterIterator iter(this, true);
     350               0 :     iter.SetInitialMatrix(gfx);
     351                 : 
     352               0 :     if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD)
     353               0 :       gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
     354                 :     else
     355               0 :       gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
     356                 : 
     357               0 :     if (renderMode == SVGAutoRenderState::CLIP_MASK) {
     358               0 :       gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
     359               0 :       DrawCharacters(&iter, gfx, gfxFont::GLYPH_FILL);
     360                 :     } else {
     361               0 :       DrawCharacters(&iter, gfx, gfxFont::GLYPH_PATH);
     362                 :     }
     363                 : 
     364               0 :     gfx->SetMatrix(matrix);
     365               0 :     return NS_OK;
     366                 :   }
     367                 : 
     368                 :   // We are adding patterns or gradients to the context. Save
     369                 :   // it so we don't leak them into the next object we draw
     370               0 :   gfx->Save();
     371               0 :   SetupGlobalTransform(gfx);
     372                 : 
     373               0 :   CharacterIterator iter(this, true);
     374               0 :   iter.SetInitialMatrix(gfx);
     375                 : 
     376               0 :   nsRefPtr<gfxPattern> strokePattern;
     377               0 :   DrawMode drawMode = SetupCairoState(gfx, getter_AddRefs(strokePattern));
     378                 : 
     379               0 :   if (drawMode) {
     380               0 :     DrawCharacters(&iter, gfx, drawMode, strokePattern);
     381                 :   }
     382                 :   
     383               0 :   gfx->Restore();
     384                 : 
     385               0 :   return NS_OK;
     386                 : }
     387                 : 
     388                 : NS_IMETHODIMP_(nsIFrame*)
     389               0 : nsSVGGlyphFrame::GetFrameForPoint(const nsPoint &aPoint)
     390                 : {
     391               0 :   PRUint16 hitTestFlags = GetHitTestFlags();
     392               0 :   if (!hitTestFlags) {
     393               0 :     return nsnull;
     394                 :   }
     395                 : 
     396               0 :   nsRefPtr<gfxContext> context = MakeTmpCtx();
     397               0 :   SetupGlobalTransform(context);
     398               0 :   CharacterIterator iter(this, true);
     399               0 :   iter.SetInitialMatrix(context);
     400                 : 
     401                 :   // The SVG 1.1 spec says that text is hit tested against the character cells
     402                 :   // of the text, not the fill and stroke. See the section starting "For text
     403                 :   // elements..." here:
     404                 :   //
     405                 :   //   http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
     406                 :   //
     407                 :   // Currently we just test the character cells if GetHitTestFlags says we're
     408                 :   // supposed to be testing either the fill OR the stroke:
     409                 : 
     410                 :   PRUint32 i;
     411               0 :   while ((i = iter.NextCluster()) != iter.InvalidCluster()) {
     412                 :     gfxTextRun::Metrics metrics =
     413                 :     mTextRun->MeasureText(i, iter.ClusterLength(),
     414               0 :                           gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
     415               0 :     iter.SetupForMetrics(context);
     416               0 :     context->Rectangle(metrics.mBoundingBox);
     417                 :   }
     418                 : 
     419                 :   gfxPoint userSpacePoint =
     420                 :     context->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
     421               0 :                                    PresContext()->AppUnitsToGfxUnits(aPoint.y)));
     422                 : 
     423               0 :   bool isHit = false;
     424               0 :   if (hitTestFlags & SVG_HIT_TEST_FILL || hitTestFlags & SVG_HIT_TEST_STROKE) {
     425               0 :     isHit = context->PointInFill(userSpacePoint);
     426                 :   }
     427                 : 
     428                 :   // If isHit is false, we may also want to fill and stroke the text to check
     429                 :   // whether the pointer is over an area of fill or stroke that lies outside
     430                 :   // the character cells. (With a thick stroke, or with fonts like Zapfino, such
     431                 :   // areas may be very significant.) This is what Opera appears to do, but
     432                 :   // currently we do not.
     433                 : 
     434               0 :   if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
     435               0 :     return this;
     436                 : 
     437               0 :   return nsnull;
     438                 : }
     439                 : 
     440                 : NS_IMETHODIMP_(nsRect)
     441               0 : nsSVGGlyphFrame::GetCoveredRegion()
     442                 : {
     443                 :   // See bug 614732 comment 32:
     444                 :   //return nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext());
     445               0 :   return mCoveredRegion;
     446                 : }
     447                 : 
     448                 : NS_IMETHODIMP
     449               0 : nsSVGGlyphFrame::UpdateCoveredRegion()
     450                 : {
     451               0 :   mRect.SetEmpty();
     452                 : 
     453                 :   // XXX here we have tmpCtx use its default identity matrix, but does this
     454                 :   // function call anything that will call GetCanvasTM and break things?
     455               0 :   nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
     456                 : 
     457               0 :   bool hasStroke = HasStroke();
     458               0 :   if (hasStroke) {
     459               0 :     SetupCairoStrokeGeometry(tmpCtx);
     460               0 :   } else if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_None) {
     461               0 :     return NS_OK;
     462                 :   }
     463                 : 
     464               0 :   CharacterIterator iter(this, true);
     465               0 :   iter.SetInitialMatrix(tmpCtx);
     466               0 :   AddBoundingBoxesToPath(&iter, tmpCtx);
     467               0 :   tmpCtx->IdentityMatrix();
     468                 : 
     469                 :   // Be careful when replacing the following logic to get the fill and stroke
     470                 :   // extents independently (instead of computing the stroke extents from the
     471                 :   // path extents). You may think that you can just use the stroke extents if
     472                 :   // there is both a fill and a stroke. In reality it's necessary to calculate
     473                 :   // both the fill and stroke extents, and take the union of the two. There are
     474                 :   // two reasons for this:
     475                 :   //
     476                 :   // # Due to stroke dashing, in certain cases the fill extents could actually
     477                 :   //   extend outside the stroke extents.
     478                 :   // # If the stroke is very thin, cairo won't paint any stroke, and so the
     479                 :   //   stroke bounds that it will return will be empty.
     480                 :   //
     481                 :   // Another thing to be aware of is that under AddBoundingBoxesToPath the
     482                 :   // gfxContext has SetLineWidth() called on it, so if we want to ask the
     483                 :   // gfxContext for *stroke* extents, we'll neet to wrap the
     484                 :   // AddBoundingBoxesToPath() call with CurrentLineWidth()/SetLineWidth()
     485                 :   // calls to record and then reset the stroke width.
     486               0 :   gfxRect extent = tmpCtx->GetUserPathExtent();
     487               0 :   if (hasStroke) {
     488                 :     extent =
     489               0 :       nsSVGUtils::PathExtentsToMaxStrokeExtents(extent, this, gfxMatrix());
     490                 :   }
     491                 : 
     492               0 :   if (!extent.IsEmpty()) {
     493                 :     mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, 
     494               0 :               PresContext()->AppUnitsPerCSSPixel());
     495                 :   }
     496                 : 
     497                 :   // See bug 614732 comment 32.
     498                 :   mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
     499               0 :     mRect, GetCanvasTM(), PresContext());
     500                 : 
     501               0 :   return NS_OK;
     502                 : }
     503                 : 
     504                 : NS_IMETHODIMP
     505               0 : nsSVGGlyphFrame::InitialUpdate()
     506                 : {
     507               0 :   NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
     508                 :                "Yikes! We've been called already! Hopefully we weren't called "
     509                 :                "before our nsSVGOuterSVGFrame's initial Reflow()!!!");
     510                 : 
     511               0 :   NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
     512                 :                "We don't actually participate in reflow");
     513                 : 
     514                 :   // Do unset the various reflow bits, though.
     515                 :   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
     516               0 :               NS_FRAME_HAS_DIRTY_CHILDREN);
     517                 :   
     518               0 :   return NS_OK;
     519                 : }  
     520                 : 
     521                 : void
     522               0 : nsSVGGlyphFrame::NotifySVGChanged(PRUint32 aFlags)
     523                 : {
     524               0 :   NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
     525                 :                     (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
     526                 :                     "Must be NS_STATE_SVG_NONDISPLAY_CHILD!");
     527                 : 
     528               0 :   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
     529                 :                     "Invalidation logic may need adjusting");
     530                 : 
     531               0 :   if (aFlags & TRANSFORM_CHANGED) {
     532               0 :     ClearTextRun();
     533                 :   }
     534               0 :   if (!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS)) {
     535               0 :     nsSVGUtils::UpdateGraphic(this);
     536                 :   }
     537               0 : }
     538                 : 
     539                 : void
     540               0 : nsSVGGlyphFrame::NotifyRedrawSuspended()
     541                 : {
     542               0 :   AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
     543               0 : }
     544                 : 
     545                 : void
     546               0 : nsSVGGlyphFrame::NotifyRedrawUnsuspended()
     547                 : {
     548               0 :   RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
     549                 : 
     550               0 :   if (GetStateBits() & NS_STATE_SVG_DIRTY)
     551               0 :     nsSVGUtils::UpdateGraphic(this);
     552               0 : }
     553                 : 
     554                 : void
     555               0 : nsSVGGlyphFrame::AddBoundingBoxesToPath(CharacterIterator *aIter,
     556                 :                                         gfxContext *aContext)
     557                 : {
     558               0 :   if (aIter->SetupForDirectTextRunMetrics(aContext)) {
     559                 :     gfxTextRun::Metrics metrics =
     560                 :       mTextRun->MeasureText(0, mTextRun->GetLength(),
     561               0 :                             gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
     562               0 :     aContext->Rectangle(metrics.mBoundingBox);
     563               0 :     return;
     564                 :   }
     565                 : 
     566                 :   PRUint32 i;
     567               0 :   while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
     568               0 :     aIter->SetupForMetrics(aContext);
     569                 :     gfxTextRun::Metrics metrics =
     570                 :       mTextRun->MeasureText(i, aIter->ClusterLength(),
     571               0 :                             gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
     572               0 :     aContext->Rectangle(metrics.mBoundingBox);
     573                 :   }
     574                 : }
     575                 : 
     576                 : void
     577               0 : nsSVGGlyphFrame::DrawCharacters(CharacterIterator *aIter,
     578                 :                                 gfxContext *aContext,
     579                 :                                 DrawMode aDrawMode,
     580                 :                                 gfxPattern *aStrokePattern)
     581                 : {
     582               0 :   if (aDrawMode & gfxFont::GLYPH_STROKE) {
     583               0 :     aIter->SetLineWidthAndDashesForDrawing(aContext);
     584                 :   }
     585                 : 
     586               0 :   if (aIter->SetupForDirectTextRunDrawing(aContext)) {
     587                 :     mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, 0,
     588               0 :                    mTextRun->GetLength(), nsnull, nsnull, aStrokePattern);
     589               0 :     return;
     590                 :   }
     591                 : 
     592                 :   PRUint32 i;
     593               0 :   while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
     594               0 :     aIter->SetupForDrawing(aContext);
     595                 :     mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, i,
     596               0 :                    aIter->ClusterLength(), nsnull, nsnull, aStrokePattern);
     597                 :   }
     598                 : }
     599                 : 
     600                 : gfxRect
     601               0 : nsSVGGlyphFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
     602                 :                                      PRUint32 aFlags)
     603                 : {
     604               0 :   if (mOverrideCanvasTM) {
     605               0 :     *mOverrideCanvasTM = aToBBoxUserspace;
     606                 :   } else {
     607               0 :     mOverrideCanvasTM = new gfxMatrix(aToBBoxUserspace);
     608                 :   }
     609                 : 
     610               0 :   nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
     611               0 :   SetupGlobalTransform(tmpCtx);
     612               0 :   CharacterIterator iter(this, true);
     613               0 :   iter.SetInitialMatrix(tmpCtx);
     614               0 :   AddBoundingBoxesToPath(&iter, tmpCtx);
     615               0 :   tmpCtx->IdentityMatrix();
     616                 : 
     617               0 :   mOverrideCanvasTM = nsnull;
     618                 : 
     619               0 :   gfxRect bbox;
     620                 : 
     621               0 :   gfxRect pathExtents = tmpCtx->GetUserPathExtent();
     622                 : 
     623                 :   // Account for fill:
     624               0 :   if ((aFlags & nsSVGUtils::eBBoxIncludeFill) != 0 &&
     625                 :       ((aFlags & nsSVGUtils::eBBoxIgnoreFillIfNone) == 0 ||
     626               0 :        GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
     627               0 :     bbox = pathExtents;
     628                 :   }
     629                 : 
     630                 :   // Account for stroke:
     631               0 :   if ((aFlags & nsSVGUtils::eBBoxIncludeStroke) != 0 &&
     632               0 :       ((aFlags & nsSVGUtils::eBBoxIgnoreStrokeIfNone) == 0 || HasStroke())) {
     633                 :     bbox =
     634                 :       bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
     635                 :                                                            this,
     636               0 :                                                            aToBBoxUserspace));
     637                 :   }
     638                 : 
     639                 :   return bbox;
     640                 : }
     641                 : 
     642                 : //----------------------------------------------------------------------
     643                 : // nsSVGGeometryFrame methods:
     644                 : 
     645                 : gfxMatrix
     646               0 : nsSVGGlyphFrame::GetCanvasTM()
     647                 : {
     648               0 :   if (mOverrideCanvasTM) {
     649               0 :     return *mOverrideCanvasTM;
     650                 :   }
     651               0 :   NS_ASSERTION(mParent, "null parent");
     652               0 :   return static_cast<nsSVGContainerFrame*>(mParent)->GetCanvasTM();
     653                 : }
     654                 : 
     655                 : //----------------------------------------------------------------------
     656                 : // nsSVGGlyphFrame methods:
     657                 : 
     658                 : bool
     659               0 : nsSVGGlyphFrame::GetCharacterData(nsAString & aCharacterData)
     660                 : {
     661               0 :   nsAutoString characterData;
     662               0 :   mContent->AppendTextTo(characterData);
     663                 : 
     664               0 :   if (mCompressWhitespace) {
     665                 :     characterData.CompressWhitespace(mTrimLeadingWhitespace,
     666               0 :                                      mTrimTrailingWhitespace);
     667                 :   } else {
     668               0 :     nsAString::iterator start, end;
     669               0 :     characterData.BeginWriting(start);
     670               0 :     characterData.EndWriting(end);
     671               0 :     while (start != end) {
     672               0 :       if (NS_IsAsciiWhitespace(*start))
     673               0 :         *start = ' ';
     674               0 :       ++start;
     675                 :     }
     676                 :   }
     677               0 :   aCharacterData = characterData;
     678                 : 
     679               0 :   return !characterData.IsEmpty();
     680                 : }
     681                 : 
     682                 : bool
     683               0 : nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPositions,
     684                 :                                        float aMetricsScale)
     685                 : {
     686               0 :   PRUint32 strLength = mTextRun->GetLength();
     687               0 :   NS_ABORT_IF_FALSE(strLength > 0, "no text");
     688                 : 
     689               0 :   const gfxFloat radPerDeg = M_PI / 180.0;
     690                 : 
     691               0 :   nsTArray<float> xList, yList;
     692               0 :   GetEffectiveXY(strLength, xList, yList);
     693               0 :   nsTArray<float> dxList, dyList;
     694               0 :   GetEffectiveDxDy(strLength, dxList, dyList);
     695               0 :   nsTArray<float> rotateList;
     696               0 :   GetEffectiveRotate(strLength, rotateList);
     697                 : 
     698               0 :   gfxPoint pos = mPosition;
     699               0 :   gfxFloat angle = 0.0;
     700                 : 
     701               0 :   nsSVGTextPathFrame *textPath = FindTextPathParent();
     702                 : 
     703               0 :   if (textPath) {
     704               0 :     nsRefPtr<gfxFlattenedPath> data = textPath->GetFlattenedPath();
     705                 : 
     706                 :     // textPath frame, but invalid target
     707               0 :     if (!data)
     708               0 :       return false;
     709                 : 
     710               0 :     if (!aCharacterPositions->SetLength(strLength))
     711               0 :       return false;
     712                 : 
     713               0 :     gfxFloat pathScale = textPath->GetOffsetScale();
     714                 : 
     715               0 :     CharacterPosition *cp = aCharacterPositions->Elements();
     716                 : 
     717               0 :     gfxFloat length = data->GetLength();
     718                 : 
     719               0 :     for (PRUint32 i = 0; i < strLength; i++) {
     720                 :       gfxFloat halfAdvance =
     721               0 :         mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale / 2.0;
     722                 : 
     723                 :       // use only x position for horizontal writing
     724               0 :       if (i > 0 && i < xList.Length()) {
     725               0 :         pos.x = xList[i];
     726                 :       }
     727               0 :       pos.x += (i > 0 && i < dxList.Length()) ? dxList[i] * pathScale : 0.0;
     728               0 :       pos.y += (i > 0 && i < dyList.Length()) ? dyList[i] * pathScale : 0.0;
     729               0 :       if (i < rotateList.Length()) {
     730               0 :         angle = rotateList[i] * radPerDeg;
     731                 :       }
     732                 : 
     733                 :       // check that we're within the path boundaries
     734               0 :       cp[i].draw = (pos.x + halfAdvance >= 0.0 &&
     735               0 :                     pos.x + halfAdvance <= length);
     736                 : 
     737               0 :       if (cp[i].draw) {
     738                 : 
     739                 :         // add y (normal)
     740                 :         // add rotation
     741                 :         // move point back along tangent
     742                 :         gfxPoint pt = data->FindPoint(gfxPoint(pos.x + halfAdvance, pos.y),
     743               0 :                                       &(cp[i].angle));
     744                 :         cp[i].pos =
     745               0 :           pt - gfxPoint(cos(cp[i].angle), sin(cp[i].angle)) * halfAdvance;
     746               0 :         cp[i].angle += angle;
     747                 :       }
     748               0 :       pos.x += 2 * halfAdvance;
     749                 :     }
     750               0 :     return true;
     751                 :   }
     752                 : 
     753               0 :   if (xList.Length() <= 1 &&
     754               0 :       yList.Length() <= 1 &&
     755               0 :       dxList.Length() <= 1 &&
     756               0 :       dyList.Length() <= 1 &&
     757               0 :       rotateList.IsEmpty()) {
     758                 :     // simple text without individual positioning
     759               0 :     return true;
     760                 :   }
     761                 : 
     762               0 :   if (!aCharacterPositions->SetLength(strLength))
     763               0 :     return false;
     764                 : 
     765               0 :   CharacterPosition *cp = aCharacterPositions->Elements();
     766                 : 
     767               0 :   PRUint16 anchor = GetTextAnchor();
     768                 : 
     769               0 :   for (PRUint32 i = 0; i < strLength; i++) {
     770               0 :     cp[i].draw = true;
     771                 : 
     772               0 :     gfxFloat advance = mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale;
     773               0 :     if (xList.Length() > 1 && i < xList.Length()) {
     774               0 :       pos.x = xList[i];
     775                 : 
     776                 :       // apply text-anchor to character
     777               0 :       if (anchor == NS_STYLE_TEXT_ANCHOR_MIDDLE)
     778               0 :         pos.x -= advance/2.0;
     779               0 :       else if (anchor == NS_STYLE_TEXT_ANCHOR_END)
     780               0 :         pos.x -= advance;
     781                 :     }
     782               0 :     if (yList.Length() > 1 && i < yList.Length()) {
     783               0 :       pos.y = yList[i];
     784                 :     }
     785               0 :     pos.x += (i > 0 && i < dxList.Length()) ? dxList[i] : 0.0;
     786               0 :     pos.y += (i > 0 && i < dyList.Length()) ? dyList[i] : 0.0;
     787               0 :     cp[i].pos = pos;
     788               0 :     pos.x += advance;
     789               0 :     if (i < rotateList.Length()) {
     790               0 :       angle = rotateList[i] * radPerDeg;
     791                 :     }
     792               0 :     cp[i].angle = angle;
     793                 :   }
     794               0 :   return true;
     795                 : }
     796                 : 
     797                 : PRUint32
     798               0 : nsSVGGlyphFrame::GetTextRunFlags(PRUint32 strLength)
     799                 : {
     800                 :   // Keep the logic here consistent with GetCharacterPositions
     801                 : 
     802               0 :   if (FindTextPathParent()) {
     803               0 :     return gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
     804                 :   }
     805                 : 
     806               0 :   nsTArray<float> xList, yList;
     807               0 :   GetEffectiveXY(strLength, xList, yList);
     808               0 :   nsTArray<float> dxList, dyList;
     809               0 :   GetEffectiveDxDy(strLength, dxList, dyList);
     810               0 :   nsTArray<float> rotateList;
     811               0 :   GetEffectiveRotate(strLength, rotateList);
     812                 : 
     813               0 :   return (xList.Length() > 1 ||
     814               0 :           yList.Length() > 1 ||
     815               0 :           dxList.Length() > 1 ||
     816               0 :           dyList.Length() > 1 ||
     817               0 :           !rotateList.IsEmpty()) ?
     818               0 :     gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES : 0;
     819                 : }
     820                 : 
     821                 : float
     822               0 : nsSVGGlyphFrame::GetSubStringAdvance(PRUint32 aCharnum, 
     823                 :                                      PRUint32 aFragmentChars,
     824                 :                                      float aMetricsScale)
     825                 : {
     826               0 :   if (aFragmentChars == 0)
     827               0 :     return 0.0f;
     828                 :  
     829                 :   gfxFloat advance =
     830               0 :     mTextRun->GetAdvanceWidth(aCharnum, aFragmentChars, nsnull) * aMetricsScale;
     831                 : 
     832               0 :   nsTArray<float> dxlist, notUsed;
     833               0 :   GetEffectiveDxDy(mTextRun->GetLength(), dxlist, notUsed);
     834               0 :   PRUint32 dxcount = dxlist.Length();
     835               0 :   if (dxcount) {
     836               0 :     gfxFloat pathScale = 1.0;
     837               0 :     nsSVGTextPathFrame *textPath = FindTextPathParent();
     838               0 :     if (textPath)
     839               0 :       pathScale = textPath->GetOffsetScale();
     840               0 :     if (dxcount > aFragmentChars) 
     841               0 :       dxcount = aFragmentChars;
     842               0 :     for (PRUint32 i = aCharnum; i < dxcount; i++) {
     843               0 :       advance += dxlist[i] * pathScale;
     844                 :     }
     845                 :   }
     846                 : 
     847               0 :   return float(advance);
     848                 : }
     849                 : 
     850                 : gfxFloat
     851               0 : nsSVGGlyphFrame::GetBaselineOffset(float aMetricsScale)
     852                 : {
     853                 :   gfxTextRun::Metrics metrics =
     854                 :     mTextRun->MeasureText(0, mTextRun->GetLength(),
     855               0 :                           gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
     856                 : 
     857                 :   PRUint16 dominantBaseline;
     858                 : 
     859               0 :   for (nsIFrame *frame = GetParent(); frame; frame = frame->GetParent()) {
     860               0 :     dominantBaseline = frame->GetStyleSVGReset()->mDominantBaseline;
     861               0 :     if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO ||
     862               0 :         frame->GetType() == nsGkAtoms::svgTextFrame) {
     863               0 :       break;
     864                 :     }
     865                 :   }
     866                 : 
     867                 :   gfxFloat baselineAppUnits;
     868               0 :   switch (dominantBaseline) {
     869                 :   case NS_STYLE_DOMINANT_BASELINE_HANGING:
     870                 :     // not really right, but the best we can do with the information provided
     871                 :     // FALLTHROUGH
     872                 :   case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
     873               0 :     baselineAppUnits = -metrics.mAscent;
     874               0 :     break;
     875                 :   case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
     876                 :   case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
     877               0 :     baselineAppUnits = metrics.mDescent;
     878               0 :     break;
     879                 :   case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
     880                 :   case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
     881               0 :     baselineAppUnits = -(metrics.mAscent - metrics.mDescent) / 2.0;
     882               0 :     break;
     883                 :   case NS_STYLE_DOMINANT_BASELINE_AUTO:
     884                 :   case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
     885               0 :     return 0.0;
     886                 :   default:
     887               0 :     NS_WARNING("We don't know about this type of dominant-baseline");
     888               0 :     return 0.0;
     889                 :   }
     890               0 :   return baselineAppUnits * aMetricsScale;
     891                 : }
     892                 : 
     893                 : DrawMode
     894               0 : nsSVGGlyphFrame::SetupCairoState(gfxContext *aContext, gfxPattern **aStrokePattern)
     895                 : {
     896               0 :   DrawMode toDraw = DrawMode(0);
     897               0 :   const nsStyleSVG* style = GetStyleSVG();
     898                 : 
     899               0 :   if (HasStroke()) {
     900               0 :     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
     901               0 :     aContext->IdentityMatrix();
     902                 : 
     903               0 :     toDraw = DrawMode(toDraw | gfxFont::GLYPH_STROKE);
     904                 : 
     905               0 :     SetupCairoStrokeHitGeometry(aContext);
     906               0 :     float opacity = style->mStrokeOpacity;
     907                 :     nsSVGPaintServerFrame *ps = GetPaintServer(&style->mStroke,
     908               0 :                                                nsSVGEffects::StrokeProperty());
     909                 : 
     910               0 :     nsRefPtr<gfxPattern> strokePattern;
     911                 : 
     912               0 :     if (ps) {
     913                 :       // Gradient or Pattern: can get pattern directly from frame
     914               0 :       strokePattern = ps->GetPaintServerPattern(this, opacity);
     915                 :     }
     916                 : 
     917               0 :     if (!strokePattern) {
     918                 :       nscolor color;
     919                 :       nsSVGUtils::GetFallbackOrPaintColor(aContext, GetStyleContext(),
     920                 :                                           &nsStyleSVG::mStroke, &opacity,
     921               0 :                                           &color);
     922                 :       strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0,
     923                 :                                              NS_GET_G(color) / 255.0,
     924                 :                                              NS_GET_B(color) / 255.0,
     925               0 :                                              NS_GET_A(color) / 255.0 * opacity));
     926                 :     }
     927                 : 
     928               0 :     strokePattern.forget(aStrokePattern);
     929                 :   }
     930                 : 
     931               0 :   if (SetupCairoFill(aContext)) {
     932               0 :     toDraw = DrawMode(toDraw | gfxFont::GLYPH_FILL);
     933                 :   }
     934                 : 
     935               0 :   return toDraw;
     936                 : }
     937                 : 
     938                 : //----------------------------------------------------------------------
     939                 : 
     940                 : // Utilities for converting from indices in the uncompressed content
     941                 : // element strings to compressed frame string and back:
     942                 : static int
     943               0 : CompressIndex(int index, const nsTextFragment*fragment)
     944                 : {
     945               0 :   int ci=0;
     946               0 :   if (fragment->Is2b()) {
     947               0 :     const PRUnichar *data=fragment->Get2b();
     948               0 :     while(*data && index) {
     949               0 :       if (XP_IS_SPACE_W(*data)){
     950               0 :         do {
     951               0 :           ++data;
     952               0 :           --index;
     953                 :         }while(XP_IS_SPACE_W(*data) && index);
     954                 :       }
     955                 :       else {
     956               0 :         ++data;
     957               0 :         --index;
     958                 :       }
     959               0 :       ++ci;
     960                 :     }
     961                 :   }
     962                 :   else {
     963               0 :     const char *data=fragment->Get1b();
     964               0 :     while(*data && index) {
     965               0 :       if (XP_IS_SPACE_W(*data)){
     966               0 :         do {
     967               0 :           ++data;
     968               0 :           --index;
     969                 :         }while(XP_IS_SPACE_W(*data) && index);
     970                 :       }
     971                 :       else {
     972               0 :         ++data;
     973               0 :         --index;
     974                 :       }
     975               0 :       ++ci;
     976                 :     }
     977                 :   }
     978                 :     
     979               0 :   return ci;
     980                 : }
     981                 : 
     982                 : nsresult
     983               0 : nsSVGGlyphFrame::GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
     984                 :                               nscolor *foreground, nscolor *background)
     985                 : {
     986               0 :   *foreground = NS_RGB(255,255,255);
     987               0 :   *background = NS_RGB(0,0,0); 
     988               0 :   *charnum=0;
     989               0 :   *nchars=0;
     990                 : 
     991               0 :   bool hasHighlight = IsSelected();
     992               0 :   if (!hasHighlight) {
     993               0 :     NS_ERROR("nsSVGGlyphFrame::GetHighlight() called by renderer when there is no highlight");
     994               0 :     return NS_ERROR_FAILURE;
     995                 :   }
     996                 : 
     997               0 :   nsPresContext *presContext = PresContext();
     998                 : 
     999                 :   // The selection ranges are relative to the uncompressed text in
    1000                 :   // the content element. We'll need the text fragment:
    1001               0 :   const nsTextFragment *fragment = mContent->GetText();
    1002               0 :   NS_ASSERTION(fragment, "no text");
    1003                 :   
    1004                 :   // get the selection details 
    1005               0 :   SelectionDetails *details = nsnull;
    1006                 :   {
    1007               0 :     nsRefPtr<nsFrameSelection> frameSelection = presContext->PresShell()->FrameSelection();
    1008               0 :     if (!frameSelection) {
    1009               0 :       NS_ERROR("no frameselection interface");
    1010               0 :       return NS_ERROR_FAILURE;
    1011                 :     }
    1012                 : 
    1013                 :     details = frameSelection->LookUpSelection(
    1014               0 :       mContent, 0, fragment->GetLength(), false
    1015               0 :       );
    1016                 :   }
    1017                 : 
    1018                 : #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
    1019                 :   {
    1020                 :     SelectionDetails *dp = details;
    1021                 :     printf("nsSVGGlyphFrame(%p)::GetHighlight() [\n", this);
    1022                 :     while (dp) {
    1023                 :       printf("selection detail: %d(%d)->%d(%d) type %d\n",
    1024                 :              dp->mStart, CompressIndex(dp->mStart, fragment),
    1025                 :              dp->mEnd, CompressIndex(dp->mEnd, fragment),
    1026                 :              dp->mType);
    1027                 :       dp = dp->mNext;
    1028                 :     }
    1029                 :     printf("]\n");
    1030                 :       
    1031                 :   }
    1032                 : #endif
    1033                 :   
    1034               0 :   if (details) {
    1035               0 :     NS_ASSERTION(details->mNext==nsnull, "can't do multiple selection ranges");
    1036                 : 
    1037               0 :     *charnum=CompressIndex(details->mStart, fragment);
    1038               0 :     *nchars=CompressIndex(details->mEnd, fragment)-*charnum;  
    1039                 : 
    1040                 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
    1041               0 :                           background);
    1042                 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
    1043               0 :                           foreground);
    1044                 : 
    1045               0 :     SelectionDetails *dp = details;
    1046               0 :     while ((dp=details->mNext) != nsnull) {
    1047               0 :       delete details;
    1048               0 :       details = dp;
    1049                 :     }
    1050               0 :     delete details;
    1051                 :   }
    1052                 :   
    1053               0 :   return NS_OK;
    1054                 : }
    1055                 : 
    1056                 : 
    1057                 : //----------------------------------------------------------------------
    1058                 : // Internal methods
    1059                 : 
    1060                 : void
    1061               0 : nsSVGGlyphFrame::SetGlyphPosition(gfxPoint *aPosition, bool aForceGlobalTransform)
    1062                 : {
    1063                 :   float drawScale, metricsScale;
    1064                 : 
    1065               0 :   nsSVGTextPathFrame *textPath = FindTextPathParent();
    1066                 :   // In a textPath, the 'y' attribute has no effect, so we reset 'y' here
    1067                 :   // to use aPosition.y for dy only
    1068               0 :   if (textPath && textPath->GetFirstPrincipalChild() == this) {
    1069               0 :     aPosition->y = 0.0;
    1070                 :   }
    1071                 : 
    1072               0 :   if (!EnsureTextRun(&drawScale, &metricsScale, aForceGlobalTransform))
    1073               0 :     return;
    1074                 : 
    1075               0 :   mPosition.MoveTo(aPosition->x, aPosition->y - GetBaselineOffset(metricsScale));
    1076                 : 
    1077               0 :   PRUint32 strLength = mTextRun->GetLength();
    1078                 : 
    1079               0 :   nsTArray<float> xList, yList;
    1080               0 :   GetEffectiveXY(strLength, xList, yList);
    1081               0 :   PRUint32 xCount = NS_MIN(xList.Length(), strLength);
    1082               0 :   PRUint32 yCount = NS_MIN(yList.Length(), strLength);
    1083                 : 
    1084                 :   // move aPosition to the last glyph position
    1085               0 :   gfxFloat x = aPosition->x;
    1086               0 :   if (xCount > 1) {
    1087               0 :     x = xList[xCount - 1];
    1088                 :     x +=
    1089               0 :       mTextRun->GetAdvanceWidth(xCount - 1, 1, nsnull) * metricsScale;
    1090                 : 
    1091                 :       // advance to the last glyph
    1092               0 :       if (strLength > xCount) {
    1093                 :         x +=
    1094               0 :           mTextRun->GetAdvanceWidth(xCount, strLength - xCount, nsnull) *
    1095               0 :             metricsScale;
    1096                 :       }
    1097                 :   } else {
    1098               0 :     x += mTextRun->GetAdvanceWidth(0, strLength, nsnull) * metricsScale;
    1099                 :   }
    1100                 : 
    1101               0 :   gfxFloat y = (textPath || yCount <= 1) ? aPosition->y : yList[yCount - 1];
    1102               0 :   aPosition->MoveTo(x, y);
    1103                 : 
    1104               0 :   gfxFloat pathScale = 1.0;
    1105               0 :   if (textPath)
    1106               0 :     pathScale = textPath->GetOffsetScale();
    1107                 : 
    1108               0 :   nsTArray<float> dxList, dyList;
    1109               0 :   GetEffectiveDxDy(strLength, dxList, dyList);
    1110                 : 
    1111               0 :   PRUint32 dxcount = NS_MIN(dxList.Length(), strLength);
    1112               0 :   if (dxcount > 0) {
    1113               0 :     mPosition.x += dxList[0] * pathScale;
    1114                 :   }
    1115               0 :   for (PRUint32 i = 0; i < dxcount; i++) {
    1116               0 :     aPosition->x += dxList[i] * pathScale;
    1117                 :   }
    1118               0 :   PRUint32 dycount = NS_MIN(dyList.Length(), strLength);
    1119               0 :   if (dycount > 0) {
    1120               0 :     mPosition.y += dyList[0]* pathScale;
    1121                 :   }
    1122               0 :   for (PRUint32 i = 0; i < dycount; i++) {
    1123               0 :     aPosition->y += dyList[i] * pathScale;
    1124                 :   }
    1125                 : }
    1126                 : 
    1127                 : nsresult
    1128               0 : nsSVGGlyphFrame::GetStartPositionOfChar(PRUint32 charnum,
    1129                 :                                         nsIDOMSVGPoint **_retval)
    1130                 : {
    1131               0 :   *_retval = nsnull;
    1132                 : 
    1133               0 :   CharacterIterator iter(this, false);
    1134               0 :   if (!iter.AdvanceToCharacter(charnum))
    1135               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1136                 : 
    1137               0 :   NS_ADDREF(*_retval = new DOMSVGPoint(iter.GetPositionData().pos));
    1138               0 :   return NS_OK;
    1139                 : }
    1140                 : 
    1141                 : nsresult
    1142               0 : nsSVGGlyphFrame::GetEndPositionOfChar(PRUint32 charnum,
    1143                 :                                       nsIDOMSVGPoint **_retval)
    1144                 : {
    1145               0 :   *_retval = nsnull;
    1146                 : 
    1147               0 :   CharacterIterator iter(this, false);
    1148               0 :   if (!iter.AdvanceToCharacter(charnum))
    1149               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1150                 : 
    1151               0 :   nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
    1152               0 :   iter.SetupForMetrics(tmpCtx);
    1153               0 :   tmpCtx->MoveTo(gfxPoint(mTextRun->GetAdvanceWidth(charnum, 1, nsnull), 0));
    1154               0 :   tmpCtx->IdentityMatrix();
    1155               0 :   NS_ADDREF(*_retval = new DOMSVGPoint(tmpCtx->CurrentPoint()));
    1156               0 :   return NS_OK;
    1157                 : }
    1158                 : 
    1159                 : nsresult
    1160               0 : nsSVGGlyphFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
    1161                 : {
    1162               0 :   *_retval = nsnull;
    1163                 : 
    1164               0 :   CharacterIterator iter(this, false);
    1165               0 :   if (!iter.AdvanceToCharacter(0))
    1166               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1167                 : 
    1168               0 :   PRUint32 start = charnum, limit = charnum + 1;
    1169               0 :   while (start > 0 && !mTextRun->IsClusterStart(start)) {
    1170               0 :     --start;
    1171                 :   }
    1172               0 :   while (limit < mTextRun->GetLength() && !mTextRun->IsClusterStart(limit)) {
    1173               0 :     ++limit;
    1174                 :   }
    1175                 : 
    1176               0 :   if (start > 0 && !iter.AdvanceToCharacter(start))
    1177               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1178                 : 
    1179                 :   gfxTextRun::Metrics metrics =
    1180                 :     mTextRun->MeasureText(start, limit - start, gfxFont::LOOSE_INK_EXTENTS,
    1181               0 :                           nsnull, nsnull);
    1182                 : 
    1183               0 :   nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
    1184               0 :   iter.SetupForMetrics(tmpCtx);
    1185                 :   tmpCtx->Rectangle(gfxRect(0, -metrics.mAscent,
    1186                 :                             metrics.mAdvanceWidth,
    1187               0 :                             metrics.mAscent + metrics.mDescent));
    1188               0 :   tmpCtx->IdentityMatrix();
    1189               0 :   return NS_NewSVGRect(_retval, tmpCtx->GetUserPathExtent());
    1190                 : }
    1191                 : 
    1192                 : nsresult
    1193               0 : nsSVGGlyphFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
    1194                 : {
    1195               0 :   CharacterIterator iter(this, false);
    1196               0 :   if (!iter.AdvanceToCharacter(charnum))
    1197               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1198                 : 
    1199               0 :   CharacterPosition pos = iter.GetPositionData();
    1200               0 :   if (!pos.draw)
    1201               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1202                 : 
    1203               0 :   const gfxFloat radPerDeg = M_PI/180.0;
    1204               0 :   *_retval = float(pos.angle / radPerDeg);
    1205               0 :   return NS_OK;
    1206                 : }
    1207                 : 
    1208                 : float
    1209               0 : nsSVGGlyphFrame::GetAdvance(bool aForceGlobalTransform)
    1210                 : {
    1211                 :   float drawScale, metricsScale;
    1212               0 :   if (!EnsureTextRun(&drawScale, &metricsScale, aForceGlobalTransform))
    1213               0 :     return 0.0f;
    1214                 : 
    1215               0 :   return GetSubStringAdvance(0, mTextRun->GetLength(), metricsScale);
    1216                 : }
    1217                 : 
    1218                 : nsSVGTextPathFrame*
    1219               0 : nsSVGGlyphFrame::FindTextPathParent()
    1220                 : {
    1221                 :   /* check if we're the child of a textPath */
    1222               0 :   for (nsIFrame *frame = GetParent();
    1223                 :        frame != nsnull;
    1224                 :        frame = frame->GetParent()) {
    1225               0 :     nsIAtom* type = frame->GetType();
    1226               0 :     if (type == nsGkAtoms::svgTextPathFrame) {
    1227               0 :       return static_cast<nsSVGTextPathFrame*>(frame);
    1228               0 :     } else if (type == nsGkAtoms::svgTextFrame)
    1229               0 :       return nsnull;
    1230                 :   }
    1231               0 :   return nsnull;
    1232                 : }
    1233                 : 
    1234                 : bool
    1235               0 : nsSVGGlyphFrame::IsStartOfChunk()
    1236                 : {
    1237                 :   // this fragment is a chunk if it has a corresponding absolute
    1238                 :   // position adjustment in an ancestors' x or y array. (At the moment
    1239                 :   // we don't map the full arrays, but only the first elements.)
    1240                 : 
    1241               0 :   return false;
    1242                 : }
    1243                 : 
    1244                 : void
    1245               0 : nsSVGGlyphFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
    1246                 : {
    1247               0 :   static_cast<nsSVGTextContainerFrame *>(mParent)->GetXY(aX, aY);
    1248               0 : }
    1249                 : 
    1250                 : void
    1251               0 : nsSVGGlyphFrame::SetStartIndex(PRUint32 aStartIndex)
    1252                 : {
    1253               0 :   mStartIndex = aStartIndex;
    1254               0 : }
    1255                 : 
    1256                 : void
    1257               0 : nsSVGGlyphFrame::GetEffectiveXY(PRInt32 strLength, nsTArray<float> &aX, nsTArray<float> &aY)
    1258                 : {
    1259               0 :   nsTArray<float> x, y;
    1260               0 :   static_cast<nsSVGTextContainerFrame *>(mParent)->GetEffectiveXY(x, y);
    1261                 : 
    1262               0 :   PRInt32 xCount = NS_MAX((PRInt32)(x.Length() - mStartIndex), 0);
    1263               0 :   xCount = NS_MIN(xCount, strLength);
    1264               0 :   aX.AppendElements(x.Elements() + mStartIndex, xCount);
    1265                 : 
    1266               0 :   PRInt32 yCount = NS_MAX((PRInt32)(y.Length() - mStartIndex), 0);
    1267               0 :   yCount = NS_MIN(yCount, strLength);
    1268               0 :   aY.AppendElements(y.Elements() + mStartIndex, yCount);
    1269               0 : }
    1270                 : 
    1271                 : void
    1272               0 : nsSVGGlyphFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
    1273                 : {
    1274               0 :   static_cast<nsSVGTextContainerFrame *>(mParent)->GetDxDy(aDx, aDy);
    1275               0 : }
    1276                 : 
    1277                 : void
    1278               0 : nsSVGGlyphFrame::GetEffectiveDxDy(PRInt32 strLength, nsTArray<float> &aDx, nsTArray<float> &aDy)
    1279                 : {
    1280               0 :   nsTArray<float> dx, dy;
    1281               0 :   static_cast<nsSVGTextContainerFrame *>(mParent)->GetEffectiveDxDy(dx, dy);
    1282                 : 
    1283               0 :   PRInt32 dxCount = NS_MAX((PRInt32)(dx.Length() - mStartIndex), 0);
    1284               0 :   dxCount = NS_MIN(dxCount, strLength);
    1285               0 :   aDx.AppendElements(dx.Elements() + mStartIndex, dxCount);
    1286                 : 
    1287               0 :   PRInt32 dyCount = NS_MAX((PRInt32)(dy.Length() - mStartIndex), 0);
    1288               0 :   dyCount = NS_MIN(dyCount, strLength);
    1289               0 :   aDy.AppendElements(dy.Elements() + mStartIndex, dyCount);
    1290               0 : }
    1291                 : 
    1292                 : const SVGNumberList*
    1293               0 : nsSVGGlyphFrame::GetRotate()
    1294                 : {
    1295                 :   nsSVGTextContainerFrame *containerFrame;
    1296               0 :   containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
    1297               0 :   if (containerFrame)
    1298               0 :     return containerFrame->GetRotate();
    1299               0 :   return nsnull;
    1300                 : }
    1301                 : 
    1302                 : void
    1303               0 : nsSVGGlyphFrame::GetEffectiveRotate(PRInt32 strLength, nsTArray<float> &aRotate)
    1304                 : {
    1305               0 :   nsTArray<float> rotate;
    1306               0 :   static_cast<nsSVGTextContainerFrame *>(mParent)->GetEffectiveRotate(rotate);
    1307                 : 
    1308               0 :   PRInt32 rotateCount = NS_MAX((PRInt32)(rotate.Length() - mStartIndex), 0);
    1309               0 :   rotateCount = NS_MIN(rotateCount, strLength);
    1310               0 :   if (rotateCount > 0) {
    1311               0 :     aRotate.AppendElements(rotate.Elements() + mStartIndex, rotateCount);
    1312               0 :   } else if (!rotate.IsEmpty()) {
    1313                 :     // rotate is applied for extra characters too
    1314               0 :     aRotate.AppendElement(rotate[rotate.Length() - 1]);
    1315                 :   }
    1316               0 : }
    1317                 : 
    1318                 : PRUint16
    1319               0 : nsSVGGlyphFrame::GetTextAnchor()
    1320                 : {
    1321               0 :   return GetStyleSVG()->mTextAnchor;
    1322                 : }
    1323                 : 
    1324                 : bool
    1325               0 : nsSVGGlyphFrame::IsAbsolutelyPositioned()
    1326                 : {
    1327               0 :   bool hasTextPathAncestor = false;
    1328               0 :   for (nsIFrame *frame = GetParent();
    1329                 :        frame != nsnull;
    1330                 :        frame = frame->GetParent()) {
    1331                 : 
    1332                 :     // at the start of a 'text' element
    1333                 :     // at the start of each 'textPath' element
    1334               0 :     if (frame->GetType() == nsGkAtoms::svgTextPathFrame) {
    1335               0 :       hasTextPathAncestor = true;
    1336                 :     }
    1337               0 :     if ((frame->GetType() == nsGkAtoms::svgTextFrame ||
    1338               0 :          frame->GetType() == nsGkAtoms::svgTextPathFrame) &&
    1339               0 :         frame->GetFirstPrincipalChild() == this) {
    1340               0 :         return true;
    1341                 :     }
    1342                 : 
    1343               0 :     if (frame->GetType() == nsGkAtoms::svgTextFrame)
    1344               0 :       break;
    1345                 :   }
    1346                 : 
    1347                 :   // for each character within a 'text', 'tspan', 'tref' and 'altGlyph' element
    1348                 :   // which has an x or y attribute value assigned to it explicitly
    1349               0 :   nsTArray<float> x, y;
    1350               0 :   GetEffectiveXY(GetNumberOfChars(), x, y);
    1351                 :   // Note: the y of descendants of textPath has no effect in horizontal writing
    1352               0 :   return (!x.IsEmpty() || (!hasTextPathAncestor && !y.IsEmpty()));
    1353                 : }
    1354                 : 
    1355                 : 
    1356                 : //----------------------------------------------------------------------
    1357                 : // nsISVGGlyphFragmentNode interface:
    1358                 : 
    1359                 : PRUint32
    1360               0 : nsSVGGlyphFrame::GetNumberOfChars()
    1361                 : {
    1362               0 :   if (mCompressWhitespace) {
    1363               0 :     nsAutoString text;
    1364               0 :     GetCharacterData(text);
    1365               0 :     return text.Length();
    1366                 :   }
    1367                 : 
    1368               0 :   return mContent->TextLength();
    1369                 : }
    1370                 : 
    1371                 : float
    1372               0 : nsSVGGlyphFrame::GetComputedTextLength()
    1373                 : {
    1374               0 :   return GetAdvance(false);
    1375                 : }
    1376                 : 
    1377                 : float
    1378               0 : nsSVGGlyphFrame::GetSubStringLength(PRUint32 charnum, PRUint32 fragmentChars)
    1379                 : {
    1380                 :   float drawScale, metricsScale;
    1381               0 :   if (!EnsureTextRun(&drawScale, &metricsScale, false))
    1382               0 :     return 0.0f;
    1383                 : 
    1384               0 :   return GetSubStringAdvance(charnum, fragmentChars, metricsScale);
    1385                 : }
    1386                 : 
    1387                 : PRInt32
    1388               0 : nsSVGGlyphFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
    1389                 : {
    1390                 :   float xPos, yPos;
    1391               0 :   point->GetX(&xPos);
    1392               0 :   point->GetY(&yPos);
    1393                 : 
    1394               0 :   nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
    1395               0 :   CharacterIterator iter(this, false);
    1396                 : 
    1397                 :   PRUint32 i;
    1398               0 :   PRInt32 last = -1;
    1399               0 :   gfxPoint pt(xPos, yPos);
    1400               0 :   while ((i = iter.NextCluster()) != iter.InvalidCluster()) {
    1401               0 :     PRUint32 limit = i + iter.ClusterLength();
    1402                 :     gfxTextRun::Metrics metrics =
    1403                 :       mTextRun->MeasureText(i, limit - i, gfxFont::LOOSE_INK_EXTENTS,
    1404               0 :                             nsnull, nsnull);
    1405                 : 
    1406                 :     // the SVG spec tells us to divide the width of the cluster equally among
    1407                 :     // its chars, so we'll step through the chars, allocating a share of the
    1408                 :     // total advance to each
    1409                 :     PRInt32 current, end, step;
    1410               0 :     if (mTextRun->IsRightToLeft()) {
    1411               0 :       current = limit - 1;
    1412               0 :       end = i - 1;
    1413               0 :       step = -1;
    1414                 :     } else {
    1415               0 :       current = i;
    1416               0 :       end = limit;
    1417               0 :       step = 1;
    1418                 :     }
    1419               0 :     gfxFloat leftEdge = 0.0;
    1420               0 :     gfxFloat width = metrics.mAdvanceWidth / (limit - i);
    1421               0 :     while (current != end) {
    1422               0 :       iter.SetupForMetrics(tmpCtx);
    1423               0 :       tmpCtx->NewPath();
    1424                 :       tmpCtx->Rectangle(gfxRect(leftEdge, -metrics.mAscent,
    1425               0 :                                 width, metrics.mAscent + metrics.mDescent));
    1426               0 :       tmpCtx->IdentityMatrix();
    1427               0 :       if (tmpCtx->PointInFill(pt)) {
    1428                 :         // Can't return yet; if there's glyph overlap, the last character
    1429                 :         // to be rendered wins, so we still have to check the rest...
    1430               0 :         last = current;
    1431               0 :         break; // ...but we don't need to check more slices of this cluster
    1432                 :       }
    1433               0 :       current += step;
    1434               0 :       leftEdge += width;
    1435                 :     }
    1436                 :   }
    1437                 : 
    1438               0 :   return last;
    1439                 : }
    1440                 : 
    1441                 : NS_IMETHODIMP_(nsSVGGlyphFrame *)
    1442               0 : nsSVGGlyphFrame::GetFirstGlyphFrame()
    1443                 : {
    1444               0 :   nsSVGGlyphFrame *frame = this;
    1445               0 :   while (frame && frame->IsTextEmpty()) {
    1446               0 :     frame = frame->GetNextGlyphFrame();
    1447                 :   }
    1448               0 :   return frame;
    1449                 : }
    1450                 : 
    1451                 : NS_IMETHODIMP_(nsSVGGlyphFrame *)
    1452               0 : nsSVGGlyphFrame::GetNextGlyphFrame()
    1453                 : {
    1454               0 :   nsIFrame* sibling = GetNextSibling();
    1455               0 :   while (sibling) {
    1456               0 :     nsISVGGlyphFragmentNode *node = do_QueryFrame(sibling);
    1457               0 :     if (node)
    1458               0 :       return node->GetFirstGlyphFrame();
    1459               0 :     sibling = sibling->GetNextSibling();
    1460                 :   }
    1461                 : 
    1462                 :   // no more siblings. go back up the tree.
    1463                 :   
    1464               0 :   NS_ASSERTION(GetParent(), "null parent");
    1465               0 :   nsISVGGlyphFragmentNode *node = do_QueryFrame(GetParent());
    1466               0 :   return node ? node->GetNextGlyphFrame() : nsnull;
    1467                 : }
    1468                 : 
    1469                 : bool
    1470               0 : nsSVGGlyphFrame::EndsWithWhitespace() const
    1471                 : {
    1472               0 :   const nsTextFragment* text = mContent->GetText();
    1473               0 :   NS_ABORT_IF_FALSE(text->GetLength() > 0, "text expected");
    1474                 : 
    1475               0 :   return NS_IsAsciiWhitespace(text->CharAt(text->GetLength() - 1));
    1476                 : }
    1477                 : 
    1478                 : bool
    1479               0 : nsSVGGlyphFrame::IsAllWhitespace() const
    1480                 : {
    1481               0 :   const nsTextFragment* text = mContent->GetText();
    1482                 : 
    1483               0 :   if (text->Is2b())
    1484               0 :     return false;
    1485               0 :   PRInt32 len = text->GetLength();
    1486               0 :   const char* str = text->Get1b();
    1487               0 :   for (PRInt32 i = 0; i < len; ++i) {
    1488               0 :     if (!NS_IsAsciiWhitespace(str[i]))
    1489               0 :       return false;
    1490                 :   }
    1491               0 :   return true;
    1492                 : }
    1493                 : 
    1494                 : //----------------------------------------------------------------------
    1495                 : //
    1496                 : 
    1497                 : void
    1498               0 : nsSVGGlyphFrame::NotifyGlyphMetricsChange()
    1499                 : {
    1500                 :   nsSVGTextContainerFrame *containerFrame =
    1501               0 :     static_cast<nsSVGTextContainerFrame *>(mParent);
    1502               0 :   if (containerFrame)
    1503               0 :     containerFrame->NotifyGlyphMetricsChange();
    1504               0 : }
    1505                 : 
    1506                 : void
    1507               0 : nsSVGGlyphFrame::SetupGlobalTransform(gfxContext *aContext)
    1508                 : {
    1509               0 :   gfxMatrix matrix = GetCanvasTM();
    1510               0 :   if (!matrix.IsSingular()) {
    1511               0 :     aContext->Multiply(matrix);
    1512                 :   }
    1513               0 : }
    1514                 : 
    1515                 : void
    1516               0 : nsSVGGlyphFrame::ClearTextRun()
    1517                 : {
    1518               0 :   delete mTextRun;
    1519               0 :   mTextRun = nsnull;
    1520               0 : }
    1521                 : 
    1522                 : bool
    1523               0 : nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
    1524                 :                                bool aForceGlobalTransform)
    1525                 : {
    1526                 :   // Compute the size at which the text should render (excluding the CTM)
    1527               0 :   const nsStyleFont* fontData = GetStyleFont();
    1528                 :   // Since SVG has its own scaling, we really don't want
    1529                 :   // fonts in SVG to respond to the browser's "TextZoom"
    1530                 :   // (Ctrl++,Ctrl+-)
    1531               0 :   nsPresContext *presContext = PresContext();
    1532               0 :   float textZoom = presContext->TextZoom();
    1533                 :   double size =
    1534               0 :     presContext->AppUnitsToFloatCSSPixels(fontData->mSize) / textZoom;
    1535                 : 
    1536                 :   double textRunSize;
    1537               0 :   if (mTextRun) {
    1538               0 :     textRunSize = mTextRun->GetFontGroup()->GetStyle()->size;
    1539                 :   } else {
    1540               0 :     nsAutoString text;
    1541               0 :     if (!GetCharacterData(text))
    1542               0 :       return false;
    1543                 : 
    1544               0 :     nsAutoString visualText;
    1545                 :       
    1546                 :     /*
    1547                 :      * XXXsmontagu: The SVG spec says:
    1548                 :      *
    1549                 :      * http://www.w3.org/TR/SVG11/text.html#DirectionProperty
    1550                 :      *  "For the 'direction' property to have any effect, the 'unicode-bidi'
    1551                 :      *   property's value must be embed or bidi-override."
    1552                 :      *
    1553                 :      * The SVGTiny spec, on the other hand, says 
    1554                 :      *
    1555                 :      * http://www.w3.org/TR/SVGTiny12/text.html#DirectionProperty
    1556                 :      *  "For the 'direction' property to have any effect on an element that 
    1557                 :      *   does not by itself establish a new text chunk (such as the 'tspan'
    1558                 :      *   element in SVG 1.2 Tiny), the 'unicode-bidi' property's value must
    1559                 :      *   be embed or bidi-override."
    1560                 :      *
    1561                 :      * Note that this is different from HTML/CSS, where setting the 'dir'
    1562                 :      *  attribute on an inline element automatically sets unicode-bidi: embed
    1563                 :      *
    1564                 :      * Our current implementation of bidi in SVG does not distinguish between
    1565                 :      * different text elements, but treats every text container frame as a
    1566                 :      * new text chunk, so we always set the base direction according to the
    1567                 :      * direction property
    1568                 :      *
    1569                 :      * See also XXXsmontagu comments in nsSVGTextFrame::UpdateGlyphPositioning
    1570                 :      */
    1571                 :     
    1572                 :     // Get the unicodeBidi property from the parent, because it doesn't
    1573                 :     // inherit
    1574               0 :     bool bidiOverride = !!(mParent->GetStyleTextReset()->mUnicodeBidi &
    1575               0 :                            NS_STYLE_UNICODE_BIDI_OVERRIDE);
    1576                 :     nsBidiLevel baseDirection =
    1577               0 :       GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL ?
    1578               0 :         NSBIDI_RTL : NSBIDI_LTR;
    1579                 :     nsBidiPresUtils::CopyLogicalToVisual(text, visualText,
    1580               0 :                                          baseDirection, bidiOverride);
    1581               0 :     if (!visualText.IsEmpty()) {
    1582               0 :       text = visualText;
    1583                 :     }
    1584                 : 
    1585               0 :     gfxMatrix m;
    1586               0 :     if (aForceGlobalTransform ||
    1587               0 :         !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
    1588               0 :       m = GetCanvasTM();
    1589               0 :       if (m.IsSingular())
    1590               0 :         return false;
    1591                 :     }
    1592                 : 
    1593                 :     // The context scale is the ratio of the length of the transformed
    1594                 :     // diagonal vector (1,1) to the length of the untransformed diagonal
    1595                 :     // (which is sqrt(2)).
    1596               0 :     gfxPoint p = m.Transform(gfxPoint(1, 1)) - m.Transform(gfxPoint(0, 0));
    1597               0 :     double contextScale = nsSVGUtils::ComputeNormalizedHypotenuse(p.x, p.y);
    1598                 : 
    1599               0 :     if (GetStyleSVG()->mTextRendering ==
    1600                 :         NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION) {
    1601               0 :       textRunSize = PRECISE_SIZE;
    1602                 :     } else {
    1603               0 :       textRunSize = size*contextScale;
    1604               0 :       textRunSize = NS_MAX(textRunSize, double(CLAMP_MIN_SIZE));
    1605               0 :       textRunSize = NS_MIN(textRunSize, double(CLAMP_MAX_SIZE));
    1606                 :     }
    1607                 : 
    1608               0 :     const nsFont& font = fontData->mFont;
    1609               0 :     bool printerFont = (presContext->Type() == nsPresContext::eContext_PrintPreview ||
    1610               0 :                           presContext->Type() == nsPresContext::eContext_Print);
    1611                 :     gfxFontStyle fontStyle(font.style, font.weight, font.stretch, textRunSize,
    1612               0 :                            mStyleContext->GetStyleFont()->mLanguage,
    1613                 :                            font.sizeAdjust, font.systemFont,
    1614                 :                            printerFont,
    1615                 :                            font.featureSettings,
    1616               0 :                            font.languageOverride);
    1617                 : 
    1618                 :     nsRefPtr<gfxFontGroup> fontGroup =
    1619               0 :       gfxPlatform::GetPlatform()->CreateFontGroup(font.name, &fontStyle, presContext->GetUserFontSet());
    1620                 : 
    1621                 :     PRUint32 flags = gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX |
    1622               0 :       GetTextRunFlags(text.Length()) |
    1623               0 :       nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(), GetStyleText(), GetStyleFont());
    1624                 : 
    1625                 :     // XXX We should use a better surface here! But then we'd have to
    1626                 :     // change things so we can ensure we always have the "right" sort of
    1627                 :     // surface available, by creating the textrun only at the right times
    1628               0 :     nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
    1629               0 :     tmpCtx->SetMatrix(m);
    1630                 : 
    1631                 :     // Use only the fonts' internal word caching here.
    1632                 :     // We don't cache the textrun globally because we create
    1633                 :     // a new fontgroup every time. Even if we cached fontgroups, we
    1634                 :     // might render at very many different sizes (e.g. during zoom
    1635                 :     // animation) and caching a textrun for each such size would be bad.
    1636                 :     gfxTextRunFactory::Parameters params = {
    1637               0 :         tmpCtx, nsnull, nsnull, nsnull, 0, GetTextRunUnitsFactor()
    1638               0 :     };
    1639                 :     mTextRun =
    1640               0 :       fontGroup->MakeTextRun(text.get(), text.Length(), &params, flags);
    1641               0 :     if (!mTextRun)
    1642               0 :       return false;
    1643                 :   }
    1644                 : 
    1645               0 :   *aDrawScale = float(size/textRunSize);
    1646               0 :   *aMetricsScale = (*aDrawScale)/GetTextRunUnitsFactor();
    1647               0 :   return true;
    1648                 : }
    1649                 : 
    1650                 : //----------------------------------------------------------------------
    1651                 : // helper class
    1652                 : 
    1653               0 : CharacterIterator::CharacterIterator(nsSVGGlyphFrame *aSource,
    1654                 :         bool aForceGlobalTransform)
    1655                 :   : mSource(aSource)
    1656                 :   , mCurrentAdvance(0)
    1657                 :   , mCurrentChar(PRUint32(-1))
    1658               0 :   , mInError(false)
    1659                 : {
    1660               0 :   if (!aSource->EnsureTextRun(&mDrawScale, &mMetricsScale,
    1661               0 :                               aForceGlobalTransform) ||
    1662               0 :       !aSource->GetCharacterPositions(&mPositions, mMetricsScale)) {
    1663               0 :     mInError = true;
    1664                 :   }
    1665               0 : }
    1666                 : 
    1667                 : bool
    1668               0 : CharacterIterator::SetupForDirectTextRun(gfxContext *aContext, float aScale)
    1669                 : {
    1670               0 :   if (!mPositions.IsEmpty() || mInError)
    1671               0 :     return false;
    1672               0 :   aContext->SetMatrix(mInitialMatrix);
    1673               0 :   aContext->Translate(mSource->mPosition);
    1674               0 :   aContext->Scale(aScale, aScale);
    1675               0 :   return true;
    1676                 : }
    1677                 : 
    1678                 : PRUint32
    1679               0 : CharacterIterator::NextCluster()
    1680                 : {
    1681               0 :   if (mInError) {
    1682                 : #ifdef DEBUG
    1683               0 :     if (mCurrentChar != InvalidCluster()) {
    1684               0 :       bool pastEnd = (mCurrentChar >= mSource->mTextRun->GetLength());
    1685               0 :       NS_ABORT_IF_FALSE(pastEnd, "Past the end of CharacterIterator. Missing Reset?");
    1686                 :     }
    1687                 : #endif
    1688               0 :     return InvalidCluster();
    1689                 :   }
    1690                 : 
    1691               0 :   while (true) {
    1692               0 :     if (mCurrentChar != InvalidCluster() &&
    1693               0 :         (mPositions.IsEmpty() || mPositions[mCurrentChar].draw)) {
    1694                 :       mCurrentAdvance +=
    1695               0 :         mSource->mTextRun->GetAdvanceWidth(mCurrentChar, 1, nsnull);
    1696                 :     }
    1697               0 :     ++mCurrentChar;
    1698                 : 
    1699               0 :     if (mCurrentChar >= mSource->mTextRun->GetLength()) {
    1700               0 :       mInError = true;
    1701               0 :       return InvalidCluster();
    1702                 :     }
    1703                 : 
    1704               0 :     if (mSource->mTextRun->IsClusterStart(mCurrentChar) &&
    1705               0 :         (mPositions.IsEmpty() || mPositions[mCurrentChar].draw)) {
    1706               0 :       return mCurrentChar;
    1707                 :     }
    1708                 :   }
    1709                 : }
    1710                 : 
    1711                 : PRUint32
    1712               0 : CharacterIterator::ClusterLength()
    1713                 : {
    1714               0 :   if (mInError) {
    1715               0 :     return 0;
    1716                 :   }
    1717                 : 
    1718               0 :   PRUint32 i = mCurrentChar;
    1719               0 :   while (++i < mSource->mTextRun->GetLength()) {
    1720               0 :     if (mSource->mTextRun->IsClusterStart(i)) {
    1721               0 :       break;
    1722                 :     }
    1723                 :   }
    1724               0 :   return i - mCurrentChar;
    1725                 : }
    1726                 : 
    1727                 : bool
    1728               0 : CharacterIterator::AdvanceToCharacter(PRUint32 aIndex)
    1729                 : {
    1730               0 :   while (NextCluster() != InvalidCluster()) {
    1731               0 :     if (mCurrentChar == aIndex)
    1732               0 :       return true;
    1733                 :   }
    1734               0 :   return false;
    1735                 : }
    1736                 : 
    1737                 : void
    1738               0 : CharacterIterator::SetupFor(gfxContext *aContext, float aScale)
    1739                 : {
    1740               0 :   NS_ASSERTION(!mInError, "We should not have reached here");
    1741                 : 
    1742               0 :   aContext->SetMatrix(mInitialMatrix);
    1743               0 :   if (mPositions.IsEmpty()) {
    1744               0 :     aContext->Translate(mSource->mPosition);
    1745               0 :     aContext->Scale(aScale, aScale);
    1746               0 :     aContext->Translate(gfxPoint(mCurrentAdvance, 0));
    1747                 :   } else {
    1748               0 :     aContext->Translate(mPositions[mCurrentChar].pos);
    1749               0 :     aContext->Rotate(mPositions[mCurrentChar].angle);
    1750               0 :     aContext->Scale(aScale, aScale);
    1751                 :   }
    1752               0 : }
    1753                 : 
    1754                 : CharacterPosition
    1755               0 : CharacterIterator::GetPositionData()
    1756                 : {
    1757               0 :   if (!mPositions.IsEmpty())
    1758               0 :     return mPositions[mCurrentChar];
    1759                 : 
    1760               0 :   gfxFloat advance = mCurrentAdvance * mMetricsScale;
    1761                 :   CharacterPosition cp =
    1762               0 :     { mSource->mPosition + gfxPoint(advance, 0), 0, true };
    1763               0 :   return cp;
    1764                 : }

Generated by: LCOV version 1.7