KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pdf_test_utils.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#include <gal/color4d.h>
22
23#include <wx/filename.h>
24#include <wx/filefn.h>
25#include <wx/ffile.h>
26#include <wx/utils.h>
27#include <wx/image.h>
28#include <wx/imagpng.h>
29
30#include <zlib.h>
31
32wxString MakeTempPdfPath( const wxString& aPrefix )
33{
34 wxFileName fn = wxFileName::CreateTempFileName( aPrefix );
35 fn.SetExt( "pdf" );
36 return fn.GetFullPath();
37}
38
40{
41 m_background = KIGFX::COLOR4D( 1.0, 1.0, 1.0, 1.0 );
42 m_grid = KIGFX::COLOR4D( 0.8, 0.8, 0.8, 1.0 );
43 m_cursor = KIGFX::COLOR4D( 0.0, 0.0, 0.0, 1.0 );
44}
45
47{
48 return KIGFX::COLOR4D( 0.0, 0.0, 0.0, 1.0 );
49}
50
51TEXT_ATTRIBUTES BuildTextAttributes( int aSizeIu, int aStrokeWidth, bool aBold, bool aItalic )
52{
53 TEXT_ATTRIBUTES attrs;
54 attrs.m_Size = VECTOR2I( aSizeIu, aSizeIu );
55 attrs.m_StrokeWidth = aStrokeWidth;
56 attrs.m_Multiline = false;
57 attrs.m_Italic = aItalic;
58 attrs.m_Bold = aBold;
61 attrs.m_Angle = ANGLE_0;
62 attrs.m_Mirrored = false;
63 return attrs;
64}
65
66std::unique_ptr<KIFONT::STROKE_FONT> LoadStrokeFontUnique()
67{
68 return std::unique_ptr<KIFONT::STROKE_FONT>( KIFONT::STROKE_FONT::LoadFont( wxEmptyString ) );
69}
70
71static void append_decompressed_streams( std::string& aBuffer )
72{
73 std::string aggregate = aBuffer;
74 size_t pos = 0;
75
76 while( true )
77 {
78 size_t streamPos = aBuffer.find( "stream\n", pos );
79
80 if( streamPos == std::string::npos )
81 break;
82
83 size_t endPos = aBuffer.find( "endstream", streamPos );
84
85 if( endPos == std::string::npos )
86 break;
87
88 size_t dataStart = streamPos + 7; // skip "stream\n"
89 size_t dataLen = ( endPos > dataStart ) ? ( endPos - dataStart ) : 0;
90
91 if( dataLen > 0 )
92 {
93 const unsigned char* data = reinterpret_cast<const unsigned char*>( aBuffer.data() + dataStart );
94 z_stream zs{};
95 zs.next_in = const_cast<Bytef*>( data );
96 zs.avail_in = static_cast<uInt>( dataLen );
97
98 if( inflateInit( &zs ) == Z_OK )
99 {
100 std::string out;
101 out.resize( dataLen * 4 + 64 );
102 zs.next_out = reinterpret_cast<Bytef*>( out.data() );
103 zs.avail_out = static_cast<uInt>( out.size() );
104 int ret = inflate( &zs, Z_FINISH );
105
106 if( ret == Z_STREAM_END )
107 {
108 out.resize( zs.total_out );
109 aggregate += out;
110 }
111
112 inflateEnd( &zs );
113 }
114 }
115
116 pos = endPos + 9; // skip past "endstream"
117 }
118
119 aBuffer.swap( aggregate );
120}
121
122bool ReadPdfWithDecompressedStreams( const wxString& aPdfPath, std::string& aOutBuffer )
123{
124 wxFFile file( aPdfPath, "rb" );
125
126 if( !file.IsOpened() )
127 return false;
128
129 wxFileOffset len = file.Length();
130 if( len <= 0 )
131 return false;
132
133 aOutBuffer.resize( static_cast<size_t>( len ) );
134 file.Read( aOutBuffer.data(), len );
135
136 append_decompressed_streams( aOutBuffer );
137 return true;
138}
139
140int CountOccurrences( const std::string& aHaystack, const std::string& aNeedle )
141{
142 if( aNeedle.empty() )
143 return 0;
144
145 int count = 0;
146 size_t pos = 0;
147
148 while( true )
149 {
150 size_t p = aHaystack.find( aNeedle, pos );
151 if( p == std::string::npos )
152 break;
153 ++count;
154 pos = p + 1; // allow overlaps
155 }
156
157 return count;
158}
159
160bool RasterizePdfCountDark( const wxString& aPdfPath, int aDpi, int aNearWhiteThresh,
161 long& aOutDarkPixels )
162{
163 aOutDarkPixels = 0;
164
165 wxString rasterBase = wxFileName::CreateTempFileName( wxT( "kicad_pdf_raster" ) );
166 wxString cmd = wxString::Format( wxT( "pdftoppm -r %d -singlefile -png \"%s\" \"%s\"" ),
167 aDpi, aPdfPath, rasterBase );
168 int ret = wxExecute( cmd, wxEXEC_SYNC );
169
170 if( ret != 0 )
171 return false;
172
173 wxString pngPath = rasterBase + wxT( ".png" );
174
175 if( !wxFileExists( pngPath ) )
176 return false;
177
178 if( !wxImage::FindHandler( wxBITMAP_TYPE_PNG ) )
179 wxImage::AddHandler( new wxPNGHandler );
180
181 wxImage img( pngPath );
182 if( !img.IsOk() )
183 return false;
184
185 int w = img.GetWidth();
186 int h = img.GetHeight();
187
188 long dark = 0;
189 for( int y = 0; y < h; ++y )
190 {
191 for( int x = 0; x < w; ++x )
192 {
193 unsigned char r = img.GetRed( x, y );
194 unsigned char g = img.GetGreen( x, y );
195 unsigned char b = img.GetBlue( x, y );
196
197 if( r < aNearWhiteThresh || g < aNearWhiteThresh || b < aNearWhiteThresh )
198 ++dark;
199 }
200 }
201
202 aOutDarkPixels = dark;
203
204 // cleanup the rasterized file
205 wxRemoveFile( pngPath );
206 return true;
207}
208
209void MaybeRemoveFile( const wxString& aPath, const wxString& aEnvVar )
210{
211 wxString keepEnv;
212 if( !wxGetEnv( aEnvVar, &keepEnv ) || keepEnv.IsEmpty() )
213 wxRemoveFile( aPath );
214}
static STROKE_FONT * LoadFont(const wxString &aFontName)
Load a stroke font.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
KIGFX::COLOR4D m_cursor
KIGFX::COLOR4D m_background
KIGFX::COLOR4D GetColor(const KIGFX::VIEW_ITEM *, int) const override
Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer using curr...
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
int CountOccurrences(const std::string &aHaystack, const std::string &aNeedle)
Count occurrences of a substring in a string (overlapping allowed).
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.
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.
static void append_decompressed_streams(std::string &aBuffer)
wxString MakeTempPdfPath(const wxString &aPrefix)
Make a temporary file path with .pdf extension using a given prefix.
TEXT_ATTRIBUTES BuildTextAttributes(int aSizeIu, int aStrokeWidth, bool aBold, bool aItalic)
Build a commonly used set of text attributes for plotting text in tests.
void MaybeRemoveFile(const wxString &aPath, const wxString &aEnvVar)
Remove a file unless the given environment variable is set (defaults to KICAD_KEEP_TEST_PDF).
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683