/*
    This file is part of Msc-generator.
    Copyright (C) 2008-2024 Zoltan Turanyi
    Distributed under GNU Affero General Public License.

    Msc-generator is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Msc-generator is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
*/

/** @file stringparse.h The declaration of arrowhead styles.
 * @ingroup libcgencommon_files */

#if !defined(STRINGPARSE_H)
#define STRINGPARSE_H
#include "cairo.h"
#include "cgen_attribute.h"
#include "csh.h"

class Label;
class Canvas;
class Chart;
class ShapeCollection;
class Shape;

using std::string;

/** Describes the values of the 'side' attribute*/
enum class ESide
{
    INVALID = 0,///<The invalid value.
    LEFT,       ///<The left side
    RIGHT,      ///<The right side
    END         ///<At the bottom of the chart (used for comments only)
};

constexpr ESide Opposite(ESide s) noexcept { return s==ESide::LEFT ? ESide::RIGHT : s==ESide::RIGHT ? ESide::LEFT : s; }

/** Describes text identation */
enum EIdentType {
    MSC_IDENT_INVALID = 0, ///<The invalid value
    MSC_IDENT_LEFT,        ///<Left-aligned
    MSC_IDENT_CENTER,      ///<Centered
    MSC_IDENT_RIGHT        ///<Right-aligned
};

/** Describes vertial identation */
enum EVIdentType
{
    MSC_VIDENT_INVALID = 0, ///<The invalid value
    MSC_VIDENT_TOP,         ///<Left-aligned
    MSC_VIDENT_CENTER,      ///<Centered
    MSC_VIDENT_BOTTOM,      ///<Bottom-aligned
    MSC_VIDENT_JUSTIFIED    ///<Spread out (centered in case of a single element)
};

/**  Describes text characteristics */
enum EFontType {
    MSC_FONT_NORMAL=0,      ///<Regular text
    MSC_FONT_SMALL=1,       ///<Small font
    MSC_FONT_SUPERSCRIPT=2, ///<Small font superscript
    MSC_FONT_SUBSCRIPT=3    ///<Small font subscript
};

/** Describes, set, unset and invert for text attributes*/
enum ETriState {no=0, yes, invert};

/** Escape character showing the location in the input file.
 * Preceeded by backslash and followed by a FileLineCol in parenthesis.*/
#define ESCAPE_CHAR_LOCATION ((char)1)
/** Same as ESCAPE_CHAR_LOCATION, but in string*/
#define ESCAPE_STRING_LOCATION "\x01"
/** Escape character representing a numbering format token (abc, 123, etc.).
 * Preceeded by backslash and followed by a code showing the format.*/
#define ESCAPE_CHAR_NUMBERFORMAT ((char)2)
/** Same as ESCAPE_CHAR_NUMBERFORMAT, but in string*/
#define ESCAPE_STRING_NUMBERFORMAT "\x02"
/** Escape character representing a soft new line (a line break in the input file).
 * Preceeded by backslash.*/
#define ESCAPE_CHAR_SOFT_NEWLINE ((char)3)
/** Same as ESCAPE_CHAR_SOFT_NEWLINE, but in string*/
#define ESCAPE_STRING_SOFT_NEWLINE "\x03"
/** Escape character representing a word_wrap status
 * Preceeded by backslash, followed by a + or a - character.*/
#define ESCAPE_CHAR_WORD_WRAP ((char)4)
/** Same as ESCAPE_CHAR_WORD_WRAP, but in string*/
#define ESCAPE_STRING_WORD_WRAP "\x04"
/** Escape character representing a space
* Preceeded by backslash. Used only by CshIze to maintain
* the correct number of spaces. It should be a char
* that is not accessible to the user, but lately 
* xsltproc started complaining about \\x05.*/
#define ESCAPE_CHAR_SPACE ((char)'~')
/** Same as ESCAPE_CHAR_SPACE, but in string*/
#define ESCAPE_STRING_SPACE "~"
/** Escape character representing a link escape
 * whose format changing effect is now represented
 * by specific formatting escapes. This one just
 * marks the link and does not implact formatting.
 * Preceeded by backslash */
