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