1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Inkscape::Text::Layout - text layout engine
4 *
5 * Authors:
6 * Richard Hughes <cyreve@users.sf.net>
7 *
8 * Copyright (C) 2005 Richard Hughes
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12 #ifndef __LAYOUT_TNG_H__
13 #define __LAYOUT_TNG_H__
14
15 //#define DEBUG_TEXTLAYOUT_DUMPASTEXT
16
17 #include <2geom/d2.h>
18 #include <2geom/affine.h>
19 #include <glibmm/ustring.h>
20 #include <memory>
21 #include <pango/pango-break.h>
22 #include <algorithm>
23 #include <vector>
24 #include <optional>
25 #include <svg/svg-length.h>
26 #include "style-enums.h"
27
28 namespace Inkscape {
29 namespace Extension {
30 namespace Internal {
31 class CairoRenderContext;
32 }
33 }
34 }
35
36 using Inkscape::Extension::Internal::CairoRenderContext;
37
38 class SPStyle;
39 class SPObject;
40 class Shape;
41 struct SPPrintContext;
42 class Path;
43 class SPCurve;
44 class font_instance;
45 typedef struct _PangoFontDescription PangoFontDescription;
46
47 namespace Inkscape {
48 class DrawingGroup;
49
50 namespace Text {
51
52 /** \brief Generates the layout for either wrapped or non-wrapped text and stores the result
53
54 Use this class for all your text output needs. It takes text with formatting
55 markup as input and turns that into the glyphs and their necessary positions.
56 It stores the glyphs internally, but maintains enough information to both
57 retrieve your own rendering information if you wish and to perform visual
58 text editing where the output refers back to where it came from.
59
60 Usage:
61 -# Construct
62 -# Set the text using appendText() and appendControlCode()
63 -# If you want text wrapping, call appendWrapShape() a few times
64 -# Call calculateFlow()
65 -# You can go several directions from here, but the most interesting
66 things start with creating a Layout::iterator with begin() or end().
67
68 Terminology, in descending order of size:
69 - Flow: Not often used, but when it is it means all the text
70 - Shape: A Shape object which is used to represent one of the regions inside
71 which to flow the text. Can overlap with...
72 - Paragraph: Err...A paragraph. Contains one or more...
73 - Line: An entire horizontal line with a common baseline. Contains one or
74 more...
75 - Chunk: You only get more than one of these when a shape is sufficiently
76 complex that the text has to flow either side of some obstruction in
77 the middle. A chunk is the base unit for wrapping. Contains one or more...
78 - Span: A convenient subset of a chunk with the same font, style,
79 directionality, block progression and input stream. Fill and outline
80 need not be constant because that's a later rendering stage.
81 - This is where it gets weird because a span will contain one or more
82 elements of both of the following, which can overlap with each other in
83 any way:
84 - Character: a single Unicode codepoint from an input stream. Many arabic
85 characters contain multiple glyphs
86 - Glyph: a rendering primitive for font engines. A ligature glyph will
87 represent multiple characters.
88
89 Other terminology:
90 - Input stream: An object representing a single call to appendText() or
91 appendControlCode().
92 - Control code: Metadata in the text stream to signify items that occupy
93 real space (unlike style changes) but don't belong in the text string.
94 Paragraph breaks are in this category. See Layout::TextControlCode.
95 - SVG1.1: The W3C Recommendation "Scalable Vector Graphics (SVG) 1.1"
96 http://www.w3.org/TR/SVG11/
97 - 'left', 'down', etc: These terms are generally used to mean what they
98 mean in left-to-right, top-to-bottom text but rotated or reflected for
99 the current directionality. Thus, the 'width' of a ttb line is actually
100 its height, and the (internally stored) y coordinate of a glyph is
101 actually its x coordinate. Confusing to the reader but much simpler in
102 the code. All public methods use real x and y.
103
104 Comments:
105 - There's a strong emphasis on international support in this class, but
106 that's primarily because once you can display all the insane things
107 required by various languages, simple things like styling text are
108 almost trivial.
109 - There are a few places (appendText() is one) where pointers are held to
110 caller-owned objects and used for quite a long time. This is messy but
111 is safe for our usage scenario and in many cases the cost of copying the
112 objects is quite high.
113 - "Why isn't foo here?": Ask yourself if it's possible to implement foo
114 externally using iterators. However this may not mean that it doesn't
115 belong as a member, though.
116 - I've used floats rather than doubles to store relative distances in some
117 places (internal only) where it would save significant amounts of memory.
118 The SVG spec allows you to do this as long as intermediate calculations
119 are done double. Very very long lines might not finish precisely where
120 you want, but that's to be expected with any typesetting. Also,
121 SVGLength only uses floats.
122 - If you look at the six arrays for holding the output data you'll realise
123 that there's no O(1) way to drill down from a paragraph to find its
124 starting glyph. This was a conscious decision to reduce complexity and
125 to save memory. Drilling down isn't actually that slow because a binary
126 chop will work nicely. Add this to the realisation that most of the
127 times you do this will be in response to user actions and hence you only
128 need to be faster than the user and I think the design makes sense.
129 - There are a massive number of functions acting on Layout::iterator. A
130 large number are trivial and will be inline, but is it really necessary
131 to have all these, especially when some can be implemented by the caller
132 using the others?
133 - The separation of methods between Layout and Layout::iterator is a
134 bit arbitrary, because many methods could go in either. I've used the STL
135 model where the iterator itself can only move around; the base class is
136 required to do anything interesting.
137 - I use Pango internally, not Pangomm. The reason for this is lots of
138 Pangomm methods take Glib::ustrings as input and then output byte offsets
139 within the strings. There's simply no way to use byte offsets with
140 ustrings without some very entertaining reinterpret_cast<>s. The Pangomm
141 docs seem to be lacking quite a lot of things mentioned in the Pango
142 docs, too.
143 */
144 class Layout {
145 public:
146 class iterator;
147 friend class iterator;
148 class Calculator;
149 friend class Calculator;
150 class ScanlineMaker;
151 class InfiniteScanlineMaker;
152 class ShapeScanlineMaker;
153
154 Layout();
155 virtual ~Layout();
156
157 /** Used to specify any particular text direction required. Used for
158 both the 'direction' and 'block-progression' CSS attributes. */
159 enum Direction {LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP};
160
161 /** Used to specify orientation of glyphs in vertical text. */
162 enum Orientation {ORIENTATION_UPRIGHT, ORIENTATION_SIDEWAYS};
163
164
165 /** Display alignment for shapes. See appendWrapShape(). */
166 enum DisplayAlign {DISPLAY_ALIGN_BEFORE, DISPLAY_ALIGN_CENTER, DISPLAY_ALIGN_AFTER};
167
168 /** lengthAdjust values */
169 enum LengthAdjust {LENGTHADJUST_SPACING, LENGTHADJUST_SPACINGANDGLYPHS};
170
171 enum WrapMode {
172 WRAP_NONE, // No wrapping or wrapping via role="line".
173 WRAP_WHITE_SPACE, // Wrapping via 'white-space' property.
174 WRAP_INLINE_SIZE, // Wrapping via 'inline-size' property.
175 WRAP_SHAPE_INSIDE // Wrapping via 'shape-inside' propertry.
176 } wrap_mode = WRAP_NONE;
177
178 /** The optional attributes which can be applied to a SVG text or
179 related tag. See appendText(). See SVG1.1 section 10.4 for the
180 definitions of all these members. See sp_svg_length_list_read() for
181 the standard way to make these vectors. It is the responsibility of
182 the caller to deal with the inheritance of these values using its
183 knowledge of the parse tree. */
184 struct OptionalTextTagAttrs {
185 std::vector<SVGLength> x;
186 std::vector<SVGLength> y;
187 std::vector<SVGLength> dx;
188 std::vector<SVGLength> dy;
189 std::vector<SVGLength> rotate;
190 SVGLength textLength;
191 LengthAdjust lengthAdjust;
192 };
193
194 /** Control codes which can be embedded in the text to be flowed. See
195 appendControlCode(). */
196 enum TextControlCode {
197 PARAGRAPH_BREAK, /// forces the flow to move on to the next line
198 SHAPE_BREAK, /// forces the flow to ignore the remainder of the current shape (from #flow_inside_shapes) and continue at the top of the one after.
199 ARBITRARY_GAP /// inserts an arbitrarily-sized hole in the flow in line with the current text.
200 };
201
202 /** For expressing paragraph alignment. These values are rotated in the
203 case of vertical text, but are not dependent on whether the paragraph is
204 rtl or ltr, thus LEFT is always either left or top. */
205 enum Alignment {LEFT, CENTER, RIGHT, FULL, NONE};
206
207 /** The CSS spec allows line-height:normal to be whatever the user agent
208 thinks will look good. This is our value, as a multiple of font-size. */
209 static const double LINE_HEIGHT_NORMAL;
210
211 // ************************** describing the stuff to flow *************************
212
213 /** \name Input
214 Methods for describing the text you want to flow, its style, and the
215 shapes to flow in to.
216 */
217 //@{
218
219 /** Empties everything stored in this class and resets it to its
220 original state, like when it was created. All iterators on this
221 object will be invalidated (but can be revalidated using
222 validateIterator(). */
223 void clear();
224
225 /** Queries whether any calls have been made to appendText() or
226 appendControlCode() since the object was last cleared. */
inputExists()227 bool inputExists() const
228 {return !_input_stream.empty();}
229
230 bool _input_truncated = false;
inputTruncated()231 bool inputTruncated() const
232 {return _input_truncated;}
233
234 /** adds a new piece of text to the end of the current list of text to
235 be processed. This method can only add text of a consistent style.
236 To add lots of different styles, call it lots of times.
237 \param text The text. \b Note: only a \em pointer is stored. Do not
238 mess with the text until after you have called
239 calculateFlow().
240 \param style The font style. Layout will hold a reference to this
241 object for the duration of its ownership, ie until you
242 call clear() or the class is destroyed. Must not be NULL.
243 \param source Pointer to object that is source of text.
244 \param optional_attributes A structure containing additional options
245 for this text. See OptionalTextTagAttrs. The values are
246 copied to internal storage before this method returns.
247 \param optional_attributes_offset It is convenient for callers to be
248 able to use the same \a optional_attributes structure for
249 several sequential text fields, in which case the vectors
250 will need to be offset. This parameter causes the <i>n</i>th
251 element of all the vectors to be read as if it were the
252 first.
253 \param text_begin Used for selecting only a substring of \a text
254 to process.
255 \param text_end Used for selecting only a substring of \a text
256 to process.
257 */
258 void appendText(Glib::ustring const &text, SPStyle *style, SPObject *source, OptionalTextTagAttrs const *optional_attributes, unsigned optional_attributes_offset, Glib::ustring::const_iterator text_begin, Glib::ustring::const_iterator text_end);
259 inline void appendText(Glib::ustring const &text, SPStyle *style, SPObject *source, OptionalTextTagAttrs const *optional_attributes = nullptr, unsigned optional_attributes_offset = 0)
260 {appendText(text, style, source, optional_attributes, optional_attributes_offset, text.begin(), text.end());}
261
262 /** Control codes are metadata in the text stream to signify items
263 that occupy real space (unlike style changes) but don't belong in the
264 text string. See TextControlCode for the types available.
265
266 A control code \em cannot be the first item in the input stream. Use
267 appendText() with an empty string to set up the paragraph properties.
268 \param code A member of the TextFlowControlCode enumeration.
269 \param width The width in pixels that this item occupies.
270 \param ascent The number of pixels above the text baseline that this
271 control code occupies.
272 \param descent The number of pixels below the text baseline that this
273 control code occupies.
274 \param source Pointer to object that is source of control code.
275 Note that for some control codes (eg tab) the values of the \a width,
276 \a ascender and \a descender are implied by the surrounding text (and
277 in the case of tabs, the values set in tab_stops) so the values you pass
278 here are ignored.
279 */
280 void appendControlCode(TextControlCode code, SPObject *source, double width = 0.0, double ascent = 0.0, double descent = 0.0);
281
282 /** Stores another shape inside which to flow the text. If this method
283 is never called then no automatic wrapping is done and lines will
284 continue to infinity if necessary. Text can be flowed inside multiple
285 shapes in sequence, like with frames in a DTP package. If the text flows
286 past the end of the last shape all remaining text is ignored.
287
288 \param shape The Shape to use next in the flow. The storage for this
289 is managed by the caller, and need only be valid for
290 the duration of the call to calculateFlow().
291 \param display_align The vertical alignment of the text within this
292 shape. See XSL1.0 section 7.13.4. The behaviour of
293 settings other than DISPLAY_ALIGN_BEFORE when using
294 non-rectangular shapes is undefined.
295 */
296 void appendWrapShape(Shape const *shape, DisplayAlign display_align = DISPLAY_ALIGN_BEFORE);
297
298 // ************************** textLength and friends *************************
299
300 /** Gives the length target of this layout, as given by textLength attribute.
301
302 FIXME: by putting it here we only support @textLength on text and flowRoot, not on any
303 spans inside. For spans, we will need to add markers of start and end of a textLength span
304 into the _input_stream. These spans can nest (SVG 1.1, section 10.5). After a first layout
305 calculation, we would go through the input stream and, for each end of a textLength span,
306 go through its items, choose those where it wasn't yet set by a nested span, calculate
307 their number of characters, divide the length deficit by it, and set set the
308 textLengthMultiplier for those characters only. For now we do this for the entire layout,
309 without dealing with spans.
310 */
311 SVGLength textLength;
312
313 /** How do we meet textLength if specified: by letterspacing or by scaling horizontally */
314 LengthAdjust lengthAdjust = LENGTHADJUST_SPACING;
315
316 /** By how much each character needs to be wider or narrower, using the specified lengthAdjust
317 strategy, for the layout to meet its textLength target. Is set to non-zero after the layout
318 is calculated for the first time, then it is recalculated with each glyph getting its adjustment. */
319 /** This one is used by scaling strategies: each glyph width is multiplied by this */
320 double textLengthMultiplier = 1;
321 /** This one is used by letterspacing strategy: to each glyph width, this is added */
322 double textLengthIncrement = 0;
323
324 /** Get the actual spacing increment if it's due with the current values of above stuff, otherwise 0 */
325 double getTextLengthIncrementDue() const;
326 /** Get the actual scale multiplier if it's due with the current values of above stuff, otherwise 1 */
327 double getTextLengthMultiplierDue() const;
328
329 /** Get actual length of layout, by summing span lengths. For one-line non-flowed text, just
330 the width; for multiline non-flowed, sum of lengths of all lines; for flowed text, sum of
331 scanline widths for all non-last lines plus text width of last line. */
332 double getActualLength() const;
333
334
335 // ************************** doing the actual flowing *************************
336
337 /** \name Processing
338 The method to do the actual work of converting text into glyphs.
339 */
340 //@{
341
342 /** Takes all the stuff you set with the members above here and creates
343 a load of glyphs for use with the members below here. All iterators on
344 this object will be invalidated (but can be fixed with validateIterator().
345 The implementation just creates a new Layout::Calculator and calls its
346 Calculator::Calculate() method, so if you want more details on the
347 internals, go there.
348 \return false on failure.
349 */
350 bool calculateFlow();
351
352 //@}
353
354 // ************************** operating on the output glyphs *************************
355
356 /** \name Output
357 Methods for reading and interpreting the output glyphs. See also
358 Layout::iterator.
359 */
360 //@{
361
362 /** Returns true if there are some glyphs in this object, ie whether
363 computeFlow() has been called on a non-empty input since the object was
364 created or the last call to clear(). */
outputExists()365 inline bool outputExists() const
366 {return !_characters.empty();}
367
368 /** Adds all the output glyphs to \a in_arena using the given \a paintbox.
369 \param in_arena The arena to add the glyphs group to
370 \param paintbox The current rendering tile
371 */
372 void show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const;
373
374 /** Calculates the smallest rectangle completely enclosing all the
375 glyphs.
376 \param bounding_box Where to store the box
377 \param transform The transform to be applied to the entire object
378 prior to calculating its bounds.
379 */
380 Geom::OptRect bounds(Geom::Affine const &transform, int start = -1, int length = -1) const;
381
382 /** Sends all the glyphs to the given print context.
383 \param ctx I have
384 \param pbox no idea
385 \param dbox what these
386 \param bbox parameters
387 \param ctm do yet
388 */
389 void print(SPPrintContext *ctx, Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox, Geom::Affine const &ctm) const;
390
391 /** Renders all the glyphs to the given Cairo rendering context.
392 \param ctx The Cairo rendering context to be used
393 */
394 void showGlyphs(CairoRenderContext *ctx) const;
395
396 /** Returns the font family of the indexed span */
397 Glib::ustring getFontFamily(unsigned span_index) const;
398
399 #if DEBUG_TEXTLAYOUT_DUMPASTEXT
400 /** debug and unit test method. Creates a textual representation of the
401 contents of this object. The output is designed to be both human-readable
402 and comprehensible when diffed with a known-good dump. */
403 Glib::ustring dumpAsText() const;
404 #endif
405
406 /** Moves all the glyphs in the structure so that the baseline of all
407 the characters sits neatly along the path specified. If the text has
408 more than one line the results are undefined. The 'align' means to
409 use the SVG align method as documented in SVG1.1 section 10.13.2.
410 NB: njh has suggested that it would be cool if we could flow from
411 shape to path and back again. This is possible, so this method will be
412 removed at some point.
413 A pointer to \a path is retained by the class for use by the cursor
414 positioning functions. */
415 void fitToPathAlign(SVGLength const &startOffset, Path const &path);
416
417 /** Convert the specified range of characters into their bezier
418 outlines.
419 */
420 std::unique_ptr<SPCurve> convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const;
421 std::unique_ptr<SPCurve> convertToCurves() const;
422
423 /** Apply the given transform to all the output presently stored in
424 this object. This only transforms the glyph positions, The glyphs
425 themselves will not be transformed. */
426 void transform(Geom::Affine const &transform);
427
428 //@}
429
430 // **********
431
432 /** \name Output (Iterators)
433 Methods for operating with the Layout::iterator class. The method
434 names ending with 'Index' return 0-based offsets of the number of
435 items since the beginning of the flow.
436 */
437 //@{
438
439 /** Returns an iterator pointing at the first glyph of the flowed output.
440 The first glyph is also the first character, line, paragraph, etc. */
441 inline iterator begin() const;
442
443 /** Returns an iterator pointing just past the end of the last glyph,
444 which is also just past the end of the last chunk, span, etc, etc. */
445 inline iterator end() const;
446
447 /** Returns an iterator pointing at the given character index. This
448 index should be related to the result from a prior call to
449 iteratorToCharIndex(). */
450 inline iterator charIndexToIterator(int char_index) const;
451
452 /** Returns the character index from the start of the flow represented
453 by the given iterator. This number isn't very useful, except for when
454 editing text it will stay valid across calls to computeFlow() and will
455 change in predictable ways when characters are added and removed. It's
456 also useful when transitioning old code. */
457 inline int iteratorToCharIndex(iterator const &it) const;
458
459 /** Checks the validity of the given iterator over the current layout.
460 If it points to a position out of the bounds for this layout it will
461 be corrected to the nearest valid position. If you pass an iterator
462 belonging to a different layout it will be converted to one for this
463 layout. */
464 inline void validateIterator(iterator *it) const;
465
466 /** Returns an iterator pointing to the cursor position for a mouse
467 click at the given coordinates. */
468 iterator getNearestCursorPositionTo(double x, double y) const;
469 inline iterator getNearestCursorPositionTo(Geom::Point const &point) const;
470
471 /** Returns an iterator pointing to the letter whose bounding box contains
472 the given coordinates. end() if the point is not over any letter. The
473 iterator will \em not point at the specific glyph within the character. */
474 iterator getLetterAt(double x, double y) const;
475 inline iterator getLetterAt(Geom::Point &point) const;
476
477 /* Returns an iterator pointing to the character in the output which
478 was created from the given input. If the character at the given byte
479 offset was removed (soft hyphens, for example) the next character after
480 it is returned. If no input was added with the given object, end() is
481 returned. If more than one input has the same object, the first will
482 be used regardless of the value of \a text_iterator. If
483 \a text_iterator is out of bounds, the first or last character belonging
484 to the given input will be returned accordingly.
485 iterator sourceToIterator(SPObject *source, Glib::ustring::const_iterator text_iterator) const;
486 */
487
488 /** Returns an iterator pointing to the first character in the output
489 which was created from the given source. If \a source object is invalid,
490 end() is returned. If more than one input has the same object, the
491 first one will be used. */
492 iterator sourceToIterator(SPObject *source) const;
493
494 // many functions acting on iterators, most of which are obvious
495 // also most of them don't check that \a it != end(). Be careful.
496
497 /** Returns the bounding box of the given glyph, and its rotation.
498 The centre of rotation is the horizontal centre of the box at the
499 text baseline. */
500 Geom::OptRect glyphBoundingBox(iterator const &it, double *rotation) const;
501
502 /** Returns the zero-based line number of the character pointed to by
503 \a it. */
504 inline unsigned lineIndex(iterator const &it) const;
505
506 /** Returns the zero-based number of the shape which contains the
507 character pointed to by \a it. */
508 inline unsigned shapeIndex(iterator const &it) const;
509
510 /** Returns true if the character at \a it is a whitespace, as defined
511 by Pango. This is not meant to be used for picking out words from the
512 output, use iterator::nextStartOfWord() and friends instead. */
513 inline bool isWhitespace(iterator const &it) const;
514
515 /** Returns character pointed to by \a it. If \a it == end() the result is undefined. */
516 inline gchar characterAt(iterator const &it) const;
517
518 /** Returns true if the text at \a it is hidden (i.e. overflowed). */
519 bool isHidden(iterator const &it) const;
520
521 /** Discovers where the character pointed to by \a it came from, by
522 retrieving the object that was passed to the call to appendText() or
523 appendControlCode() which generated that output. If \a it == end()
524 then NULL is returned as the object. If the character was generated
525 from a call to appendText() then the optional \a text_iterator
526 parameter is set to point to the actual character, otherwise
527 \a text_iterator is unaltered. */
528 void getSourceOfCharacter(iterator const &it, SPObject **source, Glib::ustring::iterator *text_iterator = nullptr) const;
529
530 /** For latin text, the left side of the character, on the baseline */
531 Geom::Point characterAnchorPoint(iterator const &it) const;
532
533 /** For left aligned text, the leftmost end of the baseline
534 For rightmost text, the rightmost... you probably got it by now ;-)*/
535 std::optional<Geom::Point> baselineAnchorPoint() const;
536
537 Geom::Path baseline() const;
538
539 /** This is that value to apply to the x,y attributes of tspan role=line
540 elements, and hence it takes alignment into account. */
541 Geom::Point chunkAnchorPoint(iterator const &it) const;
542
543 /** Returns the box extents (not ink extents) of the given character.
544 The centre of rotation is at the horizontal centre of the box on the
545 text baseline. */
546 Geom::Rect characterBoundingBox(iterator const &it, double *rotation = nullptr) const;
547
548 /** Basically uses characterBoundingBox() on all the characters from
549 \a start to \a end and returns the union of these boxes. The return value
550 is a list of zero or more quadrilaterals specified by a group of four
551 points for each, thus size() is always a multiple of four. */
552 std::vector<Geom::Point> createSelectionShape(iterator const &it_start, iterator const &it_end, Geom::Affine const &transform) const;
553
554 /** Returns true if \a it points to a character which is a valid cursor
555 position, as defined by Pango. */
556 inline bool isCursorPosition(iterator const &it) const;
557
558 /** Gets the ideal cursor shape for a given iterator. The result is
559 undefined if \a it is not at a valid cursor position.
560 \param it The location in the output
561 \param position The pixel location of the centre of the 'bottom' of
562 the cursor.
563 \param height The height in pixels of the surrounding text
564 \param rotation The angle to draw from \a position. Radians, zero up,
565 increasing clockwise.
566 */
567 void queryCursorShape(iterator const &it, Geom::Point &position, double &height, double &rotation) const;
568
569 /** Returns true if \a it points to a character which is a the start of
570 a word, as defined by Pango. */
571 inline bool isStartOfWord(iterator const &it) const;
572
573 /** Returns true if \a it points to a character which is a the end of
574 a word, as defined by Pango. */
575 inline bool isEndOfWord(iterator const &it) const;
576
577 /** Returns true if \a it points to a character which is a the start of
578 a sentence, as defined by Pango. */
579 inline bool isStartOfSentence(iterator const &it) const;
580
581 /** Returns true if \a it points to a character which is a the end of
582 a sentence, as defined by Pango. */
583 inline bool isEndOfSentence(iterator const &it) const;
584
585 /** Returns the zero-based number of the paragraph containing the
586 character pointed to by \a it. */
587 inline unsigned paragraphIndex(iterator const &it) const;
588
589 /** Returns the actual alignment used for the paragraph containing
590 the character pointed to by \a it. This means that the CSS 'start'
591 and 'end' are correctly translated into LEFT or RIGHT according to
592 the paragraph's directionality. For vertical text, LEFT is top
593 alignment and RIGHT is bottom. */
594 inline Alignment paragraphAlignment(iterator const &it) const;
595
596 /** Returns kerning information which could cause the current output
597 to be exactly reproduced if the letter and word spacings were zero and
598 full justification was not used. The x and y arrays are not used, but
599 they are cleared. The dx applied to the first character in a chunk
600 will always be zero. If the region between \a from and \a to crosses
601 a line break then the results may be surprising, and are undefined.
602 Trailing zeros on the returned arrays will be trimmed. */
603 void simulateLayoutUsingKerning(iterator const &from, iterator const &to, OptionalTextTagAttrs *result) const;
604
605 //@}
606
607
608 /**
609 * Keep track of font metrics. Two use cases:
610 * 1. Keep track of ascent, descent, and x-height of an individual font.
611 * 2. Keep track of effective ascent and descent that includes half-leading.
612 *
613 * Note: Leading refers to the "external" leading which is added (subtracted) due to
614 * a computed value of 'line-height' that differs from 'font-size'. "Internal" leading
615 * which is specified inside a font is not used in CSS. The 'font-size' is based on
616 * the font's em size which is 'ascent' + 'descent'.
617 *
618 * This structure was renamed (and modified) from "LineHeight".
619 *
620 * It's useful for this to be public so that ScanlineMaker can use it.
621 */
622 class FontMetrics {
623
624 public:
FontMetrics()625 FontMetrics() { reset(); }
626
reset()627 void reset() {
628 ascent = 0.8;
629 descent = 0.2;
630 xheight = 0.5;
631 ascent_max = 0.8;
632 descent_max = 0.2;
633 }
634
635 void set( font_instance *font );
636
637 // CSS 2.1 dictates that font-size is based on em-size which is defined as ascent + descent
emSize()638 inline double emSize() const {return ascent + descent;}
639 // Alternatively name function for use 2.
lineSize()640 inline double lineSize() const { return ascent + descent; }
setZero()641 inline void setZero() {ascent = descent = xheight = ascent_max = descent_max = 0.0;}
642
643 // For scaling for 'font-size'.
644 inline FontMetrics& operator*=(double x) {
645 ascent *= x; descent *= x; xheight *= x; ascent_max *= x; descent_max *= x;
646 return *this;
647 }
648
649 /// Save the larger values of ascent and descent between this and other. Needed for laying
650 /// out a line with mixed font-sizes, fonts, or line spacings.
651 void max(FontMetrics const &other);
652
653 /// Calculate the effective ascent and descent including half "leading".
654 void computeEffective( const double &line_height );
655
getTypoAscent()656 inline double getTypoAscent() const {return ascent; }
getTypoDescent()657 inline double getTypoDescent() const {return descent; }
getXHeight()658 inline double getXHeight() const {return xheight; }
getMaxAscent()659 inline double getMaxAscent() const {return ascent_max; }
getMaxDescent()660 inline double getMaxDescent() const {return descent_max; }
661
662 // private:
663 double ascent; // Typographic ascent.
664 double descent; // Typographic descent. (Normally positive).
665 double xheight; // Height of 'x' measured from alphabetic baseline.
666 double ascent_max; // Maximum ascent of all glyphs in font.
667 double descent_max; // Maximum descent of all glyphs in font.
668
669 }; // End FontMetrics
670
671 /** The strut is the minimum value used in calculating line height. */
672 FontMetrics strut;
673
674 private:
675 /** Erases all the stuff set by the owner as input, ie #_input_stream
676 and #_input_wrap_shapes. */
677 void _clearInputObjects();
678
679 /** Erases all the stuff output by computeFlow(). Glyphs and things. */
680 void _clearOutputObjects();
681
682 static const gunichar UNICODE_SOFT_HYPHEN;
683
684 // ******************* input flow
685
686 enum InputStreamItemType {TEXT_SOURCE, CONTROL_CODE};
687
688 class InputStreamItem {
689 public:
690 virtual ~InputStreamItem() = default;
691 virtual InputStreamItemType Type() =0;
692 SPObject *source;
693 };
694
695 /** Represents a text item in the input stream. See #_input_stream.
696 Most of the members are copies of the values passed to appendText(). */
697 class InputStreamTextSource : public InputStreamItem {
698 public:
Type()699 InputStreamItemType Type() override {return TEXT_SOURCE;}
700 ~InputStreamTextSource() override;
701 Glib::ustring const *text; /// owned by the caller
702 Glib::ustring::const_iterator text_begin, text_end;
703 int text_length; /// in characters, from text_start to text_end only
704 SPStyle *style;
705 /** These vectors can (often will) be shorter than the text
706 in this source, but never longer. */
707 std::vector<SVGLength> x;
708 std::vector<SVGLength> y;
709 std::vector<SVGLength> dx;
710 std::vector<SVGLength> dy;
711 std::vector<SVGLength> rotate;
712 SVGLength textLength;
713 LengthAdjust lengthAdjust;
714 Glib::ustring lang;
715
716 // a few functions for some of the more complicated style accesses
717 /// The return value must be freed with pango_font_description_free()
718 PangoFontDescription *styleGetFontDescription() const;
719 font_instance *styleGetFontInstance() const;
720 Direction styleGetBlockProgression() const;
721 SPCSSTextOrientation styleGetTextOrientation() const;
722 SPCSSBaseline styleGetDominantBaseline() const;
723 Alignment styleGetAlignment(Direction para_direction, bool try_text_align) const;
724 };
725
726 /** Represents a control code item in the input stream. See
727 #_input_streams. All the members are copies of the values passed to
728 appendControlCode(). */
729 class InputStreamControlCode : public InputStreamItem {
730 public:
Type()731 InputStreamItemType Type() override {return CONTROL_CODE;}
732 TextControlCode code;
733 double ascent;
734 double descent;
735 double width;
736 };
737
738 /** This is our internal storage for all the stuff passed to the
739 appendText() and appendControlCode() functions. */
740 std::vector<InputStreamItem*> _input_stream;
741
742 /** The parameters to appendText() are allowed to be a little bit
743 complex. This copies them to be the right length and starting at zero.
744 We also don't want to write five bits of identical code just with
745 different variable names. */
746 static void _copyInputVector(std::vector<SVGLength> const &input_vector, unsigned input_offset, std::vector<SVGLength> *output_vector, size_t max_length);
747
748 /** The overall block-progression of the whole flow. */
_blockProgression()749 inline Direction _blockProgression() const
750 {
751 if(!_input_stream.empty())
752 return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetBlockProgression();
753 return TOP_TO_BOTTOM;
754 }
755
756 /** The overall text-orientation of the whole flow. */
_blockTextOrientation()757 inline SPCSSTextOrientation _blockTextOrientation() const
758 {
759 if(!_input_stream.empty())
760 return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetTextOrientation();
761 return SP_CSS_TEXT_ORIENTATION_MIXED;
762 }
763
764 /** The overall text-orientation of the whole flow. */
_blockBaseline()765 inline SPCSSBaseline _blockBaseline() const
766 {
767 if(!_input_stream.empty())
768 return static_cast<InputStreamTextSource*>(_input_stream.front())->styleGetDominantBaseline();
769 return SP_CSS_BASELINE_AUTO;
770 }
771
772 /** so that LEFT_TO_RIGHT == RIGHT_TO_LEFT but != TOP_TO_BOTTOM */
773 static bool _directions_are_orthogonal(Direction d1, Direction d2);
774
775 /** If the output is empty callers still want to be able to call
776 queryCursorShape() and get a valid answer so, while #_input_wrap_shapes
777 can still be considered valid, we need to precompute the cursor shape
778 for this case. */
779 void _calculateCursorShapeForEmpty();
780
781 struct CursorShape {
782 Geom::Point position;
783 double height;
784 double rotation;
785 } _empty_cursor_shape;
786
787 // ******************* input shapes
788
789 struct InputWrapShape {
790 Shape const *shape; /// as passed to Layout::appendWrapShape()
791 DisplayAlign display_align; /// as passed to Layout::appendWrapShape()
792 };
793 std::vector<InputWrapShape> _input_wrap_shapes;
794
795 // ******************* output
796
797 /** as passed to fitToPathAlign() */
798 Path const *_path_fitted = nullptr;
799
800 struct Glyph;
801 struct Character;
802 struct Span;
803 struct Chunk;
804 struct Line;
805 struct Paragraph;
806
807 // A glyph
808 struct Glyph {
809 int glyph;
810 unsigned in_character;
811 bool hidden;
812 float x; /// relative to the start of the chunk
813 float y; /// relative to the current line's baseline
814 float rotation; /// absolute, modulo any object transforms, which we don't know about
815 Orientation orientation; /// Orientation of glyph in vertical text
816 float advance; /// for positioning next glyph
817 float vertical_scale; /// to implement lengthAdjust="spacingAndGlyphs" that must scale glyphs only horizontally; instead we change font size and then undo that change vertically only
spanGlyph818 inline Span const & span (Layout const *l) const {return l->_spans[l->_characters[in_character].in_span];}
chunkGlyph819 inline Chunk const & chunk(Layout const *l) const {return l->_chunks[l->_spans[l->_characters[in_character].in_span].in_chunk];}
lineGlyph820 inline Line const & line (Layout const *l) const {return l->_lines[l->_chunks[l->_spans[l->_characters[in_character].in_span].in_chunk].in_line];}
821 };
822
823 // A unicode character
824 struct Character {
825 unsigned in_span;
826 float x; /// relative to the start of the *span* (so we can do block-progression)
827 PangoLogAttr char_attributes;
828 gchar the_char = '#';
829 int in_glyph; /// will be -1 if this character has no visual representation
spanCharacter830 inline Span const & span (Layout const *l) const {return l->_spans[in_span];}
chunkCharacter831 inline Chunk const & chunk (Layout const *l) const {return l->_chunks[l->_spans[in_span].in_chunk];}
lineCharacter832 inline Line const & line (Layout const *l) const {return l->_lines[l->_chunks[l->_spans[in_span].in_chunk].in_line];}
paragraphCharacter833 inline Paragraph const & paragraph(Layout const *l) const {return l->_paragraphs[l->_lines[l->_chunks[l->_spans[in_span].in_chunk].in_line].in_paragraph];}
834 // to get the advance width of a character, subtract the x values if it's in the middle of a span, or use span.x_end if it's at the end
835 };
836
837 // A collection of characters that share the same style and position start (<text> or <tspan> x, y attributes).
838 struct Span {
839 unsigned in_chunk;
840 font_instance *font;
841 float font_size;
842 float x_start; /// relative to the start of the chunk
843 float x_end; /// relative to the start of the chunk
844 float y_offset; /// relative to line baseline (without baseline shift)
widthSpan845 inline float width() const {return std::abs(x_start - x_end);}
846 FontMetrics line_height;
847 double baseline_shift; /// relative to the line's baseline (CSS)
848 SPCSSTextOrientation text_orientation;
849 Direction direction; /// See CSS3 section 3.2. Either rtl or ltr
850 Direction block_progression; /// See CSS3 section 3.2. The direction in which lines go.
851 unsigned in_input_stream_item;
852 Glib::ustring::const_iterator input_stream_first_character;
chunkSpan853 inline Chunk const & chunk (Layout const *l) const {return l->_chunks[in_chunk]; }
lineSpan854 inline Line const & line (Layout const *l) const {return l->_lines[l->_chunks[in_chunk].in_line]; }
paragraphSpan855 inline Paragraph const & paragraph(Layout const *l) const {return l->_paragraphs[l->_lines[l->_chunks[in_chunk].in_line].in_paragraph];}
856 };
857
858 // A part of a line that is not broken.
859 struct Chunk {
860 unsigned in_line;
861 double left_x;
862 };
863
864 // A line of text. Depending on the shape, it may contain one or more chunks.
865 struct Line {
866 unsigned in_paragraph;
867 double baseline_y;
868 unsigned in_shape;
869 bool hidden;
870 };
871
872 // A paragraph. SVG 2 does not contain native paragraphs.
873 struct Paragraph {
874 Direction base_direction; /// can be overridden by child Span objects
875 Alignment alignment;
876 };
877
878 std::vector<Paragraph> _paragraphs;
879 std::vector<Line> _lines;
880 std::vector<Chunk> _chunks;
881 std::vector<Span> _spans;
882 std::vector<Character> _characters;
883 std::vector<Glyph> _glyphs;
884
885 /** gets the overall matrix that transforms the given glyph from local
886 space to world space. */
887 void _getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) const;
888
889 // loads of functions to drill down the object tree, all of them
890 // annoyingly similar and all of them requiring predicate functors.
891 // I'll be buggered if I can find a way to make it work with
892 // functions or with a templated functor, so macros it is.
893 #define EMIT_PREDICATE(name, object_type, index_generator) \
894 class name { \
895 Layout const * const _flow; \
896 public: \
897 inline name(Layout const *flow) : _flow(flow) {} \
898 inline bool operator()(object_type const &object, unsigned index) \
899 {g_assert(_flow); return index_generator < index;} \
900 }
901 // end of macro
902 EMIT_PREDICATE(PredicateLineToSpan, Span, _flow->_chunks[object.in_chunk].in_line);
903 EMIT_PREDICATE(PredicateLineToCharacter, Character, _flow->_chunks[_flow->_spans[object.in_span].in_chunk].in_line);
904 EMIT_PREDICATE(PredicateSpanToCharacter, Character, object.in_span);
905 EMIT_PREDICATE(PredicateSourceToCharacter, Character, _flow->_spans[object.in_span].in_input_stream_item);
906
_lineToSpan(unsigned line_index)907 inline unsigned _lineToSpan(unsigned line_index) const
908 {return std::lower_bound(_spans.begin(), _spans.end(), line_index, PredicateLineToSpan(this)) - _spans.begin();}
_lineToCharacter(unsigned line_index)909 inline unsigned _lineToCharacter(unsigned line_index) const
910 {return std::lower_bound(_characters.begin(), _characters.end(), line_index, PredicateLineToCharacter(this)) - _characters.begin();}
_spanToCharacter(unsigned span_index)911 inline unsigned _spanToCharacter(unsigned span_index) const
912 {return std::lower_bound(_characters.begin(), _characters.end(), span_index, PredicateSpanToCharacter(this)) - _characters.begin();}
_sourceToCharacter(unsigned source_index)913 inline unsigned _sourceToCharacter(unsigned source_index) const
914 {return std::lower_bound(_characters.begin(), _characters.end(), source_index, PredicateSourceToCharacter(this)) - _characters.begin();}
915
916 /** given an x and y coordinate and a line number, returns an iterator
917 pointing to the closest cursor position on that line to the
918 coordinate.
919 ('y' is needed to handle cases where multiline text is simulated via the 'y' attribute.) */
920 iterator _cursorXOnLineToIterator(unsigned line_index, double local_x, double local_y = 0) const;
921
922 /** calculates the width of a chunk, which is the largest x
923 coordinate (start or end) of the spans contained within it. */
924 double _getChunkWidth(unsigned chunk_index) const;
925 };
926
927 /** \brief Holds a position within the glyph output of Layout.
928
929 Used to access the output of a Layout, query information and generally
930 move around in it. See Layout for a glossary of the names of functions.
931
932 I'm not going to document all the methods because most of their names make
933 their function self-evident.
934
935 A lot of the functions would do the same thing in a naive implementation
936 for latin-only text, for example nextCharacter(), nextCursorPosition() and
937 cursorRight(). Generally it's fairly obvious which one you should use in a
938 given situation, but sometimes you might need to put some thought in to it.
939
940 All the methods return false if the requested action would have caused the
941 current position to move out of bounds. In this case the position is moved
942 to either begin() or end(), depending on which direction you were going.
943
944 Note that some characters do not have a glyph representation (eg line
945 breaks), so if you try using prev/nextGlyph() from one of these you're
946 heading for a crash.
947 */
948 class Layout::iterator {
949 public:
950 friend class Layout;
951 // this is just so you can create uninitialised iterators - don't actually try to use one
iterator()952 iterator() :
953 _parent_layout(nullptr),
954 _glyph_index(-1),
955 _char_index(0),
956 _cursor_moving_vertically(false),
957 _x_coordinate(0.0){}
958 // no copy constructor required, the default does what we want
959 bool operator== (iterator const &other) const
960 {return _glyph_index == other._glyph_index && _char_index == other._char_index;}
961 bool operator!= (iterator const &other) const
962 {return _glyph_index != other._glyph_index || _char_index != other._char_index;}
963
964 /* mustn't compare _glyph_index in these operators because for characters
965 that don't have glyphs (line breaks, elided soft hyphens, etc), the glyph
966 index is -1 which makes them not well-ordered. To be honest, iterating by
967 glyphs is not very useful and should be avoided. */
968 bool operator< (iterator const &other) const
969 {return _char_index < other._char_index;}
970 bool operator<= (iterator const &other) const
971 {return _char_index <= other._char_index;}
972 bool operator> (iterator const &other) const
973 {return _char_index > other._char_index;}
974 bool operator>= (iterator const &other) const
975 {return _char_index >= other._char_index;}
976
977 /* **** visual-oriented methods **** */
978
979 //glyphs
980 inline bool prevGlyph();
981 inline bool nextGlyph();
982
983 //span
984 bool prevStartOfSpan();
985 bool thisStartOfSpan();
986 bool nextStartOfSpan();
987
988 //chunk
989 bool prevStartOfChunk();
990 bool thisStartOfChunk();
991 bool nextStartOfChunk();
992
993 //line
994 bool prevStartOfLine();
995 bool thisStartOfLine();
996 bool nextStartOfLine();
997 bool thisEndOfLine();
998
999 //shape
1000 bool prevStartOfShape();
1001 bool thisStartOfShape();
1002 bool nextStartOfShape();
1003
1004 /* **** text-oriented methods **** */
1005
1006 //characters
1007 inline bool nextCharacter();
1008 inline bool prevCharacter();
1009
1010 bool nextCursorPosition();
1011 bool prevCursorPosition();
1012 bool nextLineCursor(int n = 1);
1013 bool prevLineCursor(int n = 1);
1014
1015 //words
1016 bool nextStartOfWord();
1017 bool prevStartOfWord();
1018 bool nextEndOfWord();
1019 bool prevEndOfWord();
1020
1021 //sentences
1022 bool nextStartOfSentence();
1023 bool prevStartOfSentence();
1024 bool nextEndOfSentence();
1025 bool prevEndOfSentence();
1026
1027 //paragraphs
1028 bool prevStartOfParagraph();
1029 bool thisStartOfParagraph();
1030 bool nextStartOfParagraph();
1031 //no endOfPara methods because that's just the previous char
1032
1033 //sources
1034 bool prevStartOfSource();
1035 bool thisStartOfSource();
1036 bool nextStartOfSource();
1037
1038 //logical cursor movement
1039 bool cursorUp(int n = 1);
1040 bool cursorDown(int n = 1);
1041 bool cursorLeft();
1042 bool cursorRight();
1043
1044 //logical cursor movement (by word or paragraph)
1045 bool cursorUpWithControl();
1046 bool cursorDownWithControl();
1047 bool cursorLeftWithControl();
1048 bool cursorRightWithControl();
1049
1050 private:
1051 Layout const *_parent_layout;
1052 int _glyph_index; /// index into Layout::glyphs, or -1
1053 unsigned _char_index; /// index into Layout::character
1054 bool _cursor_moving_vertically;
1055 /** for cursor up/down movement we must maintain the x position where
1056 we started so the cursor doesn't 'drift' left or right with the repeated
1057 quantization to character boundaries. */
1058 double _x_coordinate;
1059
iterator(Layout const * p,unsigned c,int g)1060 inline iterator(Layout const *p, unsigned c, int g)
1061 : _parent_layout(p), _glyph_index(g), _char_index(c), _cursor_moving_vertically(false), _x_coordinate(0.0) {}
iterator(Layout const * p,unsigned c)1062 inline iterator(Layout const *p, unsigned c)
1063 : _parent_layout(p), _glyph_index(p->_characters[c].in_glyph), _char_index(c), _cursor_moving_vertically(false), _x_coordinate(0.0) {}
1064 // no dtor required
1065 void beginCursorUpDown(); /// stores the current x coordinate so that the cursor won't drift. See #_x_coordinate
1066
1067 /** moves forward or backwards one cursor position according to the
1068 directionality of the current paragraph, but ignoring block progression.
1069 Helper for the cursor*() functions. */
1070 bool _cursorLeftOrRightLocalX(Direction direction);
1071
1072 /** moves forward or backwards by until the next character with
1073 is_word_start according to the directionality of the current paragraph,
1074 but ignoring block progression. Helper for the cursor*WithControl()
1075 functions. */
1076 bool _cursorLeftOrRightLocalXByWord(Direction direction);
1077 };
1078
1079 // ************************** inline methods
1080
begin()1081 inline Layout::iterator Layout::begin() const
1082 {return iterator(this, 0, 0);}
1083
end()1084 inline Layout::iterator Layout::end() const
1085 {return iterator(this, _characters.size(), _glyphs.size());}
1086
charIndexToIterator(int char_index)1087 inline Layout::iterator Layout::charIndexToIterator(int char_index) const
1088 {
1089 if (char_index < 0) return begin();
1090 if (char_index >= (int)_characters.size()) return end();
1091 return iterator(this, char_index);
1092 }
1093
iteratorToCharIndex(Layout::iterator const & it)1094 inline int Layout::iteratorToCharIndex(Layout::iterator const &it) const
1095 {return it._char_index;}
1096
validateIterator(Layout::iterator * it)1097 inline void Layout::validateIterator(Layout::iterator *it) const
1098 {
1099 it->_parent_layout = this;
1100 if (it->_char_index >= _characters.size()) {
1101 it->_char_index = _characters.size();
1102 it->_glyph_index = _glyphs.size();
1103 } else
1104 it->_glyph_index = _characters[it->_char_index].in_glyph;
1105 }
1106
getNearestCursorPositionTo(Geom::Point const & point)1107 inline Layout::iterator Layout::getNearestCursorPositionTo(Geom::Point const &point) const
1108 {return getNearestCursorPositionTo(point[0], point[1]);}
1109
getLetterAt(Geom::Point & point)1110 inline Layout::iterator Layout::getLetterAt(Geom::Point &point) const
1111 {return getLetterAt(point[0], point[1]);}
1112
lineIndex(iterator const & it)1113 inline unsigned Layout::lineIndex(iterator const &it) const
1114 {return it._char_index == _characters.size() ? _lines.size() - 1 : _characters[it._char_index].chunk(this).in_line;}
1115
shapeIndex(iterator const & it)1116 inline unsigned Layout::shapeIndex(iterator const &it) const
1117 {return it._char_index == _characters.size() ? _input_wrap_shapes.size() - 1 : _characters[it._char_index].line(this).in_shape;}
1118
isWhitespace(iterator const & it)1119 inline bool Layout::isWhitespace(iterator const &it) const
1120 {return it._char_index == _characters.size() || _characters[it._char_index].char_attributes.is_white;}
1121
characterAt(iterator const & it)1122 inline gchar Layout::characterAt(iterator const &it) const
1123 {
1124 return _characters[it._char_index].the_char;
1125 }
1126
isCursorPosition(iterator const & it)1127 inline bool Layout::isCursorPosition(iterator const &it) const
1128 {return it._char_index == _characters.size() || _characters[it._char_index].char_attributes.is_cursor_position;}
1129
isStartOfWord(iterator const & it)1130 inline bool Layout::isStartOfWord(iterator const &it) const
1131 {return it._char_index != _characters.size() && _characters[it._char_index].char_attributes.is_word_start;}
1132
isEndOfWord(iterator const & it)1133 inline bool Layout::isEndOfWord(iterator const &it) const
1134 {return it._char_index == _characters.size() || _characters[it._char_index].char_attributes.is_word_end;}
1135
isStartOfSentence(iterator const & it)1136 inline bool Layout::isStartOfSentence(iterator const &it) const
1137 {return it._char_index != _characters.size() && _characters[it._char_index].char_attributes.is_sentence_start;}
1138
isEndOfSentence(iterator const & it)1139 inline bool Layout::isEndOfSentence(iterator const &it) const
1140 {return it._char_index == _characters.size() || _characters[it._char_index].char_attributes.is_sentence_end;}
1141
paragraphIndex(iterator const & it)1142 inline unsigned Layout::paragraphIndex(iterator const &it) const
1143 {return it._char_index == _characters.size() ? _paragraphs.size() - 1 : _characters[it._char_index].line(this).in_paragraph;}
1144
paragraphAlignment(iterator const & it)1145 inline Layout::Alignment Layout::paragraphAlignment(iterator const &it) const
1146 {return (_paragraphs.size() == 0) ? NONE : _paragraphs[paragraphIndex(it)].alignment;}
1147
nextGlyph()1148 inline bool Layout::iterator::nextGlyph()
1149 {
1150 _cursor_moving_vertically = false;
1151 if (_glyph_index >= (int)_parent_layout->_glyphs.size() - 1) {
1152 if (_glyph_index == (int)_parent_layout->_glyphs.size()) return false;
1153 _char_index = _parent_layout->_characters.size();
1154 _glyph_index = _parent_layout->_glyphs.size();
1155 }
1156 else _char_index = _parent_layout->_glyphs[++_glyph_index].in_character;
1157 return true;
1158 }
1159
prevGlyph()1160 inline bool Layout::iterator::prevGlyph()
1161 {
1162 _cursor_moving_vertically = false;
1163 if (_glyph_index == 0) return false;
1164 _char_index = _parent_layout->_glyphs[--_glyph_index].in_character;
1165 return true;
1166 }
1167
nextCharacter()1168 inline bool Layout::iterator::nextCharacter()
1169 {
1170 _cursor_moving_vertically = false;
1171 if (_char_index + 1 >= _parent_layout->_characters.size()) {
1172 if (_char_index == _parent_layout->_characters.size()) return false;
1173 _char_index = _parent_layout->_characters.size();
1174 _glyph_index = _parent_layout->_glyphs.size();
1175 }
1176 else _glyph_index = _parent_layout->_characters[++_char_index].in_glyph;
1177 return true;
1178 }
1179
prevCharacter()1180 inline bool Layout::iterator::prevCharacter()
1181 {
1182 _cursor_moving_vertically = false;
1183 if (_char_index == 0) return false;
1184 _glyph_index = _parent_layout->_characters[--_char_index].in_glyph;
1185 return true;
1186 }
1187
1188 }//namespace Text
1189 }//namespace Inkscape
1190
1191 std::ostream &operator<<(std::ostream &out, const Inkscape::Text::Layout::FontMetrics &f);
1192 std::ostream &operator<<(std::ostream &out, const Inkscape::Text::Layout::FontMetrics *f);
1193
1194
1195 #endif
1196
1197
1198 /*
1199 Local Variables:
1200 mode:c++
1201 c-file-style:"stroustrup"
1202 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1203 indent-tabs-mode:nil
1204 fill-column:99
1205 End:
1206 */
1207 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
1208