#define ESCAPE_CHAR_NON_FORMATTING_LINK ((char)6)
/** Same as ESCAPE_CHAR_NON_FORMATTING_LINK, but in string*/
#define ESCAPE_STRING_NON_FORMATTING_LINK "\x06"
/** Used in Csh::Cshize as a temporary substitute for '\'*/
#define ESCAPE_STRING_TEMP_SLASH "\x07"
/** Escape character representing a closing parenthesis.
 * Used, when a user specifies an closing parenthesis
 * as part of the "url" attribute - which is turned
 * into a "\L(<url>)" escape, where closing parenthesis
 * cannot be part of the url.
 * NOT Preceeded by backslash */
//Note: ASCII chars 8-13 are sort of meaningful, so we dont use them here.
#define ESCAPE_CHAR_CLOSING_PARA ((char)14)
/** Same as ESCAPE_CHAR_CLOSING_PARA, but in string*/
#define ESCAPE_STRING_CLOSING_PARA "\x0e"
/** Escape character followed by a hidden string.
 * Will be removed on every operation.*/
#define ESCAPE_CHAR_HIDDEN ((char)15)
/** Same as ESCAPE_CHAR_HIDDEN, but in string*/
#define ESCAPE_STRING_HIDDEN "\x0f"

bool CshHintGraphicCallbackForTextIdent(Canvas *canvas, CshHintGraphicParam p, CshHintStore &);
bool CshHintGraphicCallbackForVIdent(Canvas *canvas, CshHintGraphicParam p, CshHintStore &);

/**This class stores string formatting (bold, color, fontsize, etc.)
 * Contrary to other attribute types (LineAttr, FillAttr, ShadowAttr,
 * FullArrowAttr and NoteAttr) its default constructor creates an empty set.
 * The MscChart::defaultStringFormat value is used for elements not set, 
 * whenever applied to a context.
 * This class contains the functions to parse labels and resolve
 * escape sequences.
 * This object can do operations on a text fragment. A *fragment* is a piece
 * of a label containing no line breaks and having the same formatting,
 * thus a fragment contains no escape sequences.
 *  - It calculates height or width of a fragment using a drawing context.
 *  - It draws a fragment using a drawing context. */
class StringFormat {
public:
    /** Parses and described the content of a \ S(name,height,fillcolor) escape*/
    struct ShapeEscape {
        bool Parse(std::string_view text, CharByteIndex startloc_csh, FileLineCol startloc_parse, const ShapeCollection *shapes, const ColorSet *colors, MscError *error, Csh *csh);
        std::string Print(const ShapeCollection &shapes) const;
        CshPos loc_name;        ///<The location of the 'name' component, first_pos==-1, if none
        CshPos loc_height;      ///<The location of the 'height' component, first_pos==-1, if none
        CshPos loc_fillcolor;   ///<The location of the 'color' component, first_pos==-1, if none
        std::string shape_name; ///<The shape name - empty if we know it is invalid
        int shape;              ///<The shape number, we have extracted, -1 if none or invalid
        double height;          ///<The height, we have extracted, -1 if none
        ColorType fillcolor;    ///<The fill color, we have extracted, fully transparent if none
    };

    OptAttr<ColorType>   color;        ///<The color of the font.
    OptAttr<ColorType>   bgcolor;      ///<The color behind the text.
    OptAttr<EFontType>   fontType;     ///<The size/index of the font.
    OptAttr<double>      spacingBelow; ///<The extra spacing to add below this line.
    OptAttr<ETriState>   bold;         ///<Indicates if the font is thick.
    OptAttr<ETriState>   italics;      ///<Indicates if the font is italic.
    OptAttr<ETriState>   underline;    ///<Indicates if the font is underlined.
    OptAttr<std::string> face;         ///<The face name of the font.
    OptAttr<std::string> lang;         ///<The language to use when selecting the font.

