KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_outline_font.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <memory>
21#include <vector>
22
23#include <wx/filename.h>
24
26
27#include <font/font.h>
28#include <font/glyph.h>
29#include <font/outline_font.h>
30
31BOOST_AUTO_TEST_SUITE( OutlineFont )
32
33
34static wxString getTestFontPath( const wxString& aFileName )
35{
36 wxFileName fontFile( KI_TEST::GetTestDataRootDir() );
37 fontFile.RemoveLastDir();
38 fontFile.AppendDir( wxT( "resources" ) );
39 fontFile.AppendDir( wxT( "fonts" ) );
40 fontFile.SetFullName( aFileName );
41
42 return fontFile.GetFullPath();
43}
44
45
46static KIFONT::OUTLINE_FONT* loadTestOutlineFont( const wxString& aFontName,
47 const wxString& aFileName )
48{
49 wxString fontPath = getTestFontPath( aFileName );
50 BOOST_REQUIRE( wxFileExists( fontPath ) );
51
52 std::vector<wxString> embeddedFonts{ fontPath };
53 KIFONT::FONT* font = KIFONT::FONT::GetFont( aFontName, false, false, &embeddedFonts );
54
55 BOOST_REQUIRE( font );
56 BOOST_REQUIRE( font->IsOutline() );
57
58 return static_cast<KIFONT::OUTLINE_FONT*>( font );
59}
60
61
62// Legacy "symbol" fonts place their glyphs in the U+F000..U+F0FF private-use range and carry no
63// Unicode charmap that maps Basic Latin, so forcing the Unicode charmap maps every character to
64// .notdef and text renders as tofu boxes. Drive the full load-and-shape path and confirm such a
65// font resolves the symbol charmap and produces distinct ASCII glyphs.
66BOOST_AUTO_TEST_CASE( SymbolFontRendersAsciiGlyphs )
67{
68 KIFONT::OUTLINE_FONT* outline = loadTestOutlineFont( wxT( "KiCadSymbolTest" ),
69 wxT( "symbol_pua_test.ttf" ) );
70 FT_Face face = outline->GetFace();
71
72 // A Unicode charmap would mean fontconfig substituted a different font, or the symbol charmap
73 // was not selected; either way the glyph lookup that resolves the symbol layout would not run.
74 BOOST_REQUIRE( face );
75 BOOST_REQUIRE( face->charmap );
76 BOOST_CHECK_EQUAL( face->charmap->encoding, FT_ENCODING_MS_SYMBOL );
77
78 std::vector<std::unique_ptr<KIFONT::GLYPH>> glyphs;
79 outline->GetTextAsGlyphs( nullptr, &glyphs, wxT( "AB" ), VECTOR2I( 10000, 10000 ),
80 VECTOR2I( 0, 0 ), ANGLE_0, false, VECTOR2I( 0, 0 ), 0 );
81
82 BOOST_REQUIRE_EQUAL( glyphs.size(), 2 );
83
84 // The test font draws 'A' wider than 'B'; tofu would map both to the same .notdef glyph.
85 double widthA = glyphs[0]->BoundingBox().GetWidth();
86 double widthB = glyphs[1]->BoundingBox().GetWidth();
87
88 BOOST_CHECK_GT( widthA, 0.0 );
89 BOOST_CHECK_GT( widthB, 0.0 );
90 BOOST_CHECK_GT( widthA, widthB );
91}
92
93
94// A normal Unicode font must keep its Unicode charmap and still map Basic Latin directly.
95BOOST_AUTO_TEST_CASE( UnicodeFontKeepsUnicodeCharmap )
96{
97 KIFONT::OUTLINE_FONT* outline = loadTestOutlineFont( wxT( "Noto Sans" ),
98 wxT( "NotoSans-Regular.ttf" ) );
99 FT_Face face = outline->GetFace();
100
101 BOOST_REQUIRE( face );
102 BOOST_REQUIRE( face->charmap );
103 BOOST_CHECK_EQUAL( face->charmap->encoding, FT_ENCODING_UNICODE );
104 BOOST_CHECK( FT_Get_Char_Index( face, 'A' ) != 0 );
105}
106
107
108// FreeType clears the shared glyph slot before the font driver validates the glyph index, so a
109// failed FT_Load_Glyph leaves an empty outline that decomposes to nothing. The failure must be
110// reported so the caller can draw a placeholder box instead of silently rendering an invisible
111// glyph. Prove the guard by loading a valid glyph, then requesting an out-of-range index.
112BOOST_AUTO_TEST_CASE( FailedGlyphLoadReported )
113{
114 KIFONT::OUTLINE_FONT* outline = loadTestOutlineFont( wxT( "Noto Sans" ),
115 wxT( "NotoSans-Regular.ttf" ) );
116 FT_Face face = outline->GetFace();
117
118 BOOST_REQUIRE( face );
119
120 unsigned int validIndex = FT_Get_Char_Index( face, 'A' );
121 BOOST_REQUIRE( validIndex != 0 );
122
123 std::vector<KIFONT::CONTOUR> contours;
124 BOOST_CHECK( outline->LoadGlyphContours( validIndex, contours ) );
125 BOOST_CHECK( !contours.empty() );
126
127 // face->num_glyphs is one past the last valid index, so this load must fail. Without the
128 // guard the failure was swallowed and the glyph silently rendered as nothing.
129 std::vector<KIFONT::CONTOUR> staleContours;
130 BOOST_CHECK( !outline->LoadGlyphContours( (unsigned int) face->num_glyphs, staleContours ) );
131 BOOST_CHECK( staleContours.empty() );
132}
133
134
135// The line spacing multiplier is FT_Face::height / FT_Face::units_per_EM. Both fields are integers,
136// so an integer division truncated the fractional font-height ratio (and produced 0 for fonts with
137// height < units_per_EM), leaving multiline text with lines too close together or overlapping. Drive
138// GetInterline and confirm the fractional ratio survives. NotoSans has height 1362 over 1000 units,
139// so the true 1.362 ratio truncated to 1.
140BOOST_AUTO_TEST_CASE( InterlinePreservesFractionalFontHeight )
141{
142 KIFONT::OUTLINE_FONT* outline = loadTestOutlineFont( wxT( "Noto Sans" ),
143 wxT( "NotoSans-Regular.ttf" ) );
144 FT_Face face = outline->GetFace();
145
146 BOOST_REQUIRE( face );
147 BOOST_REQUIRE_GT( face->units_per_EM, 0 );
148 BOOST_REQUIRE_GT( face->height, face->units_per_EM );
149
150 const double glyphHeight = 10000.0;
151 const KIFONT::METRICS& metrics = KIFONT::METRICS::Default();
152
153 double ratio = static_cast<double>( face->height ) / static_cast<double>( face->units_per_EM );
154 double expected = metrics.GetInterline( glyphHeight * ratio );
155
156 BOOST_CHECK_CLOSE( outline->GetInterline( glyphHeight, metrics ), expected, 1e-6 );
157
158 // The integer-division bug truncated the ratio to 1.0, so the correct spacing must exceed it.
159 BOOST_CHECK_GT( outline->GetInterline( glyphHeight, metrics ),
160 metrics.GetInterline( glyphHeight ) );
161}
162
163
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
virtual bool IsOutline() const
Definition font.h:102
double GetInterline(double aFontHeight) const
static const METRICS & Default()
Definition font.cpp:48
Class OUTLINE_FONT implements outline font drawing.
double GetInterline(double aGlyphHeight, const METRICS &aFontMetrics) const override
Compute the distance (interline) between 2 lines of text (for multiline texts).
bool LoadGlyphContours(unsigned int aGlyphIndex, std::vector< CONTOUR > &aContours) const
Load a single glyph into the shared FreeType slot and decompose its outline into line-chain contours.
VECTOR2I GetTextAsGlyphs(BOX2I *aBoundingBox, 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 override
Convert text string to an array of GLYPHs.
const FT_Face & GetFace() const
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
std::string GetTestDataRootDir()
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
static wxString getTestFontPath(const wxString &aFileName)
static KIFONT::OUTLINE_FONT * loadTestOutlineFont(const wxString &aFontName, const wxString &aFileName)
BOOST_AUTO_TEST_CASE(SymbolFontRendersAsciiGlyphs)
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683