PageRenderTime 26ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/firefox-14.0.1/mozilla-release/layout/svg/base/src/nsSVGTextFrame.cpp

#
C++ | 457 lines | 265 code | 88 blank | 104 comment | 52 complexity | 0fb1f514665b04568090ed3f09435982 MD5 | raw file
Possible License(s): MIT, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause, GPL-2.0, JSON, Apache-2.0, 0BSD, LGPL-3.0, AGPL-1.0
  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
  4. *
  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/
  9. *
  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.
  14. *
  15. * The Original Code is the Mozilla SVG project.
  16. *
  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.
  21. *
  22. * Contributor(s):
  23. * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
  24. *
  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.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. // Main header first:
  39. #include "nsSVGTextFrame.h"
  40. // Keep others in (case-insensitive) order:
  41. #include "nsGkAtoms.h"
  42. #include "nsIDOMSVGRect.h"
  43. #include "nsIDOMSVGTextElement.h"
  44. #include "nsISVGGlyphFragmentNode.h"
  45. #include "nsSVGGlyphFrame.h"
  46. #include "nsSVGGraphicElement.h"
  47. #include "nsSVGPathElement.h"
  48. #include "nsSVGTextPathFrame.h"
  49. #include "nsSVGUtils.h"
  50. #include "SVGLengthList.h"
  51. using namespace mozilla;
  52. //----------------------------------------------------------------------
  53. // Implementation
  54. nsIFrame*
  55. NS_NewSVGTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  56. {
  57. return new (aPresShell) nsSVGTextFrame(aContext);
  58. }
  59. NS_IMPL_FRAMEARENA_HELPERS(nsSVGTextFrame)
  60. //----------------------------------------------------------------------
  61. // nsIFrame methods
  62. #ifdef DEBUG
  63. NS_IMETHODIMP
  64. nsSVGTextFrame::Init(nsIContent* aContent,
  65. nsIFrame* aParent,
  66. nsIFrame* aPrevInFlow)
  67. {
  68. nsCOMPtr<nsIDOMSVGTextElement> text = do_QueryInterface(aContent);
  69. NS_ASSERTION(text, "Content is not an SVG text");
  70. return nsSVGTextFrameBase::Init(aContent, aParent, aPrevInFlow);
  71. }
  72. #endif /* DEBUG */
  73. NS_IMETHODIMP
  74. nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID,
  75. nsIAtom* aAttribute,
  76. PRInt32 aModType)
  77. {
  78. if (aNameSpaceID != kNameSpaceID_None)
  79. return NS_OK;
  80. if (aAttribute == nsGkAtoms::transform) {
  81. NotifySVGChanged(TRANSFORM_CHANGED);
  82. } else if (aAttribute == nsGkAtoms::x ||
  83. aAttribute == nsGkAtoms::y ||
  84. aAttribute == nsGkAtoms::dx ||
  85. aAttribute == nsGkAtoms::dy ||
  86. aAttribute == nsGkAtoms::rotate) {
  87. NotifyGlyphMetricsChange();
  88. }
  89. return NS_OK;
  90. }
  91. nsIAtom *
  92. nsSVGTextFrame::GetType() const
  93. {
  94. return nsGkAtoms::svgTextFrame;
  95. }
  96. //----------------------------------------------------------------------
  97. // nsSVGTextContainerFrame
  98. PRUint32
  99. nsSVGTextFrame::GetNumberOfChars()
  100. {
  101. UpdateGlyphPositioning(false);
  102. return nsSVGTextFrameBase::GetNumberOfChars();
  103. }
  104. float
  105. nsSVGTextFrame::GetComputedTextLength()
  106. {
  107. UpdateGlyphPositioning(false);
  108. return nsSVGTextFrameBase::GetComputedTextLength();
  109. }
  110. float
  111. nsSVGTextFrame::GetSubStringLength(PRUint32 charnum, PRUint32 nchars)
  112. {
  113. UpdateGlyphPositioning(false);
  114. return nsSVGTextFrameBase::GetSubStringLength(charnum, nchars);
  115. }
  116. PRInt32
  117. nsSVGTextFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
  118. {
  119. UpdateGlyphPositioning(false);
  120. return nsSVGTextFrameBase::GetCharNumAtPosition(point);
  121. }
  122. NS_IMETHODIMP
  123. nsSVGTextFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
  124. {
  125. UpdateGlyphPositioning(false);
  126. return nsSVGTextFrameBase::GetStartPositionOfChar(charnum, _retval);
  127. }
  128. NS_IMETHODIMP
  129. nsSVGTextFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
  130. {
  131. UpdateGlyphPositioning(false);
  132. return nsSVGTextFrameBase::GetEndPositionOfChar(charnum, _retval);
  133. }
  134. NS_IMETHODIMP
  135. nsSVGTextFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
  136. {
  137. UpdateGlyphPositioning(false);
  138. return nsSVGTextFrameBase::GetExtentOfChar(charnum, _retval);
  139. }
  140. NS_IMETHODIMP
  141. nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
  142. {
  143. UpdateGlyphPositioning(false);
  144. return nsSVGTextFrameBase::GetRotationOfChar(charnum, _retval);
  145. }
  146. //----------------------------------------------------------------------
  147. // nsISVGChildFrame methods
  148. void
  149. nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags)
  150. {
  151. NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
  152. (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
  153. "Must be NS_STATE_SVG_NONDISPLAY_CHILD!");
  154. NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
  155. "Invalidation logic may need adjusting");
  156. bool updateGlyphMetrics = false;
  157. if (aFlags & COORD_CONTEXT_CHANGED) {
  158. updateGlyphMetrics = true;
  159. }
  160. if (aFlags & TRANSFORM_CHANGED) {
  161. if (mCanvasTM && mCanvasTM->IsSingular()) {
  162. // We won't have calculated the glyph positions correctly
  163. updateGlyphMetrics = true;
  164. }
  165. // make sure our cached transform matrix gets (lazily) updated
  166. mCanvasTM = nsnull;
  167. }
  168. nsSVGTextFrameBase::NotifySVGChanged(aFlags);
  169. if (updateGlyphMetrics) {
  170. // If we are positioned using percentage values we need to update our
  171. // position whenever our viewport's dimensions change.
  172. // XXX We could check here whether the text frame or any of its children
  173. // have any percentage co-ordinates and only update if they don't. This
  174. // may not be worth it as we might need to check each glyph
  175. NotifyGlyphMetricsChange();
  176. }
  177. }
  178. NS_IMETHODIMP
  179. nsSVGTextFrame::PaintSVG(nsRenderingContext* aContext,
  180. const nsIntRect *aDirtyRect)
  181. {
  182. UpdateGlyphPositioning(true);
  183. return nsSVGTextFrameBase::PaintSVG(aContext, aDirtyRect);
  184. }
  185. NS_IMETHODIMP_(nsIFrame*)
  186. nsSVGTextFrame::GetFrameForPoint(const nsPoint &aPoint)
  187. {
  188. UpdateGlyphPositioning(true);
  189. return nsSVGTextFrameBase::GetFrameForPoint(aPoint);
  190. }
  191. void
  192. nsSVGTextFrame::UpdateBounds()
  193. {
  194. NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingUpdateBounds(this),
  195. "This call is probaby a wasteful mistake");
  196. NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
  197. "UpdateBounds mechanism not designed for this");
  198. if (!nsSVGUtils::NeedsUpdatedBounds(this)) {
  199. NS_ASSERTION(!mPositioningDirty, "How did this happen?");
  200. return;
  201. }
  202. // UpdateGlyphPositioning may have been called under DOM calls and set
  203. // mPositioningDirty to false. We may now have better positioning, though, so
  204. // set it to true so that UpdateGlyphPositioning will do its work.
  205. mPositioningDirty = true;
  206. UpdateGlyphPositioning(false);
  207. // With glyph positions updated, our descendants can invalidate their new
  208. // areas correctly:
  209. nsSVGTextFrameBase::UpdateBounds();
  210. // XXXsvgreflow once we store bounds on containers, call
  211. // nsSVGUtils::InvalidateBounds(this) if not first reflow.
  212. }
  213. SVGBBox
  214. nsSVGTextFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
  215. PRUint32 aFlags)
  216. {
  217. UpdateGlyphPositioning(true);
  218. return nsSVGTextFrameBase::GetBBoxContribution(aToBBoxUserspace, aFlags);
  219. }
  220. //----------------------------------------------------------------------
  221. // nsSVGContainerFrame methods:
  222. gfxMatrix
  223. nsSVGTextFrame::GetCanvasTM()
  224. {
  225. if (!mCanvasTM) {
  226. NS_ASSERTION(mParent, "null parent");
  227. nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
  228. nsSVGGraphicElement *content = static_cast<nsSVGGraphicElement*>(mContent);
  229. gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
  230. mCanvasTM = new gfxMatrix(tm);
  231. }
  232. return *mCanvasTM;
  233. }
  234. //----------------------------------------------------------------------
  235. //
  236. void
  237. nsSVGTextFrame::NotifyGlyphMetricsChange()
  238. {
  239. nsSVGUtils::InvalidateAndScheduleBoundsUpdate(this);
  240. mPositioningDirty = true;
  241. }
  242. void
  243. nsSVGTextFrame::SetWhitespaceHandling(nsSVGGlyphFrame *aFrame)
  244. {
  245. SetWhitespaceCompression();
  246. nsSVGGlyphFrame* firstFrame = aFrame;
  247. bool trimLeadingWhitespace = true;
  248. nsSVGGlyphFrame* lastNonWhitespaceFrame = aFrame;
  249. // If the previous frame ended with whitespace
  250. // then display of leading whitespace should be suppressed
  251. // when we are compressing whitespace.
  252. while (aFrame) {
  253. if (!aFrame->IsAllWhitespace()) {
  254. lastNonWhitespaceFrame = aFrame;
  255. }
  256. aFrame->SetTrimLeadingWhitespace(trimLeadingWhitespace);
  257. trimLeadingWhitespace = aFrame->EndsWithWhitespace();
  258. aFrame = aFrame->GetNextGlyphFrame();
  259. }
  260. // When there is only whitespace left we need to trim off
  261. // the end of the last frame that isn't entirely whitespace.
  262. // Making sure that we reset earlier frames as they may once
  263. // have been the last non-whitespace frame.
  264. aFrame = firstFrame;
  265. while (aFrame != lastNonWhitespaceFrame) {
  266. aFrame->SetTrimTrailingWhitespace(false);
  267. aFrame = aFrame->GetNextGlyphFrame();
  268. }
  269. // We're at the last non-whitespace frame so trim off the end
  270. // and make sure we set one of the trim bits so that any
  271. // further whitespace is compressed to nothing
  272. while (aFrame) {
  273. aFrame->SetTrimTrailingWhitespace(true);
  274. aFrame = aFrame->GetNextGlyphFrame();
  275. }
  276. }
  277. void
  278. nsSVGTextFrame::UpdateGlyphPositioning(bool aForceGlobalTransform)
  279. {
  280. if (!mPositioningDirty)
  281. return;
  282. mPositioningDirty = false;
  283. nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
  284. if (!node)
  285. return;
  286. nsSVGGlyphFrame *frame, *firstFrame;
  287. firstFrame = node->GetFirstGlyphFrame();
  288. if (!firstFrame) {
  289. return;
  290. }
  291. SetWhitespaceHandling(firstFrame);
  292. BuildPositionList(0, 0);
  293. gfxPoint ctp(0.0, 0.0);
  294. // loop over chunks
  295. while (firstFrame) {
  296. nsSVGTextPathFrame *textPath = firstFrame->FindTextPathParent();
  297. nsTArray<float> effectiveXList, effectiveYList;
  298. firstFrame->GetEffectiveXY(firstFrame->GetNumberOfChars(),
  299. effectiveXList, effectiveYList);
  300. if (!effectiveXList.IsEmpty()) ctp.x = effectiveXList[0];
  301. if (!textPath && !effectiveYList.IsEmpty()) ctp.y = effectiveYList[0];
  302. // check for startOffset on textPath
  303. if (textPath) {
  304. if (!textPath->GetPathFrame()) {
  305. // invalid text path, give up
  306. return;
  307. }
  308. ctp.x = textPath->GetStartOffset();
  309. }
  310. // determine x offset based on text_anchor:
  311. PRUint8 anchor = firstFrame->GetTextAnchor();
  312. /**
  313. * XXXsmontagu: The SVG spec is very vague as to how 'text-anchor'
  314. * interacts with bidirectional text. It says:
  315. *
  316. * "For scripts that are inherently right to left such as Hebrew and
  317. * Arabic [text-anchor: start] is equivalent to right alignment."
  318. * and
  319. * "For scripts that are inherently right to left such as Hebrew and
  320. * Arabic, [text-anchor: end] is equivalent to left alignment.
  321. *
  322. * It's not clear how this should be implemented in terms of defined
  323. * properties, i.e. how one should determine that a particular element
  324. * contains a script that is inherently right to left.
  325. *
  326. * The code below follows http://www.w3.org/TR/SVGTiny12/text.html#TextAnchorProperty
  327. * and swaps the values of text-anchor: end and text-anchor: start
  328. * whenever the 'direction' property is rtl.
  329. *
  330. * This is probably the "right" thing to do, but other browsers don't do it,
  331. * so I am leaving it inside #if 0 for now for interoperability.
  332. *
  333. * See also XXXsmontagu comments in nsSVGGlyphFrame::EnsureTextRun
  334. */
  335. #if 0
  336. if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
  337. if (anchor == NS_STYLE_TEXT_ANCHOR_END) {
  338. anchor = NS_STYLE_TEXT_ANCHOR_START;
  339. } else if (anchor == NS_STYLE_TEXT_ANCHOR_START) {
  340. anchor = NS_STYLE_TEXT_ANCHOR_END;
  341. }
  342. }
  343. #endif
  344. float chunkLength = 0.0f;
  345. if (anchor != NS_STYLE_TEXT_ANCHOR_START) {
  346. // need to get the total chunk length
  347. frame = firstFrame;
  348. while (frame) {
  349. chunkLength += frame->GetAdvance(aForceGlobalTransform);
  350. frame = frame->GetNextGlyphFrame();
  351. if (frame && frame->IsAbsolutelyPositioned())
  352. break;
  353. }
  354. }
  355. if (anchor == NS_STYLE_TEXT_ANCHOR_MIDDLE)
  356. ctp.x -= chunkLength/2.0f;
  357. else if (anchor == NS_STYLE_TEXT_ANCHOR_END)
  358. ctp.x -= chunkLength;
  359. // set position of each frame in this chunk:
  360. frame = firstFrame;
  361. while (frame) {
  362. frame->SetGlyphPosition(&ctp, aForceGlobalTransform);
  363. frame = frame->GetNextGlyphFrame();
  364. if (frame && frame->IsAbsolutelyPositioned())
  365. break;
  366. }
  367. firstFrame = frame;
  368. }
  369. }