    OptAttr<double>     textHGapPre;        ///<The margin left of the text.
    OptAttr<double>     textHGapPost;       ///<The margin right of the text.
    OptAttr<double>     textVGapAbove;      ///<The margin above the text.
    OptAttr<double>     textVGapBelow;      ///<The margin below the text.
    OptAttr<double>     textVGapLineSpacing;///<The spacing between the lines of the text.
    OptAttr<EIdentType> ident;              ///<The identation/alignment of the text.

    OptAttr<double>      normalFontSize;    ///<The height of normal-sized font.
    OptAttr<double>      smallFontSize;     ///<The height of small, superscript and subscript font.

    OptAttr<bool>        word_wrap;         ///<If true, this label shall be word wrapped
    OptAttr<std::string> link_format;       ///<What formatting to apply to Links

    mutable cairo_font_extents_t smallFontExtents;  ///<Cached extent of small fonts.
    mutable cairo_font_extents_t normalFontExtents; ///<Cached extent of normal-sized fonts.
    void ApplyFontTo(Canvas &) const;
    double spaceWidth(std::string_view, Canvas &, bool front) const;


    /** Describes the type of an escape sequence, see StringFormat::ProcessEscape(). */
    enum EEscapeType {
        FORMATTING_OK, ///<A syntactically correct formatting escape
        LINK_ESCAPE,   ///<A syntactically correct URL escape "\L"
        LINK2_ESCAPE,  ///<A translated URL escape - does not modify formatting any more, but marks the hypertext
        INVALID_ESCAPE,///<A non-recognized escape 
        NON_FORMATTING,///<A non-formatting escape, such as "\{" 
        ASTERISK,      ///<The asterisk '*' escape
        SHAPE_ESCAPE,  ///<An escape representing a shape
        REFERENCE,     ///<A reference to another element "\r(xxx)"
        NON_ESCAPE,    ///<Literal text, not an escape
        LINE_BREAK,    ///<A line break "\n"
        SOFT_LINE_BREAK,///<A soft line break (a newline in input file of a colon label)
        NUMBERING,     ///<A reference to the number of this label "\N"
        NUMBERING_FORMAT, ///<A replacement for a numbering format token (such as "abc" or "roman")
        SOLO_ESCAPE,   ///<A single backslash "\"
        QUOTATION,     ///<A "\Q" escape replaced to a parameter value - reparse what has been inserted. 
        HIDDEN,        ///<A hidden escape (ESCAPE_STRING_HIDDEN). replaceto is always set to the empty string
    };
    EEscapeType ProcessEscape(std::string_view input, size_t &length,
                              bool resolve=false, bool apply=false, string *replaceto=nullptr, 
                              const StringFormat *basic=nullptr,
                              Chart *chart=nullptr, bool references=false, 
                              FileLineCol *linenum=nullptr, bool sayIgnore=true);
    friend class Label;
    friend class ParsedLine;

  public:
      /** Describes what kind of text we process*/
      enum ETextType {
          LABEL,         ///<The text is an element (entity, arc, box, etc) label. Cannot contain numbering format token escapes ("\0x2{1aAiI}").
          TEXT_FORMAT,   ///<The text is a value assigned to "text.format" attribute. Cannot contain numbering format token escapes ("\0x2{1aAiI}").
          NUMBER_FORMAT  ///<The text is a value assigned to "numbering.*" attribute. Cannot contain numbering escapes ("\N").
      };
    /** Generate an empty formatting, no attribute is set.
     * This is contrary to all other attributes, for which the default
     * constructor generates a fully specified object with the default values.*/
    StringFormat() {Empty();}
    StringFormat(const StringFormat &) = default;
    StringFormat(StringFormat &&) = default;
    StringFormat &operator =(const StringFormat &f);
    /** Parse `text` up until the first non-escape or non-formatting escape and assigns values.
     * Removes the processed escapes from the front of 'text'.*/
    explicit StringFormat(string&text) {Empty(); Apply(text);}
    /** Parse `s` up until the first non-escape or non-formatting escape and assigns values.*/
    explicit StringFormat(std::string_view s) {Empty(); Apply(s);}

