CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / layout / svg / base / src / nsSVGTextFrame.cpp
blob7cc6bca2b984b23cf9bd28ef372c1e659195ade9
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 "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 //----------------------------------------------------------------------
60 // Implementation
62 nsIFrame*
63 NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
65 return new (aPresShell) nsSVGTextFrame(aContext);
68 NS_IMPL_FRAMEARENA_HELPERS(nsSVGTextFrame)
70 //----------------------------------------------------------------------
71 // nsIFrame methods
72 #ifdef DEBUG
73 NS_IMETHODIMP
74 nsSVGTextFrame::Init(nsIContent* aContent,
75 nsIFrame* aParent,
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);
83 #endif /* DEBUG */
85 NS_IMETHODIMP
86 nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID,
87 nsIAtom* aAttribute,
88 PRInt32 aModType)
90 if (aNameSpaceID != kNameSpaceID_None)
91 return NS_OK;
93 if (aAttribute == nsGkAtoms::transform) {
94 // transform has changed
96 // make sure our cached transform matrix gets (lazily) updated
97 mCanvasTM = nsnull;
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();
109 return NS_OK;
112 nsIAtom *
113 nsSVGTextFrame::GetType() const
115 return nsGkAtoms::svgTextFrame;
118 //----------------------------------------------------------------------
119 // nsSVGTextContainerFrame
120 PRUint32
121 nsSVGTextFrame::GetNumberOfChars()
123 UpdateGlyphPositioning(PR_FALSE);
125 return nsSVGTextFrameBase::GetNumberOfChars();
128 float
129 nsSVGTextFrame::GetComputedTextLength()
131 UpdateGlyphPositioning(PR_FALSE);
133 return nsSVGTextFrameBase::GetComputedTextLength();
136 float
137 nsSVGTextFrame::GetSubStringLength(PRUint32 charnum, PRUint32 nchars)
139 UpdateGlyphPositioning(PR_FALSE);
141 return nsSVGTextFrameBase::GetSubStringLength(charnum, nchars);
144 PRInt32
145 nsSVGTextFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
147 UpdateGlyphPositioning(PR_FALSE);
149 return nsSVGTextFrameBase::GetCharNumAtPosition(point);
152 NS_IMETHODIMP
153 nsSVGTextFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
155 UpdateGlyphPositioning(PR_FALSE);
157 return nsSVGTextFrameBase::GetStartPositionOfChar(charnum, _retval);
160 NS_IMETHODIMP
161 nsSVGTextFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
163 UpdateGlyphPositioning(PR_FALSE);
165 return nsSVGTextFrameBase::GetEndPositionOfChar(charnum, _retval);
168 NS_IMETHODIMP
169 nsSVGTextFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
171 UpdateGlyphPositioning(PR_FALSE);
173 return nsSVGTextFrameBase::GetExtentOfChar(charnum, _retval);
176 NS_IMETHODIMP
177 nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
179 UpdateGlyphPositioning(PR_FALSE);
181 return nsSVGTextFrameBase::GetRotationOfChar(charnum, _retval);
184 //----------------------------------------------------------------------
185 // nsISVGChildFrame methods
187 void
188 nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags)
190 if (aFlags & TRANSFORM_CHANGED) {
191 // make sure our cached transform matrix gets (lazily) updated
192 mCanvasTM = nsnull;
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();
208 NS_IMETHODIMP
209 nsSVGTextFrame::NotifyRedrawSuspended()
211 mMetricsState = suspended;
213 return nsSVGTextFrameBase::NotifyRedrawSuspended();
216 NS_IMETHODIMP
217 nsSVGTextFrame::NotifyRedrawUnsuspended()
219 mMetricsState = unsuspended;
220 UpdateGlyphPositioning(PR_FALSE);
221 return nsSVGTextFrameBase::NotifyRedrawUnsuspended();
224 NS_IMETHODIMP
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);
241 NS_IMETHODIMP
242 nsSVGTextFrame::UpdateCoveredRegion()
244 UpdateGlyphPositioning(PR_TRUE);
246 return nsSVGTextFrameBase::UpdateCoveredRegion();
249 NS_IMETHODIMP
250 nsSVGTextFrame::InitialUpdate()
252 nsresult rv = nsSVGTextFrameBase::InitialUpdate();
254 UpdateGlyphPositioning(PR_FALSE);
256 return rv;
259 gfxRect
260 nsSVGTextFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace)
262 UpdateGlyphPositioning(PR_TRUE);
264 return nsSVGTextFrameBase::GetBBoxContribution(aToBBoxUserspace);
267 //----------------------------------------------------------------------
268 // nsSVGContainerFrame methods:
270 gfxMatrix
271 nsSVGTextFrame::GetCanvasTM()
273 if (!mCanvasTM) {
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 //----------------------------------------------------------------------
290 void
291 nsSVGTextFrame::NotifyGlyphMetricsChange()
293 mPositioningDirty = PR_TRUE;
294 UpdateGlyphPositioning(PR_FALSE);
297 void
298 nsSVGTextFrame::UpdateGlyphPositioning(PRBool aForceGlobalTransform)
300 if (mMetricsState == suspended || !mPositioningDirty)
301 return;
303 SetWhitespaceHandling();
305 nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
306 if (!node) return;
308 mPositioningDirty = PR_FALSE;
310 nsISVGGlyphFragmentLeaf *fragment, *firstFragment;
312 firstFragment = node->GetFirstGlyphFragment();
313 if (!firstFragment) {
314 return;
317 BuildPositionList(0, 0);
319 gfxPoint ctp(0.0, 0.0);
321 // loop over chunks
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
332 if (textPath) {
333 if (!textPath->GetPathFrame()) {
334 // invalid text path, give up
335 return;
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."
350 * and
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
367 #if 0
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;
375 #endif
377 float chunkLength = 0.0f;
378 if (anchor != NS_STYLE_TEXT_ANCHOR_START) {
379 // need to get the total chunk length
381 fragment = firstFragment;
382 while (fragment) {
383 chunkLength += fragment->GetAdvance(aForceGlobalTransform);
384 fragment = fragment->GetNextGlyphFragment();
385 if (fragment && fragment->IsAbsolutelyPositioned())
386 break;
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;
398 while (fragment) {
400 fragment->SetGlyphPosition(&ctp, aForceGlobalTransform);
401 fragment = fragment->GetNextGlyphFragment();
402 if (fragment && fragment->IsAbsolutelyPositioned())
403 break;
405 firstFragment = fragment;
407 nsSVGUtils::UpdateGraphic(this);