KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pdf_circle_plot.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
20#include <algorithm>
21#include <cctype>
22#include <sstream>
23#include <string>
24#include <vector>
25
26#include <boost/test/unit_test.hpp>
27
28#include <wx/string.h>
29
31#include <render_settings.h>
32
33#include <qa_utils/file_utils.h>
36
37BOOST_AUTO_TEST_SUITE( PDFCirclePlot )
38
39namespace
40{
41
42struct CIRCLE_PATH
43{
44 char m_PaintOp = '\0';
45 double m_Radius = 0.0;
46};
47
48
49bool isPaintOperator( const std::string& aToken )
50{
51 return aToken == "s" || aToken == "b" || aToken == "f";
52}
53
54
55bool isPathOperator( const std::string& aToken )
56{
57 return aToken == "m" || aToken == "c" || isPaintOperator( aToken );
58}
59
60
61CIRCLE_PATH parseCirclePath( const std::string& aBuffer )
62{
63 CIRCLE_PATH result;
64 std::istringstream buffer( aBuffer );
65 std::string line;
66
67 while( std::getline( buffer, line ) )
68 {
69 std::vector<std::string> tokens;
70 std::istringstream lineStream( line );
71 std::string token;
72
73 while( lineStream >> token )
74 tokens.push_back( token );
75
76 if( tokens.size() < 27 || !isPaintOperator( tokens.back() ) )
77 continue;
78
79 std::vector<double> coordinates;
80
81 for( const std::string& tok : tokens )
82 {
83 if( isPathOperator( tok ) )
84 continue;
85
86 try
87 {
88 coordinates.push_back( std::stod( tok ) );
89 }
90 catch( const std::exception& )
91 {
92 coordinates.clear();
93 break;
94 }
95 }
96
97 if( coordinates.size() != 26 )
98 continue;
99
100 double minX = coordinates[0];
101 double maxX = coordinates[0];
102
103 for( size_t ii = 0; ii < coordinates.size(); ii += 2 )
104 {
105 minX = std::min( minX, coordinates[ii] );
106 maxX = std::max( maxX, coordinates[ii] );
107 }
108
109 result.m_PaintOp = tokens.back()[0];
110 result.m_Radius = ( maxX - minX ) / 2.0;
111
112 return result;
113 }
114
115 return result;
116}
117
118
119CIRCLE_PATH plotOneCircle( int aDiameter, FILL_T aFill, int aWidth, int aDefaultPenWidth,
120 double aIusPerDecimil = 1.0 )
121{
122 KI_TEST::SCOPED_TEMP_DIR tempDir( wxT( "kicad_pdf_circle" ) );
123 const wxString pdfPath = tempDir.CreateChildFileStr( wxT( "output.pdf" ) );
124
125 PDF_PLOTTER plotter;
126 SIMPLE_RENDER_SETTINGS renderSettings;
127
128 renderSettings.SetDefaultPenWidth( aDefaultPenWidth );
129 plotter.SetRenderSettings( &renderSettings );
130
131 BOOST_REQUIRE( plotter.OpenFile( pdfPath ) );
132 plotter.SetViewport( VECTOR2I( 0, 0 ), aIusPerDecimil, 1.0, false );
133 BOOST_REQUIRE( plotter.StartPlot( wxT( "1" ), wxT( "TestPage" ) ) );
134
135 plotter.Circle( VECTOR2I( 0, 0 ), aDiameter, aFill, aWidth );
136
137 plotter.EndPlot();
138
139 std::string buffer;
140 BOOST_REQUIRE( ReadPdfWithDecompressedStreams( pdfPath, buffer ) );
141
142 CIRCLE_PATH path = parseCirclePath( buffer );
143 BOOST_REQUIRE_MESSAGE( path.m_PaintOp != '\0', "No circle path found in the PDF content" );
144
145 return path;
146}
147
148} // namespace
149
150
151BOOST_AUTO_TEST_CASE( StrokeNarrowerThanDiameterIsStroked )
152{
153 CIRCLE_PATH path = plotOneCircle( 20000, FILL_T::NO_FILL, 2000, 5000 );
154
155 BOOST_CHECK_EQUAL( path.m_PaintOp, 's' );
156 BOOST_CHECK_CLOSE( path.m_Radius, 10000.0, 0.01 );
157}
158
159
160BOOST_AUTO_TEST_CASE( FilledCircleWithOutlineIsFilledAndStroked )
161{
162 CIRCLE_PATH path = plotOneCircle( 20000, FILL_T::FILLED_SHAPE, 2000, 5000 );
163
164 BOOST_CHECK_EQUAL( path.m_PaintOp, 'b' );
165 BOOST_CHECK_CLOSE( path.m_Radius, 10000.0, 0.01 );
166}
167
168
169BOOST_AUTO_TEST_CASE( StrokeWiderThanDiameterIsFilledOnly )
170{
171 CIRCLE_PATH path = plotOneCircle( 2000, FILL_T::NO_FILL, 20000, 5000 );
172
173 BOOST_CHECK_EQUAL( path.m_PaintOp, 'f' );
174 BOOST_CHECK_CLOSE( path.m_Radius, 11000.0, 0.01 );
175}
176
177
178BOOST_AUTO_TEST_CASE( StrokeWiderThanDiameterUsesDeviceUnits )
179{
180 CIRCLE_PATH path = plotOneCircle( 2000, FILL_T::NO_FILL, 20000, 5000, 25.4 );
181
182 BOOST_CHECK_EQUAL( path.m_PaintOp, 'f' );
183 BOOST_CHECK_CLOSE( path.m_Radius, 11000.0 / 25.4, 0.01 );
184}
185
186
187BOOST_AUTO_TEST_CASE( DefaultPenWidthIsResolvedBeforeSizingTheDisk )
188{
189 CIRCLE_PATH path = plotOneCircle( 2000, FILL_T::NO_FILL, PLOTTER::USE_DEFAULT_LINE_WIDTH,
190 20000 );
191
192 BOOST_CHECK_EQUAL( path.m_PaintOp, 'f' );
193 BOOST_CHECK_CLOSE( path.m_Radius, 11000.0, 0.01 );
194}
195
196
197BOOST_AUTO_TEST_CASE( FilledCircleWithoutOutlineIsFilledOnly )
198{
199 CIRCLE_PATH path = plotOneCircle( 20000, FILL_T::FILLED_SHAPE, 0, 5000 );
200
201 BOOST_CHECK_EQUAL( path.m_PaintOp, 'f' );
202 BOOST_CHECK_CLOSE( path.m_Radius, 10000.0, 0.01 );
203}
204
205
void SetDefaultPenWidth(int aWidth)
virtual bool EndPlot() override
virtual void Circle(const VECTOR2I &pos, int diametre, FILL_T fill, int width) override
Circle drawing for PDF.
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...
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:166
static const int USE_DEFAULT_LINE_WIDTH
Definition plotter.h:140
Minimal concrete render settings suitable for plotters in tests.
FILL_T
Definition eda_fill.h:29
@ NO_FILL
Definition eda_fill.h:30
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
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...
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()
std::string path
BOOST_AUTO_TEST_CASE(StrokeNarrowerThanDiameterIsStroked)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708