Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / svg / base / src / nsSVGGlyphFrame.cpp
blob42656353a23a0a753f7f2e7424cb74f1012a2509
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
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is the Mozilla SVG project.
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.
22 * Contributor(s):
23 * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
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.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsSVGTextFrame.h"
40 #include "nsILookAndFeel.h"
41 #include "nsTextFragment.h"
42 #include "nsSVGUtils.h"
43 #include "nsIDOMSVGLengthList.h"
44 #include "nsIDOMSVGLength.h"
45 #include "nsIDOMSVGRect.h"
46 #include "nsIDOMSVGPoint.h"
47 #include "nsSVGGlyphFrame.h"
48 #include "nsSVGTextPathFrame.h"
49 #include "nsSVGPathElement.h"
50 #include "nsSVGPoint.h"
51 #include "nsSVGRect.h"
52 #include "nsDOMError.h"
53 #include "gfxContext.h"
54 #include "gfxMatrix.h"
55 #include "gfxPlatform.h"
56 #include "gfxTextRunWordCache.h"
58 struct CharacterPosition {
59 gfxPoint pos;
60 gfxFloat angle;
61 PRBool draw;
64 /**
65 * This is a do-it-all helper class. It supports iterating through the
66 * drawable characters of a string. For each character, it can set up
67 * a graphics context with a transform appropriate for drawing the
68 * character, or a transform appropriate for emitting geometry in the
69 * text metrics coordinate system (which differs from the drawing
70 * coordinate system by a scale factor of AppUnitPerCSSPixels). These
71 * transforms include offsets and rotations of characters along paths, and
72 * the mPosition of the nsSVGGlyphFrame.
74 * This helper also creates the textrun as needed. It supports detecting
75 * the special case when the entire textrun can be drawn or measured
76 * as a unit, and setting the graphics context transform up for that. It
77 * takes care of setting up the global transform if requested. It also
78 * provides direct access to the character path position data for the
79 * DOM APIs that need that.
81 * If an error occurs, for example, a canvas TM is not available because
82 * the element is in a <defs> section, then the CharacterIterator will
83 * behave as if the frame has no drawable characters.
85 * XXX should make this iterate clusters instead
86 * XXX needs RTL love
87 * XXX might want to make AdvanceToCharacter constant time (e.g. by
88 * caching advances and/or the CharacterPosition array across DOM
89 * API calls) to ensure that calling Get*OfChar (etc) for each character
90 * in the text is O(N)
92 class CharacterIterator
94 public:
95 /**
96 * Sets up the iterator so that NextChar will return the first drawable
97 * char.
98 * @param aForceGlobalTransform passed on to EnsureTextRun (see below)
100 CharacterIterator(nsSVGGlyphFrame *aSource, PRBool aForceGlobalTransform);
102 * This matrix will be applied to aContext in the SetupFor methods below,
103 * before any glyph translation/rotation.
105 void SetInitialMatrix(gfxContext *aContext) {
106 mInitialMatrix = aContext->CurrentMatrix();
107 if (mInitialMatrix.IsSingular()) {
108 mInError = PR_TRUE;
112 * Try to set up aContext so we can draw the whole textrun at once.
113 * This applies any global transform requested by SetInitialMatrix,
114 * then applies the positioning of the text. Returns false if drawing
115 * the whole textrun at once is impossible due to individual positioning
116 * and/or rotation of glyphs.
118 PRBool SetupForDirectTextRunDrawing(gfxContext *aContext) {
119 return SetupForDirectTextRun(aContext, mDrawScale);
122 * Try to set up aContext so we can measure the whole textrun at once.
123 * This applies any global transform requested by SetInitialMatrix,
124 * then applies the positioning of the text, then applies a scale
125 * from appunits to device pixels so drawing in appunits works.
126 * Returns false if drawing the whole textrun at once is impossible due
127 * to individual positioning and/or rotation of glyphs.
129 PRBool SetupForDirectTextRunMetrics(gfxContext *aContext) {
130 return SetupForDirectTextRun(aContext, mMetricsScale);
134 * Returns the index of the next char in the string that should be
135 * drawn, or -1 if there is no such character.
137 PRInt32 NextChar();
139 * Repeated calls NextChar until it returns aIndex (i.e. aIndex is the
140 * current drawable character). Returns false if that never happens
141 * (because aIndex is before or equal to the current character, or
142 * out of bounds, or not drawable).
144 PRBool AdvanceToCharacter(PRInt32 aIndex);
147 * Set up aContext for glyph drawing. This applies any global transform
148 * requested by SetInitialMatrix, then applies any positioning and
149 * rotation for the current character.
151 void SetupForDrawing(gfxContext *aContext) {
152 return SetupFor(aContext, mDrawScale);
155 * Set up aContext for glyph measuring. This applies any global transform
156 * requested by SetInitialMatrix, then applies any positioning and
157 * rotation for the current character, then applies a scale from appunits
158 * to device pixels so that drawing in appunits sizes works.
160 void SetupForMetrics(gfxContext *aContext) {
161 return SetupFor(aContext, mMetricsScale);
164 * Get the raw position data for the current character.
166 CharacterPosition GetPositionData();
168 private:
169 PRBool SetupForDirectTextRun(gfxContext *aContext, float aScale);
170 void SetupFor(gfxContext *aContext, float aScale);
172 nsSVGGlyphFrame *mSource;
173 nsAutoTArray<CharacterPosition,80> mPositions;
174 gfxMatrix mInitialMatrix;
175 // Textrun advance width from start to mCurrentChar, in appunits
176 gfxFloat mCurrentAdvance;
177 PRInt32 mCurrentChar;
178 float mDrawScale;
179 float mMetricsScale;
180 PRPackedBool mInError;
183 //----------------------------------------------------------------------
184 // Implementation
186 nsIFrame*
187 NS_NewSVGGlyphFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame* parentFrame, nsStyleContext* aContext)
189 NS_ASSERTION(parentFrame, "null parent");
190 nsISVGTextContentMetrics *metrics;
191 CallQueryInterface(parentFrame, &metrics);
192 NS_ASSERTION(metrics, "trying to construct an SVGGlyphFrame for an invalid container");
194 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
195 "trying to construct an SVGGlyphFrame for wrong content element");
197 return new (aPresShell) nsSVGGlyphFrame(aContext);
200 //----------------------------------------------------------------------
201 // nsISupports methods
203 NS_INTERFACE_MAP_BEGIN(nsSVGGlyphFrame)
204 NS_INTERFACE_MAP_ENTRY(nsISVGGlyphFragmentLeaf)
205 NS_INTERFACE_MAP_ENTRY(nsISVGGlyphFragmentNode)
206 NS_INTERFACE_MAP_ENTRY(nsISVGChildFrame)
207 NS_INTERFACE_MAP_END_INHERITING(nsSVGGlyphFrameBase)
209 //----------------------------------------------------------------------
210 // nsIFrame methods
212 NS_IMETHODIMP
213 nsSVGGlyphFrame::CharacterDataChanged(nsPresContext* aPresContext,
214 nsIContent* aChild,
215 PRBool aAppend)
217 ClearTextRun();
218 NotifyGlyphMetricsChange();
220 return NS_OK;
223 // Usable font size range in devpixels / user-units
224 #define CLAMP_MIN_SIZE 8
225 #define CLAMP_MAX_SIZE 200
226 #define PRECISE_SIZE 200
228 /* virtual */ void
229 nsSVGGlyphFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
231 nsSVGGlyphFrameBase::DidSetStyleContext(aOldStyleContext);
233 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
234 ClearTextRun();
235 NotifyGlyphMetricsChange();
239 NS_IMETHODIMP
240 nsSVGGlyphFrame::SetSelected(nsPresContext* aPresContext,
241 nsIDOMRange* aRange,
242 PRBool aSelected,
243 nsSpread aSpread,
244 SelectionType aType)
246 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
247 printf("nsSVGGlyphFrame(%p)::SetSelected()\n", this);
248 #endif
249 // return nsSVGGlyphFrameBase::SetSelected(aPresContext, aRange, aSelected, aSpread, aType);
251 if (aType == nsISelectionController::SELECTION_NORMAL) {
252 // check whether style allows selection
253 PRBool selectable;
254 IsSelectable(&selectable, nsnull);
255 if (!selectable)
256 return NS_OK;
259 if ( aSelected ){
260 mState |= NS_FRAME_SELECTED_CONTENT;
262 else
263 mState &= ~NS_FRAME_SELECTED_CONTENT;
265 nsSVGUtils::UpdateGraphic(this);
267 return NS_OK;
270 NS_IMETHODIMP
271 nsSVGGlyphFrame::GetSelected(PRBool *aSelected) const
273 nsresult rv = nsSVGGlyphFrameBase::GetSelected(aSelected);
274 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
275 printf("nsSVGGlyphFrame(%p)::GetSelected()=%d\n", this, *aSelected);
276 #endif
277 return rv;
280 NS_IMETHODIMP
281 nsSVGGlyphFrame::IsSelectable(PRBool* aIsSelectable,
282 PRUint8* aSelectStyle) const
284 nsresult rv = nsSVGGlyphFrameBase::IsSelectable(aIsSelectable, aSelectStyle);
285 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
286 printf("nsSVGGlyphFrame(%p)::IsSelectable()=(%d,%d)\n", this, *aIsSelectable, aSelectStyle);
287 #endif
288 return rv;
291 nsIAtom *
292 nsSVGGlyphFrame::GetType() const
294 return nsGkAtoms::svgGlyphFrame;
297 //----------------------------------------------------------------------
298 // nsISVGChildFrame methods
300 NS_IMETHODIMP
301 nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext,
302 const nsIntRect *aDirtyRect)
304 if (!GetStyleVisibility()->IsVisible())
305 return NS_OK;
307 gfxContext *gfx = aContext->GetGfxContext();
308 PRUint16 renderMode = aContext->GetRenderMode();
310 if (renderMode != nsSVGRenderState::NORMAL) {
312 gfxMatrix matrix = gfx->CurrentMatrix();
313 SetupGlobalTransform(gfx);
315 CharacterIterator iter(this, PR_TRUE);
316 iter.SetInitialMatrix(gfx);
318 if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD)
319 gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
320 else
321 gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
323 if (renderMode == nsSVGRenderState::CLIP_MASK) {
324 gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
325 gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
326 FillCharacters(&iter, gfx);
327 } else {
328 AddCharactersToPath(&iter, gfx);
331 gfx->SetMatrix(matrix);
332 return NS_OK;
335 // We are adding patterns or gradients to the context. Save
336 // it so we don't leak them into the next object we draw
337 gfx->Save();
338 SetupGlobalTransform(gfx);
340 if (SetupCairoFill(gfx)) {
341 gfxMatrix matrix = gfx->CurrentMatrix();
342 CharacterIterator iter(this, PR_TRUE);
343 iter.SetInitialMatrix(gfx);
345 FillCharacters(&iter, gfx);
346 gfx->SetMatrix(matrix);
349 if (SetupCairoStroke(gfx)) {
350 // SetupCairoStroke will clear mTextRun whenever
351 // there is a pattern or gradient on the text
352 CharacterIterator iter(this, PR_TRUE);
353 iter.SetInitialMatrix(gfx);
355 gfx->NewPath();
356 AddCharactersToPath(&iter, gfx);
357 gfx->Stroke();
358 // We need to clear the context's path so state doesn't leak
359 // out. See bug 337753.
360 gfx->NewPath();
362 gfx->Restore();
364 return NS_OK;
367 NS_IMETHODIMP_(nsIFrame*)
368 nsSVGGlyphFrame::GetFrameForPoint(const nsPoint &aPoint)
370 #ifdef DEBUG
371 //printf("nsSVGGlyphFrame(%p)::GetFrameForPoint\n", this);
372 #endif
374 if (!mRect.Contains(aPoint))
375 return nsnull;
377 PRBool events = PR_FALSE;
378 switch (GetStyleSVG()->mPointerEvents) {
379 case NS_STYLE_POINTER_EVENTS_NONE:
380 break;
381 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
382 if (GetStyleVisibility()->IsVisible() &&
383 (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None ||
384 GetStyleSVG()->mStroke.mType != eStyleSVGPaintType_None))
385 events = PR_TRUE;
386 break;
387 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
388 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
389 case NS_STYLE_POINTER_EVENTS_VISIBLE:
390 if (GetStyleVisibility()->IsVisible())
391 events = PR_TRUE;
392 break;
393 case NS_STYLE_POINTER_EVENTS_PAINTED:
394 if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None ||
395 GetStyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
396 events = PR_TRUE;
397 break;
398 case NS_STYLE_POINTER_EVENTS_FILL:
399 case NS_STYLE_POINTER_EVENTS_STROKE:
400 case NS_STYLE_POINTER_EVENTS_ALL:
401 events = PR_TRUE;
402 break;
403 default:
404 NS_ERROR("not reached");
405 break;
408 if (events && ContainsPoint(aPoint))
409 return this;
411 return nsnull;
414 NS_IMETHODIMP_(nsRect)
415 nsSVGGlyphFrame::GetCoveredRegion()
417 return mRect;
420 static gfxContext *
421 MakeTmpCtx() {
422 return new gfxContext(nsSVGUtils::GetThebesComputationalSurface());
425 NS_IMETHODIMP
426 nsSVGGlyphFrame::UpdateCoveredRegion()
428 mRect.Empty();
430 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
431 SetMatrixPropagation(PR_FALSE);
432 CharacterIterator iter(this, PR_TRUE);
434 gfxRect extent = gfxRect(0, 0, 0, 0);
436 if (SetupCairoStrokeGeometry(tmpCtx)) {
437 gfxFloat strokeWidth = tmpCtx->CurrentLineWidth();
438 AddCharactersToPath(&iter, tmpCtx);
439 tmpCtx->SetLineWidth(strokeWidth);
440 tmpCtx->IdentityMatrix();
441 extent = tmpCtx->GetUserStrokeExtent();
443 if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) {
444 AddBoundingBoxesToPath(&iter, tmpCtx);
445 tmpCtx->IdentityMatrix();
446 extent = extent.Union(tmpCtx->GetUserPathExtent());
448 SetMatrixPropagation(PR_TRUE);
450 if (!extent.IsEmpty()) {
451 gfxMatrix matrix;
452 GetGlobalTransform(&matrix);
454 extent = matrix.TransformBounds(extent);
455 mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent);
458 return NS_OK;
461 NS_IMETHODIMP
462 nsSVGGlyphFrame::InitialUpdate()
464 NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
465 "Yikes! We've been called already! Hopefully we weren't called "
466 "before our nsSVGOuterSVGFrame's initial Reflow()!!!");
468 NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
469 "We don't actually participate in reflow");
471 // Do unset the various reflow bits, though.
472 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
473 NS_FRAME_HAS_DIRTY_CHILDREN);
475 return NS_OK;
478 void
479 nsSVGGlyphFrame::NotifySVGChanged(PRUint32 aFlags)
481 if (aFlags & TRANSFORM_CHANGED) {
482 ClearTextRun();
484 if (!(aFlags & SUPPRESS_INVALIDATION)) {
485 nsSVGUtils::UpdateGraphic(this);
489 NS_IMETHODIMP
490 nsSVGGlyphFrame::NotifyRedrawSuspended()
492 // XXX should we cache the fact that redraw is suspended?
493 return NS_OK;
496 NS_IMETHODIMP
497 nsSVGGlyphFrame::NotifyRedrawUnsuspended()
499 if (GetStateBits() & NS_STATE_SVG_DIRTY)
500 nsSVGUtils::UpdateGraphic(this);
502 return NS_OK;
505 void
506 nsSVGGlyphFrame::AddCharactersToPath(CharacterIterator *aIter,
507 gfxContext *aContext)
509 if (aIter->SetupForDirectTextRunDrawing(aContext)) {
510 mTextRun->DrawToPath(aContext, gfxPoint(0, 0), 0,
511 mTextRun->GetLength(), nsnull, nsnull);
512 return;
515 PRInt32 i;
516 while ((i = aIter->NextChar()) >= 0) {
517 aIter->SetupForDrawing(aContext);
518 mTextRun->DrawToPath(aContext, gfxPoint(0, 0), i, 1, nsnull, nsnull);
522 void
523 nsSVGGlyphFrame::AddBoundingBoxesToPath(CharacterIterator *aIter,
524 gfxContext *aContext)
526 if (aIter->SetupForDirectTextRunMetrics(aContext)) {
527 gfxTextRun::Metrics metrics =
528 mTextRun->MeasureText(0, mTextRun->GetLength(),
529 PR_FALSE, nsnull, nsnull);
530 aContext->Rectangle(metrics.mBoundingBox);
531 return;
534 PRInt32 i;
535 while ((i = aIter->NextChar()) >= 0) {
536 aIter->SetupForMetrics(aContext);
537 gfxTextRun::Metrics metrics =
538 mTextRun->MeasureText(i, 1, PR_FALSE, nsnull, nsnull);
539 aContext->Rectangle(metrics.mBoundingBox);
543 void
544 nsSVGGlyphFrame::FillCharacters(CharacterIterator *aIter,
545 gfxContext *aContext)
547 if (aIter->SetupForDirectTextRunDrawing(aContext)) {
548 mTextRun->Draw(aContext, gfxPoint(0, 0), 0,
549 mTextRun->GetLength(), nsnull, nsnull, nsnull);
550 return;
553 PRInt32 i;
554 while ((i = aIter->NextChar()) >= 0) {
555 aIter->SetupForDrawing(aContext);
556 mTextRun->Draw(aContext, gfxPoint(0, 0), i, 1, nsnull, nsnull, nsnull);
560 NS_IMETHODIMP
561 nsSVGGlyphFrame::GetBBox(nsIDOMSVGRect **_retval)
563 *_retval = nsnull;
565 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
566 SetupGlobalTransform(tmpCtx);
567 CharacterIterator iter(this, PR_TRUE);
568 iter.SetInitialMatrix(tmpCtx);
569 AddCharactersToPath(&iter, tmpCtx);
571 tmpCtx->IdentityMatrix();
572 return NS_NewSVGRect(_retval, tmpCtx->GetUserPathExtent());
575 //----------------------------------------------------------------------
576 // nsSVGGeometryFrame methods:
578 /* readonly attribute nsIDOMSVGMatrix canvasTM; */
579 NS_IMETHODIMP
580 nsSVGGlyphFrame::GetCanvasTM(nsIDOMSVGMatrix * *aCTM)
582 NS_ASSERTION(mParent, "null parent");
584 nsSVGContainerFrame *containerFrame = static_cast<nsSVGContainerFrame*>
585 (mParent);
586 nsCOMPtr<nsIDOMSVGMatrix> parentTM = containerFrame->GetCanvasTM();
587 *aCTM = nsnull;
588 parentTM.swap(*aCTM);
589 return NS_OK;
592 //----------------------------------------------------------------------
593 // nsSVGGlyphFrame methods:
595 PRBool
596 nsSVGGlyphFrame::GetCharacterData(nsAString & aCharacterData)
598 nsAutoString characterData;
599 mContent->AppendTextTo(characterData);
601 if (mWhitespaceHandling & COMPRESS_WHITESPACE) {
602 PRBool trimLeadingWhitespace, trimTrailingWhitespace;
603 trimLeadingWhitespace = ((mWhitespaceHandling & TRIM_LEADING_WHITESPACE) != 0);
604 trimTrailingWhitespace = ((mWhitespaceHandling & TRIM_TRAILING_WHITESPACE) != 0);
605 characterData.CompressWhitespace(trimLeadingWhitespace,
606 trimTrailingWhitespace);
607 } else {
608 nsAString::iterator start, end;
609 characterData.BeginWriting(start);
610 characterData.EndWriting(end);
611 while (start != end) {
612 if (NS_IsAsciiWhitespace(*start))
613 *start = ' ';
614 ++start;
617 aCharacterData = characterData;
619 return !characterData.IsEmpty();
622 PRBool
623 nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPositions,
624 float aMetricsScale)
626 NS_ASSERTION(mTextRun->GetLength() > 0, "no text");
628 nsSVGTextPathFrame *textPath = FindTextPathParent();
630 /* we're an ordinary fragment - return */
631 /* XXX: we might want to use this for individual x/y/dx/dy adjustment */
632 if (!textPath)
633 return PR_TRUE;
635 nsRefPtr<gfxFlattenedPath> data = textPath->GetFlattenedPath();
637 /* textPath frame, but invalid target */
638 if (!data)
639 return PR_FALSE;
641 gfxFloat length = data->GetLength();
642 PRUint32 strLength = mTextRun->GetLength();
644 if (!aCharacterPositions->SetLength(strLength))
645 return PR_FALSE;
647 CharacterPosition *cp = aCharacterPositions->Elements();
649 for (PRUint32 k = 0; k < strLength; k++)
650 cp[k].draw = PR_FALSE;
652 gfxFloat x = mPosition.x;
653 for (PRUint32 i = 0; i < strLength; i++) {
654 gfxFloat halfAdvance =
655 mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale / 2.0;
657 /* have we run off the end of the path? */
658 if (x + halfAdvance > length)
659 break;
661 /* check that we've advanced to the start of the path */
662 if (x + halfAdvance >= 0.0) {
663 cp[i].draw = PR_TRUE;
665 // add y (normal)
666 // add rotation
667 // move point back along tangent
668 gfxPoint pt = data->FindPoint(gfxPoint(x + halfAdvance, mPosition.y),
669 &(cp[i].angle));
670 cp[i].pos =
671 pt - gfxPoint(cos(cp[i].angle), sin(cp[i].angle)) * halfAdvance;
673 x += 2 * halfAdvance;
676 return PR_TRUE;
679 //----------------------------------------------------------------------
681 // Utilities for converting from indices in the uncompressed content
682 // element strings to compressed frame string and back:
683 static int
684 CompressIndex(int index, const nsTextFragment*fragment)
686 int ci=0;
687 if (fragment->Is2b()) {
688 const PRUnichar *data=fragment->Get2b();
689 while(*data && index) {
690 if (XP_IS_SPACE_W(*data)){
691 do {
692 ++data;
693 --index;
694 }while(XP_IS_SPACE_W(*data) && index);
696 else {
697 ++data;
698 --index;
700 ++ci;
703 else {
704 const char *data=fragment->Get1b();
705 while(*data && index) {
706 if (XP_IS_SPACE_W(*data)){
707 do {
708 ++data;
709 --index;
710 }while(XP_IS_SPACE_W(*data) && index);
712 else {
713 ++data;
714 --index;
716 ++ci;
720 return ci;
723 static int
724 UncompressIndex(int index, PRBool bRightAffinity, const nsTextFragment*fragment)
726 // XXX
727 return index;
730 nsresult
731 nsSVGGlyphFrame::GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
732 nscolor *foreground, nscolor *background)
734 *foreground = NS_RGB(255,255,255);
735 *background = NS_RGB(0,0,0);
736 *charnum=0;
737 *nchars=0;
739 PRBool hasHighlight =
740 (mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
742 if (!hasHighlight) {
743 NS_ERROR("nsSVGGlyphFrame::GetHighlight() called by renderer when there is no highlight");
744 return NS_ERROR_FAILURE;
747 nsPresContext *presContext = PresContext();
749 // The selection ranges are relative to the uncompressed text in
750 // the content element. We'll need the text fragment:
751 const nsTextFragment *fragment = mContent->GetText();
752 NS_ASSERTION(fragment, "no text");
754 // get the selection details
755 SelectionDetails *details = nsnull;
757 nsCOMPtr<nsFrameSelection> frameSelection;
759 nsCOMPtr<nsISelectionController> controller;
760 GetSelectionController(presContext, getter_AddRefs(controller));
762 if (!controller) {
763 NS_ERROR("no selection controller");
764 return NS_ERROR_FAILURE;
766 frameSelection = do_QueryInterface(controller);
768 if (!frameSelection) {
769 frameSelection = presContext->PresShell()->FrameSelection();
771 if (!frameSelection) {
772 NS_ERROR("no frameselection interface");
773 return NS_ERROR_FAILURE;
776 details = frameSelection->LookUpSelection(
777 mContent, 0, fragment->GetLength(), PR_FALSE
781 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
783 SelectionDetails *dp = details;
784 printf("nsSVGGlyphFrame(%p)::GetHighlight() [\n", this);
785 while (dp) {
786 printf("selection detail: %d(%d)->%d(%d) type %d\n",
787 dp->mStart, CompressIndex(dp->mStart, fragment),
788 dp->mEnd, CompressIndex(dp->mEnd, fragment),
789 dp->mType);
790 dp = dp->mNext;
792 printf("]\n");
795 #endif
797 if (details) {
798 NS_ASSERTION(details->mNext==nsnull, "can't do multiple selection ranges");
800 *charnum=CompressIndex(details->mStart, fragment);
801 *nchars=CompressIndex(details->mEnd, fragment)-*charnum;
803 nsILookAndFeel *look = presContext->LookAndFeel();
805 look->GetColor(nsILookAndFeel::eColor_TextSelectBackground, *background);
806 look->GetColor(nsILookAndFeel::eColor_TextSelectForeground, *foreground);
808 SelectionDetails *dp = details;
809 while ((dp=details->mNext) != nsnull) {
810 delete details;
811 details = dp;
813 delete details;
816 return NS_OK;
820 //----------------------------------------------------------------------
821 // nsISVGGlyphFragmentLeaf interface:
823 NS_IMETHODIMP_(void)
824 nsSVGGlyphFrame::SetGlyphPosition(float x, float y)
826 mPosition.MoveTo(x, y);
827 nsSVGUtils::UpdateGraphic(this);
830 NS_IMETHODIMP
831 nsSVGGlyphFrame::GetStartPositionOfChar(PRUint32 charnum,
832 nsIDOMSVGPoint **_retval)
834 *_retval = nsnull;
836 CharacterIterator iter(this, PR_FALSE);
837 if (!iter.AdvanceToCharacter(charnum))
838 return NS_ERROR_DOM_INDEX_SIZE_ERR;
840 return NS_NewSVGPoint(_retval, iter.GetPositionData().pos);
843 NS_IMETHODIMP
844 nsSVGGlyphFrame::GetEndPositionOfChar(PRUint32 charnum,
845 nsIDOMSVGPoint **_retval)
847 *_retval = nsnull;
849 CharacterIterator iter(this, PR_FALSE);
850 if (!iter.AdvanceToCharacter(charnum))
851 return NS_ERROR_DOM_INDEX_SIZE_ERR;
853 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
854 iter.SetupForMetrics(tmpCtx);
855 tmpCtx->MoveTo(gfxPoint(mTextRun->GetAdvanceWidth(charnum, 1, nsnull), 0));
856 tmpCtx->IdentityMatrix();
857 return NS_NewSVGPoint(_retval, tmpCtx->CurrentPoint());
860 NS_IMETHODIMP
861 nsSVGGlyphFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
863 *_retval = nsnull;
865 CharacterIterator iter(this, PR_FALSE);
866 if (!iter.AdvanceToCharacter(charnum))
867 return NS_ERROR_DOM_INDEX_SIZE_ERR;
869 gfxTextRun::Metrics metrics =
870 mTextRun->MeasureText(charnum, 1, PR_FALSE, nsnull, nsnull);
872 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
873 iter.SetupForMetrics(tmpCtx);
874 tmpCtx->Rectangle(metrics.mBoundingBox);
875 tmpCtx->IdentityMatrix();
876 return NS_NewSVGRect(_retval, tmpCtx->GetUserPathExtent());
879 NS_IMETHODIMP
880 nsSVGGlyphFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
882 CharacterIterator iter(this, PR_FALSE);
883 if (!iter.AdvanceToCharacter(charnum))
884 return NS_ERROR_DOM_INDEX_SIZE_ERR;
886 CharacterPosition pos = iter.GetPositionData();
887 if (!pos.draw)
888 return NS_ERROR_DOM_INDEX_SIZE_ERR;
890 const gfxFloat radPerDeg = M_PI/180.0;
891 *_retval = float(pos.angle / radPerDeg);
892 return NS_OK;
895 NS_IMETHODIMP_(float)
896 nsSVGGlyphFrame::GetBaselineOffset(PRUint16 baselineIdentifier,
897 PRBool aForceGlobalTransform)
899 float drawScale, metricsScale;
901 if (!EnsureTextRun(&drawScale, &metricsScale, aForceGlobalTransform))
902 return 0.0f;
904 gfxTextRun::Metrics metrics =
905 mTextRun->MeasureText(0, mTextRun->GetLength(),
906 PR_FALSE, nsnull, nsnull);
908 gfxFloat baselineAppUnits;
909 switch (baselineIdentifier) {
910 case BASELINE_HANGING:
911 // not really right, but the best we can do with the information provided
912 // FALLTHROUGH
913 case BASELINE_TEXT_BEFORE_EDGE:
914 baselineAppUnits = -metrics.mAscent;
915 break;
916 case BASELINE_TEXT_AFTER_EDGE:
917 baselineAppUnits = metrics.mDescent;
918 break;
919 case BASELINE_CENTRAL:
920 case BASELINE_MIDDLE:
921 baselineAppUnits = -(metrics.mAscent - metrics.mDescent) / 2.0f;
922 break;
923 case BASELINE_ALPHABETIC:
924 default:
925 baselineAppUnits = 0.0;
926 break;
928 return float(baselineAppUnits)*metricsScale;
931 NS_IMETHODIMP_(float)
932 nsSVGGlyphFrame::GetAdvance(PRBool aForceGlobalTransform)
934 float drawScale, metricsScale;
935 if (!EnsureTextRun(&drawScale, &metricsScale, aForceGlobalTransform))
936 return 0.0f;
938 gfxFloat advanceAppUnits =
939 mTextRun->GetAdvanceWidth(0, mTextRun->GetLength(), nsnull);
940 return float(advanceAppUnits)*metricsScale;
943 NS_IMETHODIMP_(nsSVGTextPathFrame*)
944 nsSVGGlyphFrame::FindTextPathParent()
946 /* check if we're the child of a textPath */
947 for (nsIFrame *frame = GetParent();
948 frame != nsnull;
949 frame = frame->GetParent()) {
950 nsIAtom* type = frame->GetType();
951 if (type == nsGkAtoms::svgTextPathFrame) {
952 return static_cast<nsSVGTextPathFrame*>(frame);
953 } else if (type == nsGkAtoms::svgTextFrame)
954 return nsnull;
956 return nsnull;
959 NS_IMETHODIMP_(PRBool)
960 nsSVGGlyphFrame::IsStartOfChunk()
962 // this fragment is a chunk if it has a corresponding absolute
963 // position adjustment in an ancestors' x or y array. (At the moment
964 // we don't map the full arrays, but only the first elements.)
966 return PR_FALSE;
969 NS_IMETHODIMP_(void)
970 nsSVGGlyphFrame::GetAdjustedPosition(/* inout */ float &x, /* inout */ float &y)
974 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
975 nsSVGGlyphFrame::GetX()
977 nsSVGTextContainerFrame *containerFrame;
978 containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
979 if (containerFrame)
980 return containerFrame->GetX();
981 return nsnull;
984 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
985 nsSVGGlyphFrame::GetY()
987 nsSVGTextContainerFrame *containerFrame;
988 containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
989 if (containerFrame)
990 return containerFrame->GetY();
991 return nsnull;
994 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
995 nsSVGGlyphFrame::GetDx()
997 nsSVGTextContainerFrame *containerFrame;
998 containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
999 if (containerFrame)
1000 return containerFrame->GetDx();
1001 return nsnull;
1004 NS_IMETHODIMP_(already_AddRefed<nsIDOMSVGLengthList>)
1005 nsSVGGlyphFrame::GetDy()
1007 nsSVGTextContainerFrame *containerFrame;
1008 containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
1009 if (containerFrame)
1010 return containerFrame->GetDy();
1011 return nsnull;
1014 NS_IMETHODIMP_(PRUint16)
1015 nsSVGGlyphFrame::GetTextAnchor()
1017 return GetStyleSVG()->mTextAnchor;
1020 NS_IMETHODIMP_(PRBool)
1021 nsSVGGlyphFrame::IsAbsolutelyPositioned()
1023 nsIFrame *lastFrame = this;
1025 for (nsIFrame *frame = GetParent();
1026 frame != nsnull;
1027 lastFrame = frame, frame = frame->GetParent()) {
1029 /* need to be the first child if we are absolutely positioned */
1030 if (!frame ||
1031 frame->GetFirstChild(nsnull) != lastFrame)
1032 break;
1034 // textPath is always absolutely positioned for our purposes
1035 if (frame->GetType() == nsGkAtoms::svgTextPathFrame)
1036 return PR_TRUE;
1038 if (frame &&
1039 (frame->GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::x) ||
1040 frame->GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::y)))
1041 return PR_TRUE;
1043 if (frame->GetType() == nsGkAtoms::svgTextFrame)
1044 break;
1047 return PR_FALSE;
1051 //----------------------------------------------------------------------
1052 // nsISVGGlyphFragmentNode interface:
1054 NS_IMETHODIMP_(PRUint32)
1055 nsSVGGlyphFrame::GetNumberOfChars()
1057 if (mWhitespaceHandling == PRESERVE_WHITESPACE)
1058 return mContent->TextLength();
1060 nsAutoString text;
1061 GetCharacterData(text);
1062 return text.Length();
1065 NS_IMETHODIMP_(float)
1066 nsSVGGlyphFrame::GetComputedTextLength()
1068 return GetAdvance(PR_FALSE);
1071 NS_IMETHODIMP_(float)
1072 nsSVGGlyphFrame::GetSubStringLength(PRUint32 charnum, PRUint32 fragmentChars)
1074 float drawScale, metricsScale;
1075 if (!EnsureTextRun(&drawScale, &metricsScale, PR_FALSE))
1076 return 0.0f;
1078 gfxFloat advanceAppUnits =
1079 mTextRun->GetAdvanceWidth(charnum, fragmentChars, nsnull);
1080 return float(advanceAppUnits)*metricsScale;
1083 NS_IMETHODIMP_(PRInt32)
1084 nsSVGGlyphFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
1086 float xPos, yPos;
1087 point->GetX(&xPos);
1088 point->GetY(&yPos);
1090 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1091 CharacterIterator iter(this, PR_FALSE);
1093 PRInt32 i;
1094 PRInt32 last = -1;
1095 gfxPoint pt(xPos, yPos);
1096 while ((i = iter.NextChar()) >= 0) {
1097 gfxTextRun::Metrics metrics =
1098 mTextRun->MeasureText(i, 1, PR_FALSE, nsnull, nsnull);
1099 iter.SetupForMetrics(tmpCtx);
1100 tmpCtx->NewPath();
1101 tmpCtx->Rectangle(metrics.mBoundingBox);
1102 tmpCtx->IdentityMatrix();
1103 if (tmpCtx->PointInFill(pt)) {
1104 // Can't return now. If there's glyph overlap, the last character
1105 // to be rendered wins.
1106 last = i;
1110 return last;
1113 NS_IMETHODIMP_(nsISVGGlyphFragmentLeaf *)
1114 nsSVGGlyphFrame::GetFirstGlyphFragment()
1116 return this;
1119 NS_IMETHODIMP_(nsISVGGlyphFragmentLeaf *)
1120 nsSVGGlyphFrame::GetNextGlyphFragment()
1122 nsIFrame* sibling = mNextSibling;
1123 while (sibling) {
1124 nsISVGGlyphFragmentNode *node = nsnull;
1125 CallQueryInterface(sibling, &node);
1126 if (node)
1127 return node->GetFirstGlyphFragment();
1128 sibling = sibling->GetNextSibling();
1131 // no more siblings. go back up the tree.
1133 NS_ASSERTION(mParent, "null parent");
1134 nsISVGGlyphFragmentNode *node = nsnull;
1135 CallQueryInterface(mParent, &node);
1136 return node ? node->GetNextGlyphFragment() : nsnull;
1139 NS_IMETHODIMP_(void)
1140 nsSVGGlyphFrame::SetWhitespaceHandling(PRUint8 aWhitespaceHandling)
1142 mWhitespaceHandling = aWhitespaceHandling;
1145 //----------------------------------------------------------------------
1148 void
1149 nsSVGGlyphFrame::NotifyGlyphMetricsChange()
1151 nsSVGTextContainerFrame *containerFrame =
1152 static_cast<nsSVGTextContainerFrame *>(mParent);
1153 if (containerFrame)
1154 containerFrame->NotifyGlyphMetricsChange();
1157 PRBool
1158 nsSVGGlyphFrame::ContainsPoint(const nsPoint &aPoint)
1160 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1161 SetupGlobalTransform(tmpCtx);
1162 CharacterIterator iter(this, PR_TRUE);
1163 iter.SetInitialMatrix(tmpCtx);
1165 PRInt32 i;
1166 while ((i = iter.NextChar()) >= 0) {
1167 gfxTextRun::Metrics metrics =
1168 mTextRun->MeasureText(i, 1, PR_FALSE, nsnull, nsnull);
1169 iter.SetupForMetrics(tmpCtx);
1170 tmpCtx->Rectangle(metrics.mBoundingBox);
1173 tmpCtx->IdentityMatrix();
1174 return tmpCtx->PointInFill(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
1175 PresContext()->AppUnitsToGfxUnits(aPoint.y)));
1178 PRBool
1179 nsSVGGlyphFrame::GetGlobalTransform(gfxMatrix *aMatrix)
1181 if (!GetMatrixPropagation()) {
1182 aMatrix->Reset();
1183 return PR_TRUE;
1186 nsCOMPtr<nsIDOMSVGMatrix> ctm;
1187 GetCanvasTM(getter_AddRefs(ctm));
1188 if (!ctm)
1189 return PR_FALSE;
1191 *aMatrix = nsSVGUtils::ConvertSVGMatrixToThebes(ctm);
1192 return !aMatrix->IsSingular();
1195 void
1196 nsSVGGlyphFrame::SetupGlobalTransform(gfxContext *aContext)
1198 gfxMatrix matrix;
1199 GetGlobalTransform(&matrix);
1200 aContext->Multiply(matrix);
1203 void
1204 nsSVGGlyphFrame::ClearTextRun()
1206 if (!mTextRun)
1207 return;
1208 gfxTextRunWordCache::RemoveTextRun(mTextRun);
1209 delete mTextRun;
1210 mTextRun = nsnull;
1213 PRBool
1214 nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
1215 PRBool aForceGlobalTransform)
1217 // Compute the size at which the text should render (excluding the CTM)
1218 const nsStyleFont* fontData = GetStyleFont();
1219 // Since SVG has its own scaling, we really don't want
1220 // fonts in SVG to respond to the browser's "TextZoom"
1221 // (Ctrl++,Ctrl+-)
1222 nsPresContext *presContext = PresContext();
1223 float textZoom = presContext->TextZoom();
1224 double size =
1225 presContext->AppUnitsToFloatCSSPixels(fontData->mSize) / textZoom;
1227 double textRunSize;
1228 if (mTextRun) {
1229 textRunSize = mTextRun->GetFontGroup()->GetStyle()->size;
1230 } else {
1231 nsAutoString text;
1232 if (!GetCharacterData(text))
1233 return PR_FALSE;
1235 gfxMatrix m;
1236 if (aForceGlobalTransform ||
1237 !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
1238 if (!GetGlobalTransform(&m))
1239 return PR_FALSE;
1242 // The context scale is the ratio of the length of the transformed
1243 // diagonal vector (1,1) to the length of the untransformed diagonal
1244 // (which is sqrt(2)).
1245 gfxPoint p = m.Transform(gfxPoint(1, 1)) - m.Transform(gfxPoint(0, 0));
1246 double contextScale = nsSVGUtils::ComputeNormalizedHypotenuse(p.x, p.y);
1248 nsCAutoString langGroup;
1249 nsIAtom *langGroupAtom = presContext->GetLangGroup();
1250 if (langGroupAtom) {
1251 const char* lg;
1252 langGroupAtom->GetUTF8String(&lg);
1253 langGroup.Assign(lg);
1256 if (GetStyleSVG()->mTextRendering ==
1257 NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION) {
1258 textRunSize = PRECISE_SIZE;
1259 } else {
1260 textRunSize = size*contextScale;
1261 textRunSize = PR_MAX(textRunSize, CLAMP_MIN_SIZE);
1262 textRunSize = PR_MIN(textRunSize, CLAMP_MAX_SIZE);
1265 const nsFont& font = fontData->mFont;
1266 PRBool printerFont = (presContext->Type() == nsPresContext::eContext_PrintPreview ||
1267 presContext->Type() == nsPresContext::eContext_Print);
1268 gfxFontStyle fontStyle(font.style, font.weight, textRunSize, langGroup,
1269 font.sizeAdjust, font.systemFont,
1270 font.familyNameQuirks,
1271 printerFont);
1273 nsRefPtr<gfxFontGroup> fontGroup =
1274 gfxPlatform::GetPlatform()->CreateFontGroup(font.name, &fontStyle, presContext->GetUserFontSet());
1276 PRUint32 flags = gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX |
1277 nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(), GetStyleText(), GetStyleFont());
1279 // XXX We should use a better surface here! But then we'd have to
1280 // change things so we can ensure we always have the "right" sort of
1281 // surface available, by creating the textrun only at the right times
1282 nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1283 tmpCtx->SetMatrix(m);
1285 // Use only the word cache here. We don't want to cache the textrun
1286 // globally because we'll never hit in that cache, since we create
1287 // a new fontgroup every time. Even if we cached fontgroups, we
1288 // might render at very many different sizes (e.g. during zoom
1289 // animation) and caching a textrun for each such size would be bad.
1290 gfxTextRunFactory::Parameters params = {
1291 tmpCtx, nsnull, nsnull, nsnull, 0, GetTextRunUnitsFactor()
1293 mTextRun = gfxTextRunWordCache::MakeTextRun(text.get(), text.Length(),
1294 fontGroup, &params, flags);
1295 if (!mTextRun)
1296 return PR_FALSE;
1299 *aDrawScale = float(size/textRunSize);
1300 *aMetricsScale = (*aDrawScale)/GetTextRunUnitsFactor();
1301 return PR_TRUE;
1304 NS_IMETHODIMP
1305 nsSVGGlyphFrame::SetMatrixPropagation(PRBool aPropagate)
1307 if (aPropagate) {
1308 AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
1309 } else {
1310 RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
1312 return NS_OK;
1315 PRBool
1316 nsSVGGlyphFrame::GetMatrixPropagation()
1318 return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
1321 //----------------------------------------------------------------------
1322 // helper class
1324 CharacterIterator::CharacterIterator(nsSVGGlyphFrame *aSource,
1325 PRBool aForceGlobalTransform)
1326 : mSource(aSource), mCurrentAdvance(0), mCurrentChar(-1),
1327 mInError(PR_FALSE)
1329 if (!aSource->EnsureTextRun(&mDrawScale, &mMetricsScale,
1330 aForceGlobalTransform) ||
1331 !aSource->GetCharacterPositions(&mPositions, mMetricsScale)) {
1332 mInError = PR_TRUE;
1336 PRBool
1337 CharacterIterator::SetupForDirectTextRun(gfxContext *aContext, float aScale)
1339 if (!mPositions.IsEmpty() || mInError)
1340 return PR_FALSE;
1341 aContext->SetMatrix(mInitialMatrix);
1342 aContext->Translate(mSource->mPosition);
1343 aContext->Scale(aScale, aScale);
1344 // We are scaling the glyphs up/down to the size we want so we need to
1345 // inverse scale the outline widths of those glyphs so they are invariant
1346 aContext->SetLineWidth(aContext->CurrentLineWidth() / aScale);
1347 return PR_TRUE;
1350 PRInt32
1351 CharacterIterator::NextChar()
1353 if (mInError)
1354 return -1;
1356 while (PR_TRUE) {
1357 if (mCurrentChar >= 0 &&
1358 (mPositions.IsEmpty() || mPositions[mCurrentChar].draw)) {
1359 mCurrentAdvance +=
1360 mSource->mTextRun->GetAdvanceWidth(mCurrentChar, 1, nsnull);
1362 ++mCurrentChar;
1364 if (mCurrentChar >= PRInt32(mSource->mTextRun->GetLength()))
1365 return -1;
1367 if (mPositions.IsEmpty() || mPositions[mCurrentChar].draw)
1368 return mCurrentChar;
1372 PRBool
1373 CharacterIterator::AdvanceToCharacter(PRInt32 aIndex)
1375 while (NextChar() != -1) {
1376 if (mCurrentChar == aIndex)
1377 return PR_TRUE;
1379 return PR_FALSE;
1382 void
1383 CharacterIterator::SetupFor(gfxContext *aContext, float aScale)
1385 NS_ASSERTION(!mInError, "We should not have reached here");
1387 aContext->SetMatrix(mInitialMatrix);
1388 if (mPositions.IsEmpty()) {
1389 aContext->Translate(mSource->mPosition);
1390 aContext->Scale(aScale, aScale);
1391 aContext->Translate(gfxPoint(mCurrentAdvance, 0));
1392 } else {
1393 aContext->Translate(mPositions[mCurrentChar].pos);
1394 aContext->Rotate(mPositions[mCurrentChar].angle);
1395 aContext->Scale(aScale, aScale);
1397 // We are scaling the glyphs up/down to the size we want so we need to
1398 // inverse scale the outline widths of those glyphs so they are invariant
1399 aContext->SetLineWidth(aContext->CurrentLineWidth() / aScale);
1402 CharacterPosition
1403 CharacterIterator::GetPositionData()
1405 if (!mPositions.IsEmpty())
1406 return mPositions[mCurrentChar];
1408 gfxFloat advance = mCurrentAdvance * mMetricsScale;
1409 CharacterPosition cp =
1410 { mSource->mPosition + gfxPoint(advance, 0), 0, PR_TRUE };
1411 return cp;