KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pdf_stroke_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
21
22#include <algorithm>
23#include <cstdint>
24#include <cstdlib>
25#include <limits>
26
27#include <fmt/format.h>
28
29#include <font/glyph.h>
30#include <advanced_config.h>
31
32namespace
33{
34static constexpr int MAX_SIMPLE_FONT_CODES = 256;
35
36static std::string formatUnicodeHex( uint32_t aCodepoint )
37{
38 if( aCodepoint <= 0xFFFF )
39 return fmt::format( "{:04X}", aCodepoint );
40
41 if( aCodepoint <= 0x10FFFF )
42 {
43 uint32_t value = aCodepoint - 0x10000;
44 uint16_t high = 0xD800 + ( value >> 10 );
45 uint16_t low = 0xDC00 + ( value & 0x3FF );
46 return fmt::format( "{:04X}{:04X}", high, low );
47 }
48
49 return std::string( "003F" );
50}
51} // anonymous namespace
52
53
55 unsigned aSubsetIndex, bool aBold, bool aItalic,
56 double aStrokeWidthFactor, double aAspectRatio ) :
57 m_font( aFont ),
58 m_unitsPerEm( aUnitsPerEm ),
59 m_resourceName( fmt::format( "/KiCadStroke{}", aSubsetIndex ) ),
60 m_cmapName( fmt::format( "KiCadStrokeCMap{}", aSubsetIndex ) ),
61 m_widths( MAX_SIMPLE_FONT_CODES, 0.0 ),
62 m_nextCode( 1 ),
63 m_lastCode( 0 ),
64 m_bboxMinX( std::numeric_limits<double>::max() ),
65 m_bboxMinY( std::numeric_limits<double>::max() ),
66 m_bboxMaxX( std::numeric_limits<double>::lowest() ),
67 m_bboxMaxY( std::numeric_limits<double>::lowest() ),
69 m_fontHandle( -1 ),
71 m_isBold( aBold ),
72 m_isItalic( aItalic ),
73 m_strokeWidthFactor( aStrokeWidthFactor ),
74 m_aspectRatio( aAspectRatio )
75{
76 GLYPH notdef;
77 notdef.m_unicode = 0;
78 notdef.m_code = 0;
79 notdef.m_glyphIndex = -1;
80 notdef.m_name = ".notdef";
81 notdef.m_stream.clear();
82 notdef.m_width = 0.0;
83 notdef.m_minX = 0.0;
84 notdef.m_minY = 0.0;
85 notdef.m_maxX = 0.0;
86 notdef.m_maxY = 0.0;
87 notdef.m_charProcHandle = -1;
88
89 m_glyphs.push_back( notdef );
90 m_bboxMinX = 0.0;
91 m_bboxMinY = 0.0;
92 m_bboxMaxX = 0.0;
93 m_bboxMaxY = 0.0;
94}
95
96
97// Build the stroked path for a glyph.
98// KiCad's internal stroke font glyph coordinates use an inverted Y axis relative to the
99// PDF coordinate system we are targeting here. We therefore flip Y so text renders upright.
101{
102 if( !aGlyph )
103 return std::string();
104
105 fmt::memory_buffer buffer;
106
107 const double xEm = m_unitsPerEm * m_aspectRatio;
108 const double yEm = m_unitsPerEm;
109
110 double lw = m_unitsPerEm * m_strokeWidthFactor;
111 fmt::format_to( std::back_inserter( buffer ), "{:.3f} w 1 J 1 j ", lw );
112 auto& cfg = ADVANCED_CFG::GetCfg();
113
114 for( const std::vector<VECTOR2D>& stroke : *aGlyph )
115 {
116 bool firstPoint = true;
117
118 for( const VECTOR2D& point : stroke )
119 {
120 double x = ( point.x + cfg.m_PDFStrokeFontXOffset ) * xEm;
121 double y = point.y * yEm;
122
123 y = -y; // Mirror vertically about baseline (y=0)
124 y += cfg.m_PDFStrokeFontYOffset * yEm;
125
126 if( firstPoint )
127 {
128 fmt::format_to( std::back_inserter( buffer ), "{:.3f} {:.3f} m ", x, y );
129 firstPoint = false;
130 }
131 else
132 {
133 fmt::format_to( std::back_inserter( buffer ), "{:.3f} {:.3f} l ", x, y );
134 }
135 }
136
137 if( !stroke.empty() )
138 fmt::format_to( std::back_inserter( buffer ), "S " );
139 }
140
141 return std::string( buffer.data(), buffer.size() );
142}
143
144
145bool PDF_STROKE_FONT_SUBSET::Contains( wxUniChar aCode ) const
146{
147 return m_unicodeToCode.find( aCode ) != m_unicodeToCode.end();
148}
149
150
152{
153 auto it = m_unicodeToCode.find( aCode );
154
155 if( it != m_unicodeToCode.end() )
156 return it->second;
157
158 if( IsFull() )
159 return -1;
160
161 int glyphIndex = glyphIndexForUnicode( aCode );
162
163 int code = m_nextCode++;
164 m_lastCode = std::max( m_lastCode, code );
165
166 GLYPH data;
167 data.m_unicode = aCode;
168 data.m_code = code;
169 data.m_glyphIndex = glyphIndex;
170 data.m_name = makeGlyphName( code );
171 data.m_charProcHandle = -1;
172
173 const KIFONT::STROKE_GLYPH* glyph = nullptr;
174 BOX2D bbox;
175
176 if( m_font )
177 {
178 glyph = m_font->GetGlyph( glyphIndex );
179 bbox = m_font->GetGlyphBoundingBox( glyphIndex );
180 }
181
182 VECTOR2D origin = bbox.GetOrigin();
183 VECTOR2D size = bbox.GetSize();
184
185 const double xEm = m_unitsPerEm * m_aspectRatio;
186 const double yEm = m_unitsPerEm;
187
188 data.m_width = size.x * xEm;
189 data.m_minX = origin.x * xEm;
190 data.m_minY = origin.y * yEm;
191 data.m_maxX = ( origin.x + size.x ) * xEm;
192 data.m_maxY = ( origin.y + size.y ) * yEm;
193
194 // Invert Y so glyphs render upright in PDF coordinate space.
195 {
196 // Mirror bounding box vertically about baseline.
197 double newMinY = -data.m_maxY;
198 double newMaxY = -data.m_minY;
199 data.m_minY = newMinY;
200 data.m_maxY = newMaxY;
201
202 // Apply Y offset to bounding box to match the offset applied to stroke coordinates
203 double yOffset = ADVANCED_CFG::GetCfg().m_PDFStrokeFontYOffset * yEm;
204 data.m_minY += yOffset;
205 data.m_maxY += yOffset;
206 }
207
208 // Apply X offset to bounding box to match the offset applied to stroke coordinates
209 double xOffset = ADVANCED_CFG::GetCfg().m_PDFStrokeFontXOffset * xEm;
210 data.m_minX += xOffset;
211 data.m_maxX += xOffset;
212
213 // Expand bbox by half the stroke width so the d1 clipping rect covers the full painted
214 // area, not just the stroke center lines.
215 double halfStroke = m_unitsPerEm * m_strokeWidthFactor / 2.0;
216 data.m_minX -= halfStroke;
217 data.m_minY -= halfStroke;
218 data.m_maxX += halfStroke;
219 data.m_maxY += halfStroke;
220
221 // Build charproc stream: first specify width and bbox (d1 operator) then stroke path.
222 double kerningFactor = ADVANCED_CFG::GetCfg().m_PDFStrokeFontKerningFactor;
223
224 if( kerningFactor <= 0.0 )
225 kerningFactor = 1.0;
226
227 std::string strokes = buildGlyphStream( glyph );
228 data.m_width = size.x * xEm * kerningFactor;
229 data.m_stream = fmt::format( "{:.3f} 0 {:.3f} {:.3f} {:.3f} {:.3f} d1 {}",
230 data.m_width,
231 data.m_minX, data.m_minY, data.m_maxX, data.m_maxY,
232 strokes );
233
234 m_widths[code] = data.m_width;
235
236 m_bboxMinX = std::min( m_bboxMinX, data.m_minX );
237 m_bboxMinY = std::min( m_bboxMinY, data.m_minY );
238 m_bboxMaxX = std::max( m_bboxMaxX, data.m_maxX );
239 m_bboxMaxY = std::max( m_bboxMaxY, data.m_maxY );
240
241 m_glyphs.push_back( std::move( data ) );
242 m_unicodeToCode.emplace( aCode, code );
243
244 return code;
245}
246
247
248int PDF_STROKE_FONT_SUBSET::CodeForGlyph( wxUniChar aCode ) const
249{
250 auto it = m_unicodeToCode.find( aCode );
251
252 if( it != m_unicodeToCode.end() )
253 return it->second;
254
255 return -1;
256}
257
258
260{
261 return m_nextCode >= MAX_SIMPLE_FONT_CODES;
262}
263
264
266{
267 return static_cast<int>( m_glyphs.size() );
268}
269
270
272{
273 return 0;
274}
275
276
278{
279 return std::max( 0, m_lastCode );
280}
281
282
284{
285 int first = FirstChar();
286 int last = LastChar();
287
288 fmt::memory_buffer buffer;
289 fmt::format_to( std::back_inserter( buffer ), "[ {} ", first );
290
291 for( int code = first; code <= last; ++code )
292 {
293 const GLYPH* glyph = glyphForCode( code );
294
295 if( glyph )
296 fmt::format_to( std::back_inserter( buffer ), "/{} ", glyph->m_name );
297 else
298 fmt::format_to( std::back_inserter( buffer ), "/.notdef " );
299 }
300
301 fmt::format_to( std::back_inserter( buffer ), "]" );
302 return std::string( buffer.data(), buffer.size() );
303}
304
305
307{
308 int first = FirstChar();
309 int last = LastChar();
310
311 fmt::memory_buffer buffer;
312 fmt::format_to( std::back_inserter( buffer ), "[" );
313
314 for( int code = first; code <= last; ++code )
315 fmt::format_to( std::back_inserter( buffer ), " {:g}", m_widths[code] );
316
317 fmt::format_to( std::back_inserter( buffer ), " ]" );
318 return std::string( buffer.data(), buffer.size() );
319}
320
321
323{
324 size_t mappingCount = 0;
325
326 for( const GLYPH& glyph : m_glyphs )
327 {
328 if( glyph.m_code == 0 )
329 continue;
330
331 ++mappingCount;
332 }
333
334 fmt::memory_buffer buffer;
335
336 fmt::format_to( std::back_inserter( buffer ), "/CIDInit /ProcSet findresource begin\n" );
337 fmt::format_to( std::back_inserter( buffer ), "12 dict begin\n" );
338 fmt::format_to( std::back_inserter( buffer ), "begincmap\n" );
339 fmt::format_to( std::back_inserter( buffer ), "/CIDSystemInfo << /Registry (KiCad) /Ordering (StrokeFont) /Supplement 0 >> def\n" );
340 fmt::format_to( std::back_inserter( buffer ), "/CMapName /{} def\n", m_cmapName );
341 fmt::format_to( std::back_inserter( buffer ), "/CMapType 2 def\n" );
342 fmt::format_to( std::back_inserter( buffer ), "1 begincodespacerange\n" );
343 fmt::format_to( std::back_inserter( buffer ), "<00> <FF>\n" );
344 fmt::format_to( std::back_inserter( buffer ), "endcodespacerange\n" );
345
346 fmt::format_to( std::back_inserter( buffer ), "{} beginbfchar\n", mappingCount );
347
348 for( const GLYPH& glyph : m_glyphs )
349 {
350 if( glyph.m_code == 0 )
351 continue;
352
353 fmt::format_to( std::back_inserter( buffer ), "<{:02X}> <{}>\n", glyph.m_code,
354 formatUnicodeHex( static_cast<uint32_t>( glyph.m_unicode ) ) );
355 }
356
357 fmt::format_to( std::back_inserter( buffer ), "endbfchar\n" );
358 fmt::format_to( std::back_inserter( buffer ), "endcmap\n" );
359 fmt::format_to( std::back_inserter( buffer ), "CMapName currentdict /CMap defineresource pop\n" );
360 fmt::format_to( std::back_inserter( buffer ), "end\n" );
361 fmt::format_to( std::back_inserter( buffer ), "end\n" );
362
363 return std::string( buffer.data(), buffer.size() );
364}
365
366
368{
369 int value = static_cast<int>( aCode );
370
371 if( value < ' ' )
372 return static_cast<int>( '?' ) - ' ';
373
374 int index = value - ' ';
375 int count = static_cast<int>( m_font ? m_font->GetGlyphCount() : 0 );
376
377 if( index < 0 || index >= count )
378 return static_cast<int>( '?' ) - ' ';
379
380 return index;
381}
382
383
384std::string PDF_STROKE_FONT_SUBSET::makeGlyphName( int aCode ) const
385{
386 return fmt::format( "g{:02X}", aCode );
387}
388
389
391{
392 for( const GLYPH& glyph : m_glyphs )
393 {
394 if( glyph.m_code == aCode )
395 return &glyph;
396 }
397
398 return nullptr;
399}
400
401
403 m_font( KIFONT::STROKE_FONT::LoadFont( wxEmptyString ) ),
404 m_unitsPerEm( 1000.0 ),
406{
407 Reset();
408}
409
410
412{
413 m_styleGroups.clear();
415
416 if( !m_font )
417 m_font.reset( KIFONT::STROKE_FONT::LoadFont( wxEmptyString ) );
418}
419
420
422 int aStrokeWidth, int aFontWidth,
423 int aFontHeight )
424{
425 int strokeWidth = std::abs( aStrokeWidth );
426 int fontWidth = std::abs( aFontWidth );
427 int fontHeight = strokeWidth > 0 ? std::abs( aFontHeight ) : 0;
428 return { aBold, aItalic, strokeWidth, fontWidth, fontHeight };
429}
430
431
432void PDF_STROKE_FONT_MANAGER::EncodeString( const wxString& aText, std::vector<PDF_STROKE_FONT_RUN>* aRuns,
433 int aStrokeWidth, int aFontWidth, int aFontHeight, bool aBold,
434 bool aItalic )
435{
436 if( !aRuns )
437 return;
438
439 aRuns->clear();
440
441 if( aText.empty() )
442 return;
443
444 PDF_STROKE_FONT_SUBSET* currentSubset = nullptr;
445 std::string currentBytes;
446
447 for( wxUniChar ch : aText )
448 {
449 PDF_STROKE_FONT_SUBSET* subset = ensureSubsetForGlyph( ch, aStrokeWidth, aFontWidth, aFontHeight,
450 aBold, aItalic );
451
452 if( !subset )
453 continue;
454
455 int code = subset->EnsureGlyph( ch );
456
457 if( code < 0 )
458 continue;
459
460 if( subset != currentSubset )
461 {
462 if( !currentBytes.empty() && currentSubset )
463 aRuns->push_back( { currentSubset, currentBytes, aBold, aItalic } );
464
465 currentSubset = subset;
466 currentBytes.clear();
467 }
468
469 currentBytes.push_back( static_cast<char>( code ) );
470 }
471
472 if( !currentBytes.empty() && currentSubset )
473 aRuns->push_back( { currentSubset, std::move( currentBytes ), aBold, aItalic } );
474}
475
477 int aFontWidth, int aFontHeight,
478 bool aBold, bool aItalic )
479{
480 int strokeWidth = std::abs( aStrokeWidth );
481 int fontWidth = std::abs( aFontWidth );
482 int fontHeight = std::abs( aFontHeight );
483
484 double widthFactor;
485
486 if( strokeWidth > 0 && fontHeight > 0 )
487 {
488 widthFactor = static_cast<double>( strokeWidth ) / static_cast<double>( fontHeight );
489 }
490 else
491 {
493
494 if( widthFactor <= 0.0 )
495 widthFactor = 0.04;
496
497 if( aBold )
498 {
500
501 if( boldMul < 1.0 )
502 boldMul = 1.0;
503
504 widthFactor *= boldMul;
505 }
506 }
507
508 double aspectRatio = 1.0;
509
510 if( fontHeight > 0 && fontWidth > 0 )
511 aspectRatio = static_cast<double>( fontWidth ) / static_cast<double>( fontHeight );
512
513 STYLE_KEY key = styleKey( aBold, aItalic, aStrokeWidth, aFontWidth, aFontHeight );
515
516 for( const std::unique_ptr<PDF_STROKE_FONT_SUBSET>& subset : group.subsets )
517 {
518 if( subset->Contains( aCode ) )
519 return subset.get();
520 }
521
522 for( const std::unique_ptr<PDF_STROKE_FONT_SUBSET>& subset : group.subsets )
523 {
524 if( subset->IsFull() )
525 continue;
526
527 if( subset->EnsureGlyph( aCode ) >= 0 )
528 return subset.get();
529 }
530
531 unsigned subsetIndex = m_nextSubsetIndex++;
532 auto newSubset = std::make_unique<PDF_STROKE_FONT_SUBSET>( m_font.get(), m_unitsPerEm, subsetIndex, aBold, aItalic,
533 widthFactor, aspectRatio );
534 PDF_STROKE_FONT_SUBSET* subsetPtr = newSubset.get();
535 subsetPtr->EnsureGlyph( aCode );
536 group.subsets.emplace_back( std::move( newSubset ) );
537 return subsetPtr;
538}
539
540std::vector<PDF_STROKE_FONT_SUBSET*> PDF_STROKE_FONT_MANAGER::AllSubsets() const
541{
542 std::vector<PDF_STROKE_FONT_SUBSET*> out;
543
544 for( const auto& [key, group] : m_styleGroups )
545 {
546 (void) key;
547 for( const auto& up : group.subsets )
548 out.push_back( up.get() );
549 }
550 return out;
551}
552
int index
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
constexpr const Vec & GetOrigin() const
Definition box2.h:206
constexpr const SizeVec & GetSize() const
Definition box2.h:202
Implement a stroke font drawing.
Definition stroke_font.h:50
static STROKE_FONT * LoadFont(const wxString &aFontName)
Load a stroke font.
std::unique_ptr< KIFONT::STROKE_FONT > m_font
std::map< STYLE_KEY, STYLE_GROUP > m_styleGroups
void EncodeString(const wxString &aText, std::vector< PDF_STROKE_FONT_RUN > *aRuns, int aStrokeWidth, int aFontWidth, int aFontHeight, bool aBold=false, bool aItalic=false)
std::tuple< bool, bool, int, int, int > STYLE_KEY
PDF_STROKE_FONT_SUBSET * ensureSubsetForGlyph(wxUniChar aCode, int aStrokeWidth, int aFontWidth, int aFontHeight, bool aBold, bool aItalic)
std::vector< PDF_STROKE_FONT_SUBSET * > AllSubsets() const
static STYLE_KEY styleKey(bool aBold, bool aItalic, int aStrokeWidth, int aFontWidth, int aFontHeight)
std::string BuildDifferencesArray() const
const KIFONT::STROKE_FONT * m_font
int EnsureGlyph(wxUniChar aCode)
int CodeForGlyph(wxUniChar aCode) const
std::string BuildWidthsArray() const
std::map< wxUniChar, int > m_unicodeToCode
std::string makeGlyphName(int aCode) const
std::vector< double > m_widths
std::string BuildToUnicodeCMap() const
bool Contains(wxUniChar aCode) const
PDF_STROKE_FONT_SUBSET(const KIFONT::STROKE_FONT *aFont, double aUnitsPerEm, unsigned aSubsetIndex, bool aBold, bool aItalic, double aStrokeWidthFactor, double aAspectRatio)
std::vector< GLYPH > m_glyphs
const GLYPH * glyphForCode(int aCode) const
std::string buildGlyphStream(const KIFONT::STROKE_GLYPH *aGlyph) const
int glyphIndexForUnicode(wxUniChar aCode) const
double m_PDFStrokeFontKerningFactor
Kerning (spacing) factor applied to glyph advance (width).
double m_PDFStrokeFontXOffset
Horizontal offset factor applied to stroke font glyph coordinates (in EM units) after to compensate m...
double m_PDFStrokeFontYOffset
Vertical offset factor applied to stroke font glyph coordinates (in EM units) after Y inversion to co...
double m_PDFStrokeFontBoldMultiplier
Multiplier applied to stroke width factor when rendering bold stroke font subsets.
double m_PDFStrokeFontWidthFactor
Stroke font line width factor relative to EM size for PDF stroke fonts.
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
VECTOR2< double > VECTOR2D
Definition vector2d.h:682