KHtml

SVGCharacterLayoutInfo.cpp
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2007 Nikolas Zimmermann <[email protected]>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "wtf/Platform.h"
24 
25 #if ENABLE(SVG)
26 #include "SVGCharacterLayoutInfo.h"
27 
28 //#include "InlineFlowBox.h"
29 //#include "InlineTextBox.h"
30 #include "SVGLengthList.h"
31 #include "SVGNumberList.h"
32 #include "SVGTextPositioningElement.h"
33 #include "RenderSVGTextPath.h"
34 
35 #include <float.h>
36 
37 
38 namespace WebCore
39 {
40 using namespace khtml;
41 using namespace DOM;
42 
43 // Helper function
44 static float calculateBaselineShift(RenderObject *item)
45 {
46  const Font &font = item->style()->htmlFont();
47  const SVGRenderStyle *svgStyle = item->style()->svgStyle();
48 
49  float baselineShift = 0.0f;
50  if (svgStyle->baselineShift() == BS_LENGTH) {
51  CSSPrimitiveValueImpl *primitive = static_cast<CSSPrimitiveValueImpl *>(svgStyle->baselineShiftValue());
52  baselineShift = primitive->floatValue();
53 
54  /*FIXME khtml if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
55  baselineShift = baselineShift / 100.0f * font.pixelSize();*/
56  } else {
57  float baselineAscent = font.ascent() + font.descent();
58 
59  switch (svgStyle->baselineShift()) {
60  case BS_BASELINE:
61  break;
62  case BS_SUB:
63  baselineShift = -baselineAscent / 2.0f;
64  break;
65  case BS_SUPER:
66  baselineShift = baselineAscent / 2.0f;
67  break;
68  default:
69  ASSERT_NOT_REACHED();
70  }
71  }
72 
73  return baselineShift;
74 }
75 
76 SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar> &chars)
77  : curx(0.0f)
78  , cury(0.0f)
79  , angle(0.0f)
80  , dx(0.0f)
81  , dy(0.0f)
82  , shiftx(0.0f)
83  , shifty(0.0f)
84  , pathExtraAdvance(0.0f)
85  , pathTextLength(0.0f)
86  , pathChunkLength(0.0f)
87  , svgChars(chars)
88  , nextDrawnSeperated(false)
89  , xStackChanged(false)
90  , yStackChanged(false)
91  , dxStackChanged(false)
92  , dyStackChanged(false)
93  , angleStackChanged(false)
94  , baselineShiftStackChanged(false)
95  , pathLayout(false)
96  , currentOffset(0.0f)
97  , startOffset(0.0f)
98  , layoutPathLength(0.0f)
99 {
100 }
101 
102 bool SVGCharacterLayoutInfo::xValueAvailable() const
103 {
104  return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
105 }
106 
107 bool SVGCharacterLayoutInfo::yValueAvailable() const
108 {
109  return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
110 }
111 
112 bool SVGCharacterLayoutInfo::dxValueAvailable() const
113 {
114  return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
115 }
116 
117 bool SVGCharacterLayoutInfo::dyValueAvailable() const
118 {
119  return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
120 }
121 
122 bool SVGCharacterLayoutInfo::angleValueAvailable() const
123 {
124  return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
125 }
126 
127 bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
128 {
129  return !baselineShiftStack.isEmpty();
130 }
131 
132 float SVGCharacterLayoutInfo::xValueNext() const
133 {
134  ASSERT(!xStack.isEmpty());
135  return xStack.last().valueAtCurrentPosition();
136 }
137 
138 float SVGCharacterLayoutInfo::yValueNext() const
139 {
140  ASSERT(!yStack.isEmpty());
141  return yStack.last().valueAtCurrentPosition();
142 }
143 
144 float SVGCharacterLayoutInfo::dxValueNext() const
145 {
146  ASSERT(!dxStack.isEmpty());
147  return dxStack.last().valueAtCurrentPosition();
148 }
149 
150 float SVGCharacterLayoutInfo::dyValueNext() const
151 {
152  ASSERT(!dyStack.isEmpty());
153  return dyStack.last().valueAtCurrentPosition();
154 }
155 
156 float SVGCharacterLayoutInfo::angleValueNext() const
157 {
158  ASSERT(!angleStack.isEmpty());
159  return angleStack.last().valueAtCurrentPosition();
160 }
161 
162 float SVGCharacterLayoutInfo::baselineShiftValueNext() const
163 {
164  ASSERT(!baselineShiftStack.isEmpty());
165  return baselineShiftStack.last();
166 }
167 
168 void SVGCharacterLayoutInfo::processedSingleCharacter()
169 {
170  xStackWalk();
171  yStackWalk();
172  dxStackWalk();
173  dyStackWalk();
174  angleStackWalk();
175  baselineShiftStackWalk();
176 }
177 
178 void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY)
179 {
180  // baseline-shift doesn't span across ancestors, unlike dx/dy.
181  curx += savedShiftX - shiftx;
182  cury += savedShiftY - shifty;
183 
184  if (inPathLayout()) {
185  shiftx = savedShiftX;
186  shifty = savedShiftY;
187  }
188 
189  // rotation also doesn't span
190  angle = 0.0f;
191 
192  if (xStackChanged) {
193  ASSERT(!xStack.isEmpty());
194  xStack.removeLast();
195  xStackChanged = false;
196  }
197 
198  if (yStackChanged) {
199  ASSERT(!yStack.isEmpty());
200  yStack.removeLast();
201  yStackChanged = false;
202  }
203 
204  if (dxStackChanged) {
205  ASSERT(!dxStack.isEmpty());
206  dxStack.removeLast();
207  dxStackChanged = false;
208  }
209 
210  if (dyStackChanged) {
211  ASSERT(!dyStack.isEmpty());
212  dyStack.removeLast();
213  dyStackChanged = false;
214  }
215 
216  if (angleStackChanged) {
217  ASSERT(!angleStack.isEmpty());
218  angleStack.removeLast();
219  angleStackChanged = false;
220  }
221 
222  if (baselineShiftStackChanged) {
223  ASSERT(!baselineShiftStack.isEmpty());
224  baselineShiftStack.removeLast();
225  baselineShiftStackChanged = false;
226  }
227 }
228 
229 bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset)
230 {
231  if (layoutPathLength <= 0.0f) {
232  return false;
233  }
234 
235  if (newOffset != FLT_MIN) {
236  currentOffset = startOffset + newOffset;
237  }
238 
239  // Respect translation along path (extraAdvance is orthogonal to the path)
240  currentOffset += extraAdvance;
241 
242  float offset = currentOffset + glyphAdvance / 2.0f;
243  currentOffset += glyphAdvance + pathExtraAdvance;
244 
245  if (offset < 0.0f || offset > layoutPathLength) {
246  return false;
247  }
248 
249  bool ok = false;
250  FloatPoint point = layoutPath.pointAtLength(offset, ok);
251  ASSERT(ok);
252 
253  curx = point.x();
254  cury = point.y();
255 
256  angle = layoutPath.normalAngleAtLength(offset, ok);
257  ASSERT(ok);
258 
259  // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance);
260  return true;
261 }
262 
263 bool SVGCharacterLayoutInfo::inPathLayout() const
264 {
265  return pathLayout;
266 }
267 
268 void SVGCharacterLayoutInfo::setInPathLayout(bool value)
269 {
270  pathLayout = value;
271 
272  pathExtraAdvance = 0.0f;
273  pathTextLength = 0.0f;
274  pathChunkLength = 0.0f;
275 }
276 
277 void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox *flowBox, float textAnchorStartOffset)
278 {
279  bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
280  dxStack.isEmpty() && dyStack.isEmpty() &&
281  angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
282  curx == 0.0f && cury == 0.0f;
283 
284  RenderSVGTextPath *textPath = static_cast<RenderSVGTextPath *>(flowBox->object());
285  Path path = textPath->layoutPath();
286 
287  float baselineShift = calculateBaselineShift(textPath);
288 
289  layoutPath = path;
290  layoutPathLength = path.length();
291 
292  if (layoutPathLength <= 0.0f) {
293  return;
294  }
295 
296  startOffset = textPath->startOffset();
297 
298  if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f) {
299  startOffset *= layoutPathLength;
300  }
301 
302  startOffset += textAnchorStartOffset;
303  currentOffset = startOffset;
304 
305  // Only baseline-shift is handled through the normal layout system
306  addStackContent(BaselineShiftStack, baselineShift);
307 
308  if (isInitialLayout) {
309  xStackChanged = false;
310  yStackChanged = false;
311  dxStackChanged = false;
312  dyStackChanged = false;
313  angleStackChanged = false;
314  baselineShiftStackChanged = false;
315  }
316 }
317 
318 void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement *element)
319 {
320  bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() &&
321  dxStack.isEmpty() && dyStack.isEmpty() &&
322  angleStack.isEmpty() && baselineShiftStack.isEmpty() &&
323  curx == 0.0f && cury == 0.0f;
324 
325  float baselineShift = calculateBaselineShift(element->renderer());
326 
327  addStackContent(XStack, element->x());
328  addStackContent(YStack, element->y());
329  addStackContent(DxStack, element->dx());
330  addStackContent(DyStack, element->dy());
331  addStackContent(AngleStack, element->rotate());
332  addStackContent(BaselineShiftStack, baselineShift);
333 
334  if (isInitialLayout) {
335  xStackChanged = false;
336  yStackChanged = false;
337  dxStackChanged = false;
338  dyStackChanged = false;
339  angleStackChanged = false;
340  baselineShiftStackChanged = false;
341  }
342 }
343 
344 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList *list)
345 {
346  unsigned length = list->numberOfItems();
347  if (!length) {
348  return;
349  }
350 
351  PositionedFloatVector newLayoutInfo;
352 
353  // TODO: Convert more efficiently!
354  ExceptionCode ec = 0;
355  for (unsigned i = 0; i < length; ++i) {
356  float value = list->getItem(i, ec);
357  ASSERT(ec == 0);
358 
359  newLayoutInfo.append(value);
360  }
361 
362  addStackContent(type, newLayoutInfo);
363 }
364 
365 void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList *list)
366 {
367  unsigned length = list->numberOfItems();
368  if (!length) {
369  return;
370  }
371 
372  PositionedFloatVector newLayoutInfo;
373 
374  ExceptionCode ec = 0;
375  for (unsigned i = 0; i < length; ++i) {
376  float value = list->getItem(i, ec).value();
377  ASSERT(ec == 0);
378 
379  newLayoutInfo.append(value);
380  }
381 
382  addStackContent(type, newLayoutInfo);
383 }
384 
385 void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector &list)
386 {
387  switch (type) {
388  case XStack:
389  xStackChanged = true;
390  xStack.append(list);
391  break;
392  case YStack:
393  yStackChanged = true;
394  yStack.append(list);
395  break;
396  case DxStack:
397  dxStackChanged = true;
398  dxStack.append(list);
399  break;
400  case DyStack:
401  dyStackChanged = true;
402  dyStack.append(list);
403  break;
404  case AngleStack:
405  angleStackChanged = true;
406  angleStack.append(list);
407  break;
408  default:
409  ASSERT_NOT_REACHED();
410  }
411 }
412 
413 void SVGCharacterLayoutInfo::addStackContent(StackType type, float value)
414 {
415  if (value == 0.0f) {
416  return;
417  }
418 
419  switch (type) {
420  case BaselineShiftStack:
421  baselineShiftStackChanged = true;
422  baselineShiftStack.append(value);
423  break;
424  default:
425  ASSERT_NOT_REACHED();
426  }
427 }
428 
429 void SVGCharacterLayoutInfo::xStackWalk()
430 {
431  unsigned i = 1;
432 
433  while (!xStack.isEmpty()) {
434  PositionedFloatVector &cur = xStack.last();
435  if (i + cur.position() < cur.size()) {
436  cur.advance(i);
437  break;
438  }
439 
440  i += cur.position();
441  xStack.removeLast();
442  xStackChanged = false;
443  }
444 }
445 
446 void SVGCharacterLayoutInfo::yStackWalk()
447 {
448  unsigned i = 1;
449 
450  while (!yStack.isEmpty()) {
451  PositionedFloatVector &cur = yStack.last();
452  if (i + cur.position() < cur.size()) {
453  cur.advance(i);
454  break;
455  }
456 
457  i += cur.position();
458  yStack.removeLast();
459  yStackChanged = false;
460  }
461 }
462 
463 void SVGCharacterLayoutInfo::dxStackWalk()
464 {
465  unsigned i = 1;
466 
467  while (!dxStack.isEmpty()) {
468  PositionedFloatVector &cur = dxStack.last();
469  if (i + cur.position() < cur.size()) {
470  cur.advance(i);
471  break;
472  }
473 
474  i += cur.position();
475  dxStack.removeLast();
476  dxStackChanged = false;
477  }
478 }
479 
480 void SVGCharacterLayoutInfo::dyStackWalk()
481 {
482  unsigned i = 1;
483 
484  while (!dyStack.isEmpty()) {
485  PositionedFloatVector &cur = dyStack.last();
486  if (i + cur.position() < cur.size()) {
487  cur.advance(i);
488  break;
489  }
490 
491  i += cur.position();
492  dyStack.removeLast();
493  dyStackChanged = false;
494  }
495 }
496 
497 void SVGCharacterLayoutInfo::angleStackWalk()
498 {
499  unsigned i = 1;
500 
501  while (!angleStack.isEmpty()) {
502  PositionedFloatVector &cur = angleStack.last();
503  if (i + cur.position() < cur.size()) {
504  cur.advance(i);
505  break;
506  }
507 
508  i += cur.position();
509  angleStack.removeLast();
510  angleStackChanged = false;
511  }
512 }
513 
514 void SVGCharacterLayoutInfo::baselineShiftStackWalk()
515 {
516  if (!baselineShiftStack.isEmpty()) {
517  baselineShiftStack.removeLast();
518  baselineShiftStackChanged = false;
519  }
520 }
521 
522 bool SVGChar::isHidden() const
523 {
524  return pathData && pathData->hidden;
525 }
526 
527 AffineTransform SVGChar::characterTransform() const
528 {
529  AffineTransform ctm;
530 
531  // Rotate character around angle, and possibly scale.
532  ctm.translate(x, y);
533  ctm.rotate(angle);
534 
535  if (pathData) {
536  ctm.scale(pathData->xScale, pathData->yScale);
537  ctm.translate(pathData->xShift, pathData->yShift);
538  ctm.rotate(pathData->orientationAngle);
539  }
540 
541  ctm.translate(orientationShiftX - x, orientationShiftY - y);
542  return ctm;
543 }
544 
545 } // namespace WebCore
546 
547 #endif // ENABLE(SVG)
This file is part of the HTML rendering engine for KDE.
This library provides a full-featured HTML parser and widget.
Base Class for all rendering tree objects.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 22:48:01 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.