// SPDX-License-Identifier: GPL-2.0-or-later /* * Inkscape::Text::Layout::ScanlineMaker - text layout engine shape measurers * * Authors: * Richard Hughes * * Copyright (C) 2005 Richard Hughes * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #ifndef LAYOUT_TNG_SCANLINE_MAKER_H #define LAYOUT_TNG_SCANLINE_MAKER_H #include #include #include "libnrtype/Layout-TNG.h" class Shape; namespace Inkscape { namespace Text { /** \brief private to Layout. Generates lists of chunks within a shape. This is the abstract base class for taking a given shape and scanning through it line-by-line to get the horizontal extents of each chunk for a line of a given height. There are two specialisations: One for real shapes and one that turns off wrapping by simulating an infinite shape. In due course there will be a further specialisation to optimise for the common case where the shape is a rectangle. */ class Layout::ScanlineMaker { public: virtual ~ScanlineMaker() = default; struct ScanRun { double y; /// that's the top of the scan run, not the baseline double x_start; // these are not flipped according to the text direction double x_end; inline double width() const {return std::abs(x_start - x_end);} }; /** Returns a list of chunks on the current line which can fit text with the given properties. It is up to the caller to discard any chunks which are too narrow for its needs. This function may change the y coordinate between calls if the new height too big to fit in the space remaining in this shape. Returns an empty vector if there is no space left in the current shape. */ virtual std::vector makeScanline(Layout::FontMetrics const &line_height) = 0; /** Indicates that the caller has successfully filled the current line and hence that the next call to makeScanline() should return lines on the next lower line. There is no error return, the next call to makeScanline() will give an error if there is no more space. */ virtual void completeLine() = 0; /** Returns the y coordinate of the top of the scanline that will be returned by the next call to makeScanline(). */ virtual double yCoordinate() = 0; /** Forces an arbitrary change in the stored y coordinate of the object. The next call to makeScanline() will return runs whose top is at the new coordinate. */ virtual void setNewYCoordinate(double new_y) = 0; /** Tests whether the caller can fit a new line with the given metrics into exactly the space returned by the previous call to makeScanline(). This saves the caller from having to discard its wrapping solution and starting at the beginning of the line again when a larger font is seen. The metrics given here are considered to be the ones that are being used now, and hence is the line advance height used by completeLine(). */ virtual bool canExtendCurrentScanline(Layout::FontMetrics const &line_height) = 0; /** Sets current line block height. Call before completeLine() to correct for actually used line height (in case some chunks with larger font-size rolled back). */ virtual void setLineHeight(Layout::FontMetrics const &line_height) = 0; }; /** \brief private to Layout. Generates infinite scanlines for when you don't want wrapping This is a 'fake' scanline maker which will always return infinite results, effectively turning off wrapping. It's a very simple implementation. It does have the curious property, however, that the input coordinates are 'real' x and y, but the outputs are rotated according to the \a block_progression. */ class Layout::InfiniteScanlineMaker : public Layout::ScanlineMaker { public: InfiniteScanlineMaker(double initial_x, double initial_y, Layout::Direction block_progression); ~InfiniteScanlineMaker() override; /** Returns a single infinite run at the current location */ std::vector makeScanline(Layout::FontMetrics const &line_height) override; /** Increments the current y by the current line height */ void completeLine() override; double yCoordinate() override { return _y; } /** Just changes y */ void setNewYCoordinate(double new_y) override; /** Always true, but has to save the new height */ bool canExtendCurrentScanline(Layout::FontMetrics const &line_height) override; /** Sets current line block height. Call before completeLine() to correct for actually used line height (in case some chunks with larger font-size rolled back). */ void setLineHeight(Layout::FontMetrics const &line_height) override; private: double _x, _y; Layout::FontMetrics _current_line_height; bool _negative_block_progression; /// if true, indicates that completeLine() should decrement rather than increment, ie block-progression is either rl or bt }; /** \brief private to Layout. Generates scanlines inside an arbitrary shape This is the 'perfect', and hence slowest, implementation of a Layout::ScanlineMaker, which will return exact bounds for any given input shape. */ class Layout::ShapeScanlineMaker : public Layout::ScanlineMaker { public: ShapeScanlineMaker(Shape const *shape, Layout::Direction block_progression); ~ShapeScanlineMaker() override; std::vector makeScanline(Layout::FontMetrics const &line_height) override; void completeLine() override; double yCoordinate() override; void setNewYCoordinate(double new_y) override; /** never true */ bool canExtendCurrentScanline(Layout::FontMetrics const &line_height) override; /** Sets current line block height. Call before completeLine() to correct for actually used line height (in case some chunks with larger font-size rolled back). */ void setLineHeight(Layout::FontMetrics const &line_height) override; private: /** To generate scanlines for top-to-bottom text it is easiest if we simply rotate the given shape by a multiple of 90 degrees. This stores that. If no rotation was needed we can simply store the pointer we were given and set shape_needs_freeing appropriately. */ Shape *_rotated_shape; /// see #rotated_shape; bool _shape_needs_freeing; // Shape::BeginRaster() needs floats rather than doubles float _bounding_box_top, _bounding_box_bottom; float _y; float _rasterizer_y; int _current_rasterization_point; float _current_line_height; bool _negative_block_progression; /// if true, indicates that completeLine() should decrement rather than increment, ie block-progression is either rl or bt }; } // namespace Text } // namespace Inkscape #endif // LAYOUT_TNG_SCANLINE_MAKER_H /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :