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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25#include <gal/color4d.h>
26
27#include <wx/filename.h>
28#include <wx/filefn.h>
29#include <wx/ffile.h>
30#include <wx/utils.h>
31#include <wx/image.h>
32#include <wx/imagpng.h>
33
34#include <zlib.h>
35
36wxString MakeTempPdfPath( const wxString& aPrefix )
37{
38 wxFileName fn = wxFileName::CreateTempFileName( aPrefix );
39 fn.SetExt( "pdf" );
40 return fn.GetFullPath();
41}
42
44{
45 m_background = KIGFX::COLOR4D( 1.0, 1.0, 1.0, 1.0 );
46 m_grid = KIGFX::COLOR4D( 0.8, 0.8, 0.8, 1.0 );
47 m_cursor = KIGFX::COLOR4D( 0.0, 0.0, 0.0, 1.0 );
48}
49
51{
52 return KIGFX::COLOR4D( 0.0, 0.0, 0.0, 1.0 );
53}
54
55TEXT_ATTRIBUTES BuildTextAttributes( int aSizeIu, int aStrokeWidth, bool aBold, bool aItalic )
56{
57 TEXT_ATTRIBUTES attrs;
58 attrs.m_Size = VECTOR2I( aSizeIu, aSizeIu );
59 attrs.m_StrokeWidth = aStrokeWidth;
60 attrs.m_Multiline = false;
61 attrs.m_Italic = aItalic;
62 attrs.m_Bold = aBold;
65 attrs.m_Angle = ANGLE_0;
66 attrs.m_Mirrored = false;
67 return attrs;
68}
69
70std::unique_ptr<KIFONT::STROKE_FONT> LoadStrokeFontUnique()
71{
72 return std::unique_ptr<KIFONT::STROKE_FONT>( KIFONT::STROKE_FONT::LoadFont( wxEmptyString ) );
73}
74
75static void append_decompressed_streams( std::string& aBuffer )
76{
77 std::string aggregate = aBuffer;
78 size_t pos = 0;
79
80 while( true )
81 {
82 size_t streamPos = aBuffer.find( "stream\n", pos );
83
84 if( streamPos == std::string::npos )
85 break;
86
87 size_t endPos = aBuffer.find( "endstream", streamPos );
88
89 if( endPos == std::string::npos )
90 break;
91
92 size_t dataStart = streamPos + 7; // skip "stream\n"
93 size_t dataLen = ( endPos > dataStart ) ? ( endPos - dataStart ) : 0;
94
95 if( dataLen > 0 )
96 {
97 const unsigned char* data = reinterpret_cast<const unsigned char*>( aBuffer.data() + dataStart );
98 z_stream zs{};
99 zs.next_in = const_cast<Bytef*>( data );
100 zs.avail_in = static_cast<uInt>( dataLen );
101
102 if( inflateInit( &zs ) == Z_OK )
103 {
104 std::string out;
105 out.resize( dataLen * 4 + 64 );
106 zs.next_out = reinterpret_cast<Bytef*>( out.data() );
107 zs.avail_out = static_cast<uInt>( out.size() );
108 int ret = inflate( &zs, Z_FINISH );
109
110 if( ret == Z_STREAM_END )
111 {
112 out.resize( zs.total_out );
113 aggregate += out;
114 }
115
116 inflateEnd( &zs );
117 }
118 }
119
120 pos = endPos + 9; // skip past "endstream"
121 }
122
123 aBuffer.swap( aggregate );
124}
125
126bool ReadPdfWithDecompressedStreams( const wxString& aPdfPath, std::string& aOutBuffer )
127{
128 wxFFile file( aPdfPath, "rb" );
129
130 if( !file.IsOpened() )
131 return false;
132
133 wxFileOffset len = file.Length();
134 if( len <= 0 )
135 return false;
136
137 aOutBuffer.resize( static_cast<size_t>( len ) );
138 file.Read( aOutBuffer.data(), len );
139
140 append_decompressed_streams( aOutBuffer );
141 return true;
142}
143
144int CountOccurrences( const std::string& aHaystack, const std::string& aNeedle )
145{
146 if( aNeedle.empty() )
147 return 0;
148
149 int count = 0;
150 size_t pos = 0;
151
152 while( true )
153 {
154 size_t p = aHaystack.find( aNeedle, pos );
155 if( p == std::string::npos )
156 break;
157 ++count;
158 pos = p + 1; // allow overlaps
159 }
160
161 return count;
162}
163
164bool RasterizePdfCountDark( const wxString& aPdfPath, int aDpi, int aNearWhiteThresh,
165 long& aOutDarkPixels )
166{
167 aOutDarkPixels = 0;
168
169 wxString rasterBase = wxFileName::CreateTempFileName( wxT( "kicad_pdf_raster" ) );
170 wxString cmd = wxString::Format( wxT( "pdftoppm -r %d -singlefile -png \"%s\" \"%s\"" ),
171 aDpi, aPdfPath, rasterBase );
172 int ret = wxExecute( cmd, wxEXEC_SYNC );
173
174 if( ret != 0 )
175 return false;
176
177 wxString pngPath = rasterBase + wxT( ".png" );
178
179 if( !wxFileExists( pngPath ) )
180 return false;
181
182 if( !wxImage::FindHandler( wxBITMAP_TYPE_PNG ) )
183 wxImage::AddHandler( new wxPNGHandler );
184
185 wxImage img( pngPath );
186 if( !img.IsOk() )
187 return false;
188
189 int w = img.GetWidth();
190 int h = img.GetHeight();
191
192 long dark = 0;
193 for( int y = 0; y < h; ++y )
194 {
195 for( int x = 0; x < w; ++x )
196 {
197 unsigned char r = img.GetRed( x, y );
198 unsigned char g = img.GetGreen( x, y );
199 unsigned char b = img.GetBlue( x, y );
200
201 if( r < aNearWhiteThresh || g < aNearWhiteThresh || b < aNearWhiteThresh )
202 ++dark;
203 }
204 }
205
206 aOutDarkPixels = dark;
207
208 // cleanup the rasterized file
209 wxRemoveFile( pngPath );
210 return true;
211}
212
213void MaybeRemoveFile( const wxString& aPath, const wxString& aEnvVar )
214{
215 wxString keepEnv;
216 if( !wxGetEnv( aEnvVar, &keepEnv ) || keepEnv.IsEmpty() )
217 wxRemoveFile( aPath );
218}
static STROKE_FONT * LoadFont(const wxString &aFontName)
Load a stroke font.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:104
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:86
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:695