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
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.
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
{
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
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
92 class CharacterIterator
96 * Sets up the iterator so that NextChar will return the first drawable
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()) {
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.
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();
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
;
180 PRPackedBool mInError
;
183 //----------------------------------------------------------------------
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 //----------------------------------------------------------------------
213 nsSVGGlyphFrame::CharacterDataChanged(nsPresContext
* aPresContext
,
218 NotifyGlyphMetricsChange();
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
229 nsSVGGlyphFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
231 nsSVGGlyphFrameBase::DidSetStyleContext(aOldStyleContext
);
233 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
235 NotifyGlyphMetricsChange();
240 nsSVGGlyphFrame::SetSelected(nsPresContext
* aPresContext
,
246 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
247 printf("nsSVGGlyphFrame(%p)::SetSelected()\n", this);
249 // return nsSVGGlyphFrameBase::SetSelected(aPresContext, aRange, aSelected, aSpread, aType);
251 if (aType
== nsISelectionController::SELECTION_NORMAL
) {
252 // check whether style allows selection
254 IsSelectable(&selectable
, nsnull
);
260 mState
|= NS_FRAME_SELECTED_CONTENT
;
263 mState
&= ~NS_FRAME_SELECTED_CONTENT
;
265 nsSVGUtils::UpdateGraphic(this);
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
);
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
);
292 nsSVGGlyphFrame::GetType() const
294 return nsGkAtoms::svgGlyphFrame
;
297 //----------------------------------------------------------------------
298 // nsISVGChildFrame methods
301 nsSVGGlyphFrame::PaintSVG(nsSVGRenderState
*aContext
,
302 const nsIntRect
*aDirtyRect
)
304 if (!GetStyleVisibility()->IsVisible())
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
);
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
);
328 AddCharactersToPath(&iter
, gfx
);
331 gfx
->SetMatrix(matrix
);
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
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
);
356 AddCharactersToPath(&iter
, gfx
);
358 // We need to clear the context's path so state doesn't leak
359 // out. See bug 337753.
367 NS_IMETHODIMP_(nsIFrame
*)
368 nsSVGGlyphFrame::GetFrameForPoint(const nsPoint
&aPoint
)
371 //printf("nsSVGGlyphFrame(%p)::GetFrameForPoint\n", this);
374 if (!mRect
.Contains(aPoint
))
377 PRBool events
= PR_FALSE
;
378 switch (GetStyleSVG()->mPointerEvents
) {
379 case NS_STYLE_POINTER_EVENTS_NONE
:
381 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED
:
382 if (GetStyleVisibility()->IsVisible() &&
383 (GetStyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
||
384 GetStyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
))
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())
393 case NS_STYLE_POINTER_EVENTS_PAINTED
:
394 if (GetStyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
||
395 GetStyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
398 case NS_STYLE_POINTER_EVENTS_FILL
:
399 case NS_STYLE_POINTER_EVENTS_STROKE
:
400 case NS_STYLE_POINTER_EVENTS_ALL
:
404 NS_ERROR("not reached");
408 if (events
&& ContainsPoint(aPoint
))
414 NS_IMETHODIMP_(nsRect
)
415 nsSVGGlyphFrame::GetCoveredRegion()
422 return new gfxContext(nsSVGUtils::GetThebesComputationalSurface());
426 nsSVGGlyphFrame::UpdateCoveredRegion()
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()) {
452 GetGlobalTransform(&matrix
);
454 extent
= matrix
.TransformBounds(extent
);
455 mRect
= nsSVGUtils::ToAppPixelRect(PresContext(), extent
);
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
);
479 nsSVGGlyphFrame::NotifySVGChanged(PRUint32 aFlags
)
481 if (aFlags
& TRANSFORM_CHANGED
) {
484 if (!(aFlags
& SUPPRESS_INVALIDATION
)) {
485 nsSVGUtils::UpdateGraphic(this);
490 nsSVGGlyphFrame::NotifyRedrawSuspended()
492 // XXX should we cache the fact that redraw is suspended?
497 nsSVGGlyphFrame::NotifyRedrawUnsuspended()
499 if (GetStateBits() & NS_STATE_SVG_DIRTY
)
500 nsSVGUtils::UpdateGraphic(this);
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
);
516 while ((i
= aIter
->NextChar()) >= 0) {
517 aIter
->SetupForDrawing(aContext
);
518 mTextRun
->DrawToPath(aContext
, gfxPoint(0, 0), i
, 1, nsnull
, nsnull
);
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
);
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
);
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
);
554 while ((i
= aIter
->NextChar()) >= 0) {
555 aIter
->SetupForDrawing(aContext
);
556 mTextRun
->Draw(aContext
, gfxPoint(0, 0), i
, 1, nsnull
, nsnull
, nsnull
);
561 nsSVGGlyphFrame::GetBBox(nsIDOMSVGRect
**_retval
)
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; */
580 nsSVGGlyphFrame::GetCanvasTM(nsIDOMSVGMatrix
* *aCTM
)
582 NS_ASSERTION(mParent
, "null parent");
584 nsSVGContainerFrame
*containerFrame
= static_cast<nsSVGContainerFrame
*>
586 nsCOMPtr
<nsIDOMSVGMatrix
> parentTM
= containerFrame
->GetCanvasTM();
588 parentTM
.swap(*aCTM
);
592 //----------------------------------------------------------------------
593 // nsSVGGlyphFrame methods:
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
);
608 nsAString::iterator start
, end
;
609 characterData
.BeginWriting(start
);
610 characterData
.EndWriting(end
);
611 while (start
!= end
) {
612 if (NS_IsAsciiWhitespace(*start
))
617 aCharacterData
= characterData
;
619 return !characterData
.IsEmpty();
623 nsSVGGlyphFrame::GetCharacterPositions(nsTArray
<CharacterPosition
>* aCharacterPositions
,
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 */
635 nsRefPtr
<gfxFlattenedPath
> data
= textPath
->GetFlattenedPath();
637 /* textPath frame, but invalid target */
641 gfxFloat length
= data
->GetLength();
642 PRUint32 strLength
= mTextRun
->GetLength();
644 if (!aCharacterPositions
->SetLength(strLength
))
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
)
661 /* check that we've advanced to the start of the path */
662 if (x
+ halfAdvance
>= 0.0) {
663 cp
[i
].draw
= PR_TRUE
;
667 // move point back along tangent
668 gfxPoint pt
= data
->FindPoint(gfxPoint(x
+ halfAdvance
, mPosition
.y
),
671 pt
- gfxPoint(cos(cp
[i
].angle
), sin(cp
[i
].angle
)) * halfAdvance
;
673 x
+= 2 * halfAdvance
;
679 //----------------------------------------------------------------------
681 // Utilities for converting from indices in the uncompressed content
682 // element strings to compressed frame string and back:
684 CompressIndex(int index
, const nsTextFragment
*fragment
)
687 if (fragment
->Is2b()) {
688 const PRUnichar
*data
=fragment
->Get2b();
689 while(*data
&& index
) {
690 if (XP_IS_SPACE_W(*data
)){
694 }while(XP_IS_SPACE_W(*data
) && index
);
704 const char *data
=fragment
->Get1b();
705 while(*data
&& index
) {
706 if (XP_IS_SPACE_W(*data
)){
710 }while(XP_IS_SPACE_W(*data
) && index
);
724 UncompressIndex(int index
, PRBool bRightAffinity
, const nsTextFragment
*fragment
)
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);
739 PRBool hasHighlight
=
740 (mState
& NS_FRAME_SELECTED_CONTENT
) == NS_FRAME_SELECTED_CONTENT
;
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
));
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);
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
),
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
) {
820 //----------------------------------------------------------------------
821 // nsISVGGlyphFragmentLeaf interface:
824 nsSVGGlyphFrame::SetGlyphPosition(float x
, float y
)
826 mPosition
.MoveTo(x
, y
);
827 nsSVGUtils::UpdateGraphic(this);
831 nsSVGGlyphFrame::GetStartPositionOfChar(PRUint32 charnum
,
832 nsIDOMSVGPoint
**_retval
)
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
);
844 nsSVGGlyphFrame::GetEndPositionOfChar(PRUint32 charnum
,
845 nsIDOMSVGPoint
**_retval
)
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());
861 nsSVGGlyphFrame::GetExtentOfChar(PRUint32 charnum
, nsIDOMSVGRect
**_retval
)
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());
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();
888 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
890 const gfxFloat radPerDeg
= M_PI
/180.0;
891 *_retval
= float(pos
.angle
/ radPerDeg
);
895 NS_IMETHODIMP_(float)
896 nsSVGGlyphFrame::GetBaselineOffset(PRUint16 baselineIdentifier
,
897 PRBool aForceGlobalTransform
)
899 float drawScale
, metricsScale
;
901 if (!EnsureTextRun(&drawScale
, &metricsScale
, aForceGlobalTransform
))
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
913 case BASELINE_TEXT_BEFORE_EDGE
:
914 baselineAppUnits
= -metrics
.mAscent
;
916 case BASELINE_TEXT_AFTER_EDGE
:
917 baselineAppUnits
= metrics
.mDescent
;
919 case BASELINE_CENTRAL
:
920 case BASELINE_MIDDLE
:
921 baselineAppUnits
= -(metrics
.mAscent
- metrics
.mDescent
) / 2.0f
;
923 case BASELINE_ALPHABETIC
:
925 baselineAppUnits
= 0.0;
928 return float(baselineAppUnits
)*metricsScale
;
931 NS_IMETHODIMP_(float)
932 nsSVGGlyphFrame::GetAdvance(PRBool aForceGlobalTransform
)
934 float drawScale
, metricsScale
;
935 if (!EnsureTextRun(&drawScale
, &metricsScale
, aForceGlobalTransform
))
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();
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
)
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.)
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
);
980 return containerFrame
->GetX();
984 NS_IMETHODIMP_(already_AddRefed
<nsIDOMSVGLengthList
>)
985 nsSVGGlyphFrame::GetY()
987 nsSVGTextContainerFrame
*containerFrame
;
988 containerFrame
= static_cast<nsSVGTextContainerFrame
*>(mParent
);
990 return containerFrame
->GetY();
994 NS_IMETHODIMP_(already_AddRefed
<nsIDOMSVGLengthList
>)
995 nsSVGGlyphFrame::GetDx()
997 nsSVGTextContainerFrame
*containerFrame
;
998 containerFrame
= static_cast<nsSVGTextContainerFrame
*>(mParent
);
1000 return containerFrame
->GetDx();
1004 NS_IMETHODIMP_(already_AddRefed
<nsIDOMSVGLengthList
>)
1005 nsSVGGlyphFrame::GetDy()
1007 nsSVGTextContainerFrame
*containerFrame
;
1008 containerFrame
= static_cast<nsSVGTextContainerFrame
*>(mParent
);
1010 return containerFrame
->GetDy();
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();
1027 lastFrame
= frame
, frame
= frame
->GetParent()) {
1029 /* need to be the first child if we are absolutely positioned */
1031 frame
->GetFirstChild(nsnull
) != lastFrame
)
1034 // textPath is always absolutely positioned for our purposes
1035 if (frame
->GetType() == nsGkAtoms::svgTextPathFrame
)
1039 (frame
->GetContent()->HasAttr(kNameSpaceID_None
, nsGkAtoms::x
) ||
1040 frame
->GetContent()->HasAttr(kNameSpaceID_None
, nsGkAtoms::y
)))
1043 if (frame
->GetType() == nsGkAtoms::svgTextFrame
)
1051 //----------------------------------------------------------------------
1052 // nsISVGGlyphFragmentNode interface:
1054 NS_IMETHODIMP_(PRUint32
)
1055 nsSVGGlyphFrame::GetNumberOfChars()
1057 if (mWhitespaceHandling
== PRESERVE_WHITESPACE
)
1058 return mContent
->TextLength();
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
))
1078 gfxFloat advanceAppUnits
=
1079 mTextRun
->GetAdvanceWidth(charnum
, fragmentChars
, nsnull
);
1080 return float(advanceAppUnits
)*metricsScale
;
1083 NS_IMETHODIMP_(PRInt32
)
1084 nsSVGGlyphFrame::GetCharNumAtPosition(nsIDOMSVGPoint
*point
)
1090 nsRefPtr
<gfxContext
> tmpCtx
= MakeTmpCtx();
1091 CharacterIterator
iter(this, PR_FALSE
);
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
);
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.
1113 NS_IMETHODIMP_(nsISVGGlyphFragmentLeaf
*)
1114 nsSVGGlyphFrame::GetFirstGlyphFragment()
1119 NS_IMETHODIMP_(nsISVGGlyphFragmentLeaf
*)
1120 nsSVGGlyphFrame::GetNextGlyphFragment()
1122 nsIFrame
* sibling
= mNextSibling
;
1124 nsISVGGlyphFragmentNode
*node
= nsnull
;
1125 CallQueryInterface(sibling
, &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 //----------------------------------------------------------------------
1149 nsSVGGlyphFrame::NotifyGlyphMetricsChange()
1151 nsSVGTextContainerFrame
*containerFrame
=
1152 static_cast<nsSVGTextContainerFrame
*>(mParent
);
1154 containerFrame
->NotifyGlyphMetricsChange();
1158 nsSVGGlyphFrame::ContainsPoint(const nsPoint
&aPoint
)
1160 nsRefPtr
<gfxContext
> tmpCtx
= MakeTmpCtx();
1161 SetupGlobalTransform(tmpCtx
);
1162 CharacterIterator
iter(this, PR_TRUE
);
1163 iter
.SetInitialMatrix(tmpCtx
);
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
)));
1179 nsSVGGlyphFrame::GetGlobalTransform(gfxMatrix
*aMatrix
)
1181 if (!GetMatrixPropagation()) {
1186 nsCOMPtr
<nsIDOMSVGMatrix
> ctm
;
1187 GetCanvasTM(getter_AddRefs(ctm
));
1191 *aMatrix
= nsSVGUtils::ConvertSVGMatrixToThebes(ctm
);
1192 return !aMatrix
->IsSingular();
1196 nsSVGGlyphFrame::SetupGlobalTransform(gfxContext
*aContext
)
1199 GetGlobalTransform(&matrix
);
1200 aContext
->Multiply(matrix
);
1204 nsSVGGlyphFrame::ClearTextRun()
1208 gfxTextRunWordCache::RemoveTextRun(mTextRun
);
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"
1222 nsPresContext
*presContext
= PresContext();
1223 float textZoom
= presContext
->TextZoom();
1225 presContext
->AppUnitsToFloatCSSPixels(fontData
->mSize
) / textZoom
;
1229 textRunSize
= mTextRun
->GetFontGroup()->GetStyle()->size
;
1232 if (!GetCharacterData(text
))
1236 if (aForceGlobalTransform
||
1237 !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)) {
1238 if (!GetGlobalTransform(&m
))
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
) {
1252 langGroupAtom
->GetUTF8String(&lg
);
1253 langGroup
.Assign(lg
);
1256 if (GetStyleSVG()->mTextRendering
==
1257 NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION
) {
1258 textRunSize
= PRECISE_SIZE
;
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
,
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
, ¶ms
, flags
);
1299 *aDrawScale
= float(size
/textRunSize
);
1300 *aMetricsScale
= (*aDrawScale
)/GetTextRunUnitsFactor();
1305 nsSVGGlyphFrame::SetMatrixPropagation(PRBool aPropagate
)
1308 AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM
);
1310 RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM
);
1316 nsSVGGlyphFrame::GetMatrixPropagation()
1318 return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM
) != 0;
1321 //----------------------------------------------------------------------
1324 CharacterIterator::CharacterIterator(nsSVGGlyphFrame
*aSource
,
1325 PRBool aForceGlobalTransform
)
1326 : mSource(aSource
), mCurrentAdvance(0), mCurrentChar(-1),
1329 if (!aSource
->EnsureTextRun(&mDrawScale
, &mMetricsScale
,
1330 aForceGlobalTransform
) ||
1331 !aSource
->GetCharacterPositions(&mPositions
, mMetricsScale
)) {
1337 CharacterIterator::SetupForDirectTextRun(gfxContext
*aContext
, float aScale
)
1339 if (!mPositions
.IsEmpty() || mInError
)
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
);
1351 CharacterIterator::NextChar()
1357 if (mCurrentChar
>= 0 &&
1358 (mPositions
.IsEmpty() || mPositions
[mCurrentChar
].draw
)) {
1360 mSource
->mTextRun
->GetAdvanceWidth(mCurrentChar
, 1, nsnull
);
1364 if (mCurrentChar
>= PRInt32(mSource
->mTextRun
->GetLength()))
1367 if (mPositions
.IsEmpty() || mPositions
[mCurrentChar
].draw
)
1368 return mCurrentChar
;
1373 CharacterIterator::AdvanceToCharacter(PRInt32 aIndex
)
1375 while (NextChar() != -1) {
1376 if (mCurrentChar
== aIndex
)
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));
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
);
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
};