24#include <boost/test/unit_test.hpp>
26#include <wx/filename.h>
56 const std::string sampleUtf8 =
"ABCDEF Привет 日本語 漢字";
57 wxString sample = wxString::FromUTF8( sampleUtf8.c_str() );
58 wxString pdfPath =
getTempPdfPath(
"kicad_pdf_unicode_allstyles" );
68 auto emitStyle = [&](
bool bold,
bool italic,
int yoff )
75 strokeFont.get(), metrics );
78 emitStyle(
false,
false, 0 );
79 emitStyle(
true,
false, 8000 );
80 emitStyle(
false,
true, 16000 );
81 emitStyle(
true,
true, 24000 );
88 BOOST_CHECK( buffer.rfind(
"%PDF", 0 ) == 0 );
93 BOOST_CHECK_MESSAGE( cmapCount >= 4,
"Expected at least 4 CMaps (got " << cmapCount <<
")" );
95 auto requireAll = [&](
const char* codeHex,
const char* label ) {
97 BOOST_CHECK_MESSAGE( occurrences >= 4,
"Codepoint " << label <<
" (" << codeHex
98 <<
") expected in all 4 styles; found " << occurrences );
101 requireAll(
"041F",
"Cyrillic PE" );
102 requireAll(
"65E5",
"Kanji 日" );
103 requireAll(
"672C",
"Kanji 本" );
111 const std::string sampleUtf8 =
"ABCDEF Привет 日本語 漢字";
112 wxString sample = wxString::FromUTF8( sampleUtf8.c_str() );
140 strokeFont.get(), metrics );
147 auto contains = [&](
const char* needle ) {
return PdfContains( buffer2, needle ); };
149 BOOST_CHECK_MESSAGE( contains(
"041F" ),
"Missing Cyrillic glyph mapping (041F)" );
150 BOOST_CHECK_MESSAGE( contains(
"0420" ) || contains(
"0440" ),
"Missing Cyrillic glyph mapping (0420/0440)" );
151 BOOST_CHECK_MESSAGE( contains(
"65E5" ),
"Missing Japanese Kanji glyph mapping (65E5)" );
152 BOOST_CHECK_MESSAGE( contains(
"672C" ),
"Missing Japanese Kanji glyph mapping (672C)" );
153 BOOST_CHECK_MESSAGE( contains(
"6F22" ) || contains(
"6漢" ),
"Expect Chinese Han character mapping (6F22 / 漢)" );
162 BOOST_CHECK_MESSAGE( darkPixels > 200,
163 "Rasterized PDF appears blank or too sparse (" << darkPixels
164 <<
" dark pixels)" );
177 const std::string sampleUtf8 =
"ABCDEF Привет 日本語 漢字";
178 wxString sample = wxString::FromUTF8( sampleUtf8.c_str() );
193 strokeFont.get(), metrics );
196 auto contains = [&](
const char* needle ) {
return PdfContains( buffer3, needle ); };
197 BOOST_CHECK_MESSAGE( contains(
"041F" ),
"Missing Cyrillic glyph mapping (bold 041F)" );
198 BOOST_CHECK_MESSAGE( contains(
"65E5" ),
"Missing Japanese glyph mapping (bold 65E5)" );
205 const std::string sampleUtf8 =
"ABCDEF Привет 日本語 漢字";
206 wxString sample = wxString::FromUTF8( sampleUtf8.c_str() );
222 auto contains = [&](
const char* n ) {
return PdfContains( buffer4, n ); };
223 BOOST_CHECK_MESSAGE( contains(
"041F" ),
"Missing Cyrillic glyph mapping (italic 041F)" );
224 BOOST_CHECK_MESSAGE( contains(
"65E5" ),
"Missing Japanese glyph mapping (italic 65E5)" );
230 const std::string sampleUtf8 =
"ABCDEF Привет 日本語 漢字";
231 wxString sample = wxString::FromUTF8( sampleUtf8.c_str() );
232 wxString pdfPath =
getTempPdfPath(
"kicad_pdf_unicode_bolditalic" );
250 auto contains = [&](
const char* n ) {
return PdfContains( buffer5, n ); };
251 BOOST_CHECK_MESSAGE( contains(
"041F" ),
"Missing Cyrillic glyph mapping (bold-italic 041F)" );
252 BOOST_CHECK_MESSAGE( contains(
"65E5" ),
"Missing Japanese glyph mapping (bold-italic 65E5)" );
264 const std::string sampleUtf8 =
"Yg Test ñ";
265 wxString sample = wxString::FromUTF8( sampleUtf8.c_str() );
288 BOOST_CHECK( buffer6.rfind(
"%PDF", 0 ) == 0 );
292 BOOST_CHECK_MESSAGE( buffer6.find(
"d1" ) != std::string::npos,
293 "PDF should contain d1 operators for glyph bounding boxes" );
304 fontFile.RemoveLastDir();
305 fontFile.AppendDir( wxT(
"resources" ) );
306 fontFile.AppendDir( wxT(
"fonts" ) );
307 fontFile.SetFullName( wxT(
"NotoSans-Regular.ttf" ) );
308 wxString fontPath = fontFile.GetFullPath();
322 std::vector<wxString> embeddedFonts;
323 embeddedFonts.push_back( fontPath );
328 wxString sample = wxString::FromUTF8(
"Outline café" );
331 outlineFont, metrics );
335 wxFFile file( pdfPath,
"rb" );
337 wxFileOffset len = file.Length();
338 std::string buffer; buffer.resize( (
size_t) len ); file.Read( buffer.data(), len );
339 BOOST_CHECK( buffer.rfind(
"%PDF", 0 ) == 0 );
342 appendDecompressed();
344 BOOST_CHECK_MESSAGE( buffer.find(
"/CIDFontType2" ) != std::string::npos,
345 "Expected CIDFontType2 descendant font" );
346 BOOST_CHECK_MESSAGE( buffer.find(
"/FontFile2" ) != std::string::npos,
347 "Embedded outline font should include FontFile2 stream" );
348 BOOST_CHECK_MESSAGE( buffer.find(
"AAAAAA+Noto-Sans" ) != std::string::npos,
349 "BaseFont should reference Noto Sans subset" );
350 BOOST_CHECK_MESSAGE( buffer.find(
"00E9" ) != std::string::npos,
351 "ToUnicode map should include Latin character with accent" );
352 BOOST_CHECK_MESSAGE( buffer.find(
"/KiCadOutline" ) != std::string::npos,
353 "Outline font resource entry missing" );
358 wxString rasterBase = wxFileName::CreateTempFileName( wxT(
"kicad_pdf_raster") );
359 wxString cmd = wxString::Format( wxT(
"pdftoppm -r 72 -singlefile -png \"%s\" \"%s\""),
360 pdfPath, rasterBase );
362 int ret = wxExecute( cmd, wxEXEC_SYNC );
366 wxString pngPath = rasterBase + wxT(
".png");
368 if( wxFileExists( pngPath ) )
371 if( !wxImage::FindHandler( wxBITMAP_TYPE_PNG ) )
372 wxImage::AddHandler(
new wxPNGHandler );
374 wxImage img( pngPath );
375 BOOST_REQUIRE_MESSAGE( img.IsOk(),
"Failed to load rasterized PDF image" );
378 int w = img.GetWidth();
379 int h = img.GetHeight();
381 for(
int y = 0; y < h; ++y )
383 for(
int x = 0; x < w; ++x )
385 unsigned char r = img.GetRed( x, y );
386 unsigned char g = img.GetGreen( x, y );
387 unsigned char b = img.GetBlue( x, y );
390 if( r < 240 || g < 240 || b < 240 )
397 BOOST_CHECK_MESSAGE( darkPixels > 100,
398 "Rasterized PDF appears blank or too sparse (" << darkPixels
399 <<
" dark pixels). Outline font may not be rendering correctly." );
402 wxRemoveFile( pngPath );
406 BOOST_TEST_MESSAGE(
"pdftoppm succeeded but PNG output missing; skipping raster validation" );
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
FONT is an abstract base class for both outline and stroke fonts.
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
A color representation with 4 components: red, green, blue, alpha.
virtual bool EndPlot() override
virtual bool OpenFile(const wxString &aFullFilename) override
Open or create the plot file aFullFilename.
virtual bool StartPlot(const wxString &aPageNumber) override
The PDF engine supports multiple pages; the first one is opened 'for free' the following are to be cl...
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
PDF can have multiple pages, so SetPageSettings can be called with the outputFile open (but not insid...
virtual void PlotText(const VECTOR2I &aPos, const COLOR4D &aColor, const wxString &aText, const TEXT_ATTRIBUTES &aAttributes, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics, void *aData=nullptr) override
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Minimal concrete render settings suitable for plotters in tests.
double m_PDFStrokeFontYOffset
Vertical offset factor applied to stroke font glyph coordinates (in EM units) after Y inversion to co...
std::string GetTestDataRootDir()
int CountOccurrences(const std::string &aHaystack, const std::string &aNeedle)
Count occurrences of a substring in a string (overlapping allowed).
bool PdfContains(const std::string &aBuffer, const char *aNeedle)
Convenience contains check.
bool ReadPdfWithDecompressedStreams(const wxString &aPdfPath, std::string &aOutBuffer)
Read a PDF file and append best-effort decompressed contents of any Flate streams to the returned buf...
std::unique_ptr< KIFONT::STROKE_FONT > LoadStrokeFontUnique()
Load the default stroke font and return a unique_ptr for RAII deletion.
TEXT_ATTRIBUTES BuildTextAttributes(int aSizeIu=3000, int aStrokeWidth=300, bool aBold=false, bool aItalic=false)
Build a commonly used set of text attributes for plotting text in tests.
bool RasterizePdfCountDark(const wxString &aPdfPath, int aDpi, int aNearWhiteThresh, long &aOutDarkPixels)
Rasterize a PDF page to PNG using pdftoppm if available and count non-near-white pixels.
wxString MakeTempPdfPath(const wxString &aPrefix)
Make a temporary file path with .pdf extension using a given prefix.
void MaybeRemoveFile(const wxString &aPath, const wxString &aEnvVar=wxT("KICAD_KEEP_TEST_PDF"))
Remove a file unless the given environment variable is set (defaults to KICAD_KEEP_TEST_PDF).
Plotting engines similar to ps (PostScript, Gerber, svg)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(PlotMultilingualAllStylesMappings)
static wxString getTempPdfPath(const wxString &name)
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
VECTOR2< int32_t > VECTOR2I