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.