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 "SVGLengthList.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 "nsSVGRect.h"
50 #include "nsSVGMatrix.h"
51 #include "nsGkAtoms.h"
52 #include "nsSVGTextPathFrame.h"
53 #include "nsSVGPathElement.h"
54 #include "nsSVGUtils.h"
55 #include "nsSVGGraphicElement.h"
57 using namespace mozilla
;
59 //----------------------------------------------------------------------
63 NS_NewSVGTextFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
65 return new (aPresShell
) nsSVGTextFrame(aContext
);
68 NS_IMPL_FRAMEARENA_HELPERS(nsSVGTextFrame
)
70 //----------------------------------------------------------------------
74 nsSVGTextFrame::Init(nsIContent
* aContent
,
76 nsIFrame
* aPrevInFlow
)
78 nsCOMPtr
<nsIDOMSVGTextElement
> text
= do_QueryInterface(aContent
);
79 NS_ASSERTION(text
, "Content is not an SVG text");
81 return nsSVGTextFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
86 nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID
,
90 if (aNameSpaceID
!= kNameSpaceID_None
)
93 if (aAttribute
== nsGkAtoms::transform
) {
94 // transform has changed
96 // make sure our cached transform matrix gets (lazily) updated
99 nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED
);
101 } else if (aAttribute
== nsGkAtoms::x
||
102 aAttribute
== nsGkAtoms::y
||
103 aAttribute
== nsGkAtoms::dx
||
104 aAttribute
== nsGkAtoms::dy
||
105 aAttribute
== nsGkAtoms::rotate
) {
106 NotifyGlyphMetricsChange();
113 nsSVGTextFrame::GetType() const
115 return nsGkAtoms::svgTextFrame
;
118 //----------------------------------------------------------------------
119 // nsSVGTextContainerFrame
121 nsSVGTextFrame::GetNumberOfChars()
123 UpdateGlyphPositioning(PR_FALSE
);
125 return nsSVGTextFrameBase::GetNumberOfChars();
129 nsSVGTextFrame::GetComputedTextLength()
131 UpdateGlyphPositioning(PR_FALSE
);
133 return nsSVGTextFrameBase::GetComputedTextLength();
137 nsSVGTextFrame::GetSubStringLength(PRUint32 charnum
, PRUint32 nchars
)
139 UpdateGlyphPositioning(PR_FALSE
);
141 return nsSVGTextFrameBase::GetSubStringLength(charnum
, nchars
);
145 nsSVGTextFrame::GetCharNumAtPosition(nsIDOMSVGPoint
*point
)
147 UpdateGlyphPositioning(PR_FALSE
);
149 return nsSVGTextFrameBase::GetCharNumAtPosition(point
);
153 nsSVGTextFrame::GetStartPositionOfChar(PRUint32 charnum
, nsIDOMSVGPoint
**_retval
)
155 UpdateGlyphPositioning(PR_FALSE
);
157 return nsSVGTextFrameBase::GetStartPositionOfChar(charnum
, _retval
);
161 nsSVGTextFrame::GetEndPositionOfChar(PRUint32 charnum
, nsIDOMSVGPoint
**_retval
)
163 UpdateGlyphPositioning(PR_FALSE
);
165 return nsSVGTextFrameBase::GetEndPositionOfChar(charnum
, _retval
);
169 nsSVGTextFrame::GetExtentOfChar(PRUint32 charnum
, nsIDOMSVGRect
**_retval
)
171 UpdateGlyphPositioning(PR_FALSE
);
173 return nsSVGTextFrameBase::GetExtentOfChar(charnum
, _retval
);
177 nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum
, float *_retval
)
179 UpdateGlyphPositioning(PR_FALSE
);
181 return nsSVGTextFrameBase::GetRotationOfChar(charnum
, _retval
);
184 //----------------------------------------------------------------------
185 // nsISVGChildFrame methods
188 nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags
)
190 if (aFlags
& TRANSFORM_CHANGED
) {
191 // make sure our cached transform matrix gets (lazily) updated
195 nsSVGTextFrameBase::NotifySVGChanged(aFlags
);
197 if (aFlags
& COORD_CONTEXT_CHANGED
) {
198 // If we are positioned using percentage values we need to update our
199 // position whenever our viewport's dimensions change.
201 // XXX We could check here whether the text frame or any of its children
202 // have any percentage co-ordinates and only update if they don't. This
203 // may not be worth it as we might need to check each glyph
204 NotifyGlyphMetricsChange();
209 nsSVGTextFrame::NotifyRedrawSuspended()
211 mMetricsState
= suspended
;
213 return nsSVGTextFrameBase::NotifyRedrawSuspended();
217 nsSVGTextFrame::NotifyRedrawUnsuspended()
219 mMetricsState
= unsuspended
;
220 UpdateGlyphPositioning(PR_FALSE
);
221 return nsSVGTextFrameBase::NotifyRedrawUnsuspended();
225 nsSVGTextFrame::PaintSVG(nsSVGRenderState
* aContext
,
226 const nsIntRect
*aDirtyRect
)
228 UpdateGlyphPositioning(PR_TRUE
);
230 return nsSVGTextFrameBase::PaintSVG(aContext
, aDirtyRect
);
233 NS_IMETHODIMP_(nsIFrame
*)
234 nsSVGTextFrame::GetFrameForPoint(const nsPoint
&aPoint
)
236 UpdateGlyphPositioning(PR_TRUE
);
238 return nsSVGTextFrameBase::GetFrameForPoint(aPoint
);
242 nsSVGTextFrame::UpdateCoveredRegion()
244 UpdateGlyphPositioning(PR_TRUE
);
246 return nsSVGTextFrameBase::UpdateCoveredRegion();
250 nsSVGTextFrame::InitialUpdate()
252 nsresult rv
= nsSVGTextFrameBase::InitialUpdate();
254 UpdateGlyphPositioning(PR_FALSE
);
260 nsSVGTextFrame::GetBBoxContribution(const gfxMatrix
&aToBBoxUserspace
)
262 UpdateGlyphPositioning(PR_TRUE
);
264 return nsSVGTextFrameBase::GetBBoxContribution(aToBBoxUserspace
);
267 //----------------------------------------------------------------------
268 // nsSVGContainerFrame methods:
271 nsSVGTextFrame::GetCanvasTM()
274 NS_ASSERTION(mParent
, "null parent");
276 nsSVGContainerFrame
*parent
= static_cast<nsSVGContainerFrame
*>(mParent
);
277 nsSVGGraphicElement
*content
= static_cast<nsSVGGraphicElement
*>(mContent
);
279 gfxMatrix tm
= content
->PrependLocalTransformTo(parent
->GetCanvasTM());
281 mCanvasTM
= NS_NewSVGMatrix(tm
);
284 return nsSVGUtils::ConvertSVGMatrixToThebes(mCanvasTM
);
287 //----------------------------------------------------------------------
291 nsSVGTextFrame::NotifyGlyphMetricsChange()
293 mPositioningDirty
= PR_TRUE
;
294 UpdateGlyphPositioning(PR_FALSE
);
298 nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform
)
300 if (mMetricsState
== suspended
|| !mPositioningDirty
)
303 SetWhitespaceHandling();
305 nsISVGGlyphFragmentNode
* node
= GetFirstGlyphFragmentChildNode();
308 mPositioningDirty
= PR_FALSE
;
310 nsISVGGlyphFragmentLeaf
*fragment
, *firstFragment
;
312 firstFragment
= node
->GetFirstGlyphFragment();
313 if (!firstFragment
) {
317 BuildPositionList(0, 0);
319 gfxPoint
ctp(0.0, 0.0);
322 while (firstFragment
) {
323 nsSVGTextPathFrame
*textPath
= firstFragment
->FindTextPathParent();
325 nsTArray
<float> effectiveXList
, effectiveYList
;
326 firstFragment
->GetEffectiveXY(firstFragment
->GetNumberOfChars(),
327 effectiveXList
, effectiveYList
);
328 if (!effectiveXList
.IsEmpty()) ctp
.x
= effectiveXList
[0];
329 if (!textPath
&& !effectiveYList
.IsEmpty()) ctp
.y
= effectiveYList
[0];
331 // check for startOffset on textPath
333 if (!textPath
->GetPathFrame()) {
334 // invalid text path, give up
337 ctp
.x
= textPath
->GetStartOffset();
340 // determine x offset based on text_anchor:
342 PRUint8 anchor
= firstFragment
->GetTextAnchor();
345 * XXXsmontagu: The SVG spec is very vague as to how 'text-anchor'
346 * interacts with bidirectional text. It says:
348 * "For scripts that are inherently right to left such as Hebrew and
349 * Arabic [text-anchor: start] is equivalent to right alignment."
351 * "For scripts that are inherently right to left such as Hebrew and
352 * Arabic, [text-anchor: end] is equivalent to left alignment.
354 * It's not clear how this should be implemented in terms of defined
355 * properties, i.e. how one should determine that a particular element
356 * contains a script that is inherently right to left.
358 * The code below follows http://www.w3.org/TR/SVGTiny12/text.html#TextAnchorProperty
359 * and swaps the values of text-anchor: end and text-anchor: start
360 * whenever the 'direction' property is rtl.
362 * This is probably the "right" thing to do, but other browsers don't do it,
363 * so I am leaving it inside #if 0 for now for interoperability.
365 * See also XXXsmontagu comments in nsSVGGlyphFrame::EnsureTextRun
368 if (GetStyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {
369 if (anchor
== NS_STYLE_TEXT_ANCHOR_END
) {
370 anchor
= NS_STYLE_TEXT_ANCHOR_START
;
371 } else if (anchor
== NS_STYLE_TEXT_ANCHOR_START
) {
372 anchor
= NS_STYLE_TEXT_ANCHOR_END
;
377 float chunkLength
= 0.0f
;
378 if (anchor
!= NS_STYLE_TEXT_ANCHOR_START
) {
379 // need to get the total chunk length
381 fragment
= firstFragment
;
383 chunkLength
+= fragment
->GetAdvance(aForceGlobalTransform
);
384 fragment
= fragment
->GetNextGlyphFragment();
385 if (fragment
&& fragment
->IsAbsolutelyPositioned())
390 if (anchor
== NS_STYLE_TEXT_ANCHOR_MIDDLE
)
391 ctp
.x
-= chunkLength
/2.0f
;
392 else if (anchor
== NS_STYLE_TEXT_ANCHOR_END
)
393 ctp
.x
-= chunkLength
;
395 // set position of each fragment in this chunk:
397 fragment
= firstFragment
;
400 fragment
->SetGlyphPosition(&ctp
, aForceGlobalTransform
);
401 fragment
= fragment
->GetNextGlyphFragment();
402 if (fragment
&& fragment
->IsAbsolutelyPositioned())
405 firstFragment
= fragment
;
407 nsSVGUtils::UpdateGraphic(this);