    /** Deletes all values, clearing all attributes. */
    void Empty();
    /** True if all attributes of text formatting is specified.*/
    bool IsComplete() const noexcept;
    /** True if none of the attributes of text formatting are specified.*/
    bool IsEmpty() const noexcept;
    void Default();
    void UnsetWordWrap() {word_wrap = false;} ///<Unset the wordwrap attribute
    bool IsWordWrap() const {_ASSERT(word_wrap); return word_wrap.value_or(false); } ///<True if wordwrap is on.

    /** Parse a sequence of escapes and apply the formatting to us. 
     * If you hit something like a non-formatting escapea or a bad 
     * formatting one or one that includes style/color name, stop, 
     * _remove the processed escapes_ and return. Link escapes are kept.
     * @param [in] text The text to parse.*/
    void Apply(string &text);
    /** Parse a sequence of escapes and apply the formatting to us. 
     * If you hit something like a non-formatting escapea or a bad formatting one or 
     * one that includes style/color name, stop and return. Link escapes are consumed
     * (with no effect) and do not cause processing to stop.
     * @param [in] s The text to parse.
     * @return The number of characters processed, incl link escapes.*/
    size_t Apply(std::string_view s);
    /** Parse a sequence of escapes and apply the formatting to us. 
     * If you hit something like a non-formatting escapea or a bad formatting one or 
     * one that includes style/color name, stop and return.*/
    StringFormat &operator +=(const char*s) {Apply(s); return *this;}
    /** Merge another StringFormat to us by copying all attributes that are set. */
    StringFormat &operator +=(const StringFormat& toadd);
    /** Determines what do we need to add to 'base' in order to get to us. (We must be complete.)*/
    StringFormat &operator -=(const StringFormat& base);
    /** Determines what do we need to add to 'base' in order to get to us. (We must be complete.)*/
    StringFormat operator -(const StringFormat& base) const { StringFormat a(*this); a -= base; return a; }
    /** Set the color of the format */
    void SetColor(ColorType c);
    bool AddAttribute(const Attribute &a, Chart *chart, EStyleType t);
    static void AttributeNames(Csh &csh, std::string_view prefix);
    static bool AttributeValues(std::string_view attr, Csh &csh);
    static void EscapeHints(Csh &csh, std::string_view prefix);

    /** Returns the ident value, a MSC_IDENT_CENTER if not set.*/
    EIdentType GetIdent() const
        {return ident.value_or(MSC_IDENT_CENTER); }
    /** Returns the extra spacing below this line, 0 if not set.*/
    double getSpacingBelow(void) const
        {return spacingBelow.value_or(0);}
    /** Prints all attributes set as escape sequences */
    string Print() const;

    /** @name Static text manipulation functions
     * @{ */
    static bool HasEscapes(std::string_view text);
    static bool HasLinkEscapes(std::string_view text);
    static EEscapeHintType ExtractCSH(CharByteIndex startpos, std::string_view text, Csh &csh);
    static void AddNumbering(string &label, std::string_view num, std::string_view pre_num_post);
    static void ExpandReferences(string &text, Chart *chart, FileLineCol linenum,
                                 const StringFormat *basic, bool references, 
                                 bool ignore, ETextType textType, bool remove_hidden,
                                 std::string_view asterisk_replacement = "*");
    static int FindNumberingFormatEscape(std::string_view text);
    static std::string PushPosEscapes(std::string_view text, const FileLineCol &loc) 
         { return PushPosEscapes(text, loc, loc.reason); }
    static std::string PushPosEscapes(std::string_view text, const FileLineCol &loc, EInclusionReason r);
    static bool RemovePosEscapes(string &text, FileLineCol *l=nullptr);
    static std::string RemovePosEscapesCopy(std::string_view text, FileLineCol *l = nullptr);
    static void ConvertToPlainText(string &text, std::string_view asterisk_replacement = "*");
    static std::string ConvertToPlainTextCopy(std::string_view text, std::string_view asterisk_replacement = "*");
    static bool ReplaceHiddenEscapes(string& text, bool to_its_param = false, std::string_view to = {});
    static bool ReplaceAsteriskEscapes(string &text, std::string_view to);
    static size_t FindVerbatim(std::string_view decorated, std::string_view find, std::string_view asterisk_replacement = "*");
    static size_t FindPosInDecorated(std::string_view decorated, size_t plain_pos);
    static StringWithPosList Split(std::string_view n, FileLineCol start = FileLineCol(),
                                   std::string_view separator = ",", std::string_view action = "");
    /** Splits a list by a verbatim character. The all the other escapes kept intact. */
    static StringWithPosList Split(const StringWithPos &p, 
                                   std::string_view separator = ",", std::string_view action = "")
        { return Split(p.name, p.file_pos.start, separator, action); }


