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 IBM Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2006
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsSVGContainerFrame.h"
38 : #include "nsSVGTextFrame.h"
39 : #include "nsSVGUtils.h"
40 : #include "nsSVGOuterSVGFrame.h"
41 : #include "nsIDOMSVGTextElement.h"
42 : #include "nsIDOMSVGAnimatedLengthList.h"
43 : #include "SVGAnimatedNumberList.h"
44 : #include "SVGNumberList.h"
45 : #include "nsSVGGlyphFrame.h"
46 : #include "nsDOMError.h"
47 : #include "SVGLengthList.h"
48 : #include "nsSVGTextPositioningElement.h"
49 :
50 : using namespace mozilla;
51 :
52 : //----------------------------------------------------------------------
53 : // nsQueryFrame methods
54 :
55 0 : NS_QUERYFRAME_HEAD(nsSVGTextContainerFrame)
56 0 : NS_QUERYFRAME_ENTRY(nsSVGTextContainerFrame)
57 0 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGDisplayContainerFrame)
58 :
59 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGTextContainerFrame)
60 :
61 : void
62 0 : nsSVGTextContainerFrame::NotifyGlyphMetricsChange()
63 : {
64 0 : nsSVGTextFrame *textFrame = GetTextFrame();
65 0 : if (textFrame)
66 0 : textFrame->NotifyGlyphMetricsChange();
67 0 : }
68 :
69 : void
70 0 : nsSVGTextContainerFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
71 : {
72 : static_cast<nsSVGElement*>(mContent)->
73 0 : GetAnimatedLengthListValues(aX, aY, nsnull);
74 0 : }
75 :
76 : void
77 0 : nsSVGTextContainerFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
78 : {
79 : // SVGUserUnitList is lazy, so there's little overhead it getting the x
80 : // and y lists even though we ignore them.
81 0 : SVGUserUnitList xLengthList, yLengthList;
82 : static_cast<nsSVGElement*>(mContent)->
83 0 : GetAnimatedLengthListValues(&xLengthList, &yLengthList, aDx, aDy, nsnull);
84 0 : }
85 :
86 : const SVGNumberList*
87 0 : nsSVGTextContainerFrame::GetRotate()
88 : {
89 : SVGAnimatedNumberList *animList =
90 : static_cast<nsSVGElement*>(mContent)->
91 0 : GetAnimatedNumberList(nsGkAtoms::rotate);
92 0 : return animList ? &animList->GetAnimValue() : nsnull;
93 : }
94 :
95 : //----------------------------------------------------------------------
96 : // nsIFrame methods
97 :
98 : NS_IMETHODIMP
99 0 : nsSVGTextContainerFrame::InsertFrames(ChildListID aListID,
100 : nsIFrame* aPrevFrame,
101 : nsFrameList& aFrameList)
102 : {
103 : nsresult rv = nsSVGDisplayContainerFrame::InsertFrames(aListID,
104 : aPrevFrame,
105 0 : aFrameList);
106 :
107 0 : NotifyGlyphMetricsChange();
108 0 : return rv;
109 : }
110 :
111 : NS_IMETHODIMP
112 0 : nsSVGTextContainerFrame::RemoveFrame(ChildListID aListID, nsIFrame *aOldFrame)
113 : {
114 0 : nsSVGTextFrame *textFrame = GetTextFrame();
115 :
116 0 : nsresult rv = nsSVGDisplayContainerFrame::RemoveFrame(aListID, aOldFrame);
117 :
118 0 : if (textFrame)
119 0 : textFrame->NotifyGlyphMetricsChange();
120 :
121 0 : return rv;
122 : }
123 :
124 : NS_IMETHODIMP
125 0 : nsSVGTextContainerFrame::GetStartPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
126 : {
127 0 : *_retval = nsnull;
128 :
129 0 : if (charnum >= GetNumberOfChars()) {
130 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
131 : }
132 :
133 0 : nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
134 0 : if (!node) {
135 0 : return NS_ERROR_FAILURE;
136 : }
137 :
138 : PRUint32 offset;
139 0 : nsSVGGlyphFrame *frame = GetGlyphFrameAtCharNum(node, charnum, &offset);
140 0 : if (!frame) {
141 0 : return NS_ERROR_FAILURE;
142 : }
143 :
144 0 : return frame->GetStartPositionOfChar(charnum - offset, _retval);
145 : }
146 :
147 : NS_IMETHODIMP
148 0 : nsSVGTextContainerFrame::GetEndPositionOfChar(PRUint32 charnum, nsIDOMSVGPoint **_retval)
149 : {
150 0 : *_retval = nsnull;
151 :
152 0 : if (charnum >= GetNumberOfChars()) {
153 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
154 : }
155 :
156 0 : nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
157 0 : if (!node) {
158 0 : return NS_ERROR_FAILURE;
159 : }
160 :
161 : PRUint32 offset;
162 0 : nsSVGGlyphFrame *frame = GetGlyphFrameAtCharNum(node, charnum, &offset);
163 0 : if (!frame) {
164 0 : return NS_ERROR_FAILURE;
165 : }
166 :
167 0 : return frame->GetEndPositionOfChar(charnum - offset, _retval);
168 : }
169 :
170 : NS_IMETHODIMP
171 0 : nsSVGTextContainerFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
172 : {
173 0 : *_retval = nsnull;
174 :
175 0 : if (charnum >= GetNumberOfChars()) {
176 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
177 : }
178 :
179 0 : nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
180 0 : if (!node) {
181 0 : return NS_ERROR_FAILURE;
182 : }
183 :
184 : PRUint32 offset;
185 0 : nsSVGGlyphFrame *frame = GetGlyphFrameAtCharNum(node, charnum, &offset);
186 0 : if (!frame) {
187 0 : return NS_ERROR_FAILURE;
188 : }
189 :
190 0 : return frame->GetExtentOfChar(charnum - offset, _retval);
191 : }
192 :
193 : NS_IMETHODIMP
194 0 : nsSVGTextContainerFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
195 : {
196 0 : *_retval = 0.0f;
197 :
198 0 : if (charnum >= GetNumberOfChars()) {
199 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
200 : }
201 :
202 0 : nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
203 0 : if (!node) {
204 0 : return NS_ERROR_FAILURE;
205 : }
206 :
207 : PRUint32 offset;
208 0 : nsSVGGlyphFrame *frame = GetGlyphFrameAtCharNum(node, charnum, &offset);
209 0 : if (!frame) {
210 0 : return NS_ERROR_FAILURE;
211 : }
212 :
213 0 : return frame->GetRotationOfChar(charnum - offset, _retval);
214 : }
215 :
216 : PRUint32
217 0 : nsSVGTextContainerFrame::GetNumberOfChars()
218 : {
219 0 : PRUint32 nchars = 0;
220 0 : nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
221 :
222 0 : while (node) {
223 0 : nchars += node->GetNumberOfChars();
224 0 : node = GetNextGlyphFragmentChildNode(node);
225 : }
226 :
227 0 : return nchars;
228 : }
229 :
230 : float
231 0 : nsSVGTextContainerFrame::GetComputedTextLength()
232 : {
233 0 : float length = 0.0f;
234 0 : nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
235 :
236 0 : while (node) {
237 0 : length += node->GetComputedTextLength();
238 0 : node = GetNextGlyphFragmentChildNode(node);
239 : }
240 :
241 0 : return length;
242 : }
243 :
244 : float
245 0 : nsSVGTextContainerFrame::GetSubStringLength(PRUint32 charnum, PRUint32 nchars)
246 : {
247 0 : float length = 0.0f;
248 0 : nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
249 :
250 0 : while (node) {
251 0 : PRUint32 count = node->GetNumberOfChars();
252 0 : if (count > charnum) {
253 0 : PRUint32 fragmentChars = NS_MIN(nchars, count);
254 0 : float fragmentLength = node->GetSubStringLength(charnum, fragmentChars);
255 0 : length += fragmentLength;
256 0 : nchars -= fragmentChars;
257 0 : if (nchars == 0) break;
258 : }
259 0 : charnum -= NS_MIN(charnum, count);
260 0 : node = GetNextGlyphFragmentChildNode(node);
261 : }
262 :
263 0 : return length;
264 : }
265 :
266 : PRInt32
267 0 : nsSVGTextContainerFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
268 : {
269 0 : PRInt32 index = -1;
270 0 : PRInt32 offset = 0;
271 0 : nsISVGGlyphFragmentNode *node = GetFirstGlyphFragmentChildNode();
272 :
273 0 : while (node) {
274 0 : PRUint32 count = node->GetNumberOfChars();
275 0 : if (count > 0) {
276 0 : PRInt32 charnum = node->GetCharNumAtPosition(point);
277 0 : if (charnum >= 0) {
278 0 : index = charnum + offset;
279 : }
280 0 : offset += count;
281 : // Keep going, multiple characters may match
282 : // and we must return the last one
283 : }
284 0 : node = GetNextGlyphFragmentChildNode(node);
285 : }
286 :
287 0 : return index;
288 : }
289 :
290 : // -------------------------------------------------------------------------
291 : // Protected functions
292 : // -------------------------------------------------------------------------
293 :
294 : nsISVGGlyphFragmentNode *
295 0 : nsSVGTextContainerFrame::GetFirstGlyphFragmentChildNode()
296 : {
297 0 : nsISVGGlyphFragmentNode *retval = nsnull;
298 0 : nsIFrame* kid = mFrames.FirstChild();
299 0 : while (kid) {
300 0 : retval = do_QueryFrame(kid);
301 0 : if (retval) break;
302 0 : kid = kid->GetNextSibling();
303 : }
304 0 : return retval;
305 : }
306 :
307 : nsISVGGlyphFragmentNode *
308 0 : nsSVGTextContainerFrame::GetNextGlyphFragmentChildNode(nsISVGGlyphFragmentNode *node)
309 : {
310 0 : nsISVGGlyphFragmentNode *retval = nsnull;
311 0 : nsIFrame *frame = do_QueryFrame(node);
312 0 : NS_ASSERTION(frame, "interface not implemented");
313 0 : frame = frame->GetNextSibling();
314 0 : while (frame) {
315 0 : retval = do_QueryFrame(frame);
316 0 : if (retval) break;
317 0 : frame = frame->GetNextSibling();
318 : }
319 0 : return retval;
320 : }
321 :
322 : // -------------------------------------------------------------------------
323 : // Private functions
324 : // -------------------------------------------------------------------------
325 :
326 : nsSVGGlyphFrame *
327 0 : nsSVGTextContainerFrame::GetGlyphFrameAtCharNum(nsISVGGlyphFragmentNode* node,
328 : PRUint32 charnum,
329 : PRUint32 *offset)
330 : {
331 0 : nsSVGGlyphFrame *frame = node->GetFirstGlyphFrame();
332 0 : *offset = 0;
333 :
334 0 : while (frame) {
335 0 : PRUint32 count = frame->GetNumberOfChars();
336 0 : if (count > charnum)
337 0 : return frame;
338 0 : charnum -= count;
339 0 : *offset += count;
340 0 : frame = frame->GetNextGlyphFrame();
341 : }
342 :
343 : // not found
344 0 : return nsnull;
345 : }
346 :
347 : nsSVGTextFrame *
348 0 : nsSVGTextContainerFrame::GetTextFrame()
349 : {
350 0 : for (nsIFrame *frame = this; frame != nsnull; frame = frame->GetParent()) {
351 0 : if (frame->GetType() == nsGkAtoms::svgTextFrame) {
352 0 : return static_cast<nsSVGTextFrame*>(frame);
353 : }
354 : }
355 0 : return nsnull;
356 : }
357 :
358 : void
359 0 : nsSVGTextContainerFrame::CopyPositionList(nsTArray<float> *parentList,
360 : SVGUserUnitList *selfList,
361 : nsTArray<float> &dstList,
362 : PRUint32 aOffset)
363 : {
364 0 : dstList.Clear();
365 :
366 0 : PRUint32 strLength = GetNumberOfChars();
367 0 : PRUint32 parentCount = 0;
368 0 : if (parentList && parentList->Length() > aOffset) {
369 0 : parentCount = NS_MIN(parentList->Length() - aOffset, strLength);
370 : }
371 :
372 0 : PRUint32 selfCount = NS_MIN(selfList->Length(), strLength);
373 :
374 0 : PRUint32 count = NS_MAX(parentCount, selfCount);
375 :
376 0 : if (!dstList.SetLength(count))
377 0 : return;
378 :
379 0 : for (PRUint32 i = 0; i < selfCount; i++) {
380 0 : dstList[i] = (*selfList)[i];
381 : }
382 0 : for (PRUint32 i = selfCount; i < parentCount; i++) {
383 0 : dstList[i] = (*parentList)[aOffset + i];
384 : }
385 :
386 : }
387 :
388 : void
389 0 : nsSVGTextContainerFrame::CopyRotateList(nsTArray<float> *parentList,
390 : const SVGNumberList *selfList,
391 : nsTArray<float> &dstList,
392 : PRUint32 aOffset)
393 : {
394 0 : dstList.Clear();
395 :
396 0 : PRUint32 strLength = GetNumberOfChars();
397 0 : PRUint32 parentCount = 0;
398 0 : if (parentList && parentList->Length() > aOffset) {
399 0 : parentCount = NS_MIN(parentList->Length() - aOffset, strLength);
400 : }
401 :
402 0 : PRUint32 selfCount = NS_MIN(selfList ? selfList->Length() : 0, strLength);
403 0 : PRUint32 count = NS_MAX(parentCount, selfCount);
404 :
405 0 : if (count > 0) {
406 0 : if (!dstList.SetLength(count))
407 0 : return;
408 0 : for (PRUint32 i = 0; i < selfCount; i++) {
409 0 : dstList[i] = (*selfList)[i];
410 : }
411 0 : for (PRUint32 i = selfCount; i < parentCount; i++) {
412 0 : dstList[i] = (*parentList)[aOffset + i];
413 : }
414 0 : } else if (parentList && !parentList->IsEmpty()) {
415 : // rotate is applied to extra characters too
416 0 : dstList.AppendElement((*parentList)[parentList->Length() - 1]);
417 : }
418 : }
419 :
420 : PRUint32
421 0 : nsSVGTextContainerFrame::BuildPositionList(PRUint32 aOffset,
422 : PRUint32 aDepth)
423 : {
424 0 : nsSVGTextContainerFrame *parent = do_QueryFrame(mParent);
425 0 : nsTArray<float> *parentX = nsnull, *parentY = nsnull;
426 0 : nsTArray<float> *parentDx = nsnull, *parentDy = nsnull;
427 0 : nsTArray<float> *parentRotate = nsnull;
428 0 : if (parent) {
429 0 : parentX = &(parent->mX);
430 0 : parentY = &(parent->mY);
431 0 : parentDx = &(parent->mDx);
432 0 : parentDy = &(parent->mDy);
433 0 : parentRotate = &(parent->mRotate);
434 : }
435 :
436 0 : SVGUserUnitList x, y;
437 0 : GetXY(&x, &y);
438 0 : CopyPositionList(parentX, &x, mX, aOffset);
439 0 : CopyPositionList(parentY, &y, mY, aOffset);
440 :
441 0 : SVGUserUnitList dx, dy;
442 0 : GetDxDy(&dx, &dy);
443 0 : CopyPositionList(parentDx, &dx, mDx, aOffset);
444 0 : CopyPositionList(parentDy, &dy, mDy, aOffset);
445 :
446 0 : const SVGNumberList *rotate = GetRotate();
447 0 : CopyRotateList(parentRotate, rotate, mRotate, aOffset);
448 :
449 0 : PRUint32 startIndex = 0;
450 0 : nsIFrame* kid = mFrames.FirstChild();
451 0 : while (kid) {
452 0 : nsSVGTextContainerFrame *text = do_QueryFrame(kid);
453 0 : if (text) {
454 0 : startIndex += text->BuildPositionList(startIndex, aDepth + 1);
455 0 : } else if (kid->GetType() == nsGkAtoms::svgGlyphFrame) {
456 0 : nsSVGGlyphFrame *leaf = static_cast<nsSVGGlyphFrame*>(kid);
457 0 : leaf->SetStartIndex(startIndex);
458 0 : startIndex += leaf->GetNumberOfChars();
459 : }
460 0 : kid = kid->GetNextSibling();
461 : }
462 0 : return startIndex;
463 : }
464 :
465 : void
466 0 : nsSVGTextContainerFrame::GetEffectiveXY(nsTArray<float> &aX,
467 : nsTArray<float> &aY)
468 : {
469 0 : aX.AppendElements(mX);
470 0 : aY.AppendElements(mY);
471 0 : }
472 :
473 : void
474 0 : nsSVGTextContainerFrame::GetEffectiveDxDy(nsTArray<float> &aDx,
475 : nsTArray<float> &aDy)
476 : {
477 0 : aDx.AppendElements(mDx);
478 0 : aDy.AppendElements(mDy);
479 0 : }
480 :
481 : void
482 0 : nsSVGTextContainerFrame::GetEffectiveRotate(nsTArray<float> &aRotate)
483 : {
484 0 : aRotate.AppendElements(mRotate);
485 0 : }
486 :
487 : void
488 0 : nsSVGTextContainerFrame::SetWhitespaceCompression()
489 : {
490 0 : bool compressWhitespace = true;
491 :
492 0 : for (const nsIFrame *frame = this; frame != nsnull; frame = frame->GetParent()) {
493 : static const nsIContent::AttrValuesArray strings[] =
494 : {&nsGkAtoms::preserve, &nsGkAtoms::_default, nsnull};
495 :
496 0 : PRInt32 index = frame->GetContent()->FindAttrValueIn(
497 : kNameSpaceID_XML,
498 : nsGkAtoms::space,
499 0 : strings, eCaseMatters);
500 0 : if (index == 0) {
501 0 : compressWhitespace = false;
502 0 : break;
503 : }
504 0 : if (index != nsIContent::ATTR_MISSING ||
505 0 : (frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))
506 0 : break;
507 : }
508 :
509 0 : nsISVGGlyphFragmentNode* node = GetFirstGlyphFragmentChildNode();
510 :
511 0 : while (node) {
512 0 : node->SetWhitespaceCompression(compressWhitespace);
513 0 : node = GetNextGlyphFragmentChildNode(node);
514 : }
515 0 : }
|