1 /*
2 * Inkscape::Text::Layout::ScanlineMaker - text layout engine shape measurers
3 *
4 * Authors:
5 * Richard Hughes <cyreve@users.sf.net>
6 *
7 * Copyright (C) 2005 Richard Hughes
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11 #include "Layout-TNG-Scanline-Maker.h"
12 #include "livarot/Shape.h"
13 #include "livarot/float-line.h"
15 namespace Inkscape {
16 namespace Text {
18 // *********************** infinite version
20 Layout::InfiniteScanlineMaker::InfiniteScanlineMaker(double initial_x, double initial_y, Layout::Direction block_progression)
21 {
22 _current_line_height.ascent = 0.0;
23 _current_line_height.descent = 0.0;
24 _current_line_height.leading = 0.0;
25 switch (block_progression) {
26 case LEFT_TO_RIGHT:
27 case RIGHT_TO_LEFT:
28 _x = initial_y;
29 _y = initial_x;
30 break;
31 default:
32 _x = initial_x;
33 _y = initial_y;
34 break;
35 }
36 _negative_block_progression = block_progression == RIGHT_TO_LEFT || block_progression == BOTTOM_TO_TOP;
38 }
40 Layout::InfiniteScanlineMaker::~InfiniteScanlineMaker()
41 {
42 }
44 std::vector<Layout::ScanlineMaker::ScanRun> Layout::InfiniteScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
45 {
46 std::vector<ScanRun> runs(1);
47 runs[0].x_start = _x;
48 runs[0].x_end = FLT_MAX; // we could use DBL_MAX, but this just seems safer
49 runs[0].y = _y;
50 _current_line_height = line_height;
51 return runs;
52 }
54 void Layout::InfiniteScanlineMaker::completeLine()
55 {
56 if (_negative_block_progression)
57 _y -= _current_line_height.total();
58 else
59 _y += _current_line_height.total();
60 _current_line_height.ascent = 0.0;
61 _current_line_height.descent = 0.0;
62 _current_line_height.leading = 0.0;
63 }
65 void Layout::InfiniteScanlineMaker::setNewYCoordinate(double new_y)
66 {
67 _y = new_y;
68 }
70 bool Layout::InfiniteScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &line_height)
71 {
72 _current_line_height = line_height;
73 return true;
74 }
76 // *********************** real shapes version
78 Layout::ShapeScanlineMaker::ShapeScanlineMaker(Shape const *shape, Layout::Direction block_progression)
79 {
80 if (block_progression == TOP_TO_BOTTOM) {
81 _rotated_shape = const_cast<Shape*>(shape);
82 _shape_needs_freeing = false;
83 } else {
84 Shape *temp_rotated_shape = new Shape;
85 _shape_needs_freeing = true;
86 temp_rotated_shape->Copy(const_cast<Shape*>(shape));
87 switch (block_progression) {
88 case BOTTOM_TO_TOP: temp_rotated_shape->Transform(Geom::Matrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)); break; // reflect about x axis
89 case LEFT_TO_RIGHT: temp_rotated_shape->Transform(Geom::Matrix(0.0, 1.0, 1.0, 0.0, 0.0, 0.0)); break; // reflect about y=x
90 case RIGHT_TO_LEFT: temp_rotated_shape->Transform(Geom::Matrix(0.0, -1.0, 1.0, 0.0, 0.0, 0.0)); break; // reflect about y=-x
91 default: break;
92 }
93 _rotated_shape = new Shape;
94 _rotated_shape->ConvertToShape(temp_rotated_shape);
95 delete temp_rotated_shape;
96 }
97 _rotated_shape->CalcBBox(true);
98 _bounding_box_top = _rotated_shape->topY;
99 _bounding_box_bottom = _rotated_shape->bottomY;
100 _y = _rasterizer_y = _bounding_box_top;
101 _current_rasterization_point = 0;
102 _rotated_shape->BeginRaster(_y, _current_rasterization_point);
103 _negative_block_progression = block_progression == RIGHT_TO_LEFT || block_progression == BOTTOM_TO_TOP;
104 }
107 Layout::ShapeScanlineMaker::~ShapeScanlineMaker()
108 {
109 _rotated_shape->EndRaster();
110 if (_shape_needs_freeing)
111 delete _rotated_shape;
112 }
114 std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
115 {
116 FloatLigne line_rasterization;
117 FloatLigne line_decent_length_runs;
118 float line_text_height = (float)(line_height.ascent + line_height.descent);
120 if (_y > _bounding_box_bottom)
121 return std::vector<ScanRun>();
123 if (_y < _bounding_box_top)
124 _y = _bounding_box_top;
126 if (line_text_height == 0.0)
127 line_text_height = 0.001; // Scan() doesn't work for zero height so this will have to do
129 _current_line_height = (float)line_height.total();
131 // I think what's going on here is that we're moving the top of the scanline to the given position...
132 _rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y, line_text_height);
133 // ...then actually retreiving the scanline (which alters the first two parameters)
134 _rotated_shape->Scan(_rasterizer_y, _current_rasterization_point, _y + line_text_height , &line_rasterization, true, line_text_height);
135 // sanitise the raw rasterisation, which could have weird overlaps
136 line_rasterization.Flatten();
137 // cut out runs that cover less than 90% of the line
138 line_decent_length_runs.Over(&line_rasterization, 0.9 * line_text_height);
140 if (line_decent_length_runs.runs.empty())
141 {
142 if (line_rasterization.runs.empty())
143 return std::vector<ScanRun>(); // stop the flow
144 // make up a pointless run: anything that's not an empty vector
145 std::vector<ScanRun> result(1);
146 result[0].x_start = line_rasterization.runs[0].st;
147 result[0].x_end = line_rasterization.runs[0].st;
148 result[0].y = _negative_block_progression ? -_current_line_height - _y : _y;
149 return result;
150 }
152 // convert the FloatLigne to what we use: vector<ScanRun>
153 std::vector<ScanRun> result(line_decent_length_runs.runs.size());
154 for (unsigned i = 0 ; i < result.size() ; i++) {
155 result[i].x_start = line_decent_length_runs.runs[i].st;
156 result[i].x_end = line_decent_length_runs.runs[i].en;
157 result[i].y = _negative_block_progression ? -_current_line_height - _y : _y;
158 }
160 return result;
161 }
163 void Layout::ShapeScanlineMaker::completeLine()
164 {
165 _y += _current_line_height;
166 }
168 double Layout::ShapeScanlineMaker::yCoordinate()
169 {
170 if (_negative_block_progression) return -_current_line_height - _y;
171 return _y;
172 }
174 void Layout::ShapeScanlineMaker::setNewYCoordinate(double new_y)
175 {
176 _y = (float)new_y;
177 if (_negative_block_progression) _y = -_current_line_height - _y;
178 // what will happen with the rasteriser if we move off the shape?
179 // it's not an important question because <flowSpan> doesn't have a y attribute
180 }
182 bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &/*line_height*/)
183 {
184 //we actually could return true if only the leading changed, but that's too much effort for something that rarely happens
185 return false;
186 }
188 }//namespace Text
189 }//namespace Inkscape