    /** @}*/

    /** @name Graphics: text geometry and drawing.
     * @{ */
    /** Return the total character height of normal-sized text. */
    double getCharHeight(Canvas &) const;
    /** Return the width of a piece of text with our formatting */
    double getFragmentWidth(const string &, Canvas &) const;
    /** Return the width of a specific shape, if its height is given.
     * If height is not given it will be the height of the current text.*/
    double getFragmentWidth(const Shape &s, Canvas &canvas, double height=-1) const;
    /** Return the height of a piece of text above the base line with our formatting */
    double getFragmentHeightAboveBaseLine(std::string_view, Canvas &) const;
    double getFragmentHeightAboveBaseLine(const Shape &s, double height, Canvas &canvas) const;
    /** Return the height of a piece of text below the base line with our formatting */
    double getFragmentHeightBelowBaseLine(std::string_view, Canvas &) const;
    double getFragmentHeightBelowBaseLine(const Shape &s, double height, Canvas &canvas) const;
    double drawFragment(const string &, Canvas &, XY, Range, bool isRotated, const XY &scale) const;
    double drawFragment(const Shape &, double height, ColorType fillcolor, Canvas &, 
                        XY, Range, bool isRotated, const XY &scale) const;
    /** @}*/
};

/** A structure containing a rect and a link target*/
struct ISMapElement
{
    Block rect;         ///<The location of the target in chart coordinates.
    std::string target; ///<An URL in UTF-8
    string Print() const;
};

/** A collection of ISMap elements. */
using ISMap = std::list<ISMapElement> ;

/** An object that stores a line of text (no '\n' inside) */
class ParsedLine {
    friend class Label;
protected:
    StringFormat startFormat;        ///<The starting format valid at the beginning of the line. Values stored here are not scaled.
    string       line;               ///<The text of the line (may contain escape sequences, but not \n nor ESCAPE_CHAR_SOFT_NEWLINE)
    double       width;              ///<The pre-computed width of the line in pixels, scaled if scale!=1
    double       heightAboveBaseLine;///<The pre-computed height of the line in pixels above the baseline, scaled if scale!=1
    double       heightBelowBaseLine;///<The pre-computed height of the line in pixels below the baseline, scaled if scale!=1
    bool         hard_new_line;      ///<True if this line is terminated by a hard new line ("\n" escape)
    XY scale = {1, 1};               ///<Scaling for the text. We expose dimensions if letters were actually this much smaller.
public:
    /** Creates a parsed line from a string.
     * We also specify a canvas to be used at calculating the geometry
     * and a starting format. The latter contains the formatting at the
     * end of the line. We pre-parse the text and determine geometry.*/
    ParsedLine(std::string_view, Canvas &, const ShapeCollection &shapes, StringFormat &sf, bool h);
    void SetScale(double x, double y);
    /** Converts the line to an escape-free string*/
    operator std::string() const;
    /** Creates one ISMapElement for each link.
     * @param [in] xy Where the text shall be placed, top left corner of the text.
     * @param canvas The canvas to draw on.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [out] ismap Place generated elements into this collection.
     * @param target_at_front If the previous line ended with a link open
     *        (newline in the middle of a link), this is the target of that
     *        link. If this line (also) ends with a (the same) link open,
     *        we return the target of that.*/
    void CollectIsMapElements(XY xy, Canvas &canvas, const ShapeCollection &shapes, ISMap &ismap, string &target_at_front) const;
    /** Draws the line to a canvas.
     * @param [in] xy Where the text shall be placed, y is the height of the baseline.
     * @param canvas The canvas to draw on.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] isRotated If true, the canvas will use text paths as fallback for surfaces not supporting rotated text (WMF).*/
    void Draw(XY xy, Canvas &canvas, const ShapeCollection &shapes, bool isRotated) const;
    /** Returns the width and height of the line, taking scale into account.*/
    XY getWidthHeight(void) const
        {return XY(width, heightAboveBaseLine+heightBelowBaseLine);}
    double getHeightAboveBaseLine() const 
        { return heightAboveBaseLine; }
    const std::string &GetLine() const { return line; }
    const StringFormat &GetStartFormat() const { return startFormat; }
    const XY GetScale() const noexcept { return scale; }
    bool EndsInHardNewLine() const noexcept { return hard_new_line; }
};

