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 "nsIDOMSVGTextElement.h"
40 #include "nsSVGTextFrame.h"
41 #include "nsWeakReference.h"
42 #include "nsIDOMSVGLengthList.h"
43 #include "nsIDOMSVGLength.h"
44 #include "nsIDOMSVGAnimatedNumber.h"
45 #include "nsISVGGlyphFragmentNode.h"
46 #include "nsISVGGlyphFragmentLeaf.h"
47 #include "nsSVGOuterSVGFrame.h"
48 #include "nsIDOMSVGRect.h"
49 #include "nsISVGTextContentMetrics.h"
50 #include "nsSVGRect.h"
51 #include "nsSVGMatrix.h"
52 #include "nsGkAtoms.h"
53 #include "nsSVGTextPathFrame.h"
54 #include "nsSVGPathElement.h"
55 #include "nsSVGUtils.h"
56 #include "nsSVGGraphicElement.h"
58 //----------------------------------------------------------------------
62 NS_NewSVGTextFrame(nsIPresShell
* aPresShell
, nsIContent
* aContent
, nsStyleContext
* aContext
)
64 nsCOMPtr
<nsIDOMSVGTextElement
> text
= do_QueryInterface(aContent
);
66 NS_ERROR("Can't create frame! Content is not an SVG text");
70 return new (aPresShell
) nsSVGTextFrame(aContext
);
73 //----------------------------------------------------------------------
77 nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID
,
81 if (aNameSpaceID
!= kNameSpaceID_None
)
84 if (aAttribute
== nsGkAtoms::transform
) {
85 // transform has changed
87 // make sure our cached transform matrix gets (lazily) updated
90 nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED
);
92 } else if (aAttribute
== nsGkAtoms::x
||
93 aAttribute
== nsGkAtoms::y
||
94 aAttribute
== nsGkAtoms::dx
||
95 aAttribute
== nsGkAtoms::dy
) {
96 NotifyGlyphMetricsChange();
103 nsSVGTextFrame::GetType() const
105 return nsGkAtoms::svgTextFrame
;
108 //----------------------------------------------------------------------
109 // nsISVGTextContentMetrics
111 nsSVGTextFrame::GetNumberOfChars(PRInt32
*_retval
)
113 UpdateGlyphPositioning(PR_FALSE
);
115 return nsSVGTextFrameBase::GetNumberOfChars(_retval
);
119 nsSVGTextFrame::GetComputedTextLength(float *_retval
)
121 UpdateGlyphPositioning(PR_FALSE
);
123 return nsSVGTextFrameBase::GetComputedTextLength(_retval
);
127 nsSVGTextFrame::GetSubStringLength(PRUint32 charnum
, PRUint32 nchars
, float *_retval
)
129 UpdateGlyphPositioning(PR_FALSE
);
131 return nsSVGTextFrameBase::GetSubStringLength(charnum
, nchars
, _retval
);
135 nsSVGTextFrame::GetStartPositionOfChar(PRUint32 charnum
, nsIDOMSVGPoint
**_retval
)
137 UpdateGlyphPositioning(PR_FALSE
);
139 return nsSVGTextFrameBase::GetStartPositionOfChar(charnum
, _retval
);
143 nsSVGTextFrame::GetEndPositionOfChar(PRUint32 charnum
, nsIDOMSVGPoint
**_retval
)
145 UpdateGlyphPositioning(PR_FALSE
);
147 return nsSVGTextFrameBase::GetEndPositionOfChar(charnum
, _retval
);
151 nsSVGTextFrame::GetExtentOfChar(PRUint32 charnum
, nsIDOMSVGRect
**_retval
)
153 UpdateGlyphPositioning(PR_FALSE
);
155 return nsSVGTextFrameBase::GetExtentOfChar(charnum
, _retval
);
159 nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum
, float *_retval
)
161 UpdateGlyphPositioning(PR_FALSE
);
163 return nsSVGTextFrameBase::GetRotationOfChar(charnum
, _retval
);
167 nsSVGTextFrame::GetCharNumAtPosition(nsIDOMSVGPoint
*point
, PRInt32
*_retval
)
169 UpdateGlyphPositioning(PR_FALSE
);
171 return nsSVGTextFrameBase::GetCharNumAtPosition(point
, _retval
);
175 //----------------------------------------------------------------------
176 // nsISVGChildFrame methods
179 nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags
)
181 if (aFlags
& TRANSFORM_CHANGED
) {
182 // make sure our cached transform matrix gets (lazily) updated
186 if (aFlags
& COORD_CONTEXT_CHANGED
) {
187 // If we are positioned using percentage values we need to update our
188 // position whenever our viewport's dimensions change.
190 // XXX We could check here whether the text frame or any of its children
191 // have any percentage co-ordinates and only update if they don't. This
192 // may not be worth it as we might need to check each glyph
193 NotifyGlyphMetricsChange();
196 nsSVGTextFrameBase::NotifySVGChanged(aFlags
);
200 nsSVGTextFrame::NotifyRedrawSuspended()
202 mMetricsState
= suspended
;
204 return nsSVGTextFrameBase::NotifyRedrawSuspended();
208 nsSVGTextFrame::NotifyRedrawUnsuspended()
210 mMetricsState
= unsuspended
;
211 UpdateGlyphPositioning(PR_FALSE
);
212 return nsSVGTextFrameBase::NotifyRedrawUnsuspended();
216 nsSVGTextFrame::SetMatrixPropagation(PRBool aPropagate
)
218 mPropagateTransform
= aPropagate
;
223 nsSVGTextFrame::SetOverrideCTM(nsIDOMSVGMatrix
*aCTM
)
229 already_AddRefed
<nsIDOMSVGMatrix
>
230 nsSVGTextFrame::GetOverrideCTM()
232 nsIDOMSVGMatrix
*matrix
= mOverrideCTM
.get();
233 NS_IF_ADDREF(matrix
);
238 nsSVGTextFrame::PaintSVG(nsSVGRenderState
* aContext
, nsRect
*aDirtyRect
)
240 UpdateGlyphPositioning(PR_TRUE
);
242 return nsSVGTextFrameBase::PaintSVG(aContext
, aDirtyRect
);
246 nsSVGTextFrame::GetFrameForPointSVG(float x
, float y
, nsIFrame
** hit
)
248 UpdateGlyphPositioning(PR_TRUE
);
250 return nsSVGTextFrameBase::GetFrameForPointSVG(x
, y
, hit
);
254 nsSVGTextFrame::UpdateCoveredRegion()
256 UpdateGlyphPositioning(PR_TRUE
);
258 return nsSVGTextFrameBase::UpdateCoveredRegion();
262 nsSVGTextFrame::InitialUpdate()
264 nsresult rv
= nsSVGTextFrameBase::InitialUpdate();
266 UpdateGlyphPositioning(PR_FALSE
);
272 nsSVGTextFrame::GetBBox(nsIDOMSVGRect
**_retval
)
274 UpdateGlyphPositioning(PR_TRUE
);
276 return nsSVGTextFrameBase::GetBBox(_retval
);
279 //----------------------------------------------------------------------
280 // nsSVGContainerFrame methods:
282 already_AddRefed
<nsIDOMSVGMatrix
>
283 nsSVGTextFrame::GetCanvasTM()
285 if (!mPropagateTransform
) {
286 nsIDOMSVGMatrix
*retval
;
288 retval
= mOverrideCTM
;
291 NS_NewSVGMatrix(&retval
);
297 // get our parent's tm and append local transforms (if any):
298 NS_ASSERTION(mParent
, "null parent");
299 nsSVGContainerFrame
*containerFrame
= static_cast<nsSVGContainerFrame
*>
301 nsCOMPtr
<nsIDOMSVGMatrix
> parentTM
= containerFrame
->GetCanvasTM();
302 NS_ASSERTION(parentTM
, "null TM");
304 // got the parent tm, now check for local tm:
305 nsSVGGraphicElement
*element
=
306 static_cast<nsSVGGraphicElement
*>(mContent
);
307 nsCOMPtr
<nsIDOMSVGMatrix
> localTM
= element
->GetLocalTransformMatrix();
310 parentTM
->Multiply(localTM
, getter_AddRefs(mCanvasTM
));
312 mCanvasTM
= parentTM
;
315 nsIDOMSVGMatrix
* retval
= mCanvasTM
.get();
316 NS_IF_ADDREF(retval
);
320 //----------------------------------------------------------------------
324 nsSVGTextFrame::NotifyGlyphMetricsChange()
326 mPositioningDirty
= PR_TRUE
;
327 UpdateGlyphPositioning(PR_FALSE
);
331 GetSingleValue(nsISVGGlyphFragmentLeaf
*fragment
,
332 nsIDOMSVGLengthList
*list
, float *val
)
338 list
->GetNumberOfItems(&count
);
341 NS_WARNING("multiple lengths for x/y attributes on <text> elements not implemented yet!");
344 nsCOMPtr
<nsIDOMSVGLength
> length
;
345 list
->GetItem(0, getter_AddRefs(length
));
346 length
->GetValue(val
);
351 nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform
)
353 if (mMetricsState
== suspended
|| !mPositioningDirty
)
356 SetWhitespaceHandling();
358 nsISVGGlyphFragmentNode
* node
= GetFirstGlyphFragmentChildNode();
361 mPositioningDirty
= PR_FALSE
;
363 // we'll align every fragment in this chunk on the dominant-baseline:
364 // XXX should actually inspect 'alignment-baseline' for each fragment
367 switch(GetStyleSVGReset()->mDominantBaseline
) {
368 case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE
:
369 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_TEXT_BEFORE_EDGE
;
371 case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE
:
372 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_TEXT_AFTER_EDGE
;
374 case NS_STYLE_DOMINANT_BASELINE_MIDDLE
:
375 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_MIDDLE
;
377 case NS_STYLE_DOMINANT_BASELINE_CENTRAL
:
378 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_CENTRAL
;
380 case NS_STYLE_DOMINANT_BASELINE_MATHEMATICAL
:
381 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_MATHEMATICAL
;
383 case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC
:
384 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_IDEOGRAPHC
;
386 case NS_STYLE_DOMINANT_BASELINE_HANGING
:
387 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_HANGING
;
389 case NS_STYLE_DOMINANT_BASELINE_AUTO
:
390 case NS_STYLE_DOMINANT_BASELINE_USE_SCRIPT
:
391 case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC
:
393 baseline
= nsISVGGlyphFragmentLeaf::BASELINE_ALPHABETIC
;
397 nsISVGGlyphFragmentLeaf
*fragment
, *firstFragment
;
399 firstFragment
= node
->GetFirstGlyphFragment();
400 if (!firstFragment
) {
407 nsCOMPtr
<nsIDOMSVGLengthList
> list
= GetX();
408 GetSingleValue(firstFragment
, list
, &x
);
411 nsCOMPtr
<nsIDOMSVGLengthList
> list
= GetY();
412 GetSingleValue(firstFragment
, list
, &y
);
416 while (firstFragment
) {
418 nsCOMPtr
<nsIDOMSVGLengthList
> list
= firstFragment
->GetX();
419 GetSingleValue(firstFragment
, list
, &x
);
422 nsCOMPtr
<nsIDOMSVGLengthList
> list
= firstFragment
->GetY();
423 GetSingleValue(firstFragment
, list
, &y
);
426 // check for startOffset on textPath
427 nsSVGTextPathFrame
*textPath
= firstFragment
->FindTextPathParent();
429 x
= textPath
->GetStartOffset();
432 // determine x offset based on text_anchor:
434 PRUint8 anchor
= firstFragment
->GetTextAnchor();
436 float chunkLength
= 0.0f
;
437 if (anchor
!= NS_STYLE_TEXT_ANCHOR_START
) {
438 // need to get the total chunk length
440 fragment
= firstFragment
;
443 nsCOMPtr
<nsIDOMSVGLengthList
> list
= fragment
->GetDx();
444 GetSingleValue(fragment
, list
, &dx
);
445 chunkLength
+= dx
+ fragment
->GetAdvance(aForceGlobalTransform
);
446 fragment
= fragment
->GetNextGlyphFragment();
447 if (fragment
&& fragment
->IsAbsolutelyPositioned())
452 if (anchor
== NS_STYLE_TEXT_ANCHOR_MIDDLE
)
453 x
-= chunkLength
/2.0f
;
454 else if (anchor
== NS_STYLE_TEXT_ANCHOR_END
)
457 // set position of each fragment in this chunk:
459 fragment
= firstFragment
;
462 float dx
= 0.0f
, dy
= 0.0f
;
464 nsCOMPtr
<nsIDOMSVGLengthList
> list
= fragment
->GetDx();
465 GetSingleValue(fragment
, list
, &dx
);
468 nsCOMPtr
<nsIDOMSVGLengthList
> list
= fragment
->GetDy();
469 GetSingleValue(fragment
, list
, &dy
);
472 float baseline_offset
=
473 fragment
->GetBaselineOffset(baseline
, aForceGlobalTransform
);
474 fragment
->SetGlyphPosition(x
+ dx
, y
+ dy
- baseline_offset
);
476 x
+= dx
+ fragment
->GetAdvance(aForceGlobalTransform
);
478 fragment
= fragment
->GetNextGlyphFragment();
479 if (fragment
&& fragment
->IsAbsolutelyPositioned())
482 firstFragment
= fragment
;