Seth Hillbrand 77797103f7 Add ability to embed files in various elements
Schematics, symbols, boards and footprints all get the ability to store
files inside their file structures.  File lookups now have a
kicad-embed:// URI to allow various parts of KiCad to refer to files
stored in this manner.

kicad-embed://datasheet.pdf references the file named "datasheet.pdf"
embedded in the document.  Embeds are allowed in schematics, boards,
symbols and footprints.  Currently supported embeddings are Datasheets,
3D Models and drawingsheets

Fixes https://gitlab.com/kicad/code/kicad/-/issues/6918

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2376

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17827
2024-07-15 16:06:55 -07:00

304 lines
11 KiB
C++

/*
* 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 <gal/gal.h>
#include <iostream>
#include <map>
#include <algorithm>
#include <wx/string.h>
#include <font/glyph.h>
#include <font/text_attributes.h>
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 GAL_API 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 GAL_API 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, const std::vector<wxString>* aEmbeddedFiles = nullptr );
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<std::unique_ptr<GLYPH>>* 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<VECTOR2I>& aPositions,
std::vector<VECTOR2I>& aExtents, const TEXT_ATTRIBUTES& aAttrs,
const METRICS& aFontMetrics ) const;
VECTOR2I drawMarkup( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>* 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<std::pair<wxString, int>>* 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<wxString, bool, bool>, 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_