/* * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2021 Ola Rinta-Koski * Copyright (C) 2021-2023 Kicad Developers, see AUTHORS.txt for contributors. * * Font abstract base class * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef FONT_H_ #define FONT_H_ #include #include #include #include #include #include namespace KIGFX { class GAL; } enum TEXT_STYLE { BOLD = 1, ITALIC = 1 << 1, SUBSCRIPT = 1 << 2, SUPERSCRIPT = 1 << 3, OVERBAR = 1 << 4, UNDERLINE = 1 << 5 }; /** * Tilt factor for italic style (this is the scaling factor on dY relative coordinates to give * a tilted shape). * This is applied directly to stroke fonts, and is used as an estimate for outline fonts (which * have the actual tilt built in to their polygonal glyph outlines). */ static constexpr double ITALIC_TILT = 1.0 / 8; using TEXT_STYLE_FLAGS = unsigned int; inline bool IsBold( TEXT_STYLE_FLAGS aFlags ) { return aFlags & TEXT_STYLE::BOLD; } inline bool IsItalic( TEXT_STYLE_FLAGS aFlags ) { return aFlags & TEXT_STYLE::ITALIC; } inline bool IsSuperscript( TEXT_STYLE_FLAGS aFlags ) { return aFlags & TEXT_STYLE::SUPERSCRIPT; } inline bool IsSubscript( TEXT_STYLE_FLAGS aFlags ) { return aFlags & TEXT_STYLE::SUBSCRIPT; } namespace KIFONT { class METRICS { public: /** * Compute the vertical position of an overbar. This is the distance between the text * baseline and the overbar. */ double GetOverbarVerticalPosition( double aGlyphHeight ) const { return aGlyphHeight * m_OverbarHeight; } /** * Compute the vertical position of an underline. This is the distance between the text * baseline and the underline. */ double GetUnderlineVerticalPosition( double aGlyphHeight ) const { return aGlyphHeight * m_UnderlineOffset; } double GetInterline( double aFontHeight ) const { return aFontHeight * m_InterlinePitch; } static const METRICS& Default(); public: double m_InterlinePitch = 1.68; double m_OverbarHeight = 1.23; double m_UnderlineOffset = -0.16; }; /** * FONT is an abstract base class for both outline and stroke fonts */ class FONT { public: explicit FONT(); virtual ~FONT() { } virtual bool IsStroke() const { return false; } virtual bool IsOutline() const { return false; } virtual bool IsBold() const { return false; } virtual bool IsItalic() const { return false; } static FONT* GetFont( const wxString& aFontName = wxEmptyString, bool aBold = false, bool aItalic = false ); static bool IsStroke( const wxString& aFontName ); const wxString& GetName() const { return m_fontName; }; inline const char* NameAsToken() const { return GetName().utf8_str().data(); } /** * Draw a string. * * @param aGal is the graphics context. * @param aText is the text to be drawn. * @param aPosition is the text position in world coordinates. * @param aCursor is the current text position (for multiple text blocks within a single text * object, such as a run of superscript characters) * @param aAttrs are the styling attributes of the text, including its rotation */ void Draw( KIGFX::GAL* aGal, const wxString& aText, const VECTOR2I& aPosition, const VECTOR2I& aCursor, const TEXT_ATTRIBUTES& aAttributes, const METRICS& aFontMetrics ) const; void Draw( KIGFX::GAL* aGal, const wxString& aText, const VECTOR2I& aPosition, const TEXT_ATTRIBUTES& aAttributes, const METRICS& aFontMetrics ) const { Draw( aGal, aText, aPosition, VECTOR2I( 0, 0 ), aAttributes, aFontMetrics ); } /** * Compute the boundary limits of aText (the bounding box of all shapes). * * @return a VECTOR2I giving the width and height of text. */ VECTOR2I StringBoundaryLimits( const wxString& aText, const VECTOR2I& aSize, int aThickness, bool aBold, bool aItalic, const METRICS& aFontMetrics ) const; /** * Insert \n characters into text to ensure that no lines are wider than \a aColumnWidth. */ void LinebreakText( wxString& aText, int aColumnWidth, const VECTOR2I& aGlyphSize, int aThickness, bool aBold, bool aItalic ) const; /** * Compute the distance (interline) between 2 lines of text (for multiline texts). This is * the distance between baselines, not the space between line bounding boxes. */ virtual double GetInterline( double aGlyphHeight, const METRICS& aFontMetrics ) const = 0; /** * Convert text string to an array of GLYPHs. * * @param aBBox pointer to a BOX2I that will set to the bounding box, or nullptr * @param aGlyphs storage for the returned GLYPHs * @param aText text to convert to polygon/polyline * @param aSize is the cap-height and em-width of the text * @param aPosition position of text (cursor position before this text) * @param aAngle text angle * @param aMirror is true if text should be drawn mirrored, false otherwise. * @param aOrigin is the point around which the text should be rotated, mirrored, etc. * @param aTextStyle text style flags * @return text cursor position after this text */ virtual VECTOR2I GetTextAsGlyphs( BOX2I* aBBox, std::vector>* aGlyphs, const wxString& aText, const VECTOR2I& aSize, const VECTOR2I& aPosition, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle ) const = 0; protected: /** * Returns number of lines for a given text. * * @param aText is the text to be checked. * @return unsigned - The number of lines in aText. */ inline unsigned linesCount( const wxString& aText ) const { if( aText.empty() ) return 0; // std::count does not work well with empty strings else // aText.end() - 1 is to skip a newline character that is potentially at the end return std::count( aText.begin(), aText.end() - 1, '\n' ) + 1; } /** * Draws a single line of text. Multiline texts should be split before using the * function. * * @param aGal is a pointer to the graphics abstraction layer, or nullptr (nothing is drawn) * @param aBBox is an optional pointer to be filled with the bounding box. * @param aText is the text to be drawn. * @param aPosition is text position. * @param aSize is the cap-height and em-width of the text * @param aAngle is text angle. * @param aMirror is true if text should be drawn mirrored, false otherwise. * @param aOrigin is the point around which the text should be rotated, mirrored, etc. * @return new cursor position in non-rotated, non-mirrored coordinates */ void drawSingleLineText( KIGFX::GAL* aGal, BOX2I* aBoundingBox, const wxString& aText, const VECTOR2I& aPosition, const VECTOR2I& aSize, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin, bool aItalic, bool aUnderline, const METRICS& aFontMetrics ) const; /** * Computes the bounding box for a single line of text. * Multiline texts should be split before using the function. * * @param aBBox is an optional pointer to be filled with the bounding box. * @param aText is the text to be drawn. * @param aPosition is text position. * @param aSize is the cap-height and em-width of the text. * @return new cursor position */ VECTOR2I boundingBoxSingleLine( BOX2I* aBBox, const wxString& aText, const VECTOR2I& aPosition, const VECTOR2I& aSize, bool aItalic, const METRICS& aFontMetrics ) const; void getLinePositions( const wxString& aText, const VECTOR2I& aPosition, wxArrayString& aTextLines, std::vector& aPositions, std::vector& aExtents, const TEXT_ATTRIBUTES& aAttrs, const METRICS& aFontMetrics ) const; VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector>* aGlyphs, const wxString& aText, const VECTOR2I& aPosition, const VECTOR2I& aSize, const EDA_ANGLE& aAngle, bool aMirror, const VECTOR2I& aOrigin, TEXT_STYLE_FLAGS aTextStyle, const METRICS& aFontMetrics ) const; void wordbreakMarkup( std::vector>* aWords, const wxString& aText, const VECTOR2I& aSize, TEXT_STYLE_FLAGS aTextStyle ) const; private: static FONT* getDefaultFont(); protected: wxString m_fontName; ///< Font name wxString m_fontFileName; ///< Font file name private: static FONT* s_defaultFont; static std::map< std::tuple, FONT* > s_fontMap; }; } //namespace KIFONT inline std::ostream& operator<<(std::ostream& os, const KIFONT::FONT& aFont) { os << "[Font \"" << aFont.GetName() << "\"" << ( aFont.IsStroke() ? " stroke" : "" ) << ( aFont.IsOutline() ? " outline" : "" ) << ( aFont.IsBold() ? " bold" : "" ) << ( aFont.IsItalic() ? " italic" : "" ) << "]"; return os; } inline std::ostream& operator<<(std::ostream& os, const KIFONT::FONT* aFont) { os << *aFont; return os; } #endif // FONT_H_