KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pdf_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 3
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, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
24#include <trace_helpers.h>
25#include <wx/log.h>
26#include <cstdlib>
27
28#include <algorithm>
29#include <cctype>
30#include <limits>
31#include <cmath>
32
33#include <fmt/format.h>
34#include <harfbuzz/hb-ft.h>
35#include <harfbuzz/hb.h>
36#include <wx/ffile.h>
37
38#include <core/utf8.h>
39
40#include <ft2build.h>
41#include FT_FREETYPE_H
42
43#include <font/font.h>
44
45namespace
46{
47std::string formatUnicodeHex( uint32_t aCodepoint )
48{
49 if( aCodepoint <= 0xFFFF )
50 return fmt::format( "{:04X}", aCodepoint );
51
52 if( aCodepoint <= 0x10FFFF )
53 {
54 uint32_t value = aCodepoint - 0x10000;
55 uint16_t high = 0xD800 + ( value >> 10 );
56 uint16_t low = 0xDC00 + ( value & 0x3FF );
57 return fmt::format( "{:04X}{:04X}", high, low );
58 }
59
60 return std::string( "003F" );
61}
62
63std::u32string utf8ToU32( const std::string& aUtf8 )
64{
65 std::u32string result;
66 UTF8 utf8( aUtf8.c_str() );
67
68 for( auto it = utf8.ubegin(); it < utf8.uend(); ++it )
69 result.push_back( static_cast<uint32_t>( *it ) );
70
71 return result;
72}
73
74std::string generateSubsetPrefix( unsigned aSubsetIndex )
75{
76 static constexpr char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
77 std::string prefix( 6, 'A' );
78
79 for( int ii = 5; ii >= 0; --ii )
80 {
81 prefix[ii] = letters[ aSubsetIndex % 26 ];
82 aSubsetIndex /= 26;
83 }
84
85 return prefix;
86}
87
88double unitsToPdf( double aValue, double aUnitsPerEm )
89{
90 if( aUnitsPerEm == 0.0 )
91 return 0.0;
92
93 return aValue * 1000.0 / aUnitsPerEm;
94}
95}
96
98{
99 if( m_glyphIndex != aOther.m_glyphIndex )
100 return m_glyphIndex < aOther.m_glyphIndex;
101
102 return m_unicode < aOther.m_unicode;
103}
104
106 m_font( aFont ),
107 m_resourceName( makeResourceName( aSubsetIndex ) ),
108 m_baseFontName( makeSubsetName( aFont, aSubsetIndex ) ),
109 m_unitsPerEm( 1000.0 ),
110 m_ascent( 0.0 ),
111 m_descent( 0.0 ),
112 m_capHeight( 0.0 ),
113 m_italicAngle( 0.0 ),
114 m_stemV( 80.0 ),
115 m_bboxMinX( 0.0 ),
116 m_bboxMinY( 0.0 ),
117 m_bboxMaxX( 0.0 ),
118 m_bboxMaxY( 0.0 ),
119 m_flags( 32 ),
120 m_fontDataLoaded( false ),
121 m_nextCID( 1 ),
122 m_fontFileHandle( -1 ),
124 m_cidFontHandle( -1 ),
125 m_cidMapHandle( -1 ),
126 m_toUnicodeHandle( -1 ),
127 m_fontHandle( -1 )
128{
129 FT_Face face = aFont ? aFont->GetFace() : nullptr;
130
131 if( face )
132 {
133 if( face->units_per_EM > 0 )
134 m_unitsPerEm = static_cast<double>( face->units_per_EM );
135
136 m_ascent = unitsToPdf( static_cast<double>( face->ascender ), m_unitsPerEm );
137 m_descent = unitsToPdf( static_cast<double>( face->descender ), m_unitsPerEm );
138 m_capHeight = unitsToPdf( static_cast<double>( face->bbox.yMax ), m_unitsPerEm );
139 m_bboxMinX = unitsToPdf( static_cast<double>( face->bbox.xMin ), m_unitsPerEm );
140 m_bboxMinY = unitsToPdf( static_cast<double>( face->bbox.yMin ), m_unitsPerEm );
141 m_bboxMaxX = unitsToPdf( static_cast<double>( face->bbox.xMax ), m_unitsPerEm );
142 m_bboxMaxY = unitsToPdf( static_cast<double>( face->bbox.yMax ), m_unitsPerEm );
143
144 if( face->style_flags & FT_STYLE_FLAG_ITALIC )
145 m_italicAngle = -12.0;
146 else if( aFont->IsItalic() )
147 m_italicAngle = -12.0;
148
149 if( aFont->IsBold() )
150 m_stemV = 140.0;
151
152 if( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH )
153 m_flags |= 1;
154
155 if( aFont->IsItalic() )
156 m_flags |= 64;
157 }
158
159 m_widths.resize( 1, 0.0 );
160 m_cidToGid.resize( 1, 0 );
161 m_cidToUnicode.resize( 1 );
162}
163
165{
166 return m_nextCID > 1;
167}
168
170{
171 if( m_widths.empty() )
172 {
173 m_widths.resize( 1, 0.0 );
174 m_cidToGid.resize( 1, 0 );
175 m_cidToUnicode.resize( 1 );
176 }
177}
178
179uint16_t PDF_OUTLINE_FONT_SUBSET::EnsureGlyph( uint32_t aGlyphIndex, const std::u32string& aUnicode )
180{
181 if( aGlyphIndex == 0 )
182 return 0;
183
184 GLYPH_KEY key{ aGlyphIndex, aUnicode };
185
186 auto it = m_glyphMap.find( key );
187
188 if( it != m_glyphMap.end() )
189 return it->second;
190
191 ensureNotdef();
192
193 FT_Face face = m_font ? m_font->GetFace() : nullptr;
194
195 if( !face )
196 return 0;
197
198 if( FT_Load_Glyph( face, aGlyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) )
199 return 0;
200
201 // For FT_LOAD_NO_SCALE the advance.x should be in font units (26.6) unless metrics differ.
202 // We divide by 64.0 to get raw font units; convert to our internal PDF user units via unitsToPdf.
203 double rawAdvance266 = static_cast<double>( face->glyph->advance.x );
204 double rawAdvanceFontUnits = rawAdvance266 / 64.0;
205 double advance = unitsToPdf( rawAdvanceFontUnits, m_unitsPerEm );
206
207 uint16_t cid = m_nextCID++;
208
209 if( m_widths.size() <= cid )
210 m_widths.resize( cid + 1, 0.0 );
211
212 if( m_cidToGid.size() <= cid )
213 m_cidToGid.resize( cid + 1, 0 );
214
215 if( m_cidToUnicode.size() <= cid )
216 m_cidToUnicode.resize( cid + 1 );
217
218 m_widths[cid] = advance;
219
220 if( std::getenv( "KICAD_DEBUG_FONT_ADV" ) )
221 {
222 wxLogTrace( tracePdfPlotter,
223 "EnsureGlyph font='%s' gid=%u cid=%u rawAdvance26.6=%f rawAdvanceUnits=%f storedAdvancePdfUnits=%f unitsPerEm=%f", \
224 m_font ? m_font->GetName().ToUTF8().data() : "(null)", (unsigned) aGlyphIndex, (unsigned) cid, \
225 rawAdvance266, rawAdvanceFontUnits, advance, m_unitsPerEm );
226 }
227 m_cidToGid[cid] = static_cast<uint16_t>( aGlyphIndex );
228 m_cidToUnicode[cid] = aUnicode;
229
230 m_glyphMap.emplace( key, cid );
231
232 return cid;
233}
234
235const std::vector<uint8_t>& PDF_OUTLINE_FONT_SUBSET::FontFileData()
236{
237 if( !m_fontDataLoaded )
238 {
239 m_fontDataLoaded = true;
240
241 if( !m_font )
242 return m_fontData;
243
244 wxString fontFile = m_font->GetFileName();
245
246 if( fontFile.IsEmpty() )
247 return m_fontData;
248
249 wxFFile file( fontFile, wxT( "rb" ) );
250
251 if( !file.IsOpened() )
252 return m_fontData;
253
254 wxFileOffset length = file.Length();
255
256 if( length > 0 )
257 {
258 m_fontData.resize( static_cast<size_t>( length ) );
259 file.Read( m_fontData.data(), length );
260 }
261 }
262
263 return m_fontData;
264}
265
267{
268 // Return empty if there are no glyphs beyond .notdef
269 if( m_nextCID <= 1 )
270 return std::string( "[]" );
271
272 // PDF expects widths in 1000/em units for CIDFontType2 /W array entries.
273 // m_widths currently stores advance in PDF user units produced by unitsToPdf().
274 // This is a bit of a fudge factor to reconstruct the output width
275 double designScale = 0.0072 * 2.25;
276 int logCount = 0;
277
278 fmt::memory_buffer buffer;
279 fmt::format_to( std::back_inserter( buffer ), "[ 1 [" );
280
281 for( uint16_t cid = 1; cid < m_nextCID; ++cid )
282 {
283 double adv = m_widths[cid];
284 long width1000 = 0;
285
286 if( designScale != 0.0 )
287 width1000 = lrint( adv / designScale );
288
289 fmt::format_to( std::back_inserter( buffer ), " {}", width1000 );
290
291 if( std::getenv( "KICAD_DEBUG_FONT_ADV" ) && logCount < 16 )
292 {
293 wxLogTrace( tracePdfPlotter, "BuildWidthsArray FIXED cid=%u advPdfUnits=%f width1000=%ld", (unsigned) cid, adv, width1000 );
294 ++logCount;
295 }
296 }
297
298 fmt::format_to( std::back_inserter( buffer ), " ] ]" );
299 return std::string( buffer.data(), buffer.size() );
300}
301
303{
304 if( m_nextCID <= 1 )
305 return std::string();
306
307 fmt::memory_buffer buffer;
308
309 std::string cmapName = m_baseFontName + "_ToUnicode";
310
311 fmt::format_to( std::back_inserter( buffer ), "/CIDInit /ProcSet findresource begin\n" );
312 fmt::format_to( std::back_inserter( buffer ), "12 dict begin\n" );
313 fmt::format_to( std::back_inserter( buffer ), "begincmap\n" );
314 fmt::format_to( std::back_inserter( buffer ), "/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> def\n" );
315 fmt::format_to( std::back_inserter( buffer ), "/CMapName /{} def\n", cmapName );
316 fmt::format_to( std::back_inserter( buffer ), "/CMapType 2 def\n" );
317 fmt::format_to( std::back_inserter( buffer ), "1 begincodespacerange\n" );
318 fmt::format_to( std::back_inserter( buffer ), "<0000> <FFFF>\n" );
319 fmt::format_to( std::back_inserter( buffer ), "endcodespacerange\n" );
320
321 size_t mappingCount = 0;
322
323 for( uint16_t cid = 1; cid < m_nextCID; ++cid )
324 {
325 if( !m_cidToUnicode[cid].empty() )
326 ++mappingCount;
327 }
328
329 fmt::format_to( std::back_inserter( buffer ), "{} beginbfchar\n", mappingCount );
330
331 for( uint16_t cid = 1; cid < m_nextCID; ++cid )
332 {
333 const std::u32string& unicode = m_cidToUnicode[cid];
334
335 if( unicode.empty() )
336 continue;
337
338 fmt::format_to( std::back_inserter( buffer ), "<{:04X}> <", cid );
339
340 for( uint32_t codepoint : unicode )
341 fmt::format_to( std::back_inserter( buffer ), "{}", formatUnicodeHex( codepoint ) );
342
343 fmt::format_to( std::back_inserter( buffer ), ">\n" );
344 }
345
346 fmt::format_to( std::back_inserter( buffer ), "endbfchar\n" );
347 fmt::format_to( std::back_inserter( buffer ), "endcmap\n" );
348 fmt::format_to( std::back_inserter( buffer ), "CMapName currentdict /CMap defineresource pop\n" );
349 fmt::format_to( std::back_inserter( buffer ), "end\n" );
350 fmt::format_to( std::back_inserter( buffer ), "end\n" );
351
352 return std::string( buffer.data(), buffer.size() );
353}
354
356{
357 std::string data;
358
359 if( m_nextCID == 0 )
360 return data;
361
362 data.resize( static_cast<size_t>( m_nextCID ) * 2, 0 );
363
364 for( uint16_t cid = 0; cid < m_nextCID; ++cid )
365 {
366 uint16_t gid = cid < m_cidToGid.size() ? m_cidToGid[cid] : 0;
367 data[ cid * 2 ] = static_cast<char>( ( gid >> 8 ) & 0xFF );
368 data[ cid * 2 + 1 ] = static_cast<char>( gid & 0xFF );
369 }
370
371 return data;
372}
373
374std::string PDF_OUTLINE_FONT_SUBSET::makeResourceName( unsigned aSubsetIndex )
375{
376 return fmt::format( "/KiCadOutline{}", aSubsetIndex );
377}
378
379std::string PDF_OUTLINE_FONT_SUBSET::sanitizeFontName( const wxString& aName )
380{
381 std::string utf8 = UTF8( aName ).substr();
382 std::string sanitized;
383 sanitized.reserve( utf8.size() );
384
385 for( unsigned char ch : utf8 )
386 {
387 if( std::isalnum( ch ) )
388 sanitized.push_back( static_cast<char>( ch ) );
389 else
390 sanitized.push_back( '-' );
391 }
392
393 if( sanitized.empty() )
394 sanitized = "Font";
395
396 return sanitized;
397}
398
399std::string PDF_OUTLINE_FONT_SUBSET::makeSubsetName( KIFONT::OUTLINE_FONT* aFont, unsigned aSubsetIndex )
400{
401 std::string prefix = generateSubsetPrefix( aSubsetIndex );
402 std::string name = aFont ? sanitizeFontName( aFont->GetName() ) : std::string( "Font" );
403 return fmt::format( "{}+{}", prefix, name );
404}
405
410
412{
413 m_subsets.clear();
415}
416
418{
419 if( !aFont )
420 return nullptr;
421 SUBSET_KEY key{ aFont, aItalic, aBold };
422 auto it = m_subsets.find( key );
423 if( it != m_subsets.end() )
424 return it->second.get();
425 auto subset = std::make_unique<PDF_OUTLINE_FONT_SUBSET>( aFont, m_nextSubsetIndex++ );
426 PDF_OUTLINE_FONT_SUBSET* subsetPtr = subset.get();
427
428 // Synthetic style application: if requested styles not actually present in font face flags.
429 // Distinguish real face style from fake style flags so that a fake italic does not block
430 // PDF shear application. We consider the face to have a real italic only if FT_STYLE_FLAG_ITALIC
431 // is set. (m_fakeItal only indicates substitution missing an italic variant.)
432 bool faceHasRealItalic = false;
433 bool faceHasRealBold = false;
434 if( const FT_Face& face = aFont->GetFace() )
435 {
436 faceHasRealItalic = ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0;
437 faceHasRealBold = ( face->style_flags & FT_STYLE_FLAG_BOLD ) != 0;
438 }
439
440 bool needSyntheticItalic = aItalic && !faceHasRealItalic; // ignore fake italic
441 bool needSyntheticBold = aBold && !faceHasRealBold; // ignore fake bold
442
443 if( std::getenv( "KICAD_DEBUG_SYN_STYLE" ) )
444 {
445 const FT_Face& face = aFont->GetFace();
446 int styleFlags = face ? face->style_flags : 0;
447 const char* fname = aFont->GetName().ToUTF8().data();
448 const char* styleName = ( face && face->style_name ) ? face->style_name : "(null)";
449 bool fakeItal = static_cast<KIFONT::OUTLINE_FONT*>( aFont )->IsFakeItalic();
450 bool fakeBold = static_cast<KIFONT::OUTLINE_FONT*>( aFont )->IsFakeBold();
451 wxLogTrace( tracePdfPlotter,
452 "ensureSubset font='%s' styleName='%s' reqItalic=%d reqBold=%d FT_style_flags=%d "
453 "faceHasRealItalic=%d faceHasRealBold=%d fakeItal=%d fakeBold=%d syntheticItalic=%d "
454 "syntheticBold=%d subsetKey[i=%d b=%d] subsetIdx=%u",
455 fname ? fname : "(null)", styleName, (int) aItalic, (int) aBold, styleFlags,
456 (int) faceHasRealItalic, (int) faceHasRealBold, (int) fakeItal, (int) fakeBold,
457 (int) needSyntheticItalic, (int) needSyntheticBold, (int) aItalic, (int) aBold,
458 subsetPtr->Font()->GetFace() ? subsetPtr->Font()->GetFace()->face_index : 0 );
459 }
460
461 if( needSyntheticItalic || needSyntheticBold )
462 {
463 // Approx italic angle based on shear ITALIC_TILT (radians) => degrees
464 double angleDeg = -std::atan( ITALIC_TILT ) * 180.0 / M_PI; // negative for conventional PDF italicAngle
465 subsetPtr->ForceSyntheticStyle( needSyntheticBold, needSyntheticItalic, angleDeg );
466 if( std::getenv( "KICAD_DEBUG_SYN_STYLE" ) )
467 {
468 wxLogTrace( tracePdfPlotter, "ForceSyntheticStyle applied: bold=%d italic=%d angle=%f", (int) needSyntheticBold, (int) needSyntheticItalic, angleDeg );
469 }
470 }
471
472 m_subsets.emplace( key, std::move( subset ) );
473 return subsetPtr;
474}
475
477 bool aItalicRequested, bool aBoldRequested,
478 std::vector<PDF_OUTLINE_FONT_RUN>* aRuns )
479{
480 if( !aRuns || !aFont )
481 return;
482
483 auto permission = aFont->GetEmbeddingPermission();
484
487 {
488 return;
489 }
490
491 // If italic requested and font has a dedicated italic variant discoverable via style linkage,
492 // the caller should already have selected that font (aFont). We still separate subsets by
493 // italic flag so synthetic slant and regular do not share widths.
494 PDF_OUTLINE_FONT_SUBSET* subset = ensureSubset( aFont, aItalicRequested, aBoldRequested );
495
496 if( !subset )
497 return;
498
499 UTF8 utf8Text( aText );
500 std::string textUtf8 = utf8Text.substr();
501
502 hb_buffer_t* buffer = hb_buffer_create();
503 hb_buffer_set_cluster_level( buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES );
504 hb_buffer_add_utf8( buffer, textUtf8.c_str(), static_cast<int>( textUtf8.size() ), 0,
505 static_cast<int>( textUtf8.size() ) );
506 hb_buffer_guess_segment_properties( buffer );
507
508 hb_font_t* hbFont = hb_ft_font_create_referenced( aFont->GetFace() );
509 hb_ft_font_set_funcs( hbFont );
510 hb_shape( hbFont, buffer, nullptr, 0 );
511
512 unsigned int glyphCount = 0;
513 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buffer, &glyphCount );
514 hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buffer, &glyphCount );
515
516 hb_font_destroy( hbFont );
517
518 if( glyphCount == 0 )
519 {
520 hb_buffer_destroy( buffer );
521 return;
522 }
523
525 run.m_subset = subset;
526
527 bool hasVisibleGlyph = false;
528
529 for( unsigned int ii = 0; ii < glyphCount; ++ii )
530 {
531 uint32_t glyphIndex = glyphInfo[ii].codepoint;
532
533 size_t clusterStart = glyphInfo[ii].cluster;
534 size_t clusterEnd = ( ii + 1 < glyphCount ) ? glyphInfo[ii + 1].cluster : textUtf8.size();
535
536 if( clusterEnd < clusterStart )
537 std::swap( clusterStart, clusterEnd );
538
539 std::string clusterUtf8 = textUtf8.substr( clusterStart, clusterEnd - clusterStart );
540 std::u32string unicode = utf8ToU32( clusterUtf8 );
541
542 if( unicode.empty() )
543 unicode.push_back( 0 );
544
545 uint16_t cid = subset->EnsureGlyph( glyphIndex, unicode );
546
547 if( cid != 0 )
548 hasVisibleGlyph = true;
549
550 run.m_bytes.push_back( static_cast<char>( ( cid >> 8 ) & 0xFF ) );
551 run.m_bytes.push_back( static_cast<char>( cid & 0xFF ) );
552
553 // Capture HarfBuzz positioning information and convert to PDF units
555 glyph.cid = cid;
556 // Convert from 26.6 fixed point to font units, then to PDF units
557 double xAdvanceFontUnits = glyphPos[ii].x_advance / 64.0;
558 double yAdvanceFontUnits = glyphPos[ii].y_advance / 64.0;
559 double xOffsetFontUnits = glyphPos[ii].x_offset / 64.0;
560 double yOffsetFontUnits = glyphPos[ii].y_offset / 64.0;
561
562 glyph.xAdvance = unitsToPdf( xAdvanceFontUnits, subset->UnitsPerEm() );
563 glyph.yAdvance = unitsToPdf( yAdvanceFontUnits, subset->UnitsPerEm() );
564 glyph.xOffset = unitsToPdf( xOffsetFontUnits, subset->UnitsPerEm() );
565 glyph.yOffset = unitsToPdf( yOffsetFontUnits, subset->UnitsPerEm() );
566 run.m_glyphs.push_back( glyph );
567 }
568
569 hb_buffer_destroy( buffer );
570
571 if( hasVisibleGlyph && !run.m_bytes.empty() )
572 aRuns->push_back( std::move( run ) );
573}
574
575std::vector<PDF_OUTLINE_FONT_SUBSET*> PDF_OUTLINE_FONT_MANAGER::AllSubsets() const
576{
577 std::vector<PDF_OUTLINE_FONT_SUBSET*> result;
578 result.reserve( m_subsets.size() );
579
580 for( const auto& [ font, subset ] : m_subsets )
581 {
582 if( subset )
583 result.push_back( subset.get() );
584 }
585
586 return result;
587}
const char * name
const wxString & GetName() const
Definition font.h:149
Class OUTLINE_FONT implements outline font drawing.
const FT_Face & GetFace() const
bool IsItalic() const override
EMBEDDING_PERMISSION GetEmbeddingPermission() const
bool IsBold() const override
std::map< SUBSET_KEY, std::unique_ptr< PDF_OUTLINE_FONT_SUBSET > > m_subsets
void EncodeString(const wxString &aText, KIFONT::OUTLINE_FONT *aFont, bool aItalicRequested, bool aBoldRequested, std::vector< PDF_OUTLINE_FONT_RUN > *aRuns)
PDF_OUTLINE_FONT_SUBSET * ensureSubset(KIFONT::OUTLINE_FONT *aFont, bool aItalic, bool aBold)
std::vector< PDF_OUTLINE_FONT_SUBSET * > AllSubsets() const
std::vector< std::u32string > m_cidToUnicode
std::string BuildToUnicodeCMap() const
uint16_t EnsureGlyph(uint32_t aGlyphIndex, const std::u32string &aUnicode)
std::vector< double > m_widths
KIFONT::OUTLINE_FONT * m_font
std::vector< uint8_t > m_fontData
std::vector< uint16_t > m_cidToGid
static std::string makeSubsetName(KIFONT::OUTLINE_FONT *aFont, unsigned aSubsetIndex)
static std::string sanitizeFontName(const wxString &aName)
const std::vector< uint8_t > & FontFileData()
PDF_OUTLINE_FONT_SUBSET(KIFONT::OUTLINE_FONT *aFont, unsigned aSubsetIndex)
std::string BuildWidthsArray() const
KIFONT::OUTLINE_FONT * Font() const
static std::string makeResourceName(unsigned aSubsetIndex)
std::map< GLYPH_KEY, uint16_t > m_glyphMap
void ForceSyntheticStyle(bool aBold, bool aItalic, double aItalicAngleDeg)
std::string BuildCIDToGIDStream() const
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:72
std::string substr(size_t pos=0, size_t len=npos) const
Definition utf8.h:206
static bool empty(const wxTextEntryBase *aCtrl)
static constexpr double ITALIC_TILT
Tilt factor for italic style (this is the scaling factor on dY relative coordinates to give a tilted ...
Definition font.h:61
const wxChar *const tracePdfPlotter
Flag to enable PDF plotter debug tracing.
PDF_OUTLINE_FONT_SUBSET * m_subset
std::vector< PDF_OUTLINE_FONT_GLYPH > m_glyphs
bool operator<(const GLYPH_KEY &aOther) const
wxString result
Test unit parsing edge cases and error handling.
#define M_PI
wxLogTrace helper definitions.