33#include <fmt/format.h>
34#include <harfbuzz/hb-ft.h>
35#include <harfbuzz/hb.h>
47std::string formatUnicodeHex( uint32_t aCodepoint )
49 if( aCodepoint <= 0xFFFF )
50 return fmt::format(
"{:04X}", aCodepoint );
52 if( aCodepoint <= 0x10FFFF )
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 );
60 return std::string(
"003F" );
63std::u32string utf8ToU32(
const std::string& aUtf8 )
66 UTF8 utf8( aUtf8.c_str() );
68 for(
auto it = utf8.ubegin(); it < utf8.uend(); ++it )
69 result.push_back(
static_cast<uint32_t
>( *it ) );
74std::string generateSubsetPrefix(
unsigned aSubsetIndex )
76 static constexpr char letters[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
77 std::string prefix( 6,
'A' );
79 for(
int ii = 5; ii >= 0; --ii )
81 prefix[ii] = letters[ aSubsetIndex % 26 ];
88double unitsToPdf(
double aValue,
double aUnitsPerEm )
90 if( aUnitsPerEm == 0.0 )
93 return aValue * 1000.0 / aUnitsPerEm;
129 FT_Face face = aFont ? aFont->
GetFace() :
nullptr;
133 if( face->units_per_EM > 0 )
134 m_unitsPerEm =
static_cast<double>( face->units_per_EM );
144 if( face->style_flags & FT_STYLE_FLAG_ITALIC )
152 if( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH )
181 if( aGlyphIndex == 0 )
198 if( FT_Load_Glyph( face, aGlyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) )
203 double rawAdvance266 =
static_cast<double>( face->glyph->advance.x );
204 double rawAdvanceFontUnits = rawAdvance266 / 64.0;
205 double advance = unitsToPdf( rawAdvanceFontUnits,
m_unitsPerEm );
220 if( std::getenv(
"KICAD_DEBUG_FONT_ADV" ) )
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 );
227 m_cidToGid[cid] =
static_cast<uint16_t
>( aGlyphIndex );
244 wxString fontFile =
m_font->GetFileName();
246 if( fontFile.IsEmpty() )
249 wxFFile file( fontFile, wxT(
"rb" ) );
251 if( !file.IsOpened() )
254 wxFileOffset length = file.Length();
258 m_fontData.resize(
static_cast<size_t>( length ) );
270 return std::string(
"[]" );
275 double designScale = 0.0072 * 2.25;
278 fmt::memory_buffer buffer;
279 fmt::format_to( std::back_inserter( buffer ),
"[ 1 [" );
281 for( uint16_t cid = 1; cid <
m_nextCID; ++cid )
286 if( designScale != 0.0 )
287 width1000 = lrint( adv / designScale );
289 fmt::format_to( std::back_inserter( buffer ),
" {}", width1000 );
291 if( std::getenv(
"KICAD_DEBUG_FONT_ADV" ) && logCount < 16 )
293 wxLogTrace(
tracePdfPlotter,
"BuildWidthsArray FIXED cid=%u advPdfUnits=%f width1000=%ld", (
unsigned) cid, adv, width1000 );
298 fmt::format_to( std::back_inserter( buffer ),
" ] ]" );
299 return std::string( buffer.data(), buffer.size() );
305 return std::string();
307 fmt::memory_buffer buffer;
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" );
321 size_t mappingCount = 0;
323 for( uint16_t cid = 1; cid <
m_nextCID; ++cid )
329 fmt::format_to( std::back_inserter( buffer ),
"{} beginbfchar\n", mappingCount );
331 for( uint16_t cid = 1; cid <
m_nextCID; ++cid )
335 if( unicode.empty() )
338 fmt::format_to( std::back_inserter( buffer ),
"<{:04X}> <", cid );
340 for( uint32_t codepoint : unicode )
341 fmt::format_to( std::back_inserter( buffer ),
"{}", formatUnicodeHex( codepoint ) );
343 fmt::format_to( std::back_inserter( buffer ),
">\n" );
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" );
352 return std::string( buffer.data(), buffer.size() );
362 data.resize(
static_cast<size_t>(
m_nextCID ) * 2, 0 );
364 for( uint16_t cid = 0; cid <
m_nextCID; ++cid )
367 data[ cid * 2 ] =
static_cast<char>( ( gid >> 8 ) & 0xFF );
368 data[ cid * 2 + 1 ] =
static_cast<char>( gid & 0xFF );
376 return fmt::format(
"/KiCadOutline{}", aSubsetIndex );
382 std::string sanitized;
383 sanitized.reserve( utf8.size() );
385 for(
unsigned char ch : utf8 )
387 if( std::isalnum( ch ) )
388 sanitized.push_back(
static_cast<char>( ch ) );
390 sanitized.push_back(
'-' );
393 if( sanitized.empty() )
401 std::string prefix = generateSubsetPrefix( aSubsetIndex );
403 return fmt::format(
"{}+{}", prefix,
name );
424 return it->second.get();
425 auto subset = std::make_unique<PDF_OUTLINE_FONT_SUBSET>( aFont,
m_nextSubsetIndex++ );
432 bool faceHasRealItalic =
false;
433 bool faceHasRealBold =
false;
434 if(
const FT_Face& face = aFont->
GetFace() )
436 faceHasRealItalic = ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0;
437 faceHasRealBold = ( face->style_flags & FT_STYLE_FLAG_BOLD ) != 0;
440 bool needSyntheticItalic = aItalic && !faceHasRealItalic;
441 bool needSyntheticBold = aBold && !faceHasRealBold;
443 if( std::getenv(
"KICAD_DEBUG_SYN_STYLE" ) )
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)";
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,
461 if( needSyntheticItalic || needSyntheticBold )
466 if( std::getenv(
"KICAD_DEBUG_SYN_STYLE" ) )
468 wxLogTrace(
tracePdfPlotter,
"ForceSyntheticStyle applied: bold=%d italic=%d angle=%f", (
int) needSyntheticBold, (
int) needSyntheticItalic, angleDeg );
472 m_subsets.emplace( key, std::move( subset ) );
477 bool aItalicRequested,
bool aBoldRequested,
478 std::vector<PDF_OUTLINE_FONT_RUN>* aRuns )
480 if( !aRuns || !aFont )
499 UTF8 utf8Text( aText );
500 std::string textUtf8 = utf8Text.
substr();
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 );
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 );
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 );
516 hb_font_destroy( hbFont );
518 if( glyphCount == 0 )
520 hb_buffer_destroy( buffer );
527 bool hasVisibleGlyph =
false;
529 for(
unsigned int ii = 0; ii < glyphCount; ++ii )
531 uint32_t glyphIndex = glyphInfo[ii].codepoint;
533 size_t clusterStart = glyphInfo[ii].cluster;
534 size_t clusterEnd = ( ii + 1 < glyphCount ) ? glyphInfo[ii + 1].cluster : textUtf8.size();
536 if( clusterEnd < clusterStart )
537 std::swap( clusterStart, clusterEnd );
539 std::string clusterUtf8 = textUtf8.substr( clusterStart, clusterEnd - clusterStart );
540 std::u32string unicode = utf8ToU32( clusterUtf8 );
542 if( unicode.empty() )
543 unicode.push_back( 0 );
545 uint16_t cid = subset->
EnsureGlyph( glyphIndex, unicode );
548 hasVisibleGlyph =
true;
550 run.
m_bytes.push_back(
static_cast<char>( ( cid >> 8 ) & 0xFF ) );
551 run.
m_bytes.push_back(
static_cast<char>( cid & 0xFF ) );
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;
569 hb_buffer_destroy( buffer );
571 if( hasVisibleGlyph && !run.
m_bytes.empty() )
572 aRuns->push_back( std::move( run ) );
577 std::vector<PDF_OUTLINE_FONT_SUBSET*>
result;
580 for(
const auto& [ font, subset ] :
m_subsets )
583 result.push_back( subset.get() );
const wxString & GetName() const
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
unsigned m_nextSubsetIndex
PDF_OUTLINE_FONT_MANAGER()
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::string m_resourceName
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
double UnitsPerEm() const
KIFONT::OUTLINE_FONT * Font() const
static std::string makeResourceName(unsigned aSubsetIndex)
std::string m_baseFontName
std::map< GLYPH_KEY, uint16_t > m_glyphMap
void ForceSyntheticStyle(bool aBold, bool aItalic, double aItalicAngleDeg)
std::string BuildCIDToGIDStream() const
int m_fontDescriptorHandle
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
std::string substr(size_t pos=0, size_t len=npos) const
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 ...
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.
wxLogTrace helper definitions.