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 :
39 : #include "nsSVGTextFrame.h"
40 : #include "mozilla/LookAndFeel.h"
41 : #include "nsTextFragment.h"
42 : #include "nsBidiPresUtils.h"
43 : #include "nsSVGUtils.h"
44 : #include "SVGLengthList.h"
45 : #include "nsIDOMSVGLength.h"
46 : #include "nsIDOMSVGRect.h"
47 : #include "DOMSVGPoint.h"
48 : #include "nsSVGGlyphFrame.h"
49 : #include "nsSVGTextPathFrame.h"
50 : #include "nsSVGPathElement.h"
51 : #include "nsSVGRect.h"
52 : #include "nsDOMError.h"
53 : #include "gfxContext.h"
54 : #include "gfxMatrix.h"
55 : #include "gfxPlatform.h"
56 : #include "nsSVGEffects.h"
57 : #include "nsSVGPaintServerFrame.h"
58 :
59 : using namespace mozilla;
60 :
61 0 : struct CharacterPosition {
62 : gfxPoint pos;
63 : gfxFloat angle;
64 : bool draw;
65 : };
66 :
67 0 : static gfxContext* MakeTmpCtx() {
68 0 : return new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
69 : }
70 :
71 : /**
72 : * This is a do-it-all helper class. It supports iterating through the
73 : * drawable character clusters of a string. For each cluster, it can set up
74 : * a graphics context with a transform appropriate for drawing the
75 : * character, or a transform appropriate for emitting geometry in the
76 : * text metrics coordinate system (which differs from the drawing
77 : * coordinate system by a scale factor of AppUnitPerCSSPixels). These
78 : * transforms include offsets and rotations of characters along paths, and
79 : * the mPosition of the nsSVGGlyphFrame.
80 : *
81 : * This helper also creates the textrun as needed. It supports detecting
82 : * the special case when the entire textrun can be drawn or measured
83 : * as a unit, and setting the graphics context transform up for that. It
84 : * takes care of setting up the global transform if requested. It also
85 : * provides direct access to the character path position data for the
86 : * DOM APIs that need that.
87 : *
88 : * If an error occurs, for example, a canvas TM is not available because
89 : * the element is in a <defs> section, then the CharacterIterator will
90 : * behave as if the frame has no drawable characters.
91 : *
92 : * XXX needs RTL love
93 : * XXX might want to make AdvanceToCharacter constant time (e.g. by
94 : * caching advances and/or the CharacterPosition array across DOM
95 : * API calls) to ensure that calling Get*OfChar (etc) for each character
96 : * in the text is O(N)
97 : */
98 : class CharacterIterator
99 0 : {
100 : public:
101 : /**
102 : * Sets up the iterator so that NextCluster will return the first drawable
103 : * cluster.
104 : * @param aForceGlobalTransform passed on to EnsureTextRun (see below)
105 : */
106 : CharacterIterator(nsSVGGlyphFrame *aSource, bool aForceGlobalTransform);
107 : /**
108 : * This matrix will be applied to aContext in the SetupFor methods below,
109 : * before any glyph translation/rotation.
110 : */
111 0 : void SetInitialMatrix(gfxContext *aContext) {
112 0 : mInitialMatrix = aContext->CurrentMatrix();
113 0 : if (mInitialMatrix.IsSingular()) {
114 0 : mInError = true;
115 : }
116 0 : }
117 : /**
118 : * Try to set up aContext so we can draw the whole textrun at once.
119 : * This applies any global transform requested by SetInitialMatrix,
120 : * then applies the positioning of the text. Returns false if drawing
121 : * the whole textrun at once is impossible due to individual positioning
122 : * and/or rotation of glyphs.
123 : */
124 0 : bool SetupForDirectTextRunDrawing(gfxContext *aContext) {
125 0 : return SetupForDirectTextRun(aContext, mDrawScale);
126 : }
127 : /**
128 : * Try to set up aContext so we can measure the whole textrun at once.
129 : * This applies any global transform requested by SetInitialMatrix,
130 : * then applies the positioning of the text, then applies a scale
131 : * from appunits to device pixels so drawing in appunits works.
132 : * Returns false if drawing the whole textrun at once is impossible due
133 : * to individual positioning and/or rotation of glyphs.
134 : */
135 0 : bool SetupForDirectTextRunMetrics(gfxContext *aContext) {
136 0 : return SetupForDirectTextRun(aContext, mMetricsScale);
137 : }
138 : /**
139 : * We are scaling the glyphs up/down to the size we want so we need to
140 : * inverse scale the outline widths of those glyphs so they are invariant
141 : */
142 0 : void SetLineWidthAndDashesForDrawing(gfxContext *aContext) {
143 0 : aContext->SetLineWidth(aContext->CurrentLineWidth() / mDrawScale);
144 0 : AutoFallibleTArray<gfxFloat, 10> dashes;
145 : gfxFloat dashOffset;
146 0 : if (aContext->CurrentDash(dashes, &dashOffset)) {
147 0 : for (PRUint32 i = 0; i < dashes.Length(); i++) {
148 0 : dashes[i] /= mDrawScale;
149 : }
150 0 : aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset / mDrawScale);
151 : }
152 0 : }
153 :
154 : /**
155 : * Returns the index of the next cluster in the string that should be drawn,
156 : * or InvalidCluster() (i.e. PRUint32(-1)) if there is no such cluster.
157 : */
158 : PRUint32 NextCluster();
159 :
160 : /**
161 : * Returns the length of the current cluster (usually 1, unless there
162 : * are combining marks)
163 : */
164 : PRUint32 ClusterLength();
165 :
166 : /**
167 : * Repeated calls NextCluster until it returns aIndex (i.e. aIndex is the
168 : * current drawable character). Returns false if that never happens
169 : * (because aIndex is before or equal to the current character, or
170 : * out of bounds, or not drawable).
171 : */
172 : bool AdvanceToCharacter(PRUint32 aIndex);
173 :
174 : /**
175 : * Resets the iterator to the beginning of the string.
176 : */
177 : void Reset() {
178 : // There are two ways mInError can be set
179 : // a) If there was a problem creating the iterator (mCurrentChar == -1)
180 : // b) If we ran off the end of the string (mCurrentChar != -1)
181 : // We can only reset the mInError flag in case b)
182 : if (mCurrentChar != InvalidCluster()) {
183 : mCurrentChar = InvalidCluster();
184 : mInError = false;
185 : }
186 : }
187 :
188 : /**
189 : * Set up aContext for glyph drawing. This applies any global transform
190 : * requested by SetInitialMatrix, then applies any positioning and
191 : * rotation for the current character.
192 : */
193 0 : void SetupForDrawing(gfxContext *aContext) {
194 0 : return SetupFor(aContext, mDrawScale);
195 : }
196 : /**
197 : * Set up aContext for glyph measuring. This applies any global transform
198 : * requested by SetInitialMatrix, then applies any positioning and
199 : * rotation for the current character, then applies a scale from appunits
200 : * to device pixels so that drawing in appunits sizes works.
201 : */
202 0 : void SetupForMetrics(gfxContext *aContext) {
203 0 : return SetupFor(aContext, mMetricsScale);
204 : }
205 : /**
206 : * Get the raw position data for the current character.
207 : */
208 : CharacterPosition GetPositionData();
209 :
210 : /**
211 : * "Invalid" cluster index returned to indicate error state
212 : */
213 0 : PRUint32 InvalidCluster() {
214 0 : return PRUint32(-1);
215 : }
216 :
217 : private:
218 : bool SetupForDirectTextRun(gfxContext *aContext, float aScale);
219 : void SetupFor(gfxContext *aContext, float aScale);
220 :
221 : nsSVGGlyphFrame *mSource;
222 : nsAutoTArray<CharacterPosition,80> mPositions;
223 : gfxMatrix mInitialMatrix;
224 : // Textrun advance width from start to mCurrentChar, in appunits
225 : gfxFloat mCurrentAdvance;
226 : PRUint32 mCurrentChar;
227 : float mDrawScale;
228 : float mMetricsScale;
229 : bool mInError;
230 : };
231 :
232 : //----------------------------------------------------------------------
233 : // Implementation
234 :
235 : nsIFrame*
236 0 : NS_NewSVGGlyphFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
237 : {
238 0 : return new (aPresShell) nsSVGGlyphFrame(aContext);
239 : }
240 :
241 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGGlyphFrame)
242 :
243 : //----------------------------------------------------------------------
244 : // nsQueryFrame methods
245 :
246 0 : NS_QUERYFRAME_HEAD(nsSVGGlyphFrame)
247 0 : NS_QUERYFRAME_ENTRY(nsISVGGlyphFragmentNode)
248 0 : NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
249 0 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGGlyphFrameBase)
250 :
251 : //----------------------------------------------------------------------
252 : // nsIFrame methods
253 :
254 : NS_IMETHODIMP
255 0 : nsSVGGlyphFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
256 : {
257 0 : ClearTextRun();
258 0 : NotifyGlyphMetricsChange();
259 0 : if (IsTextEmpty()) {
260 : // That's it for this frame. Leave no trace we were here
261 0 : nsSVGUtils::UpdateGraphic(this);
262 : }
263 :
264 0 : return NS_OK;
265 : }
266 :
267 : // Usable font size range in devpixels / user-units
268 : #define CLAMP_MIN_SIZE 8
269 : #define CLAMP_MAX_SIZE 200
270 : #define PRECISE_SIZE 200
271 :
272 : /* virtual */ void
273 0 : nsSVGGlyphFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
274 : {
275 0 : nsSVGGlyphFrameBase::DidSetStyleContext(aOldStyleContext);
276 :
277 0 : if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
278 0 : ClearTextRun();
279 0 : NotifyGlyphMetricsChange();
280 : }
281 0 : }
282 :
283 : NS_IMETHODIMP
284 0 : nsSVGGlyphFrame::IsSelectable(bool* aIsSelectable,
285 : PRUint8* aSelectStyle) const
286 : {
287 0 : nsresult rv = nsSVGGlyphFrameBase::IsSelectable(aIsSelectable, aSelectStyle);
288 : #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
289 : printf("nsSVGGlyphFrame(%p)::IsSelectable()=(%d,%d)\n", this, *aIsSelectable, aSelectStyle);
290 : #endif
291 0 : return rv;
292 : }
293 :
294 : NS_IMETHODIMP
295 0 : nsSVGGlyphFrame::Init(nsIContent* aContent,
296 : nsIFrame* aParent,
297 : nsIFrame* aPrevInFlow)
298 : {
299 : #ifdef DEBUG
300 0 : NS_ASSERTION(aParent, "null parent");
301 :
302 0 : nsIFrame* ancestorFrame = nsSVGUtils::GetFirstNonAAncestorFrame(aParent);
303 0 : NS_ASSERTION(ancestorFrame, "Must have ancestor");
304 :
305 0 : nsSVGTextContainerFrame *metrics = do_QueryFrame(ancestorFrame);
306 0 : NS_ASSERTION(metrics,
307 : "trying to construct an SVGGlyphFrame for an invalid container");
308 :
309 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
310 : "trying to construct an SVGGlyphFrame for wrong content element");
311 : #endif /* DEBUG */
312 :
313 0 : return nsSVGGlyphFrameBase::Init(aContent, aParent, aPrevInFlow);
314 : }
315 :
316 : nsIAtom *
317 0 : nsSVGGlyphFrame::GetType() const
318 : {
319 0 : return nsGkAtoms::svgGlyphFrame;
320 : }
321 :
322 : //----------------------------------------------------------------------
323 : // nsISVGChildFrame methods
324 :
325 : NS_IMETHODIMP
326 0 : nsSVGGlyphFrame::PaintSVG(nsRenderingContext *aContext,
327 : const nsIntRect *aDirtyRect)
328 : {
329 0 : if (!GetStyleVisibility()->IsVisible())
330 0 : return NS_OK;
331 :
332 0 : gfxContext *gfx = aContext->ThebesContext();
333 0 : PRUint16 renderMode = SVGAutoRenderState::GetRenderMode(aContext);
334 :
335 0 : switch (GetStyleSVG()->mTextRendering) {
336 : case NS_STYLE_TEXT_RENDERING_OPTIMIZESPEED:
337 0 : gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
338 0 : break;
339 : default:
340 0 : gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
341 0 : break;
342 : }
343 :
344 0 : if (renderMode != SVGAutoRenderState::NORMAL) {
345 :
346 0 : gfxMatrix matrix = gfx->CurrentMatrix();
347 0 : SetupGlobalTransform(gfx);
348 :
349 0 : CharacterIterator iter(this, true);
350 0 : iter.SetInitialMatrix(gfx);
351 :
352 0 : if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD)
353 0 : gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
354 : else
355 0 : gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
356 :
357 0 : if (renderMode == SVGAutoRenderState::CLIP_MASK) {
358 0 : gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
359 0 : DrawCharacters(&iter, gfx, gfxFont::GLYPH_FILL);
360 : } else {
361 0 : DrawCharacters(&iter, gfx, gfxFont::GLYPH_PATH);
362 : }
363 :
364 0 : gfx->SetMatrix(matrix);
365 0 : return NS_OK;
366 : }
367 :
368 : // We are adding patterns or gradients to the context. Save
369 : // it so we don't leak them into the next object we draw
370 0 : gfx->Save();
371 0 : SetupGlobalTransform(gfx);
372 :
373 0 : CharacterIterator iter(this, true);
374 0 : iter.SetInitialMatrix(gfx);
375 :
376 0 : nsRefPtr<gfxPattern> strokePattern;
377 0 : DrawMode drawMode = SetupCairoState(gfx, getter_AddRefs(strokePattern));
378 :
379 0 : if (drawMode) {
380 0 : DrawCharacters(&iter, gfx, drawMode, strokePattern);
381 : }
382 :
383 0 : gfx->Restore();
384 :
385 0 : return NS_OK;
386 : }
387 :
388 : NS_IMETHODIMP_(nsIFrame*)
389 0 : nsSVGGlyphFrame::GetFrameForPoint(const nsPoint &aPoint)
390 : {
391 0 : PRUint16 hitTestFlags = GetHitTestFlags();
392 0 : if (!hitTestFlags) {
393 0 : return nsnull;
394 : }
395 :
396 0 : nsRefPtr<gfxContext> context = MakeTmpCtx();
397 0 : SetupGlobalTransform(context);
398 0 : CharacterIterator iter(this, true);
399 0 : iter.SetInitialMatrix(context);
400 :
401 : // The SVG 1.1 spec says that text is hit tested against the character cells
402 : // of the text, not the fill and stroke. See the section starting "For text
403 : // elements..." here:
404 : //
405 : // http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
406 : //
407 : // Currently we just test the character cells if GetHitTestFlags says we're
408 : // supposed to be testing either the fill OR the stroke:
409 :
410 : PRUint32 i;
411 0 : while ((i = iter.NextCluster()) != iter.InvalidCluster()) {
412 : gfxTextRun::Metrics metrics =
413 : mTextRun->MeasureText(i, iter.ClusterLength(),
414 0 : gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
415 0 : iter.SetupForMetrics(context);
416 0 : context->Rectangle(metrics.mBoundingBox);
417 : }
418 :
419 : gfxPoint userSpacePoint =
420 : context->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
421 0 : PresContext()->AppUnitsToGfxUnits(aPoint.y)));
422 :
423 0 : bool isHit = false;
424 0 : if (hitTestFlags & SVG_HIT_TEST_FILL || hitTestFlags & SVG_HIT_TEST_STROKE) {
425 0 : isHit = context->PointInFill(userSpacePoint);
426 : }
427 :
428 : // If isHit is false, we may also want to fill and stroke the text to check
429 : // whether the pointer is over an area of fill or stroke that lies outside
430 : // the character cells. (With a thick stroke, or with fonts like Zapfino, such
431 : // areas may be very significant.) This is what Opera appears to do, but
432 : // currently we do not.
433 :
434 0 : if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
435 0 : return this;
436 :
437 0 : return nsnull;
438 : }
439 :
440 : NS_IMETHODIMP_(nsRect)
441 0 : nsSVGGlyphFrame::GetCoveredRegion()
442 : {
443 : // See bug 614732 comment 32:
444 : //return nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext());
445 0 : return mCoveredRegion;
446 : }
447 :
448 : NS_IMETHODIMP
449 0 : nsSVGGlyphFrame::UpdateCoveredRegion()
450 : {
451 0 : mRect.SetEmpty();
452 :
453 : // XXX here we have tmpCtx use its default identity matrix, but does this
454 : // function call anything that will call GetCanvasTM and break things?
455 0 : nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
456 :
457 0 : bool hasStroke = HasStroke();
458 0 : if (hasStroke) {
459 0 : SetupCairoStrokeGeometry(tmpCtx);
460 0 : } else if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_None) {
461 0 : return NS_OK;
462 : }
463 :
464 0 : CharacterIterator iter(this, true);
465 0 : iter.SetInitialMatrix(tmpCtx);
466 0 : AddBoundingBoxesToPath(&iter, tmpCtx);
467 0 : tmpCtx->IdentityMatrix();
468 :
469 : // Be careful when replacing the following logic to get the fill and stroke
470 : // extents independently (instead of computing the stroke extents from the
471 : // path extents). You may think that you can just use the stroke extents if
472 : // there is both a fill and a stroke. In reality it's necessary to calculate
473 : // both the fill and stroke extents, and take the union of the two. There are
474 : // two reasons for this:
475 : //
476 : // # Due to stroke dashing, in certain cases the fill extents could actually
477 : // extend outside the stroke extents.
478 : // # If the stroke is very thin, cairo won't paint any stroke, and so the
479 : // stroke bounds that it will return will be empty.
480 : //
481 : // Another thing to be aware of is that under AddBoundingBoxesToPath the
482 : // gfxContext has SetLineWidth() called on it, so if we want to ask the
483 : // gfxContext for *stroke* extents, we'll neet to wrap the
484 : // AddBoundingBoxesToPath() call with CurrentLineWidth()/SetLineWidth()
485 : // calls to record and then reset the stroke width.
486 0 : gfxRect extent = tmpCtx->GetUserPathExtent();
487 0 : if (hasStroke) {
488 : extent =
489 0 : nsSVGUtils::PathExtentsToMaxStrokeExtents(extent, this, gfxMatrix());
490 : }
491 :
492 0 : if (!extent.IsEmpty()) {
493 : mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
494 0 : PresContext()->AppUnitsPerCSSPixel());
495 : }
496 :
497 : // See bug 614732 comment 32.
498 : mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
499 0 : mRect, GetCanvasTM(), PresContext());
500 :
501 0 : return NS_OK;
502 : }
503 :
504 : NS_IMETHODIMP
505 0 : nsSVGGlyphFrame::InitialUpdate()
506 : {
507 0 : NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
508 : "Yikes! We've been called already! Hopefully we weren't called "
509 : "before our nsSVGOuterSVGFrame's initial Reflow()!!!");
510 :
511 0 : NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
512 : "We don't actually participate in reflow");
513 :
514 : // Do unset the various reflow bits, though.
515 : mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
516 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
517 :
518 0 : return NS_OK;
519 : }
520 :
521 : void
522 0 : nsSVGGlyphFrame::NotifySVGChanged(PRUint32 aFlags)
523 : {
524 0 : NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
525 : (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
526 : "Must be NS_STATE_SVG_NONDISPLAY_CHILD!");
527 :
528 0 : NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
529 : "Invalidation logic may need adjusting");
530 :
531 0 : if (aFlags & TRANSFORM_CHANGED) {
532 0 : ClearTextRun();
533 : }
534 0 : if (!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS)) {
535 0 : nsSVGUtils::UpdateGraphic(this);
536 : }
537 0 : }
538 :
539 : void
540 0 : nsSVGGlyphFrame::NotifyRedrawSuspended()
541 : {
542 0 : AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
543 0 : }
544 :
545 : void
546 0 : nsSVGGlyphFrame::NotifyRedrawUnsuspended()
547 : {
548 0 : RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
549 :
550 0 : if (GetStateBits() & NS_STATE_SVG_DIRTY)
551 0 : nsSVGUtils::UpdateGraphic(this);
552 0 : }
553 :
554 : void
555 0 : nsSVGGlyphFrame::AddBoundingBoxesToPath(CharacterIterator *aIter,
556 : gfxContext *aContext)
557 : {
558 0 : if (aIter->SetupForDirectTextRunMetrics(aContext)) {
559 : gfxTextRun::Metrics metrics =
560 : mTextRun->MeasureText(0, mTextRun->GetLength(),
561 0 : gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
562 0 : aContext->Rectangle(metrics.mBoundingBox);
563 0 : return;
564 : }
565 :
566 : PRUint32 i;
567 0 : while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
568 0 : aIter->SetupForMetrics(aContext);
569 : gfxTextRun::Metrics metrics =
570 : mTextRun->MeasureText(i, aIter->ClusterLength(),
571 0 : gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
572 0 : aContext->Rectangle(metrics.mBoundingBox);
573 : }
574 : }
575 :
576 : void
577 0 : nsSVGGlyphFrame::DrawCharacters(CharacterIterator *aIter,
578 : gfxContext *aContext,
579 : DrawMode aDrawMode,
580 : gfxPattern *aStrokePattern)
581 : {
582 0 : if (aDrawMode & gfxFont::GLYPH_STROKE) {
583 0 : aIter->SetLineWidthAndDashesForDrawing(aContext);
584 : }
585 :
586 0 : if (aIter->SetupForDirectTextRunDrawing(aContext)) {
587 : mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, 0,
588 0 : mTextRun->GetLength(), nsnull, nsnull, aStrokePattern);
589 0 : return;
590 : }
591 :
592 : PRUint32 i;
593 0 : while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) {
594 0 : aIter->SetupForDrawing(aContext);
595 : mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, i,
596 0 : aIter->ClusterLength(), nsnull, nsnull, aStrokePattern);
597 : }
598 : }
599 :
600 : gfxRect
601 0 : nsSVGGlyphFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
602 : PRUint32 aFlags)
603 : {
604 0 : if (mOverrideCanvasTM) {
605 0 : *mOverrideCanvasTM = aToBBoxUserspace;
606 : } else {
607 0 : mOverrideCanvasTM = new gfxMatrix(aToBBoxUserspace);
608 : }
609 :
610 0 : nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
611 0 : SetupGlobalTransform(tmpCtx);
612 0 : CharacterIterator iter(this, true);
613 0 : iter.SetInitialMatrix(tmpCtx);
614 0 : AddBoundingBoxesToPath(&iter, tmpCtx);
615 0 : tmpCtx->IdentityMatrix();
616 :
617 0 : mOverrideCanvasTM = nsnull;
618 :
619 0 : gfxRect bbox;
620 :
621 0 : gfxRect pathExtents = tmpCtx->GetUserPathExtent();
622 :
623 : // Account for fill:
624 0 : if ((aFlags & nsSVGUtils::eBBoxIncludeFill) != 0 &&
625 : ((aFlags & nsSVGUtils::eBBoxIgnoreFillIfNone) == 0 ||
626 0 : GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
627 0 : bbox = pathExtents;
628 : }
629 :
630 : // Account for stroke:
631 0 : if ((aFlags & nsSVGUtils::eBBoxIncludeStroke) != 0 &&
632 0 : ((aFlags & nsSVGUtils::eBBoxIgnoreStrokeIfNone) == 0 || HasStroke())) {
633 : bbox =
634 : bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
635 : this,
636 0 : aToBBoxUserspace));
637 : }
638 :
639 : return bbox;
640 : }
641 :
642 : //----------------------------------------------------------------------
643 : // nsSVGGeometryFrame methods:
644 :
645 : gfxMatrix
646 0 : nsSVGGlyphFrame::GetCanvasTM()
647 : {
648 0 : if (mOverrideCanvasTM) {
649 0 : return *mOverrideCanvasTM;
650 : }
651 0 : NS_ASSERTION(mParent, "null parent");
652 0 : return static_cast<nsSVGContainerFrame*>(mParent)->GetCanvasTM();
653 : }
654 :
655 : //----------------------------------------------------------------------
656 : // nsSVGGlyphFrame methods:
657 :
658 : bool
659 0 : nsSVGGlyphFrame::GetCharacterData(nsAString & aCharacterData)
660 : {
661 0 : nsAutoString characterData;
662 0 : mContent->AppendTextTo(characterData);
663 :
664 0 : if (mCompressWhitespace) {
665 : characterData.CompressWhitespace(mTrimLeadingWhitespace,
666 0 : mTrimTrailingWhitespace);
667 : } else {
668 0 : nsAString::iterator start, end;
669 0 : characterData.BeginWriting(start);
670 0 : characterData.EndWriting(end);
671 0 : while (start != end) {
672 0 : if (NS_IsAsciiWhitespace(*start))
673 0 : *start = ' ';
674 0 : ++start;
675 : }
676 : }
677 0 : aCharacterData = characterData;
678 :
679 0 : return !characterData.IsEmpty();
680 : }
681 :
682 : bool
683 0 : nsSVGGlyphFrame::GetCharacterPositions(nsTArray<CharacterPosition>* aCharacterPositions,
684 : float aMetricsScale)
685 : {
686 0 : PRUint32 strLength = mTextRun->GetLength();
687 0 : NS_ABORT_IF_FALSE(strLength > 0, "no text");
688 :
689 0 : const gfxFloat radPerDeg = M_PI / 180.0;
690 :
691 0 : nsTArray<float> xList, yList;
692 0 : GetEffectiveXY(strLength, xList, yList);
693 0 : nsTArray<float> dxList, dyList;
694 0 : GetEffectiveDxDy(strLength, dxList, dyList);
695 0 : nsTArray<float> rotateList;
696 0 : GetEffectiveRotate(strLength, rotateList);
697 :
698 0 : gfxPoint pos = mPosition;
699 0 : gfxFloat angle = 0.0;
700 :
701 0 : nsSVGTextPathFrame *textPath = FindTextPathParent();
702 :
703 0 : if (textPath) {
704 0 : nsRefPtr<gfxFlattenedPath> data = textPath->GetFlattenedPath();
705 :
706 : // textPath frame, but invalid target
707 0 : if (!data)
708 0 : return false;
709 :
710 0 : if (!aCharacterPositions->SetLength(strLength))
711 0 : return false;
712 :
713 0 : gfxFloat pathScale = textPath->GetOffsetScale();
714 :
715 0 : CharacterPosition *cp = aCharacterPositions->Elements();
716 :
717 0 : gfxFloat length = data->GetLength();
718 :
719 0 : for (PRUint32 i = 0; i < strLength; i++) {
720 : gfxFloat halfAdvance =
721 0 : mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale / 2.0;
722 :
723 : // use only x position for horizontal writing
724 0 : if (i > 0 && i < xList.Length()) {
725 0 : pos.x = xList[i];
726 : }
727 0 : pos.x += (i > 0 && i < dxList.Length()) ? dxList[i] * pathScale : 0.0;
728 0 : pos.y += (i > 0 && i < dyList.Length()) ? dyList[i] * pathScale : 0.0;
729 0 : if (i < rotateList.Length()) {
730 0 : angle = rotateList[i] * radPerDeg;
731 : }
732 :
733 : // check that we're within the path boundaries
734 0 : cp[i].draw = (pos.x + halfAdvance >= 0.0 &&
735 0 : pos.x + halfAdvance <= length);
736 :
737 0 : if (cp[i].draw) {
738 :
739 : // add y (normal)
740 : // add rotation
741 : // move point back along tangent
742 : gfxPoint pt = data->FindPoint(gfxPoint(pos.x + halfAdvance, pos.y),
743 0 : &(cp[i].angle));
744 : cp[i].pos =
745 0 : pt - gfxPoint(cos(cp[i].angle), sin(cp[i].angle)) * halfAdvance;
746 0 : cp[i].angle += angle;
747 : }
748 0 : pos.x += 2 * halfAdvance;
749 : }
750 0 : return true;
751 : }
752 :
753 0 : if (xList.Length() <= 1 &&
754 0 : yList.Length() <= 1 &&
755 0 : dxList.Length() <= 1 &&
756 0 : dyList.Length() <= 1 &&
757 0 : rotateList.IsEmpty()) {
758 : // simple text without individual positioning
759 0 : return true;
760 : }
761 :
762 0 : if (!aCharacterPositions->SetLength(strLength))
763 0 : return false;
764 :
765 0 : CharacterPosition *cp = aCharacterPositions->Elements();
766 :
767 0 : PRUint16 anchor = GetTextAnchor();
768 :
769 0 : for (PRUint32 i = 0; i < strLength; i++) {
770 0 : cp[i].draw = true;
771 :
772 0 : gfxFloat advance = mTextRun->GetAdvanceWidth(i, 1, nsnull)*aMetricsScale;
773 0 : if (xList.Length() > 1 && i < xList.Length()) {
774 0 : pos.x = xList[i];
775 :
776 : // apply text-anchor to character
777 0 : if (anchor == NS_STYLE_TEXT_ANCHOR_MIDDLE)
778 0 : pos.x -= advance/2.0;
779 0 : else if (anchor == NS_STYLE_TEXT_ANCHOR_END)
780 0 : pos.x -= advance;
781 : }
782 0 : if (yList.Length() > 1 && i < yList.Length()) {
783 0 : pos.y = yList[i];
784 : }
785 0 : pos.x += (i > 0 && i < dxList.Length()) ? dxList[i] : 0.0;
786 0 : pos.y += (i > 0 && i < dyList.Length()) ? dyList[i] : 0.0;
787 0 : cp[i].pos = pos;
788 0 : pos.x += advance;
789 0 : if (i < rotateList.Length()) {
790 0 : angle = rotateList[i] * radPerDeg;
791 : }
792 0 : cp[i].angle = angle;
793 : }
794 0 : return true;
795 : }
796 :
797 : PRUint32
798 0 : nsSVGGlyphFrame::GetTextRunFlags(PRUint32 strLength)
799 : {
800 : // Keep the logic here consistent with GetCharacterPositions
801 :
802 0 : if (FindTextPathParent()) {
803 0 : return gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES;
804 : }
805 :
806 0 : nsTArray<float> xList, yList;
807 0 : GetEffectiveXY(strLength, xList, yList);
808 0 : nsTArray<float> dxList, dyList;
809 0 : GetEffectiveDxDy(strLength, dxList, dyList);
810 0 : nsTArray<float> rotateList;
811 0 : GetEffectiveRotate(strLength, rotateList);
812 :
813 0 : return (xList.Length() > 1 ||
814 0 : yList.Length() > 1 ||
815 0 : dxList.Length() > 1 ||
816 0 : dyList.Length() > 1 ||
817 0 : !rotateList.IsEmpty()) ?
818 0 : gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES : 0;
819 : }
820 :
821 : float
822 0 : nsSVGGlyphFrame::GetSubStringAdvance(PRUint32 aCharnum,
823 : PRUint32 aFragmentChars,
824 : float aMetricsScale)
825 : {
826 0 : if (aFragmentChars == 0)
827 0 : return 0.0f;
828 :
829 : gfxFloat advance =
830 0 : mTextRun->GetAdvanceWidth(aCharnum, aFragmentChars, nsnull) * aMetricsScale;
831 :
832 0 : nsTArray<float> dxlist, notUsed;
833 0 : GetEffectiveDxDy(mTextRun->GetLength(), dxlist, notUsed);
834 0 : PRUint32 dxcount = dxlist.Length();
835 0 : if (dxcount) {
836 0 : gfxFloat pathScale = 1.0;
837 0 : nsSVGTextPathFrame *textPath = FindTextPathParent();
838 0 : if (textPath)
839 0 : pathScale = textPath->GetOffsetScale();
840 0 : if (dxcount > aFragmentChars)
841 0 : dxcount = aFragmentChars;
842 0 : for (PRUint32 i = aCharnum; i < dxcount; i++) {
843 0 : advance += dxlist[i] * pathScale;
844 : }
845 : }
846 :
847 0 : return float(advance);
848 : }
849 :
850 : gfxFloat
851 0 : nsSVGGlyphFrame::GetBaselineOffset(float aMetricsScale)
852 : {
853 : gfxTextRun::Metrics metrics =
854 : mTextRun->MeasureText(0, mTextRun->GetLength(),
855 0 : gfxFont::LOOSE_INK_EXTENTS, nsnull, nsnull);
856 :
857 : PRUint16 dominantBaseline;
858 :
859 0 : for (nsIFrame *frame = GetParent(); frame; frame = frame->GetParent()) {
860 0 : dominantBaseline = frame->GetStyleSVGReset()->mDominantBaseline;
861 0 : if (dominantBaseline != NS_STYLE_DOMINANT_BASELINE_AUTO ||
862 0 : frame->GetType() == nsGkAtoms::svgTextFrame) {
863 0 : break;
864 : }
865 : }
866 :
867 : gfxFloat baselineAppUnits;
868 0 : switch (dominantBaseline) {
869 : case NS_STYLE_DOMINANT_BASELINE_HANGING:
870 : // not really right, but the best we can do with the information provided
871 : // FALLTHROUGH
872 : case NS_STYLE_DOMINANT_BASELINE_TEXT_BEFORE_EDGE:
873 0 : baselineAppUnits = -metrics.mAscent;
874 0 : break;
875 : case NS_STYLE_DOMINANT_BASELINE_TEXT_AFTER_EDGE:
876 : case NS_STYLE_DOMINANT_BASELINE_IDEOGRAPHIC:
877 0 : baselineAppUnits = metrics.mDescent;
878 0 : break;
879 : case NS_STYLE_DOMINANT_BASELINE_CENTRAL:
880 : case NS_STYLE_DOMINANT_BASELINE_MIDDLE:
881 0 : baselineAppUnits = -(metrics.mAscent - metrics.mDescent) / 2.0;
882 0 : break;
883 : case NS_STYLE_DOMINANT_BASELINE_AUTO:
884 : case NS_STYLE_DOMINANT_BASELINE_ALPHABETIC:
885 0 : return 0.0;
886 : default:
887 0 : NS_WARNING("We don't know about this type of dominant-baseline");
888 0 : return 0.0;
889 : }
890 0 : return baselineAppUnits * aMetricsScale;
891 : }
892 :
893 : DrawMode
894 0 : nsSVGGlyphFrame::SetupCairoState(gfxContext *aContext, gfxPattern **aStrokePattern)
895 : {
896 0 : DrawMode toDraw = DrawMode(0);
897 0 : const nsStyleSVG* style = GetStyleSVG();
898 :
899 0 : if (HasStroke()) {
900 0 : gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
901 0 : aContext->IdentityMatrix();
902 :
903 0 : toDraw = DrawMode(toDraw | gfxFont::GLYPH_STROKE);
904 :
905 0 : SetupCairoStrokeHitGeometry(aContext);
906 0 : float opacity = style->mStrokeOpacity;
907 : nsSVGPaintServerFrame *ps = GetPaintServer(&style->mStroke,
908 0 : nsSVGEffects::StrokeProperty());
909 :
910 0 : nsRefPtr<gfxPattern> strokePattern;
911 :
912 0 : if (ps) {
913 : // Gradient or Pattern: can get pattern directly from frame
914 0 : strokePattern = ps->GetPaintServerPattern(this, opacity);
915 : }
916 :
917 0 : if (!strokePattern) {
918 : nscolor color;
919 : nsSVGUtils::GetFallbackOrPaintColor(aContext, GetStyleContext(),
920 : &nsStyleSVG::mStroke, &opacity,
921 0 : &color);
922 : strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0,
923 : NS_GET_G(color) / 255.0,
924 : NS_GET_B(color) / 255.0,
925 0 : NS_GET_A(color) / 255.0 * opacity));
926 : }
927 :
928 0 : strokePattern.forget(aStrokePattern);
929 : }
930 :
931 0 : if (SetupCairoFill(aContext)) {
932 0 : toDraw = DrawMode(toDraw | gfxFont::GLYPH_FILL);
933 : }
934 :
935 0 : return toDraw;
936 : }
937 :
938 : //----------------------------------------------------------------------
939 :
940 : // Utilities for converting from indices in the uncompressed content
941 : // element strings to compressed frame string and back:
942 : static int
943 0 : CompressIndex(int index, const nsTextFragment*fragment)
944 : {
945 0 : int ci=0;
946 0 : if (fragment->Is2b()) {
947 0 : const PRUnichar *data=fragment->Get2b();
948 0 : while(*data && index) {
949 0 : if (XP_IS_SPACE_W(*data)){
950 0 : do {
951 0 : ++data;
952 0 : --index;
953 : }while(XP_IS_SPACE_W(*data) && index);
954 : }
955 : else {
956 0 : ++data;
957 0 : --index;
958 : }
959 0 : ++ci;
960 : }
961 : }
962 : else {
963 0 : const char *data=fragment->Get1b();
964 0 : while(*data && index) {
965 0 : if (XP_IS_SPACE_W(*data)){
966 0 : do {
967 0 : ++data;
968 0 : --index;
969 : }while(XP_IS_SPACE_W(*data) && index);
970 : }
971 : else {
972 0 : ++data;
973 0 : --index;
974 : }
975 0 : ++ci;
976 : }
977 : }
978 :
979 0 : return ci;
980 : }
981 :
982 : nsresult
983 0 : nsSVGGlyphFrame::GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
984 : nscolor *foreground, nscolor *background)
985 : {
986 0 : *foreground = NS_RGB(255,255,255);
987 0 : *background = NS_RGB(0,0,0);
988 0 : *charnum=0;
989 0 : *nchars=0;
990 :
991 0 : bool hasHighlight = IsSelected();
992 0 : if (!hasHighlight) {
993 0 : NS_ERROR("nsSVGGlyphFrame::GetHighlight() called by renderer when there is no highlight");
994 0 : return NS_ERROR_FAILURE;
995 : }
996 :
997 0 : nsPresContext *presContext = PresContext();
998 :
999 : // The selection ranges are relative to the uncompressed text in
1000 : // the content element. We'll need the text fragment:
1001 0 : const nsTextFragment *fragment = mContent->GetText();
1002 0 : NS_ASSERTION(fragment, "no text");
1003 :
1004 : // get the selection details
1005 0 : SelectionDetails *details = nsnull;
1006 : {
1007 0 : nsRefPtr<nsFrameSelection> frameSelection = presContext->PresShell()->FrameSelection();
1008 0 : if (!frameSelection) {
1009 0 : NS_ERROR("no frameselection interface");
1010 0 : return NS_ERROR_FAILURE;
1011 : }
1012 :
1013 : details = frameSelection->LookUpSelection(
1014 0 : mContent, 0, fragment->GetLength(), false
1015 0 : );
1016 : }
1017 :
1018 : #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
1019 : {
1020 : SelectionDetails *dp = details;
1021 : printf("nsSVGGlyphFrame(%p)::GetHighlight() [\n", this);
1022 : while (dp) {
1023 : printf("selection detail: %d(%d)->%d(%d) type %d\n",
1024 : dp->mStart, CompressIndex(dp->mStart, fragment),
1025 : dp->mEnd, CompressIndex(dp->mEnd, fragment),
1026 : dp->mType);
1027 : dp = dp->mNext;
1028 : }
1029 : printf("]\n");
1030 :
1031 : }
1032 : #endif
1033 :
1034 0 : if (details) {
1035 0 : NS_ASSERTION(details->mNext==nsnull, "can't do multiple selection ranges");
1036 :
1037 0 : *charnum=CompressIndex(details->mStart, fragment);
1038 0 : *nchars=CompressIndex(details->mEnd, fragment)-*charnum;
1039 :
1040 : LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
1041 0 : background);
1042 : LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
1043 0 : foreground);
1044 :
1045 0 : SelectionDetails *dp = details;
1046 0 : while ((dp=details->mNext) != nsnull) {
1047 0 : delete details;
1048 0 : details = dp;
1049 : }
1050 0 : delete details;
1051 : }
1052 :
1053 0 : return NS_OK;
1054 : }
1055 :
1056 :
1057 : //----------------------------------------------------------------------
1058 : // Internal methods
1059 :
1060 : void
1061 0 : nsSVGGlyphFrame::SetGlyphPosition(gfxPoint *aPosition, bool aForceGlobalTransform)
1062 : {
1063 : float drawScale, metricsScale;
1064 :
1065 0 : nsSVGTextPathFrame *textPath = FindTextPathParent();
1066 : // In a textPath, the 'y' attribute has no effect, so we reset 'y' here
1067 : // to use aPosition.y for dy only
1068 0 : if (textPath && textPath->GetFirstPrincipalChild() == this) {
1069 0 : aPosition->y = 0.0;
1070 : }
1071 :
1072 0 : if (!EnsureTextRun(&drawScale, &metricsScale, aForceGlobalTransform))
1073 0 : return;
1074 :
1075 0 : mPosition.MoveTo(aPosition->x, aPosition->y - GetBaselineOffset(metricsScale));
1076 :
1077 0 : PRUint32 strLength = mTextRun->GetLength();
1078 :
1079 0 : nsTArray<float> xList, yList;
1080 0 : GetEffectiveXY(strLength, xList, yList);
1081 0 : PRUint32 xCount = NS_MIN(xList.Length(), strLength);
1082 0 : PRUint32 yCount = NS_MIN(yList.Length(), strLength);
1083 :
1084 : // move aPosition to the last glyph position
1085 0 : gfxFloat x = aPosition->x;
1086 0 : if (xCount > 1) {
1087 0 : x = xList[xCount - 1];
1088 : x +=
1089 0 : mTextRun->GetAdvanceWidth(xCount - 1, 1, nsnull) * metricsScale;
1090 :
1091 : // advance to the last glyph
1092 0 : if (strLength > xCount) {
1093 : x +=
1094 0 : mTextRun->GetAdvanceWidth(xCount, strLength - xCount, nsnull) *
1095 0 : metricsScale;
1096 : }
1097 : } else {
1098 0 : x += mTextRun->GetAdvanceWidth(0, strLength, nsnull) * metricsScale;
1099 : }
1100 :
1101 0 : gfxFloat y = (textPath || yCount <= 1) ? aPosition->y : yList[yCount - 1];
1102 0 : aPosition->MoveTo(x, y);
1103 :
1104 0 : gfxFloat pathScale = 1.0;
1105 0 : if (textPath)
1106 0 : pathScale = textPath->GetOffsetScale();
1107 :
1108 0 : nsTArray<float> dxList, dyList;
1109 0 : GetEffectiveDxDy(strLength, dxList, dyList);
1110 :
1111 0 : PRUint32 dxcount = NS_MIN(dxList.Length(), strLength);
1112 0 : if (dxcount > 0) {
1113 0 : mPosition.x += dxList[0] * pathScale;
1114 : }
1115 0 : for (PRUint32 i = 0; i < dxcount; i++) {
1116 0 : aPosition->x += dxList[i] * pathScale;
1117 : }
1118 0 : PRUint32 dycount = NS_MIN(dyList.Length(), strLength);
1119 0 : if (dycount > 0) {
1120 0 : mPosition.y += dyList[0]* pathScale;
1121 : }
1122 0 : for (PRUint32 i = 0; i < dycount; i++) {
1123 0 : aPosition->y += dyList[i] * pathScale;
1124 : }
1125 : }
1126 :
1127 : nsresult
1128 0 : nsSVGGlyphFrame::GetStartPositionOfChar(PRUint32 charnum,
1129 : nsIDOMSVGPoint **_retval)
1130 : {
1131 0 : *_retval = nsnull;
1132 :
1133 0 : CharacterIterator iter(this, false);
1134 0 : if (!iter.AdvanceToCharacter(charnum))
1135 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1136 :
1137 0 : NS_ADDREF(*_retval = new DOMSVGPoint(iter.GetPositionData().pos));
1138 0 : return NS_OK;
1139 : }
1140 :
1141 : nsresult
1142 0 : nsSVGGlyphFrame::GetEndPositionOfChar(PRUint32 charnum,
1143 : nsIDOMSVGPoint **_retval)
1144 : {
1145 0 : *_retval = nsnull;
1146 :
1147 0 : CharacterIterator iter(this, false);
1148 0 : if (!iter.AdvanceToCharacter(charnum))
1149 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1150 :
1151 0 : nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1152 0 : iter.SetupForMetrics(tmpCtx);
1153 0 : tmpCtx->MoveTo(gfxPoint(mTextRun->GetAdvanceWidth(charnum, 1, nsnull), 0));
1154 0 : tmpCtx->IdentityMatrix();
1155 0 : NS_ADDREF(*_retval = new DOMSVGPoint(tmpCtx->CurrentPoint()));
1156 0 : return NS_OK;
1157 : }
1158 :
1159 : nsresult
1160 0 : nsSVGGlyphFrame::GetExtentOfChar(PRUint32 charnum, nsIDOMSVGRect **_retval)
1161 : {
1162 0 : *_retval = nsnull;
1163 :
1164 0 : CharacterIterator iter(this, false);
1165 0 : if (!iter.AdvanceToCharacter(0))
1166 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1167 :
1168 0 : PRUint32 start = charnum, limit = charnum + 1;
1169 0 : while (start > 0 && !mTextRun->IsClusterStart(start)) {
1170 0 : --start;
1171 : }
1172 0 : while (limit < mTextRun->GetLength() && !mTextRun->IsClusterStart(limit)) {
1173 0 : ++limit;
1174 : }
1175 :
1176 0 : if (start > 0 && !iter.AdvanceToCharacter(start))
1177 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1178 :
1179 : gfxTextRun::Metrics metrics =
1180 : mTextRun->MeasureText(start, limit - start, gfxFont::LOOSE_INK_EXTENTS,
1181 0 : nsnull, nsnull);
1182 :
1183 0 : nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1184 0 : iter.SetupForMetrics(tmpCtx);
1185 : tmpCtx->Rectangle(gfxRect(0, -metrics.mAscent,
1186 : metrics.mAdvanceWidth,
1187 0 : metrics.mAscent + metrics.mDescent));
1188 0 : tmpCtx->IdentityMatrix();
1189 0 : return NS_NewSVGRect(_retval, tmpCtx->GetUserPathExtent());
1190 : }
1191 :
1192 : nsresult
1193 0 : nsSVGGlyphFrame::GetRotationOfChar(PRUint32 charnum, float *_retval)
1194 : {
1195 0 : CharacterIterator iter(this, false);
1196 0 : if (!iter.AdvanceToCharacter(charnum))
1197 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1198 :
1199 0 : CharacterPosition pos = iter.GetPositionData();
1200 0 : if (!pos.draw)
1201 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
1202 :
1203 0 : const gfxFloat radPerDeg = M_PI/180.0;
1204 0 : *_retval = float(pos.angle / radPerDeg);
1205 0 : return NS_OK;
1206 : }
1207 :
1208 : float
1209 0 : nsSVGGlyphFrame::GetAdvance(bool aForceGlobalTransform)
1210 : {
1211 : float drawScale, metricsScale;
1212 0 : if (!EnsureTextRun(&drawScale, &metricsScale, aForceGlobalTransform))
1213 0 : return 0.0f;
1214 :
1215 0 : return GetSubStringAdvance(0, mTextRun->GetLength(), metricsScale);
1216 : }
1217 :
1218 : nsSVGTextPathFrame*
1219 0 : nsSVGGlyphFrame::FindTextPathParent()
1220 : {
1221 : /* check if we're the child of a textPath */
1222 0 : for (nsIFrame *frame = GetParent();
1223 : frame != nsnull;
1224 : frame = frame->GetParent()) {
1225 0 : nsIAtom* type = frame->GetType();
1226 0 : if (type == nsGkAtoms::svgTextPathFrame) {
1227 0 : return static_cast<nsSVGTextPathFrame*>(frame);
1228 0 : } else if (type == nsGkAtoms::svgTextFrame)
1229 0 : return nsnull;
1230 : }
1231 0 : return nsnull;
1232 : }
1233 :
1234 : bool
1235 0 : nsSVGGlyphFrame::IsStartOfChunk()
1236 : {
1237 : // this fragment is a chunk if it has a corresponding absolute
1238 : // position adjustment in an ancestors' x or y array. (At the moment
1239 : // we don't map the full arrays, but only the first elements.)
1240 :
1241 0 : return false;
1242 : }
1243 :
1244 : void
1245 0 : nsSVGGlyphFrame::GetXY(SVGUserUnitList *aX, SVGUserUnitList *aY)
1246 : {
1247 0 : static_cast<nsSVGTextContainerFrame *>(mParent)->GetXY(aX, aY);
1248 0 : }
1249 :
1250 : void
1251 0 : nsSVGGlyphFrame::SetStartIndex(PRUint32 aStartIndex)
1252 : {
1253 0 : mStartIndex = aStartIndex;
1254 0 : }
1255 :
1256 : void
1257 0 : nsSVGGlyphFrame::GetEffectiveXY(PRInt32 strLength, nsTArray<float> &aX, nsTArray<float> &aY)
1258 : {
1259 0 : nsTArray<float> x, y;
1260 0 : static_cast<nsSVGTextContainerFrame *>(mParent)->GetEffectiveXY(x, y);
1261 :
1262 0 : PRInt32 xCount = NS_MAX((PRInt32)(x.Length() - mStartIndex), 0);
1263 0 : xCount = NS_MIN(xCount, strLength);
1264 0 : aX.AppendElements(x.Elements() + mStartIndex, xCount);
1265 :
1266 0 : PRInt32 yCount = NS_MAX((PRInt32)(y.Length() - mStartIndex), 0);
1267 0 : yCount = NS_MIN(yCount, strLength);
1268 0 : aY.AppendElements(y.Elements() + mStartIndex, yCount);
1269 0 : }
1270 :
1271 : void
1272 0 : nsSVGGlyphFrame::GetDxDy(SVGUserUnitList *aDx, SVGUserUnitList *aDy)
1273 : {
1274 0 : static_cast<nsSVGTextContainerFrame *>(mParent)->GetDxDy(aDx, aDy);
1275 0 : }
1276 :
1277 : void
1278 0 : nsSVGGlyphFrame::GetEffectiveDxDy(PRInt32 strLength, nsTArray<float> &aDx, nsTArray<float> &aDy)
1279 : {
1280 0 : nsTArray<float> dx, dy;
1281 0 : static_cast<nsSVGTextContainerFrame *>(mParent)->GetEffectiveDxDy(dx, dy);
1282 :
1283 0 : PRInt32 dxCount = NS_MAX((PRInt32)(dx.Length() - mStartIndex), 0);
1284 0 : dxCount = NS_MIN(dxCount, strLength);
1285 0 : aDx.AppendElements(dx.Elements() + mStartIndex, dxCount);
1286 :
1287 0 : PRInt32 dyCount = NS_MAX((PRInt32)(dy.Length() - mStartIndex), 0);
1288 0 : dyCount = NS_MIN(dyCount, strLength);
1289 0 : aDy.AppendElements(dy.Elements() + mStartIndex, dyCount);
1290 0 : }
1291 :
1292 : const SVGNumberList*
1293 0 : nsSVGGlyphFrame::GetRotate()
1294 : {
1295 : nsSVGTextContainerFrame *containerFrame;
1296 0 : containerFrame = static_cast<nsSVGTextContainerFrame *>(mParent);
1297 0 : if (containerFrame)
1298 0 : return containerFrame->GetRotate();
1299 0 : return nsnull;
1300 : }
1301 :
1302 : void
1303 0 : nsSVGGlyphFrame::GetEffectiveRotate(PRInt32 strLength, nsTArray<float> &aRotate)
1304 : {
1305 0 : nsTArray<float> rotate;
1306 0 : static_cast<nsSVGTextContainerFrame *>(mParent)->GetEffectiveRotate(rotate);
1307 :
1308 0 : PRInt32 rotateCount = NS_MAX((PRInt32)(rotate.Length() - mStartIndex), 0);
1309 0 : rotateCount = NS_MIN(rotateCount, strLength);
1310 0 : if (rotateCount > 0) {
1311 0 : aRotate.AppendElements(rotate.Elements() + mStartIndex, rotateCount);
1312 0 : } else if (!rotate.IsEmpty()) {
1313 : // rotate is applied for extra characters too
1314 0 : aRotate.AppendElement(rotate[rotate.Length() - 1]);
1315 : }
1316 0 : }
1317 :
1318 : PRUint16
1319 0 : nsSVGGlyphFrame::GetTextAnchor()
1320 : {
1321 0 : return GetStyleSVG()->mTextAnchor;
1322 : }
1323 :
1324 : bool
1325 0 : nsSVGGlyphFrame::IsAbsolutelyPositioned()
1326 : {
1327 0 : bool hasTextPathAncestor = false;
1328 0 : for (nsIFrame *frame = GetParent();
1329 : frame != nsnull;
1330 : frame = frame->GetParent()) {
1331 :
1332 : // at the start of a 'text' element
1333 : // at the start of each 'textPath' element
1334 0 : if (frame->GetType() == nsGkAtoms::svgTextPathFrame) {
1335 0 : hasTextPathAncestor = true;
1336 : }
1337 0 : if ((frame->GetType() == nsGkAtoms::svgTextFrame ||
1338 0 : frame->GetType() == nsGkAtoms::svgTextPathFrame) &&
1339 0 : frame->GetFirstPrincipalChild() == this) {
1340 0 : return true;
1341 : }
1342 :
1343 0 : if (frame->GetType() == nsGkAtoms::svgTextFrame)
1344 0 : break;
1345 : }
1346 :
1347 : // for each character within a 'text', 'tspan', 'tref' and 'altGlyph' element
1348 : // which has an x or y attribute value assigned to it explicitly
1349 0 : nsTArray<float> x, y;
1350 0 : GetEffectiveXY(GetNumberOfChars(), x, y);
1351 : // Note: the y of descendants of textPath has no effect in horizontal writing
1352 0 : return (!x.IsEmpty() || (!hasTextPathAncestor && !y.IsEmpty()));
1353 : }
1354 :
1355 :
1356 : //----------------------------------------------------------------------
1357 : // nsISVGGlyphFragmentNode interface:
1358 :
1359 : PRUint32
1360 0 : nsSVGGlyphFrame::GetNumberOfChars()
1361 : {
1362 0 : if (mCompressWhitespace) {
1363 0 : nsAutoString text;
1364 0 : GetCharacterData(text);
1365 0 : return text.Length();
1366 : }
1367 :
1368 0 : return mContent->TextLength();
1369 : }
1370 :
1371 : float
1372 0 : nsSVGGlyphFrame::GetComputedTextLength()
1373 : {
1374 0 : return GetAdvance(false);
1375 : }
1376 :
1377 : float
1378 0 : nsSVGGlyphFrame::GetSubStringLength(PRUint32 charnum, PRUint32 fragmentChars)
1379 : {
1380 : float drawScale, metricsScale;
1381 0 : if (!EnsureTextRun(&drawScale, &metricsScale, false))
1382 0 : return 0.0f;
1383 :
1384 0 : return GetSubStringAdvance(charnum, fragmentChars, metricsScale);
1385 : }
1386 :
1387 : PRInt32
1388 0 : nsSVGGlyphFrame::GetCharNumAtPosition(nsIDOMSVGPoint *point)
1389 : {
1390 : float xPos, yPos;
1391 0 : point->GetX(&xPos);
1392 0 : point->GetY(&yPos);
1393 :
1394 0 : nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1395 0 : CharacterIterator iter(this, false);
1396 :
1397 : PRUint32 i;
1398 0 : PRInt32 last = -1;
1399 0 : gfxPoint pt(xPos, yPos);
1400 0 : while ((i = iter.NextCluster()) != iter.InvalidCluster()) {
1401 0 : PRUint32 limit = i + iter.ClusterLength();
1402 : gfxTextRun::Metrics metrics =
1403 : mTextRun->MeasureText(i, limit - i, gfxFont::LOOSE_INK_EXTENTS,
1404 0 : nsnull, nsnull);
1405 :
1406 : // the SVG spec tells us to divide the width of the cluster equally among
1407 : // its chars, so we'll step through the chars, allocating a share of the
1408 : // total advance to each
1409 : PRInt32 current, end, step;
1410 0 : if (mTextRun->IsRightToLeft()) {
1411 0 : current = limit - 1;
1412 0 : end = i - 1;
1413 0 : step = -1;
1414 : } else {
1415 0 : current = i;
1416 0 : end = limit;
1417 0 : step = 1;
1418 : }
1419 0 : gfxFloat leftEdge = 0.0;
1420 0 : gfxFloat width = metrics.mAdvanceWidth / (limit - i);
1421 0 : while (current != end) {
1422 0 : iter.SetupForMetrics(tmpCtx);
1423 0 : tmpCtx->NewPath();
1424 : tmpCtx->Rectangle(gfxRect(leftEdge, -metrics.mAscent,
1425 0 : width, metrics.mAscent + metrics.mDescent));
1426 0 : tmpCtx->IdentityMatrix();
1427 0 : if (tmpCtx->PointInFill(pt)) {
1428 : // Can't return yet; if there's glyph overlap, the last character
1429 : // to be rendered wins, so we still have to check the rest...
1430 0 : last = current;
1431 0 : break; // ...but we don't need to check more slices of this cluster
1432 : }
1433 0 : current += step;
1434 0 : leftEdge += width;
1435 : }
1436 : }
1437 :
1438 0 : return last;
1439 : }
1440 :
1441 : NS_IMETHODIMP_(nsSVGGlyphFrame *)
1442 0 : nsSVGGlyphFrame::GetFirstGlyphFrame()
1443 : {
1444 0 : nsSVGGlyphFrame *frame = this;
1445 0 : while (frame && frame->IsTextEmpty()) {
1446 0 : frame = frame->GetNextGlyphFrame();
1447 : }
1448 0 : return frame;
1449 : }
1450 :
1451 : NS_IMETHODIMP_(nsSVGGlyphFrame *)
1452 0 : nsSVGGlyphFrame::GetNextGlyphFrame()
1453 : {
1454 0 : nsIFrame* sibling = GetNextSibling();
1455 0 : while (sibling) {
1456 0 : nsISVGGlyphFragmentNode *node = do_QueryFrame(sibling);
1457 0 : if (node)
1458 0 : return node->GetFirstGlyphFrame();
1459 0 : sibling = sibling->GetNextSibling();
1460 : }
1461 :
1462 : // no more siblings. go back up the tree.
1463 :
1464 0 : NS_ASSERTION(GetParent(), "null parent");
1465 0 : nsISVGGlyphFragmentNode *node = do_QueryFrame(GetParent());
1466 0 : return node ? node->GetNextGlyphFrame() : nsnull;
1467 : }
1468 :
1469 : bool
1470 0 : nsSVGGlyphFrame::EndsWithWhitespace() const
1471 : {
1472 0 : const nsTextFragment* text = mContent->GetText();
1473 0 : NS_ABORT_IF_FALSE(text->GetLength() > 0, "text expected");
1474 :
1475 0 : return NS_IsAsciiWhitespace(text->CharAt(text->GetLength() - 1));
1476 : }
1477 :
1478 : bool
1479 0 : nsSVGGlyphFrame::IsAllWhitespace() const
1480 : {
1481 0 : const nsTextFragment* text = mContent->GetText();
1482 :
1483 0 : if (text->Is2b())
1484 0 : return false;
1485 0 : PRInt32 len = text->GetLength();
1486 0 : const char* str = text->Get1b();
1487 0 : for (PRInt32 i = 0; i < len; ++i) {
1488 0 : if (!NS_IsAsciiWhitespace(str[i]))
1489 0 : return false;
1490 : }
1491 0 : return true;
1492 : }
1493 :
1494 : //----------------------------------------------------------------------
1495 : //
1496 :
1497 : void
1498 0 : nsSVGGlyphFrame::NotifyGlyphMetricsChange()
1499 : {
1500 : nsSVGTextContainerFrame *containerFrame =
1501 0 : static_cast<nsSVGTextContainerFrame *>(mParent);
1502 0 : if (containerFrame)
1503 0 : containerFrame->NotifyGlyphMetricsChange();
1504 0 : }
1505 :
1506 : void
1507 0 : nsSVGGlyphFrame::SetupGlobalTransform(gfxContext *aContext)
1508 : {
1509 0 : gfxMatrix matrix = GetCanvasTM();
1510 0 : if (!matrix.IsSingular()) {
1511 0 : aContext->Multiply(matrix);
1512 : }
1513 0 : }
1514 :
1515 : void
1516 0 : nsSVGGlyphFrame::ClearTextRun()
1517 : {
1518 0 : delete mTextRun;
1519 0 : mTextRun = nsnull;
1520 0 : }
1521 :
1522 : bool
1523 0 : nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
1524 : bool aForceGlobalTransform)
1525 : {
1526 : // Compute the size at which the text should render (excluding the CTM)
1527 0 : const nsStyleFont* fontData = GetStyleFont();
1528 : // Since SVG has its own scaling, we really don't want
1529 : // fonts in SVG to respond to the browser's "TextZoom"
1530 : // (Ctrl++,Ctrl+-)
1531 0 : nsPresContext *presContext = PresContext();
1532 0 : float textZoom = presContext->TextZoom();
1533 : double size =
1534 0 : presContext->AppUnitsToFloatCSSPixels(fontData->mSize) / textZoom;
1535 :
1536 : double textRunSize;
1537 0 : if (mTextRun) {
1538 0 : textRunSize = mTextRun->GetFontGroup()->GetStyle()->size;
1539 : } else {
1540 0 : nsAutoString text;
1541 0 : if (!GetCharacterData(text))
1542 0 : return false;
1543 :
1544 0 : nsAutoString visualText;
1545 :
1546 : /*
1547 : * XXXsmontagu: The SVG spec says:
1548 : *
1549 : * http://www.w3.org/TR/SVG11/text.html#DirectionProperty
1550 : * "For the 'direction' property to have any effect, the 'unicode-bidi'
1551 : * property's value must be embed or bidi-override."
1552 : *
1553 : * The SVGTiny spec, on the other hand, says
1554 : *
1555 : * http://www.w3.org/TR/SVGTiny12/text.html#DirectionProperty
1556 : * "For the 'direction' property to have any effect on an element that
1557 : * does not by itself establish a new text chunk (such as the 'tspan'
1558 : * element in SVG 1.2 Tiny), the 'unicode-bidi' property's value must
1559 : * be embed or bidi-override."
1560 : *
1561 : * Note that this is different from HTML/CSS, where setting the 'dir'
1562 : * attribute on an inline element automatically sets unicode-bidi: embed
1563 : *
1564 : * Our current implementation of bidi in SVG does not distinguish between
1565 : * different text elements, but treats every text container frame as a
1566 : * new text chunk, so we always set the base direction according to the
1567 : * direction property
1568 : *
1569 : * See also XXXsmontagu comments in nsSVGTextFrame::UpdateGlyphPositioning
1570 : */
1571 :
1572 : // Get the unicodeBidi property from the parent, because it doesn't
1573 : // inherit
1574 0 : bool bidiOverride = !!(mParent->GetStyleTextReset()->mUnicodeBidi &
1575 0 : NS_STYLE_UNICODE_BIDI_OVERRIDE);
1576 : nsBidiLevel baseDirection =
1577 0 : GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL ?
1578 0 : NSBIDI_RTL : NSBIDI_LTR;
1579 : nsBidiPresUtils::CopyLogicalToVisual(text, visualText,
1580 0 : baseDirection, bidiOverride);
1581 0 : if (!visualText.IsEmpty()) {
1582 0 : text = visualText;
1583 : }
1584 :
1585 0 : gfxMatrix m;
1586 0 : if (aForceGlobalTransform ||
1587 0 : !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
1588 0 : m = GetCanvasTM();
1589 0 : if (m.IsSingular())
1590 0 : return false;
1591 : }
1592 :
1593 : // The context scale is the ratio of the length of the transformed
1594 : // diagonal vector (1,1) to the length of the untransformed diagonal
1595 : // (which is sqrt(2)).
1596 0 : gfxPoint p = m.Transform(gfxPoint(1, 1)) - m.Transform(gfxPoint(0, 0));
1597 0 : double contextScale = nsSVGUtils::ComputeNormalizedHypotenuse(p.x, p.y);
1598 :
1599 0 : if (GetStyleSVG()->mTextRendering ==
1600 : NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION) {
1601 0 : textRunSize = PRECISE_SIZE;
1602 : } else {
1603 0 : textRunSize = size*contextScale;
1604 0 : textRunSize = NS_MAX(textRunSize, double(CLAMP_MIN_SIZE));
1605 0 : textRunSize = NS_MIN(textRunSize, double(CLAMP_MAX_SIZE));
1606 : }
1607 :
1608 0 : const nsFont& font = fontData->mFont;
1609 0 : bool printerFont = (presContext->Type() == nsPresContext::eContext_PrintPreview ||
1610 0 : presContext->Type() == nsPresContext::eContext_Print);
1611 : gfxFontStyle fontStyle(font.style, font.weight, font.stretch, textRunSize,
1612 0 : mStyleContext->GetStyleFont()->mLanguage,
1613 : font.sizeAdjust, font.systemFont,
1614 : printerFont,
1615 : font.featureSettings,
1616 0 : font.languageOverride);
1617 :
1618 : nsRefPtr<gfxFontGroup> fontGroup =
1619 0 : gfxPlatform::GetPlatform()->CreateFontGroup(font.name, &fontStyle, presContext->GetUserFontSet());
1620 :
1621 : PRUint32 flags = gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX |
1622 0 : GetTextRunFlags(text.Length()) |
1623 0 : nsLayoutUtils::GetTextRunFlagsForStyle(GetStyleContext(), GetStyleText(), GetStyleFont());
1624 :
1625 : // XXX We should use a better surface here! But then we'd have to
1626 : // change things so we can ensure we always have the "right" sort of
1627 : // surface available, by creating the textrun only at the right times
1628 0 : nsRefPtr<gfxContext> tmpCtx = MakeTmpCtx();
1629 0 : tmpCtx->SetMatrix(m);
1630 :
1631 : // Use only the fonts' internal word caching here.
1632 : // We don't cache the textrun globally because we create
1633 : // a new fontgroup every time. Even if we cached fontgroups, we
1634 : // might render at very many different sizes (e.g. during zoom
1635 : // animation) and caching a textrun for each such size would be bad.
1636 : gfxTextRunFactory::Parameters params = {
1637 0 : tmpCtx, nsnull, nsnull, nsnull, 0, GetTextRunUnitsFactor()
1638 0 : };
1639 : mTextRun =
1640 0 : fontGroup->MakeTextRun(text.get(), text.Length(), ¶ms, flags);
1641 0 : if (!mTextRun)
1642 0 : return false;
1643 : }
1644 :
1645 0 : *aDrawScale = float(size/textRunSize);
1646 0 : *aMetricsScale = (*aDrawScale)/GetTextRunUnitsFactor();
1647 0 : return true;
1648 : }
1649 :
1650 : //----------------------------------------------------------------------
1651 : // helper class
1652 :
1653 0 : CharacterIterator::CharacterIterator(nsSVGGlyphFrame *aSource,
1654 : bool aForceGlobalTransform)
1655 : : mSource(aSource)
1656 : , mCurrentAdvance(0)
1657 : , mCurrentChar(PRUint32(-1))
1658 0 : , mInError(false)
1659 : {
1660 0 : if (!aSource->EnsureTextRun(&mDrawScale, &mMetricsScale,
1661 0 : aForceGlobalTransform) ||
1662 0 : !aSource->GetCharacterPositions(&mPositions, mMetricsScale)) {
1663 0 : mInError = true;
1664 : }
1665 0 : }
1666 :
1667 : bool
1668 0 : CharacterIterator::SetupForDirectTextRun(gfxContext *aContext, float aScale)
1669 : {
1670 0 : if (!mPositions.IsEmpty() || mInError)
1671 0 : return false;
1672 0 : aContext->SetMatrix(mInitialMatrix);
1673 0 : aContext->Translate(mSource->mPosition);
1674 0 : aContext->Scale(aScale, aScale);
1675 0 : return true;
1676 : }
1677 :
1678 : PRUint32
1679 0 : CharacterIterator::NextCluster()
1680 : {
1681 0 : if (mInError) {
1682 : #ifdef DEBUG
1683 0 : if (mCurrentChar != InvalidCluster()) {
1684 0 : bool pastEnd = (mCurrentChar >= mSource->mTextRun->GetLength());
1685 0 : NS_ABORT_IF_FALSE(pastEnd, "Past the end of CharacterIterator. Missing Reset?");
1686 : }
1687 : #endif
1688 0 : return InvalidCluster();
1689 : }
1690 :
1691 0 : while (true) {
1692 0 : if (mCurrentChar != InvalidCluster() &&
1693 0 : (mPositions.IsEmpty() || mPositions[mCurrentChar].draw)) {
1694 : mCurrentAdvance +=
1695 0 : mSource->mTextRun->GetAdvanceWidth(mCurrentChar, 1, nsnull);
1696 : }
1697 0 : ++mCurrentChar;
1698 :
1699 0 : if (mCurrentChar >= mSource->mTextRun->GetLength()) {
1700 0 : mInError = true;
1701 0 : return InvalidCluster();
1702 : }
1703 :
1704 0 : if (mSource->mTextRun->IsClusterStart(mCurrentChar) &&
1705 0 : (mPositions.IsEmpty() || mPositions[mCurrentChar].draw)) {
1706 0 : return mCurrentChar;
1707 : }
1708 : }
1709 : }
1710 :
1711 : PRUint32
1712 0 : CharacterIterator::ClusterLength()
1713 : {
1714 0 : if (mInError) {
1715 0 : return 0;
1716 : }
1717 :
1718 0 : PRUint32 i = mCurrentChar;
1719 0 : while (++i < mSource->mTextRun->GetLength()) {
1720 0 : if (mSource->mTextRun->IsClusterStart(i)) {
1721 0 : break;
1722 : }
1723 : }
1724 0 : return i - mCurrentChar;
1725 : }
1726 :
1727 : bool
1728 0 : CharacterIterator::AdvanceToCharacter(PRUint32 aIndex)
1729 : {
1730 0 : while (NextCluster() != InvalidCluster()) {
1731 0 : if (mCurrentChar == aIndex)
1732 0 : return true;
1733 : }
1734 0 : return false;
1735 : }
1736 :
1737 : void
1738 0 : CharacterIterator::SetupFor(gfxContext *aContext, float aScale)
1739 : {
1740 0 : NS_ASSERTION(!mInError, "We should not have reached here");
1741 :
1742 0 : aContext->SetMatrix(mInitialMatrix);
1743 0 : if (mPositions.IsEmpty()) {
1744 0 : aContext->Translate(mSource->mPosition);
1745 0 : aContext->Scale(aScale, aScale);
1746 0 : aContext->Translate(gfxPoint(mCurrentAdvance, 0));
1747 : } else {
1748 0 : aContext->Translate(mPositions[mCurrentChar].pos);
1749 0 : aContext->Rotate(mPositions[mCurrentChar].angle);
1750 0 : aContext->Scale(aScale, aScale);
1751 : }
1752 0 : }
1753 :
1754 : CharacterPosition
1755 0 : CharacterIterator::GetPositionData()
1756 : {
1757 0 : if (!mPositions.IsEmpty())
1758 0 : return mPositions[mCurrentChar];
1759 :
1760 0 : gfxFloat advance = mCurrentAdvance * mMetricsScale;
1761 : CharacterPosition cp =
1762 0 : { mSource->mPosition + gfxPoint(advance, 0), 0, true };
1763 0 : return cp;
1764 : }
|