/** A class holding a list of parsed lines*/
class Label : public std::vector<ParsedLine>
{
    using std::vector<ParsedLine>::at;
    using std::vector<ParsedLine>::clear;
    using std::vector<ParsedLine>::swap;
protected:
    double first_line_extra_spacing; ///<Extra spacing to add after first line (for arrow width). Must survive a reflow. This value is unscaled.
    /** Helper to determine cover & to draw*/
    void CoverOrDrawOrISMap(Canvas *canvas, const ShapeCollection &shapes, double sx, double dx, double y, double cx, bool isRotated, Contour *area, ISMap *ismap) const;
public:
    using std::vector<ParsedLine>::size;
    /** Creates a Label from a string.
     * We also specify a canvas to be used at calculating the geometry
     * and a starting format. We pre-parse the text and determine geometry.*/
    Label(std::string_view s, Canvas &c, const ShapeCollection &shapes, const StringFormat &f, const XY &scale = {1,1})
        : first_line_extra_spacing(0) {Set(s,c,shapes,f, scale);}
    /** Creates an empty label*/
    Label() : first_line_extra_spacing(0) {}

    bool IsEmpty() const { return size()==0 || (size()==1 && at(0).width==0); }

    /** Set the content.
     * Split a string to substrings based on '\n' delimiters. 
     * Then parse the strings for escape control characters and
     * determine geometry (width and height of each line and in total).
     * @param [in] s The (potentially multi-line) text (potentially with escapes) to set to.
     * @param c The canvas to use to determine geometry.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] f The starting text format.
     * @param [in] scale Sets the scaling of the label
     * @returns the number of lines in the label.*/
    size_t Set(std::string_view s, Canvas &c, const ShapeCollection &shapes, StringFormat f, const XY &scale = {1,1});
    /** Set the scaling of the label. Results in re-calculation of the dimensions.*/
    void SetScale(double s) { SetScale(s,s); }
    /** Set the scaling of the label. Results in re-calculation of the dimensions.*/
    void SetScale(double x, double y);
    /** Set the scaling of the label. Results in re-calculation of the dimensions.*/
    void SetScale(const XY &xy) { SetScale(xy.x, xy.y); }
    /** Add extra spacing below the first line
     * This space remains even after a reflow. */
    void AddSpacingAfterFirstLine(double spacing) {first_line_extra_spacing += spacing;}
    /** Converts the line to an escape-free string*/
    operator std::string() const;
    /** Applies a text style. Ignores line spacing parts of the style. */
    void ApplyStyle(const StringFormat &sf);
    /** Ensures we have at least this much left and right margin in all lines.
     * These margins are interpeted without scaling in this call and will be
     * scaled at drawing if scale!=(1,1).*/
    void EnsureMargins(double left, double right);
    /** Return the size of a line of text. 
     * @param [in] line The number of the line starting from 0. 
     *                  If -1 we return the total size of all lines.*/
    XY getTextWidthHeight(int line=-1) const;
    /** Reflows the text top fit a box of x width.
     * Honours hard line breaks, but not soft ones. Takes 'scale' into 
     * account, thus is scale=0.5, then twice as much text fits into
     * the same 'x' space. (Uses the scale of the first line.)
     * @param c The canvas to calculate on.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param x The width to reflow in.
     * @returns nonzero, if there was overflow and the size of overflow. */
    double Reflow(Canvas &c, const ShapeCollection &shapes, double x);
    /** Return the width requirement of the label.
     * If word_wrap is off, this is the width of the label.
     * If it is on, it is the first parameter */
    double getSpaceRequired(double def = 0, int line = -1) const;
    /** Returns true if word wrapping is enabled for this label */
    bool IsWordWrap() const {if (size()==0) return false; return at(0).startFormat.word_wrap.value_or(false);} 
    /** Creates one ISMapElement for each link.
     * @param [out] ismap Place generated elements into this collection.
     * @param canvas The canvas to calculate geometry on.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] sx The left margin.
     * @param [in] dx The right margin.
     * @param [in] y The top of the label.
     * @param [in] cx If also specified, we center around it for centered lines,
     *                but taking care not to go ouside the margings.
     *                If the line is wider than `dx-sx` we will go outside
     *                as little as possible (thus we center around `(sx+dx)/2`.*/
    void CollectIsMapElements(ISMap &ismap, Canvas &canvas, const ShapeCollection &shapes, double sx, double dx, double y, double cx = -CONTOUR_INFINITY) const { CoverOrDrawOrISMap(&canvas, shapes, sx, dx, y, cx, false, nullptr, &ismap); }
    /** Creates one ISMapElement for each link for slanted labels.
     * if angle is nonzero, sx, dx, cx and y are interpreted in the rotated space.
     * @param [out] ismap Place generated elements into this collection.
     * @param canvas The canvas to calculate geometry on.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] sx The left margin.
     * @param [in] dx The right margin.
     * @param [in] y The top of the label.
     * @param [in] cx If also specified, we center around it for centered lines,
     *                but taking care not to go ouside the margings.
     *                If the line is wider than `dx-sx` we will go outside
     *                as little as possible (thus we center around `(sx+dx)/2`.
     * @param [in] c The center of rotation in case of a slanted arrow label.
     * @param [in] angle The degrees of rotation in case of a slanted arrow label.*/
    void CollectIsMapElements(ISMap &ismap, Canvas &canvas, const ShapeCollection &shapes, double sx, double dx, double y, double cx, const XY &c, double angle) const;
    /** Return the cover for the label.
     * We lay out the label between `sx` and `dx` according to the ident of each line.
     * Each fragment is modelled as a rectangle.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] sx The left margin.
     * @param [in] dx The right margin.
     * @param [in] y The top of the label.
     * @param [in] cx If also specified, we center around it for centered lines, 
     *                but taking care not to go ouside the margings. 
     *                If the line is wider than `dx-sx` we will go outside
     *                as little as possible (thus we center around `(sx+dx)/2`.
     * @returns The cover of the label.*/
    Contour Cover(const ShapeCollection &shapes, double sx, double dx, double y, double cx=-CONTOUR_INFINITY) const {Contour a; CoverOrDrawOrISMap(nullptr, shapes, sx, dx, y, cx, false, &a, nullptr); return a;}
    /** Draw the label onto a canvas 
     * We lay out the label between `sx` and `dx` according to the ident of each line.
     * Each fragment is modelled as a rectangle.
     * @param canvas The canvas to draw onto.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] sx The left margin.
     * @param [in] dx The right margin.
     * @param [in] y The top of the label.
     * @param [in] cx If also specified, we center around it for centered lines, 
     *                but taking care not to go ouside the margings. 
     *                If the line is wider than `dx-sx` we will go outside
     *                as little as possible (thus we center around `(sx+dx)/2`.
     * @param [in] isRotated If true then the canvas will fall back to text_path
     *                       for surfaces that do not support rotated text (WMF)*/
    void Draw(Canvas &canvas, const ShapeCollection &shapes, double sx, double dx, double y, double cx=-CONTOUR_INFINITY, bool isRotated=false) const {CoverOrDrawOrISMap(&canvas, shapes, sx, dx, y, cx, isRotated, nullptr, nullptr);}
    /** Creates one ISMapElement for each link for 90 degree rotated labels.
     * We lay out the label between `s` and `d` according to the ident of each line.
     * Each fragment is modelled as a rectangle.
     * If the label is vertical, s, d and c are Y coordinates and t is X coordinate.
     * If the label is horizontal, vice versa.
     * @param [out] ismap Place generated elements into this collection.
     * @param canvas The canvas to calculate geometry on.
     * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
     * @param [in] s The left edge of the text (as the text reads)
     *               In the result this is the Left/Top/Bottom edge of text for side==END/LEFT/RIGHT.
     * @param [in] d The right edge of the text (as the text reads)
     *               In the result this is the Right/Bottom/Top edge of text for side==END/LEFT/RIGHT
     * @param [in] t The top edge of the text (as the text reads)
     *               In the result this is the Top/Right/Left edge of text for side==END/LEFT/RIGHT.
     * @param [in] side from which direction is the text read. For END it will be laid out horizontally.
     * @param [in] c If also specified, we center around it for centered lines,
     *                but taking care not to go ouside the margings.
     *                If the line is wider than `d-s` we will go outside
     *                as little as possible (thus we center around `(s+d)/2`.*/
    void CollectIsMapElements(ISMap &ismap, Canvas &canvas, const ShapeCollection &shapes, double s, double d, double t, ESide side, double c = -CONTOUR_INFINITY) const;
    /** Return the cover for the a label that can be 90 degree rotated or not.
    * We lay out the label between `s` and `d` according to the ident of each line.
    * Each fragment is modelled as a rectangle.
    * If the label is vertical, s, d and c are Y coordinates and t is X coordinate.
    * If the label is horizontal, vice versa.
    * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
    * @param [in] s The left edge of the text (as the text reads)
    *               In the result this is the Left/Top/Bottom edge of text for side==END/LEFT/RIGHT. 
    * @param [in] d The right edge of the text (as the text reads)
    *               In the result this is the Right/Bottom/Top edge of text for side==END/LEFT/RIGHT
    * @param [in] t The top edge of the text (as the text reads)
    *               In the result this is the Top/Right/Left edge of text for side==END/LEFT/RIGHT.  
    * @param [in] side from which direction is the text read. For END it will be laid out horizontally.
    * @param [in] c If also specified, we center around it for centered lines,
    *                but taking care not to go ouside the margings.
    *                If the line is wider than `d-s` we will go outside
    *                as little as possible (thus we center around `(s+d)/2`.
    * @returns The cover of the label.*/
    Contour Cover(const ShapeCollection &shapes, double s, double d, double t, ESide side, double c = -CONTOUR_INFINITY) const;
    /** Draw the label rotated 90 degrees onto a canvas
    * We lay out the label between `s` and `d` according to the ident of each line.
    * Each fragment is modelled as a rectangle.
    * If the label is vertical, s, d and c are Y coordinates and t is X coordinate.
    * If the label is horizontal, vice versa.
    * @param canvas The canvas to draw onto.
    * @param shapes [in] The shape collection to take the shapes for the \ S escape from.
    * @param [in] s The left edge of the text (as the text reads)
    *               In the result this is the Left/Top/Bottom edge of text for side==END/LEFT/RIGHT.
    * @param [in] d The right edge of the text (as the text reads)
    *               In the result this is the Right/Bottom/Top edge of text for side==END/LEFT/RIGHT
    * @param [in] t The top edge of the text (as the text reads)
    *               In the result this is the Top/Right/Left edge of text for side==END/LEFT/RIGHT.
    * @param [in] side from which direction is the text read. For END it will be laid out horizontally.
    * @param [in] c If also specified, we center around it for centered lines,
    *                but taking care not to go ouside the margings.
    *                If the line is wider than `d-s` we will go outside
    *                as little as possible (thus we center around `(s+d)/2`.*/
    void Draw(Canvas &canvas, const ShapeCollection &shapes, double s, double d, double t, ESide side, double c = -CONTOUR_INFINITY) const;
};

#endif //STRINGPARSE_H
