Code

bff94c151fad6b8445e6246233e21c2dced8e407
[inkscape.git] / src / libnrtype / Layout-TNG-Scanline-Makers.cpp
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;
37         
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(NR::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(NR::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(NR::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;
107 Layout::ShapeScanlineMaker::~ShapeScanlineMaker()
109     _rotated_shape->EndRaster();  
110     if (_shape_needs_freeing)
111         delete _rotated_shape;
114 std::vector<Layout::ScanlineMaker::ScanRun> Layout::ShapeScanlineMaker::makeScanline(Layout::LineHeight const &line_height)
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;
163 void Layout::ShapeScanlineMaker::completeLine()
165     _y += _current_line_height;
168 double Layout::ShapeScanlineMaker::yCoordinate()
170     if (_negative_block_progression) return -_current_line_height - _y;
171     return _y;
174 void Layout::ShapeScanlineMaker::setNewYCoordinate(double new_y)
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
182 bool Layout::ShapeScanlineMaker::canExtendCurrentScanline(Layout::LineHeight const &/*line_height*/)
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;
188 }//namespace Text
189 }//namespace Inkscape