30#include <fmt/format.h>
31#include <harfbuzz/hb-ft.h>
32#include <harfbuzz/hb.h>
44std::string formatUnicodeHex( uint32_t aCodepoint )
46 if( aCodepoint <= 0xFFFF )
47 return fmt::format(
"{:04X}", aCodepoint );
49 if( aCodepoint <= 0x10FFFF )
51 uint32_t value = aCodepoint - 0x10000;
52 uint16_t high = 0xD800 + ( value >> 10 );
53 uint16_t low = 0xDC00 + ( value & 0x3FF );
54 return fmt::format(
"{:04X}{:04X}", high, low );
57 return std::string(
"003F" );
60std::u32string utf8ToU32(
const std::string& aUtf8 )
63 UTF8 utf8( aUtf8.c_str() );
65 for(
auto it = utf8.ubegin(); it < utf8.uend(); ++it )
66 result.push_back(
static_cast<uint32_t
>( *it ) );
71std::string generateSubsetPrefix(
unsigned aSubsetIndex )
73 static constexpr char letters[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
74 std::string prefix( 6,
'A' );
76 for(
int ii = 5; ii >= 0; --ii )
78 prefix[ii] = letters[ aSubsetIndex % 26 ];
85double unitsToPdf(
double aValue,
double aUnitsPerEm )
87 if( aUnitsPerEm == 0.0 )
90 return aValue * 1000.0 / aUnitsPerEm;
126 FT_Face face = aFont ? aFont->
GetFace() :
nullptr;
130 if( face->units_per_EM > 0 )
131 m_unitsPerEm =
static_cast<double>( face->units_per_EM );
141 if( face->style_flags & FT_STYLE_FLAG_ITALIC )
149 if( face->face_flags & FT_FACE_FLAG_FIXED_WIDTH )
178 if( aGlyphIndex == 0 )
195 if( FT_Load_Glyph( face, aGlyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING ) )
200 double rawAdvance266 =
static_cast<double>( face->glyph->advance.x );
201 double rawAdvanceFontUnits = rawAdvance266 / 64.0;
202 double advance = unitsToPdf( rawAdvanceFontUnits,
m_unitsPerEm );
217 if( std::getenv(
"KICAD_DEBUG_FONT_ADV" ) )
220 "EnsureGlyph font='%s' gid=%u cid=%u rawAdvance26.6=%f rawAdvanceUnits=%f storedAdvancePdfUnits=%f unitsPerEm=%f", \
221 m_font ?
m_font->GetName().ToUTF8().data() :
"(null)", (
unsigned) aGlyphIndex, (
unsigned) cid, \
222 rawAdvance266, rawAdvanceFontUnits, advance,
m_unitsPerEm );
224 m_cidToGid[cid] =
static_cast<uint16_t
>( aGlyphIndex );
241 wxString fontFile =
m_font->GetFileName();
243 if( fontFile.IsEmpty() )
246 wxFFile file( fontFile, wxT(
"rb" ) );
248 if( !file.IsOpened() )
251 wxFileOffset length = file.Length();
255 m_fontData.resize(
static_cast<size_t>( length ) );
267 return std::string(
"[]" );
272 double designScale = 0.0072 * 2.25;
275 fmt::memory_buffer buffer;
276 fmt::format_to( std::back_inserter( buffer ),
"[ 1 [" );
278 for( uint16_t cid = 1; cid <
m_nextCID; ++cid )
283 if( designScale != 0.0 )
284 width1000 = lrint( adv / designScale );
286 fmt::format_to( std::back_inserter( buffer ),
" {}", width1000 );
288 if( std::getenv(
"KICAD_DEBUG_FONT_ADV" ) && logCount < 16 )
290 wxLogTrace(
tracePdfPlotter,
"BuildWidthsArray FIXED cid=%u advPdfUnits=%f width1000=%ld", (
unsigned) cid, adv, width1000 );
295 fmt::format_to( std::back_inserter( buffer ),
" ] ]" );
296 return std::string( buffer.data(), buffer.size() );
302 return std::string();
304 fmt::memory_buffer buffer;
308 fmt::format_to( std::back_inserter( buffer ),
"/CIDInit /ProcSet findresource begin\n" );
309 fmt::format_to( std::back_inserter( buffer ),
"12 dict begin\n" );
310 fmt::format_to( std::back_inserter( buffer ),
"begincmap\n" );
311 fmt::format_to( std::back_inserter( buffer ),
"/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> def\n" );
312 fmt::format_to( std::back_inserter( buffer ),
"/CMapName /{} def\n", cmapName );
313 fmt::format_to( std::back_inserter( buffer ),
"/CMapType 2 def\n" );
314 fmt::format_to( std::back_inserter( buffer ),
"1 begincodespacerange\n" );
315 fmt::format_to( std::back_inserter( buffer ),
"<0000> <FFFF>\n" );
316 fmt::format_to( std::back_inserter( buffer ),
"endcodespacerange\n" );
318 size_t mappingCount = 0;
320 for( uint16_t cid = 1; cid <
m_nextCID; ++cid )
326 fmt::format_to( std::back_inserter( buffer ),
"{} beginbfchar\n", mappingCount );
328 for( uint16_t cid = 1; cid <
m_nextCID; ++cid )
332 if( unicode.empty() )
335 fmt::format_to( std::back_inserter( buffer ),
"<{:04X}> <", cid );
337 for( uint32_t codepoint : unicode )
338 fmt::format_to( std::back_inserter( buffer ),
"{}", formatUnicodeHex( codepoint ) );
340 fmt::format_to( std::back_inserter( buffer ),
">\n" );
343 fmt::format_to( std::back_inserter( buffer ),
"endbfchar\n" );
344 fmt::format_to( std::back_inserter( buffer ),
"endcmap\n" );
345 fmt::format_to( std::back_inserter( buffer ),
"CMapName currentdict /CMap defineresource pop\n" );
346 fmt::format_to( std::back_inserter( buffer ),
"end\n" );
347 fmt::format_to( std::back_inserter( buffer ),
"end\n" );
349 return std::string( buffer.data(), buffer.size() );
359 data.resize(
static_cast<size_t>(
m_nextCID ) * 2, 0 );
361 for( uint16_t cid = 0; cid <
m_nextCID; ++cid )
364 data[ cid * 2 ] =
static_cast<char>( ( gid >> 8 ) & 0xFF );
365 data[ cid * 2 + 1 ] =
static_cast<char>( gid & 0xFF );
373 return fmt::format(
"/KiCadOutline{}", aSubsetIndex );
379 std::string sanitized;
380 sanitized.reserve( utf8.size() );
382 for(
unsigned char ch : utf8 )
384 if( std::isalnum( ch ) )
385 sanitized.push_back(
static_cast<char>( ch ) );
387 sanitized.push_back(
'-' );
390 if( sanitized.empty() )
398 std::string prefix = generateSubsetPrefix( aSubsetIndex );
400 return fmt::format(
"{}+{}", prefix,
name );
421 return it->second.get();
422 auto subset = std::make_unique<PDF_OUTLINE_FONT_SUBSET>( aFont,
m_nextSubsetIndex++ );
429 bool faceHasRealItalic =
false;
430 bool faceHasRealBold =
false;
431 if(
const FT_Face& face = aFont->
GetFace() )
433 faceHasRealItalic = ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0;
434 faceHasRealBold = ( face->style_flags & FT_STYLE_FLAG_BOLD ) != 0;
437 bool needSyntheticItalic = aItalic && !faceHasRealItalic;
438 bool needSyntheticBold = aBold && !faceHasRealBold;
440 if( std::getenv(
"KICAD_DEBUG_SYN_STYLE" ) )
442 const FT_Face& face = aFont->
GetFace();
443 int styleFlags = face ? face->style_flags : 0;
444 const char* fname = aFont->
GetName().ToUTF8().data();
445 const char* styleName = ( face && face->style_name ) ? face->style_name :
"(null)";
449 "ensureSubset font='%s' styleName='%s' reqItalic=%d reqBold=%d FT_style_flags=%d "
450 "faceHasRealItalic=%d faceHasRealBold=%d fakeItal=%d fakeBold=%d syntheticItalic=%d "
451 "syntheticBold=%d subsetKey[i=%d b=%d] subsetIdx=%u",
452 fname ? fname :
"(null)", styleName, (
int) aItalic, (
int) aBold, styleFlags,
453 (
int) faceHasRealItalic, (
int) faceHasRealBold, (
int) fakeItal, (
int) fakeBold,
454 (
int) needSyntheticItalic, (
int) needSyntheticBold, (
int) aItalic, (
int) aBold,
458 if( needSyntheticItalic || needSyntheticBold )
463 if( std::getenv(
"KICAD_DEBUG_SYN_STYLE" ) )
465 wxLogTrace(
tracePdfPlotter,
"ForceSyntheticStyle applied: bold=%d italic=%d angle=%f", (
int) needSyntheticBold, (
int) needSyntheticItalic, angleDeg );
469 m_subsets.emplace( key, std::move( subset ) );
474 bool aItalicRequested,
bool aBoldRequested,
475 std::vector<PDF_OUTLINE_FONT_RUN>* aRuns )
477 if( !aRuns || !aFont )
496 UTF8 utf8Text( aText );
497 std::string textUtf8 = utf8Text.
substr();
499 hb_buffer_t* buffer = hb_buffer_create();
500 hb_buffer_set_cluster_level( buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES );
501 hb_buffer_add_utf8( buffer, textUtf8.c_str(),
static_cast<int>( textUtf8.size() ), 0,
502 static_cast<int>( textUtf8.size() ) );
503 hb_buffer_guess_segment_properties( buffer );
505 hb_font_t* hbFont = hb_ft_font_create_referenced( aFont->
GetFace() );
506 hb_ft_font_set_funcs( hbFont );
507 hb_shape( hbFont, buffer,
nullptr, 0 );
509 unsigned int glyphCount = 0;
510 hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buffer, &glyphCount );
511 hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buffer, &glyphCount );
513 hb_font_destroy( hbFont );
515 if( glyphCount == 0 )
517 hb_buffer_destroy( buffer );
524 bool hasVisibleGlyph =
false;
526 for(
unsigned int ii = 0; ii < glyphCount; ++ii )
528 uint32_t glyphIndex = glyphInfo[ii].codepoint;
530 size_t clusterStart = glyphInfo[ii].cluster;
531 size_t clusterEnd = ( ii + 1 < glyphCount ) ? glyphInfo[ii + 1].cluster : textUtf8.size();
533 if( clusterEnd < clusterStart )
534 std::swap( clusterStart, clusterEnd );
536 std::string clusterUtf8 = textUtf8.substr( clusterStart, clusterEnd - clusterStart );
537 std::u32string unicode = utf8ToU32( clusterUtf8 );
539 if( unicode.empty() )
540 unicode.push_back( 0 );
542 uint16_t cid = subset->
EnsureGlyph( glyphIndex, unicode );
545 hasVisibleGlyph =
true;
547 run.
m_bytes.push_back(
static_cast<char>( ( cid >> 8 ) & 0xFF ) );
548 run.
m_bytes.push_back(
static_cast<char>( cid & 0xFF ) );
554 double xAdvanceFontUnits = glyphPos[ii].x_advance / 64.0;
555 double yAdvanceFontUnits = glyphPos[ii].y_advance / 64.0;
556 double xOffsetFontUnits = glyphPos[ii].x_offset / 64.0;
557 double yOffsetFontUnits = glyphPos[ii].y_offset / 64.0;
566 hb_buffer_destroy( buffer );
568 if( hasVisibleGlyph && !run.
m_bytes.empty() )
569 aRuns->push_back( std::move( run ) );
574 std::vector<PDF_OUTLINE_FONT_SUBSET*>
result;
577 for(
const auto& [ font, subset ] :
m_subsets )
580 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.