KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_downgrade.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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 <downgrade_scan.h>
25
26#include <algorithm>
27#include <cctype>
28#include <functional>
29#include <memory>
30#include <set>
31#include <string>
32#include <vector>
33
34#include <wx/ffile.h>
35#include <wx/filefn.h>
36
37#include <base_units.h>
38#include <board.h>
40#include <footprint.h>
42#include <pad.h>
43#include <pcb_barcode.h>
44#include <pcb_shape.h>
45#include <pcb_table.h>
46#include <pcb_tablecell.h>
47#include <pcb_textbox.h>
48#include <pcb_track.h>
49#include <pcb_point.h>
50#include <pcb_reference_image.h>
51#include <zone.h>
52#include <zone_settings.h>
53#include <netinfo.h>
54#include <eda_group.h>
55#include <eda_shape.h>
56#include <pcb_group.h>
57#include <pcb_dimension.h>
58#include <pcb_generator.h>
59#include <pcb_text.h>
60#include <project.h>
61#include <common.h>
64#include <layer_ids.h>
65#include <lset.h>
72
73
74// Board format date each feature landed. A rule fires only when the target predates this.
75static constexpr int VER_USER_LAYERS = 20241229;
76static constexpr int VER_TEXTBOX_KNOCKOUT = 20250210;
77static constexpr int VER_SHAPE_HATCH = 20250222;
78static constexpr int VER_VIA_PROTECTION = 20250228;
79static constexpr int VER_ZONE_DEFAULTS = 20250302;
80static constexpr int VER_JUMPER_PADS = 20250324;
81static constexpr int VER_VIA_SPLIT = 20250926;
82static constexpr int VER_SKIP_VIA = 20250801;
83static constexpr int VER_PRESSFIT = 20250811;
84static constexpr int VER_POST_MACHINING = 20251101;
85static constexpr int VER_PCB_POINTS = 20250901;
86static constexpr int VER_ROUNDED_RECT = 20250829;
87static constexpr int VER_FP_UNITS = 20250909;
88static constexpr int VER_BARCODE = 20250914;
89static constexpr int VER_PAD_DIE_DELAY = 20250401;
90static constexpr int VER_TIME_DOMAIN_TUNING = 20250401;
91// Group placement arrived without a format bump while 20250401 was current.
92static constexpr int VER_GROUP_PLACEMENT = 20250401;
93static constexpr int VER_CELL_KNOCKOUT = 20260603;
94static constexpr int VER_BACKDRILL = 20251101;
95static constexpr int VER_EXTRUDED_BODY = 20260410;
96static constexpr int VER_ELLIPSE = 20260508;
97static constexpr int VER_THIEVING = 20260513;
98static constexpr int VER_NET_CHAINS = 20260512;
99static constexpr int VER_PAD_SIM = 20260521;
100static constexpr int VER_DIELECTRIC = 20260511;
101static constexpr int VER_GROUP_LIB_ID = 20250513;
102static constexpr int VER_IMAGE_PPI = 20260623;
103static constexpr int VER_CONSTRAINTS = 20260624;
104static constexpr int VER_GRIDS = 20260728;
105static constexpr int VER_VIA_STITCH = 20260816;
106static constexpr int VER_PROPERTY_VARIABLES = 20260817;
107static constexpr int VER_LINE_ENDINGS = 20260818;
108static constexpr int VER_SIM_EXCLUSION = 20260828;
109static constexpr int VER_VIA_STACK = 20260830;
110static constexpr int VER_CUSTOM_PROPERTIES = 20260831;
111static constexpr int VER_DRILL_DRAWINGS = 20260901;
112static constexpr int VER_FOOTPRINT_HEIGHT = 20261017;
113static constexpr int VER_LINKED_PLACEMENT = 20261018;
114static constexpr int VER_IMAGE_ROTATION = 20261025;
115static constexpr int VER_IMAGE_OPACITY = 20261025;
116
117// The board format these rules were written against. When SEXPR_BOARD_FILE_VERSION moves past
118// it, ClassifyBoardForDowngrade asserts in debug builds until the new format is covered.
119//
120// 1. Find the commit that bumped the version and read what it added to common/pcb.keywords.
121// 2. If the new data is only written to the project file, nothing is needed here. Look at
122// project_file_downgrade.cpp instead. The 20250309 component class rules and the tuning
123// profile part of 20250401 were both that kind of bump.
124// 3. Otherwise add a VER_ constant above, a BOARD_RULE row that counts and strips the feature,
125// and a dated row in BOARD_TOKENS or BOARD_VALUES for each new node head or bareword value.
126// 4. Set this constant to the new version.
127static constexpr int BOARD_DOWNGRADE_COVERED = 20261025;
128
129
130static bool isEllipse( const BOARD_ITEM* aItem )
131{
132 if( aItem->Type() != PCB_SHAPE_T )
133 return false;
134
135 SHAPE_T shape = static_cast<const PCB_SHAPE*>( aItem )->GetShape();
136 return shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC;
137}
138
139
140static bool isHatchedShape( const BOARD_ITEM* aItem )
141{
142 return aItem->Type() == PCB_SHAPE_T && static_cast<const PCB_SHAPE*>( aItem )->IsHatchedFill();
143}
144
145
146// A hatched fill on copper is copper, so losing it changes the board electrically. Elsewhere it is
147// ink. The two cases are reported and transformed separately.
148static bool isHatchedCopperShape( const BOARD_ITEM* aItem )
149{
150 return isHatchedShape( aItem ) && IsCopperLayer( aItem->GetLayer() );
151}
152
153
154static bool isHatchedInkShape( const BOARD_ITEM* aItem )
155{
156 return isHatchedShape( aItem ) && !IsCopperLayer( aItem->GetLayer() );
157}
158
159
160// FOOTPRINT::Remove does not detach group membership the way BOARD::Remove does, so items
161// deleted from a footprint must leave their group first or the writer walks a dangling pointer.
162static void detachFromGroup( BOARD_ITEM* aItem )
163{
164 if( EDA_GROUP* group = aItem->GetParentGroup() )
165 group->RemoveItem( aItem );
166}
167
168
169static void ellipseToPolygon( PCB_SHAPE* aShape, int aMaxError )
170{
171 // A board polygon is always closed, so an elliptical arc becomes a filled polygon of
172 // its stroked ink instead of an open outline.
173 if( aShape->GetShape() == SHAPE_T::ELLIPSE_ARC )
174 {
175 aShape->SetWidth( std::max( aShape->GetWidth(), 2 * aMaxError ) );
176
177 SHAPE_POLY_SET poly;
178 aShape->EDA_SHAPE::TransformShapeToPolygon( poly, 0, aMaxError, ERROR_INSIDE, false );
179 poly.Simplify();
180
181 // The writers save one outline per shape, so holes must become cuts.
182 poly.Fracture();
183
184 aShape->SetShape( SHAPE_T::POLY );
185 aShape->SetPolyShape( poly );
187 aShape->SetWidth( 0 );
188 return;
189 }
190
191 SHAPE_ELLIPSE ellipse( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(), aShape->GetEllipseMinorRadius(),
192 aShape->GetEllipseRotation() );
193
194 SHAPE_LINE_CHAIN chain = ellipse.ConvertToPolyline( aMaxError );
195 chain.SetClosed( true );
196
197 SHAPE_POLY_SET poly;
198 poly.AddOutline( chain );
199
200 aShape->SetShape( SHAPE_T::POLY );
201 aShape->SetPolyShape( poly );
202}
203
204
205// Per-footprint rule scopes. The board apply functions delegate to these, and
206// DowngradeFootprintInPlace runs them from the same rule table, so the standalone
207// .kicad_mod path cannot drift from the board path.
208static int footprintMaxError( const FOOTPRINT* aFootprint )
209{
210 return aFootprint->GetBoard() ? aFootprint->GetBoard()->GetDesignSettings().m_MaxError : pcbIUScale.mmToIU( 0.005 );
211}
212
213
214// Defined below with the rounded-rectangle helpers. Ellipses can also live in a custom pad.
215static void forEachPadPrimitive( FOOTPRINT* aFootprint, const std::function<void( PCB_SHAPE* )>& aFunc );
216
217
218static void lowerEllipsesFp( FOOTPRINT* aFootprint )
219{
220 int maxError = footprintMaxError( aFootprint );
221
222 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
223 {
224 if( isEllipse( item ) )
225 ellipseToPolygon( static_cast<PCB_SHAPE*>( item ), maxError );
226 }
227
228 forEachPadPrimitive( aFootprint,
229 [&]( PCB_SHAPE* aPrimitive )
230 {
231 if( isEllipse( aPrimitive ) )
232 ellipseToPolygon( aPrimitive, maxError );
233 } );
234}
235
236
237static void setHatchedFillsFp( FOOTPRINT* aFootprint, const std::function<bool( const BOARD_ITEM* )>& aMatches,
238 FILL_T aFill )
239{
240 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
241 {
242 if( aMatches( item ) )
243 static_cast<PCB_SHAPE*>( item )->SetFillMode( aFill );
244 }
245}
246
247
248// The sexpr writer only saves outline 0 of a polygon shape, so a barcode's ink must be
249// split into one shape per outline or all but one module would be lost on save.
250static std::vector<PCB_SHAPE*> barcodeToShapes( PCB_BARCODE* aBarcode, BOARD_ITEM_CONTAINER* aParent )
251{
252 SHAPE_POLY_SET poly;
253 aBarcode->TransformShapeToPolygon( poly, aBarcode->GetLayer(), 0, 0, ERROR_INSIDE );
254 poly.Simplify();
255
256 // Each shape below carries a single outline, so holes must become cuts first or a
257 // finder ring would print as a solid square.
258 poly.Fracture();
259
260 std::vector<PCB_SHAPE*> shapes;
261
262 for( int ii = 0; ii < poly.OutlineCount(); ++ii )
263 {
264 PCB_SHAPE* shape = new PCB_SHAPE( aParent, SHAPE_T::POLY );
265
266 SHAPE_POLY_SET single;
267 single.AddOutline( poly.Outline( ii ) );
268
269 shape->SetPolyShape( single );
271 shape->SetWidth( 0 );
272 shape->SetLayer( aBarcode->GetLayer() );
273 shape->SetLocked( aBarcode->IsLocked() );
274 shapes.push_back( shape );
275 }
276
277 return shapes;
278}
279
280
281static void lowerBarcodesFp( FOOTPRINT* aFootprint )
282{
283 std::vector<PCB_BARCODE*> barcodes;
284
285 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
286 {
287 if( item->Type() == PCB_BARCODE_T )
288 barcodes.push_back( static_cast<PCB_BARCODE*>( item ) );
289 }
290
291 for( PCB_BARCODE* barcode : barcodes )
292 {
293 EDA_GROUP* group = barcode->GetParentGroup();
294
295 for( PCB_SHAPE* shape : barcodeToShapes( barcode, aFootprint ) )
296 {
297 aFootprint->Add( shape );
298
299 if( group )
300 group->AddItem( shape );
301 }
302
303 detachFromGroup( barcode );
304 aFootprint->Remove( barcode );
305 delete barcode;
306 }
307}
308
309
310static void dropPcbPointsFp( FOOTPRINT* aFootprint )
311{
312 for( PCB_POINT* point : aFootprint->Points() )
313 {
314 detachFromGroup( point );
315 delete point;
316 }
317
318 aFootprint->Points().clear();
319}
320
321
322static void dropExtrudedBodiesFp( FOOTPRINT* aFootprint )
323{
324 aFootprint->ClearBody();
325}
326
327
328static void dropFootprintHeightsFp( FOOTPRINT* aFootprint )
329{
330 aFootprint->SetHeight( 0 );
331}
332
333
334static void dropPadSimTypesFp( FOOTPRINT* aFootprint )
335{
336 for( PAD* pad : aFootprint->Pads() )
337 pad->SetSimElectricalType( PAD_SIM_ELECTRICAL_TYPE::NONE );
338}
339
340
341static void dropUnitInfoFp( FOOTPRINT* aFootprint )
342{
343 aFootprint->SetUnitInfo( {} );
344}
345
346
347static void dropJumperPadsFp( FOOTPRINT* aFootprint )
348{
349 aFootprint->JumperPadGroups().Clear();
350 aFootprint->SetDuplicatePadNumbersAreJumpers( false );
351}
352
353
354static void dropPcbVariantsFp( FOOTPRINT* aFootprint )
355{
356 std::vector<wxString> names;
357
358 for( const auto& [name, variant] : aFootprint->GetVariants() )
359 names.push_back( name );
360
361 for( const wxString& name : names )
362 aFootprint->DeleteVariant( name );
363}
364
365
366static void dropPostMachiningFp( FOOTPRINT* aFootprint )
367{
368 for( PAD* pad : aFootprint->Pads() )
369 {
370 pad->Padstack().FrontPostMachining().mode.reset();
371 pad->Padstack().BackPostMachining().mode.reset();
372 }
373}
374
375
376static void dropPressFitPadsFp( FOOTPRINT* aFootprint )
377{
378 for( PAD* pad : aFootprint->Pads() )
379 {
380 if( pad->GetProperty() == PAD_PROP::PRESSFIT )
381 pad->SetProperty( PAD_PROP::NONE );
382 }
383}
384
385
386static void dropDieDelaysFp( FOOTPRINT* aFootprint )
387{
388 for( PAD* pad : aFootprint->Pads() )
389 pad->SetPadToDieDelay( 0 );
390}
391
392
393static void dropGroupDesignBlocksFp( FOOTPRINT* aFootprint )
394{
395 for( PCB_GROUP* group : aFootprint->Groups() )
396 group->SetDesignBlockLibId( LIB_ID() );
397}
398
399
400static int countNetChains( const BOARD* aBoard )
401{
402 int count = 0;
403
404 // Match what the writer emits: a named chain, or a net with terminal pads but no chain name.
405 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
406 {
407 if( !net->GetNetChain().IsEmpty() || net->GetTerminalPad( 0 ) || net->GetTerminalPad( 1 ) )
408 count++;
409 }
410
411 return count;
412}
413
414
415static void dropNetChains( BOARD* aBoard )
416{
417 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
418 {
419 net->SetNetChain( wxEmptyString );
420 net->ClearTerminalPad( 0 ); // terminal_pad is part of the net-chain feature
421 net->ClearTerminalPad( 1 );
422 }
423
424 std::vector<wxString> chains;
425
426 for( const auto& [chain, color] : aBoard->GetNetChainColors() )
427 chains.push_back( chain );
428
429 for( const wxString& chain : chains )
431}
432
433
434static int countEllipses( const BOARD* aBoard )
435{
436 int count = 0;
437
438 for( BOARD_ITEM* item : aBoard->Drawings() )
439 {
440 if( isEllipse( item ) )
441 count++;
442 }
443
444 for( FOOTPRINT* fp : aBoard->Footprints() )
445 {
446 for( BOARD_ITEM* item : fp->GraphicalItems() )
447 {
448 if( isEllipse( item ) )
449 count++;
450 }
451
453 [&]( PCB_SHAPE* aPrimitive )
454 {
455 if( isEllipse( aPrimitive ) )
456 count++;
457 } );
458 }
459
460 return count;
461}
462
463
464static void lowerEllipses( BOARD* aBoard )
465{
466 int maxError = aBoard->GetDesignSettings().m_MaxError;
467
468 for( BOARD_ITEM* item : aBoard->Drawings() )
469 {
470 if( isEllipse( item ) )
471 ellipseToPolygon( static_cast<PCB_SHAPE*>( item ), maxError );
472 }
473
474 for( FOOTPRINT* fp : aBoard->Footprints() )
475 lowerEllipsesFp( fp );
476}
477
478
479static int countExtrudedBodies( const BOARD* aBoard )
480{
481 int count = 0;
482
483 for( FOOTPRINT* fp : aBoard->Footprints() )
484 {
485 if( fp->HasBody() )
486 count++;
487 }
488
489 return count;
490}
491
492
493static void dropExtrudedBodies( BOARD* aBoard )
494{
495 for( FOOTPRINT* fp : aBoard->Footprints() )
497}
498
499
500static int countFootprintHeights( const BOARD* aBoard )
501{
502 int count = 0;
503
504 for( FOOTPRINT* fp : aBoard->Footprints() )
505 {
506 if( fp->GetHeight() != 0 )
507 count++;
508 }
509
510 return count;
511}
512
513
514static void dropFootprintHeights( BOARD* aBoard )
515{
516 for( FOOTPRINT* fp : aBoard->Footprints() )
518}
519
520
521static int countThievingZones( const BOARD* aBoard )
522{
523 int count = 0;
524
525 for( ZONE* zone : aBoard->Zones() )
526 {
527 if( zone->IsCopperThieving() )
528 count++;
529 }
530
531 for( FOOTPRINT* fp : aBoard->Footprints() )
532 {
533 for( ZONE* zone : fp->Zones() )
534 {
535 if( zone->IsCopperThieving() )
536 count++;
537 }
538 }
539
540 return count;
541}
542
543
544static int countPadSimTypes( const BOARD* aBoard )
545{
546 int count = 0;
547
548 for( PAD* pad : aBoard->GetPads() )
549 {
550 if( pad->GetSimElectricalType() != PAD_SIM_ELECTRICAL_TYPE::NONE )
551 count++;
552 }
553
554 return count;
555}
556
557
558static void dropPadSimTypes( BOARD* aBoard )
559{
560 for( FOOTPRINT* fp : aBoard->Footprints() )
561 dropPadSimTypesFp( fp );
562}
563
564
565static int countUnitInfo( const BOARD* aBoard )
566{
567 int count = 0;
568
569 for( FOOTPRINT* fp : aBoard->Footprints() )
570 {
571 // Every footprint carries a default single unit. Only more than one unit holds real
572 // gate-swap information, so only that is a reportable loss.
573 if( fp->GetUnitInfo().size() > 1 )
574 count++;
575 }
576
577 return count;
578}
579
580
581static void dropUnitInfo( BOARD* aBoard )
582{
583 for( FOOTPRINT* fp : aBoard->Footprints() )
584 dropUnitInfoFp( fp );
585}
586
587
588static int countBarcodes( const BOARD* aBoard )
589{
590 int count = 0;
591
592 for( BOARD_ITEM* item : aBoard->Drawings() )
593 {
594 if( item->Type() == PCB_BARCODE_T )
595 count++;
596 }
597
598 for( FOOTPRINT* fp : aBoard->Footprints() )
599 {
600 for( BOARD_ITEM* item : fp->GraphicalItems() )
601 {
602 if( item->Type() == PCB_BARCODE_T )
603 count++;
604 }
605 }
606
607 return count;
608}
609
610
611static void lowerBarcodes( BOARD* aBoard )
612{
613 std::vector<PCB_BARCODE*> barcodes;
614
615 for( BOARD_ITEM* item : aBoard->Drawings() )
616 {
617 if( item->Type() == PCB_BARCODE_T )
618 barcodes.push_back( static_cast<PCB_BARCODE*>( item ) );
619 }
620
621 for( PCB_BARCODE* barcode : barcodes )
622 {
623 EDA_GROUP* group = barcode->GetParentGroup();
624
625 for( PCB_SHAPE* shape : barcodeToShapes( barcode, aBoard ) )
626 {
627 aBoard->Add( shape );
628
629 if( group )
630 group->AddItem( shape );
631 }
632
633 aBoard->Remove( barcode );
634 delete barcode;
635 }
636
637 for( FOOTPRINT* fp : aBoard->Footprints() )
638 lowerBarcodesFp( fp );
639}
640
641
642static int countPcbVariants( const BOARD* aBoard )
643{
644 int count = aBoard->GetVariantNames().empty() ? 0 : 1;
645
646 for( FOOTPRINT* fp : aBoard->Footprints() )
647 {
648 if( !fp->GetVariants().empty() )
649 count++;
650 }
651
652 return count;
653}
654
655
656static void dropPcbVariants( BOARD* aBoard )
657{
658 aBoard->SetVariantNames( {} );
659 aBoard->SetCurrentVariant( wxEmptyString );
660
661 for( FOOTPRINT* fp : aBoard->Footprints() )
662 dropPcbVariantsFp( fp );
663}
664
665
666static int countJumperPads( const BOARD* aBoard )
667{
668 int count = 0;
669
670 for( FOOTPRINT* fp : aBoard->Footprints() )
671 {
672 if( !fp->JumperPadGroups().IsEmpty() || fp->GetDuplicatePadNumbersAreJumpers() )
673 count++;
674 }
675
676 return count;
677}
678
679
680static void dropJumperPads( BOARD* aBoard )
681{
682 for( FOOTPRINT* fp : aBoard->Footprints() )
683 dropJumperPadsFp( fp );
684}
685
686
687static bool hasBackdrill( const PADSTACK& aPadstack )
688{
689 return aPadstack.SecondaryDrill().size.x > 0 || aPadstack.TertiaryDrill().size.x > 0;
690}
691
692
693static int countBackdrills( const BOARD* aBoard )
694{
695 int count = 0;
696
697 for( PAD* pad : aBoard->GetPads() )
698 {
699 if( hasBackdrill( pad->Padstack() ) )
700 count++;
701 }
702
703 for( PCB_TRACK* track : aBoard->Tracks() )
704 {
705 if( track->Type() == PCB_VIA_T && hasBackdrill( static_cast<PCB_VIA*>( track )->Padstack() ) )
706 count++;
707 }
708
709 return count;
710}
711
712
713static bool hasPostMachining( const PADSTACK& aPadstack )
714{
715 auto set =
716 []( const PADSTACK::POST_MACHINING_PROPS& aProps )
717 {
718 return aProps.mode.has_value() && aProps.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED;
719 };
720
721 return set( aPadstack.FrontPostMachining() ) || set( aPadstack.BackPostMachining() );
722}
723
724
725static int countPostMachining( const BOARD* aBoard )
726{
727 int count = 0;
728
729 for( PAD* pad : aBoard->GetPads() )
730 {
731 if( hasPostMachining( pad->Padstack() ) )
732 count++;
733 }
734
735 for( PCB_TRACK* track : aBoard->Tracks() )
736 {
737 if( track->Type() == PCB_VIA_T && hasPostMachining( static_cast<PCB_VIA*>( track )->Padstack() ) )
738 count++;
739 }
740
741 return count;
742}
743
744
745static void dropPostMachining( BOARD* aBoard )
746{
747 auto clear =
748 []( PADSTACK& aPadstack )
749 {
750 aPadstack.FrontPostMachining().mode.reset();
751 aPadstack.BackPostMachining().mode.reset();
752 };
753
754 for( FOOTPRINT* fp : aBoard->Footprints() )
756
757 for( PCB_TRACK* track : aBoard->Tracks() )
758 {
759 if( track->Type() == PCB_VIA_T )
760 clear( static_cast<PCB_VIA*>( track )->Padstack() );
761 }
762}
763
764
765static int countPressFitPads( const BOARD* aBoard )
766{
767 int count = 0;
768
769 for( PAD* pad : aBoard->GetPads() )
770 {
771 if( pad->GetProperty() == PAD_PROP::PRESSFIT )
772 count++;
773 }
774
775 return count;
776}
777
778
779static void dropPressFitPads( BOARD* aBoard )
780{
781 for( FOOTPRINT* fp : aBoard->Footprints() )
782 dropPressFitPadsFp( fp );
783}
784
785
786static int countBuriedVias( const BOARD* aBoard )
787{
788 int count = 0;
789
790 for( PCB_TRACK* track : aBoard->Tracks() )
791 {
792 if( track->Type() == PCB_VIA_T && static_cast<PCB_VIA*>( track )->GetViaType() == VIATYPE::BURIED )
793 count++;
794 }
795
796 return count;
797}
798
799
800// The old format had one blind type covering both, so a buried via becomes blind.
801static void lowerBuriedVias( BOARD* aBoard )
802{
803 for( PCB_TRACK* track : aBoard->Tracks() )
804 {
805 if( track->Type() == PCB_VIA_T )
806 {
807 PCB_VIA* via = static_cast<PCB_VIA*>( track );
808
809 if( via->GetViaType() == VIATYPE::BURIED )
810 via->SetViaType( VIATYPE::BLIND );
811 }
812 }
813}
814
815
816static int countZoneDefaults( const BOARD* aBoard )
817{
818 return aBoard->GetDesignSettings().m_ZoneLayerProperties.empty() ? 0 : 1;
819}
820
821
822static void dropZoneDefaults( BOARD* aBoard )
823{
825}
826
827
828static int countSkipVias( const BOARD* aBoard )
829{
830 int count = 0;
831
832 for( PCB_TRACK* track : aBoard->Tracks() )
833 {
834 if( track->Type() == PCB_VIA_T )
835 {
836 PCB_VIA* via = static_cast<PCB_VIA*>( track );
837
838 if( via->Padstack().UnconnectedLayerMode() == UNCONNECTED_LAYER_MODE::START_END_ONLY )
839 count++;
840 }
841 }
842
843 return count;
844}
845
846
847// Skip vias keep copper only on the start and end layers. The nearest older mode keeps the outer
848// rings and drops the inner ones, so use that.
849static void lowerSkipVias( BOARD* aBoard )
850{
851 for( PCB_TRACK* track : aBoard->Tracks() )
852 {
853 if( track->Type() != PCB_VIA_T )
854 continue;
855
856 PADSTACK& padstack = static_cast<PCB_VIA*>( track )->Padstack();
857
860 }
861}
862
863
864static int countDieDelays( const BOARD* aBoard )
865{
866 int count = 0;
867
868 for( PAD* pad : aBoard->GetPads() )
869 {
870 if( pad->GetPadToDieDelay() != 0 )
871 count++;
872 }
873
874 return count;
875}
876
877
878static void dropDieDelays( BOARD* aBoard )
879{
880 for( FOOTPRINT* fp : aBoard->Footprints() )
881 dropDieDelaysFp( fp );
882}
883
884
885static int countHatchedFills( const BOARD* aBoard, const std::function<bool( const BOARD_ITEM* )>& aMatches )
886{
887 int count = 0;
888
889 for( BOARD_ITEM* item : aBoard->Drawings() )
890 {
891 if( aMatches( item ) )
892 count++;
893 }
894
895 for( FOOTPRINT* fp : aBoard->Footprints() )
896 {
897 for( BOARD_ITEM* item : fp->GraphicalItems() )
898 {
899 if( aMatches( item ) )
900 count++;
901 }
902 }
903
904 return count;
905}
906
907
908static void setHatchedFills( BOARD* aBoard, const std::function<bool( const BOARD_ITEM* )>& aMatches, FILL_T aFill )
909{
910 for( BOARD_ITEM* item : aBoard->Drawings() )
911 {
912 if( aMatches( item ) )
913 static_cast<PCB_SHAPE*>( item )->SetFillMode( aFill );
914 }
915
916 for( FOOTPRINT* fp : aBoard->Footprints() )
917 setHatchedFillsFp( fp, aMatches, aFill );
918}
919
920
921static int countPcbPoints( const BOARD* aBoard )
922{
923 int count = static_cast<int>( aBoard->Points().size() );
924
925 for( FOOTPRINT* fp : aBoard->Footprints() )
926 count += static_cast<int>( fp->Points().size() );
927
928 return count;
929}
930
931
932static void dropPcbPoints( BOARD* aBoard )
933{
934 std::vector<BOARD_ITEM*> points( aBoard->Points().begin(), aBoard->Points().end() );
935
936 for( BOARD_ITEM* item : points )
937 {
938 aBoard->Remove( item );
939 delete item;
940 }
941
942 for( FOOTPRINT* fp : aBoard->Footprints() )
943 dropPcbPointsFp( fp );
944}
945
946
947static int countHighUserLayers( const BOARD* aBoard )
948{
949 const LSET& enabled = aBoard->GetEnabledLayers();
950 int count = 0;
951
952 // User_10 and up only exist since 20241229. Odd ids in this range are the user layers.
953 for( int layer = User_10; layer <= User_45; layer += 2 )
954 {
955 if( enabled.Contains( PCB_LAYER_ID( layer ) ) )
956 count++;
957 }
958
959 return count;
960}
961
962
963static bool hasViaProtection( const PCB_VIA* aVia )
964{
965 const PADSTACK& padstack = aVia->Padstack();
966
967 return padstack.Drill().is_filled.has_value()
968 || padstack.Drill().is_capped.has_value()
969 || padstack.FrontOuterLayers().has_covering.has_value()
970 || padstack.BackOuterLayers().has_covering.has_value()
971 || padstack.FrontOuterLayers().has_plugging.has_value()
972 || padstack.BackOuterLayers().has_plugging.has_value();
973}
974
975
976static int countViaProtection( const BOARD* aBoard )
977{
978 int count = 0;
979
980 for( PCB_TRACK* track : aBoard->Tracks() )
981 {
982 if( track->Type() == PCB_VIA_T && hasViaProtection( static_cast<PCB_VIA*>( track ) ) )
983 count++;
984 }
985
986 const BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings();
987
989 || bds.m_FillVias )
990 {
991 count++;
992 }
993
994 return count;
995}
996
997
998static void dropViaProtection( BOARD* aBoard )
999{
1000 for( PCB_TRACK* track : aBoard->Tracks() )
1001 {
1002 if( track->Type() != PCB_VIA_T )
1003 continue;
1004
1005 PADSTACK& padstack = static_cast<PCB_VIA*>( track )->Padstack();
1006
1007 padstack.Drill().is_filled.reset();
1008 padstack.Drill().is_capped.reset();
1009 padstack.FrontOuterLayers().has_covering.reset();
1010 padstack.BackOuterLayers().has_covering.reset();
1011 padstack.FrontOuterLayers().has_plugging.reset();
1012 padstack.BackOuterLayers().has_plugging.reset();
1013 }
1014
1016
1017 bds.m_CoverViasFront = false;
1018 bds.m_CoverViasBack = false;
1019 bds.m_PlugViasFront = false;
1020 bds.m_PlugViasBack = false;
1021 bds.m_CapVias = false;
1022 bds.m_FillVias = false;
1023}
1024
1025
1026static int countDielectricModels( const BOARD* aBoard )
1027{
1028 const BOARD_STACKUP& stackup = aBoard->GetDesignSettings().GetStackupDescriptor();
1029
1030 int count = 0;
1031
1032 for( BOARD_STACKUP_ITEM* item : stackup.GetList() )
1033 {
1034 if( item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
1035 continue;
1036
1037 for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
1038 {
1039 if( item->GetDielectricModel( idx ) != DIELECTRIC_MODEL::CONSTANT )
1040 count++;
1041 }
1042 }
1043
1044 return count;
1045}
1046
1047
1048static void dropDielectricModels( BOARD* aBoard )
1049{
1051
1052 for( BOARD_STACKUP_ITEM* item : stackup.GetList() )
1053 {
1054 if( item->GetType() != BS_ITEM_TYPE_DIELECTRIC )
1055 continue;
1056
1057 for( int idx = 0; idx < item->GetSublayersCount(); idx++ )
1058 item->SetDielectricModel( DIELECTRIC_MODEL::CONSTANT, idx );
1059 }
1060}
1061
1062
1063static int countGroupDesignBlocks( const BOARD* aBoard )
1064{
1065 int count = 0;
1066
1067 for( PCB_GROUP* group : aBoard->Groups() )
1068 {
1069 if( group->HasDesignBlockLink() )
1070 count++;
1071 }
1072
1073 for( FOOTPRINT* fp : aBoard->Footprints() )
1074 {
1075 for( PCB_GROUP* group : fp->Groups() )
1076 {
1077 if( group->HasDesignBlockLink() )
1078 count++;
1079 }
1080 }
1081
1082 return count;
1083}
1084
1085
1086static void dropGroupDesignBlocks( BOARD* aBoard )
1087{
1088 for( PCB_GROUP* group : aBoard->Groups() )
1089 group->SetDesignBlockLibId( LIB_ID() );
1090
1091 for( FOOTPRINT* fp : aBoard->Footprints() )
1093}
1094
1095
1096static int countScaledFootprints( const BOARD* aBoard )
1097{
1098 int count = 0;
1099
1100 for( FOOTPRINT* fp : aBoard->Footprints() )
1101 {
1102 if( !fp->GetTransform().HasUnitScale() )
1103 count++;
1104 }
1105
1106 return count;
1107}
1108
1109
1110static bool isRoundedRect( const BOARD_ITEM* aItem )
1111{
1112 if( aItem->Type() != PCB_SHAPE_T )
1113 return false;
1114
1115 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
1116 return shape->GetShape() == SHAPE_T::RECTANGLE && shape->GetCornerRadius() > 0;
1117}
1118
1119
1120static void roundedRectToPolygon( PCB_SHAPE* aShape, int aMaxError )
1121{
1122 int radius = aShape->GetCornerRadius();
1123
1124 SHAPE_POLY_SET poly;
1125 poly.NewOutline();
1126
1127 for( const VECTOR2I& corner : aShape->GetRectCorners() )
1128 poly.Append( corner );
1129
1130 poly = poly.Fillet( radius, aMaxError );
1131
1132 aShape->SetCornerRadius( 0 );
1133 aShape->SetShape( SHAPE_T::POLY );
1134 aShape->SetPolyShape( poly );
1135}
1136
1137
1138// Rounded rectangles can also live in custom pad primitives.
1139static void forEachPadPrimitive( FOOTPRINT* aFootprint, const std::function<void( PCB_SHAPE* )>& aFunc )
1140{
1141 for( PAD* pad : aFootprint->Pads() )
1142 {
1143 pad->Padstack().ForEachUniqueLayer(
1144 [&]( PCB_LAYER_ID aLayer )
1145 {
1146 for( const std::shared_ptr<PCB_SHAPE>& primitive : pad->GetPrimitives( aLayer ) )
1147 aFunc( primitive.get() );
1148 } );
1149 }
1150}
1151
1152
1153static int countRoundedRectsFp( const FOOTPRINT* aFootprint )
1154{
1155 int count = 0;
1156
1157 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1158 {
1159 if( isRoundedRect( item ) )
1160 count++;
1161 }
1162
1163 forEachPadPrimitive( const_cast<FOOTPRINT*>( aFootprint ),
1164 [&]( PCB_SHAPE* aPrimitive )
1165 {
1166 if( isRoundedRect( aPrimitive ) )
1167 count++;
1168 } );
1169
1170 return count;
1171}
1172
1173
1174static void lowerRoundedRectsFp( FOOTPRINT* aFootprint )
1175{
1176 int maxError = footprintMaxError( aFootprint );
1177
1178 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1179 {
1180 if( isRoundedRect( item ) )
1181 roundedRectToPolygon( static_cast<PCB_SHAPE*>( item ), maxError );
1182 }
1183
1184 forEachPadPrimitive( aFootprint,
1185 [&]( PCB_SHAPE* aPrimitive )
1186 {
1187 if( isRoundedRect( aPrimitive ) )
1188 roundedRectToPolygon( aPrimitive, maxError );
1189 } );
1190}
1191
1192
1193static int countRoundedRects( const BOARD* aBoard )
1194{
1195 int count = 0;
1196
1197 for( BOARD_ITEM* item : aBoard->Drawings() )
1198 {
1199 if( isRoundedRect( item ) )
1200 count++;
1201 }
1202
1203 for( FOOTPRINT* fp : aBoard->Footprints() )
1204 count += countRoundedRectsFp( fp );
1205
1206 return count;
1207}
1208
1209
1210static void lowerRoundedRects( BOARD* aBoard )
1211{
1212 int maxError = aBoard->GetDesignSettings().m_MaxError;
1213
1214 for( BOARD_ITEM* item : aBoard->Drawings() )
1215 {
1216 if( isRoundedRect( item ) )
1217 roundedRectToPolygon( static_cast<PCB_SHAPE*>( item ), maxError );
1218 }
1219
1220 for( FOOTPRINT* fp : aBoard->Footprints() )
1221 lowerRoundedRectsFp( fp );
1222}
1223
1224
1225static int countKnockoutTextBoxesFp( const FOOTPRINT* aFootprint )
1226{
1227 int count = 0;
1228
1229 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1230 {
1231 if( item->Type() == PCB_TEXTBOX_T && item->IsKnockout() )
1232 count++;
1233 }
1234
1235 return count;
1236}
1237
1238
1239static void dropKnockoutTextBoxesFp( FOOTPRINT* aFootprint )
1240{
1241 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1242 {
1243 if( item->Type() == PCB_TEXTBOX_T && item->IsKnockout() )
1244 item->SetIsKnockout( false );
1245 }
1246}
1247
1248
1249static int countKnockoutTextBoxes( const BOARD* aBoard )
1250{
1251 int count = 0;
1252
1253 for( BOARD_ITEM* item : aBoard->Drawings() )
1254 {
1255 if( item->Type() == PCB_TEXTBOX_T && item->IsKnockout() )
1256 count++;
1257 }
1258
1259 for( FOOTPRINT* fp : aBoard->Footprints() )
1260 count += countKnockoutTextBoxesFp( fp );
1261
1262 return count;
1263}
1264
1265
1266static void dropKnockoutTextBoxes( BOARD* aBoard )
1267{
1268 for( BOARD_ITEM* item : aBoard->Drawings() )
1269 {
1270 if( item->Type() == PCB_TEXTBOX_T && item->IsKnockout() )
1271 item->SetIsKnockout( false );
1272 }
1273
1274 for( FOOTPRINT* fp : aBoard->Footprints() )
1276}
1277
1278
1279static int countKnockoutCellsIn( const BOARD_ITEM* aItem )
1280{
1281 if( aItem->Type() != PCB_TABLE_T )
1282 return 0;
1283
1284 int count = 0;
1285
1286 for( PCB_TABLECELL* cell : static_cast<const PCB_TABLE*>( aItem )->GetCells() )
1287 {
1288 if( cell->IsKnockout() )
1289 count++;
1290 }
1291
1292 return count;
1293}
1294
1295
1296static int countKnockoutTableCells( const BOARD* aBoard )
1297{
1298 int count = 0;
1299
1300 for( BOARD_ITEM* item : aBoard->Drawings() )
1301 count += countKnockoutCellsIn( item );
1302
1303 for( FOOTPRINT* fp : aBoard->Footprints() )
1304 {
1305 for( BOARD_ITEM* item : fp->GraphicalItems() )
1306 count += countKnockoutCellsIn( item );
1307 }
1308
1309 return count;
1310}
1311
1312
1313static void dropKnockoutCellsIn( BOARD_ITEM* aItem )
1314{
1315 if( aItem->Type() != PCB_TABLE_T )
1316 return;
1317
1318 for( PCB_TABLECELL* cell : static_cast<PCB_TABLE*>( aItem )->GetCells() )
1319 {
1320 if( cell->IsKnockout() )
1321 cell->SetIsKnockout( false );
1322 }
1323}
1324
1325
1326static void dropKnockoutTableCellsFp( FOOTPRINT* aFootprint )
1327{
1328 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1329 dropKnockoutCellsIn( item );
1330}
1331
1332
1333static void dropKnockoutTableCells( BOARD* aBoard )
1334{
1335 for( BOARD_ITEM* item : aBoard->Drawings() )
1336 dropKnockoutCellsIn( item );
1337
1338 for( FOOTPRINT* fp : aBoard->Footprints() )
1340}
1341
1342
1343static int countZoneLayerProperties( const BOARD* aBoard )
1344{
1345 int count = 0;
1346
1347 for( ZONE* zone : aBoard->Zones() )
1348 {
1349 if( !zone->LayerProperties().empty() )
1350 count++;
1351 }
1352
1353 for( FOOTPRINT* fp : aBoard->Footprints() )
1354 {
1355 for( ZONE* zone : fp->Zones() )
1356 {
1357 if( !zone->LayerProperties().empty() )
1358 count++;
1359 }
1360 }
1361
1362 return count;
1363}
1364
1365
1366static void dropZoneLayerPropertiesFp( FOOTPRINT* aFootprint )
1367{
1368 for( ZONE* zone : aFootprint->Zones() )
1369 zone->SetLayerProperties( {} );
1370}
1371
1372
1373static void dropZoneLayerProperties( BOARD* aBoard )
1374{
1375 for( ZONE* zone : aBoard->Zones() )
1376 zone->SetLayerProperties( {} );
1377
1378 for( FOOTPRINT* fp : aBoard->Footprints() )
1380}
1381
1382
1383static bool hasGroupPlacementLinkage( const ZONE* aZone )
1384{
1385 return aZone->GetIsRuleArea()
1387 && ( aZone->GetPlacementAreaEnabled() || !aZone->GetPlacementAreaSource().IsEmpty() );
1388}
1389
1390
1391static int countGroupPlacementAreas( const BOARD* aBoard )
1392{
1393 int count = std::count_if( aBoard->Zones().begin(), aBoard->Zones().end(), hasGroupPlacementLinkage );
1394
1395 for( const FOOTPRINT* footprint : aBoard->Footprints() )
1396 count += std::count_if( footprint->Zones().begin(), footprint->Zones().end(), hasGroupPlacementLinkage );
1397
1398 return count;
1399}
1400
1401
1402static void dropGroupPlacementLinkage( ZONE* aZone )
1403{
1405 {
1406 aZone->SetPlacementAreaEnabled( false );
1407 aZone->SetPlacementAreaSource( wxEmptyString );
1409 }
1410}
1411
1412
1413static void dropGroupPlacementAreasFp( FOOTPRINT* aFootprint )
1414{
1415 for( ZONE* zone : aFootprint->Zones() )
1417}
1418
1419
1420static void dropGroupPlacementAreas( BOARD* aBoard )
1421{
1422 for( ZONE* zone : aBoard->Zones() )
1424
1425 for( FOOTPRINT* footprint : aBoard->Footprints() )
1426 dropGroupPlacementAreasFp( footprint );
1427}
1428
1429
1430static int countCustomFpStackups( const BOARD* aBoard )
1431{
1432 int count = 0;
1433
1434 for( FOOTPRINT* fp : aBoard->Footprints() )
1435 {
1436 if( fp->GetStackupMode() != FOOTPRINT_STACKUP::EXPAND_INNER_LAYERS )
1437 count++;
1438 }
1439
1440 return count;
1441}
1442
1443
1444// Omission is distinct from lowering: do not leave replacement polygons, plain knockout
1445// text, or electrically different vias behind when the user declines approximations.
1446static void omitGraphicsFp( FOOTPRINT* aFootprint, const std::function<bool( const BOARD_ITEM* )>& aMatches )
1447{
1448 std::vector<BOARD_ITEM*> remove;
1449
1450 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1451 {
1452 if( aMatches( item ) )
1453 remove.push_back( item );
1454 }
1455
1456 for( BOARD_ITEM* item : remove )
1457 {
1458 detachFromGroup( item );
1459 aFootprint->Remove( item );
1460 delete item;
1461 }
1462}
1463
1464
1465static void omitGraphics( BOARD* aBoard, const std::function<bool( const BOARD_ITEM* )>& aMatches )
1466{
1467 std::vector<BOARD_ITEM*> remove;
1468
1469 for( BOARD_ITEM* item : aBoard->Drawings() )
1470 {
1471 if( aMatches( item ) )
1472 remove.push_back( item );
1473 }
1474
1475 for( BOARD_ITEM* item : remove )
1476 {
1477 aBoard->Remove( item );
1478 delete item;
1479 }
1480
1481 for( FOOTPRINT* fp : aBoard->Footprints() )
1482 omitGraphicsFp( fp, aMatches );
1483}
1484
1485
1486static void omitPadPrimitives( FOOTPRINT* aFootprint, const std::function<bool( const BOARD_ITEM* )>& aMatches )
1487{
1488 for( PAD* pad : aFootprint->Pads() )
1489 {
1490 pad->Padstack().ForEachUniqueLayer(
1491 [&]( PCB_LAYER_ID aLayer )
1492 {
1493 std::erase_if( pad->Padstack().Primitives( aLayer ),
1494 [&]( const std::shared_ptr<PCB_SHAPE>& aPrimitive )
1495 {
1496 return aMatches( aPrimitive.get() );
1497 } );
1498 } );
1499 pad->SetDirty();
1500 }
1501}
1502
1503
1504static void omitEllipsesFp( FOOTPRINT* aFootprint )
1505{
1506 omitGraphicsFp( aFootprint, isEllipse );
1507 omitPadPrimitives( aFootprint, isEllipse );
1508}
1509static void omitEllipses( BOARD* aBoard )
1510{
1511 omitGraphics( aBoard, isEllipse );
1512
1513 for( FOOTPRINT* fp : aBoard->Footprints() )
1515}
1516
1517static bool isBarcode( const BOARD_ITEM* aItem )
1518{
1519 return aItem->Type() == PCB_BARCODE_T;
1520}
1521static void omitBarcodesFp( FOOTPRINT* aFootprint )
1522{
1523 omitGraphicsFp( aFootprint, isBarcode );
1524}
1525static void omitBarcodes( BOARD* aBoard )
1526{
1527 omitGraphics( aBoard, isBarcode );
1528}
1529
1530static bool isKnockoutTextBox( const BOARD_ITEM* aItem )
1531{
1532 return aItem->Type() == PCB_TEXTBOX_T && aItem->IsKnockout();
1533}
1534
1535static void omitKnockoutTextBoxesFp( FOOTPRINT* aFootprint )
1536{
1537 omitGraphicsFp( aFootprint, isKnockoutTextBox );
1538}
1539static void omitKnockoutTextBoxes( BOARD* aBoard )
1540{
1542}
1543
1544
1545static void omitRoundedRectsFp( FOOTPRINT* aFootprint )
1546{
1547 omitGraphicsFp( aFootprint, isRoundedRect );
1548 omitPadPrimitives( aFootprint, isRoundedRect );
1549}
1550
1551
1552static void omitRoundedRects( BOARD* aBoard )
1553{
1554 omitGraphics( aBoard, isRoundedRect );
1555
1556 for( FOOTPRINT* fp : aBoard->Footprints() )
1558}
1559
1560
1562{
1563 if( aItem->Type() != PCB_TABLE_T )
1564 return;
1565
1566 for( PCB_TABLECELL* cell : static_cast<PCB_TABLE*>( aItem )->GetCells() )
1567 {
1568 if( cell->IsKnockout() )
1569 {
1570 // Keep the supported grid structure, but not a plain-text approximation of the ink.
1571 cell->SetText( wxEmptyString );
1572 cell->SetIsKnockout( false );
1573 }
1574 }
1575}
1576
1577
1578static void omitKnockoutTableCellsFp( FOOTPRINT* aFootprint )
1579{
1580 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
1582}
1583
1584
1585static void omitKnockoutTableCells( BOARD* aBoard )
1586{
1587 for( BOARD_ITEM* item : aBoard->Drawings() )
1589
1590 for( FOOTPRINT* fp : aBoard->Footprints() )
1592}
1593
1594
1595static int countConstraints( const BOARD* aBoard )
1596{
1597 int count = aBoard->Constraints().size();
1598
1599 for( const FOOTPRINT* fp : aBoard->Footprints() )
1600 count += fp->Constraints().size();
1601
1602 return count;
1603}
1604
1605
1606static void dropConstraintsFp( FOOTPRINT* aFootprint )
1607{
1608 const auto constraints = aFootprint->Constraints();
1609
1610 for( PCB_CONSTRAINT* constraint : constraints )
1611 {
1612 detachFromGroup( constraint );
1613 aFootprint->Remove( constraint );
1614 delete constraint;
1615 }
1616}
1617
1618
1619static void dropConstraints( BOARD* aBoard )
1620{
1621 const auto constraints = aBoard->Constraints();
1622
1623 for( PCB_CONSTRAINT* constraint : constraints )
1624 {
1625 aBoard->Remove( constraint );
1626 delete constraint;
1627 }
1628
1629 for( FOOTPRINT* fp : aBoard->Footprints() )
1630 dropConstraintsFp( fp );
1631}
1632
1633
1634static bool isGrid( const BOARD_ITEM* aItem )
1635{
1636 return aItem->Type() == PCB_GRID_ITEM_T;
1637}
1638
1639
1640static bool isDrillDrawing( const BOARD_ITEM* aItem )
1641{
1642 return aItem->Type() == PCB_DRILL_CHART_T || aItem->Type() == PCB_DRILL_MAP_T;
1643}
1644
1645
1646static int countDrawings( const BOARD* aBoard, const std::function<bool( const BOARD_ITEM* )>& aMatches )
1647{
1648 return std::count_if( aBoard->Drawings().begin(), aBoard->Drawings().end(), aMatches );
1649}
1650
1651
1652static int countGrids( const BOARD* aBoard )
1653{
1654 return countDrawings( aBoard, isGrid );
1655}
1656
1657
1658static void dropGrids( BOARD* aBoard )
1659{
1660 omitGraphics( aBoard, isGrid );
1661}
1662
1663
1664static int countReferenceImageRotations( const BOARD* aBoard )
1665{
1666 const auto isRotatedReferenceImage = []( const BOARD_ITEM* aItem )
1667 {
1668 if( aItem->Type() != PCB_REFERENCE_IMAGE_T )
1669 return false;
1670
1671 const PCB_REFERENCE_IMAGE& refImage = static_cast<const PCB_REFERENCE_IMAGE&>( *aItem );
1672 return !refImage.GetReferenceImage().GetOrientation().IsZero();
1673 };
1674
1675 size_t count = std::count_if( aBoard->Drawings().begin(), aBoard->Drawings().end(), isRotatedReferenceImage );
1676
1677 for( FOOTPRINT* fp : aBoard->Footprints() )
1678 {
1679 count += std::count_if( fp->GraphicalItems().begin(), fp->GraphicalItems().end(), isRotatedReferenceImage );
1680 }
1681
1682 return static_cast<int>( count );
1683}
1684
1685
1686static int countReferenceImageOpacities( const BOARD* aBoard )
1687{
1688 const auto isReferenceImageWithOpacity = []( const BOARD_ITEM* aItem )
1689 {
1690 if( aItem->Type() != PCB_REFERENCE_IMAGE_T )
1691 return false;
1692
1693 const PCB_REFERENCE_IMAGE& refImage = static_cast<const PCB_REFERENCE_IMAGE&>( *aItem );
1694 return refImage.GetReferenceImage().GetOpacity() < 1.0;
1695 };
1696
1697 size_t count = std::count_if( aBoard->Drawings().begin(), aBoard->Drawings().end(), isReferenceImageWithOpacity );
1698
1699 for( FOOTPRINT* fp : aBoard->Footprints() )
1700 {
1701 count += std::count_if( fp->GraphicalItems().begin(), fp->GraphicalItems().end(), isReferenceImageWithOpacity );
1702 }
1703
1704 return static_cast<int>( count );
1705}
1706
1707
1709{
1710 const auto clearOpacity = []( BOARD_ITEM* aItem )
1711 {
1712 if( aItem->Type() == PCB_REFERENCE_IMAGE_T )
1713 static_cast<PCB_REFERENCE_IMAGE*>( aItem )->GetReferenceImage().SetOpacity( 1.0 );
1714 };
1715
1716 for( BOARD_ITEM* item : aBoard->Drawings() )
1717 clearOpacity( item );
1718
1719 for( FOOTPRINT* fp : aBoard->Footprints() )
1720 {
1721 for( BOARD_ITEM* item : fp->GraphicalItems() )
1722 clearOpacity( item );
1723 }
1724}
1725
1726
1727static int countDrillDrawings( const BOARD* aBoard )
1728{
1729 return countDrawings( aBoard, isDrillDrawing );
1730}
1731
1732
1733static void dropDrillDrawings( BOARD* aBoard )
1734{
1735 omitGraphics( aBoard, isDrillDrawing );
1736}
1737
1738
1739static int countDrillSymbolConfiguration( const BOARD* aBoard )
1740{
1741 return aBoard->GetDesignSettings().GetDrillSymbolProfile() == DRILL_SYMBOL_PROFILE() ? 0 : 1;
1742}
1743
1744
1749
1750
1751static int countGenerators( const BOARD* aBoard, const wxString& aType )
1752{
1753 return std::count_if( aBoard->Generators().begin(), aBoard->Generators().end(),
1754 [&]( const PCB_GENERATOR* aGenerator )
1755 {
1756 return aGenerator->GetGeneratorType() == aType;
1757 } );
1758}
1759
1760
1761static void dropGenerators( BOARD* aBoard, const std::function<bool( const PCB_GENERATOR* )>& aMatches )
1762{
1763 const auto generators = aBoard->Generators();
1764
1765 for( PCB_GENERATOR* generator : generators )
1766 {
1767 if( !aMatches( generator ) )
1768 continue;
1769
1770 EDA_GROUP* outer = generator->GetParentGroup();
1771 const auto members = generator->GetItems();
1772 generator->RemoveAll();
1773
1774 if( outer )
1775 {
1776 for( EDA_ITEM* member : members )
1777 outer->AddItem( member );
1778 }
1779
1780 aBoard->Remove( generator );
1781 delete generator;
1782 }
1783}
1784
1785
1786static void dropGenerators( BOARD* aBoard, const wxString& aType )
1787{
1788 dropGenerators( aBoard,
1789 [&]( const PCB_GENERATOR* aGenerator )
1790 {
1791 return aGenerator->GetGeneratorType() == aType;
1792 } );
1793}
1794
1795
1796static bool isTimeDomainTuning( const PCB_GENERATOR* aGenerator )
1797{
1798 const auto pattern = dynamic_cast<const PCB_TUNING_PATTERN*>( aGenerator );
1799 return pattern && !pattern->GetItems().empty() && pattern->GetSettings().m_isTimeDomain;
1800}
1801
1802
1803static int countTimeDomainTuning( const BOARD* aBoard )
1804{
1805 return std::count_if( aBoard->Generators().begin(), aBoard->Generators().end(), isTimeDomainTuning );
1806}
1807
1808
1809static void dropTimeDomainTuning( BOARD* aBoard )
1810{
1812}
1813
1814
1815static int countUnreviewedTuningProperties( const BOARD* aBoard )
1816{
1817 return std::count_if( aBoard->Generators().begin(), aBoard->Generators().end(),
1818 []( const PCB_GENERATOR* aGenerator )
1819 {
1820 if( aGenerator->GetGeneratorType() != wxT( "tuning_pattern" ) )
1821 return false;
1822
1823 return !dynamic_cast<const PCB_TUNING_PATTERN*>( aGenerator )
1824 || !KICAD_FORMAT::LEGACY::AreTuningPropertiesReviewed(
1825 aGenerator->GetProperties() );
1826 } );
1827}
1828
1829
1830static int countUnknownGenerators( const BOARD* aBoard )
1831{
1832 int count = 0;
1833
1834 for( PCB_GENERATOR* generator : aBoard->Generators() )
1835 {
1836 const wxString type = generator->GetGeneratorType();
1837
1838 if( type != wxT( "tuning_pattern" ) && type != wxT( "via_stitch" ) && type != wxT( "via_stack" ) )
1839 ++count;
1840
1841 for( EDA_ITEM* member : generator->GetItems() )
1842 {
1843 // Detached children would disappear when ownership metadata is removed.
1844 if( !dynamic_cast<BOARD_ITEM*>( member )
1845 || !aBoard->IsItemIndexedById( static_cast<BOARD_ITEM*>( member ) ) )
1846 {
1847 ++count;
1848 break;
1849 }
1850 }
1851 }
1852
1853 return count;
1854}
1855
1856
1857static bool isUnreviewedDrawing( const BOARD_ITEM* aItem )
1858{
1859 switch( aItem->Type() )
1860 {
1861 case PCB_SHAPE_T:
1862 case PCB_TEXT_T:
1863 case PCB_FIELD_T:
1864 case PCB_TEXTBOX_T:
1865 case PCB_TABLE_T:
1866 case PCB_DIM_ALIGNED_T:
1867 case PCB_DIM_CENTER_T:
1868 case PCB_DIM_RADIAL_T:
1870 case PCB_DIM_LEADER_T:
1872 case PCB_TARGET_T:
1873 case PCB_BARCODE_T:
1874 case PCB_GRID_ITEM_T:
1875 case PCB_DRILL_CHART_T:
1876 case PCB_DRILL_MAP_T: return false;
1877
1878 default: return true;
1879 }
1880}
1881
1882
1883static int countUnreviewedDrawings( const BOARD* aBoard )
1884{
1885 int count = countDrawings( aBoard, isUnreviewedDrawing );
1886
1887 for( FOOTPRINT* fp : aBoard->Footprints() )
1888 {
1889 count += std::count_if( fp->GraphicalItems().begin(), fp->GraphicalItems().end(), isUnreviewedDrawing );
1890 }
1891
1892 return count;
1893}
1894
1895
1896static void visitFootprintItems( const FOOTPRINT* aFootprint, const std::function<void( BOARD_ITEM* )>& aVisit )
1897{
1898 aVisit( const_cast<FOOTPRINT*>( aFootprint ) );
1899 aFootprint->RunOnChildren( aVisit, RECURSE_MODE::RECURSE );
1900}
1901
1902
1903static void visitBoardItems( const BOARD* aBoard, const std::function<void( BOARD_ITEM* )>& aVisit )
1904{
1905 aBoard->RunOnChildren( aVisit, RECURSE_MODE::RECURSE );
1906
1907 // Generators are omitted by BOARD::RunOnChildren.
1908 for( PCB_GENERATOR* generator : aBoard->Generators() )
1909 {
1910 aVisit( generator );
1911
1912 for( const auto& [name, item] : generator->GetTemplateItems() )
1913 {
1914 if( item )
1915 aVisit( const_cast<BOARD_ITEM*>( item ) );
1916 }
1917 }
1918}
1919
1920
1921static int countCustomProperties( const BOARD* aBoard )
1922{
1923 int count = 0;
1924 visitBoardItems( aBoard,
1925 [&]( BOARD_ITEM* aItem )
1926 {
1927 if( aItem && aItem->HasCustomProperties() )
1928 ++count;
1929 } );
1930 return count;
1931}
1932
1933
1934static void dropCustomPropertiesFp( FOOTPRINT* aFootprint )
1935{
1936 visitFootprintItems( aFootprint,
1937 []( BOARD_ITEM* aItem )
1938 {
1939 if( aItem )
1940 aItem->ClearCustomProperties();
1941 } );
1942}
1943
1944
1945static void dropCustomProperties( BOARD* aBoard )
1946{
1947 visitBoardItems( aBoard,
1948 []( BOARD_ITEM* aItem )
1949 {
1950 if( aItem )
1951 aItem->ClearCustomProperties();
1952 } );
1953}
1954
1955
1956static int countLinkedPlacementAreas( const BOARD* aBoard )
1957{
1958 int count = 0;
1959
1960 for( const ZONE* zone : aBoard->Zones() )
1961 {
1962 if( zone->GetLinkSet() != niluuid )
1963 ++count;
1964 }
1965
1966 return count;
1967}
1968
1969
1970static void dropLinkedPlacementAreas( BOARD* aBoard )
1971{
1972 for( ZONE* zone : aBoard->Zones() )
1973 {
1974 zone->SetLinkSet( niluuid );
1975 zone->SetRepeatOptions( REPEAT_LAYOUT_OPTIONS() );
1976 }
1977}
1978
1979
1980static int countSimulationExclusions( const BOARD* aBoard )
1981{
1982 int count = 0;
1983
1984 for( const FOOTPRINT* fp : aBoard->Footprints() )
1985 {
1986 bool excluded = fp->IsExcludedFromSim();
1987
1988 for( const auto& [name, variant] : fp->GetVariants() )
1989 excluded = excluded || variant.GetExcludedFromSim();
1990
1991 if( excluded )
1992 ++count;
1993 }
1994
1995 return count;
1996}
1997
1998
1999static void dropSimulationExclusionsFp( FOOTPRINT* aFootprint )
2000{
2001 aFootprint->SetExcludedFromSim( false );
2002
2003 for( const auto& [name, variant] : aFootprint->GetVariants() )
2004 aFootprint->GetVariant( name )->SetExcludedFromSim( false );
2005}
2006
2007
2008static void dropSimulationExclusions( BOARD* aBoard )
2009{
2010 for( FOOTPRINT* fp : aBoard->Footprints() )
2012}
2013
2014
2015static bool hasLineEndings( const BOARD_ITEM* aItem )
2016{
2017 if( !aItem || aItem->Type() != PCB_SHAPE_T )
2018 return false;
2019
2020 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
2023}
2024
2025
2026static bool unsafeLineEndings( const PCB_SHAPE* aShape )
2027{
2028 VECTOR2I start, end;
2029
2030 if( !aShape->GetLineEndingEndpoints( start, end ) )
2031 return false;
2032
2033 const auto unsafeStroke =
2034 []( const STROKE_PARAMS& aStroke )
2035 {
2036 return ( aStroke.GetLineStyle() != LINE_STYLE::DEFAULT && aStroke.GetLineStyle() != LINE_STYLE::SOLID )
2037 || aStroke.GetColor() != KIGFX::COLOR4D::UNSPECIFIED;
2038 };
2039
2040 // The native polygon helper does not preserve dash patterns or ending colors.
2041 return aShape->GetEffectiveWidth() <= 0
2042 || ( aShape->GetLineStyle() != LINE_STYLE::DEFAULT && aShape->GetLineStyle() != LINE_STYLE::SOLID )
2043 || unsafeStroke( aShape->GetStartEnding().GetStroke() )
2044 || unsafeStroke( aShape->GetEndEnding().GetStroke() );
2045}
2046
2047
2048static int countLineEndings( const BOARD* aBoard, bool aUnsafeOnly = false )
2049{
2050 int count = 0;
2051 visitBoardItems( aBoard,
2052 [&]( BOARD_ITEM* aItem )
2053 {
2054 if( hasLineEndings( aItem )
2055 && ( !aUnsafeOnly || unsafeLineEndings( static_cast<PCB_SHAPE*>( aItem ) ) ) )
2056 ++count;
2057 } );
2058 return count;
2059}
2060
2061
2062static void clearLineEndings( PCB_SHAPE* aShape )
2063{
2064 aShape->SetStartEnding( LINE_ENDING() );
2065 aShape->SetEndEnding( LINE_ENDING() );
2066}
2067
2068
2069static void lowerLineEndingShape( PCB_SHAPE* aShape, int aMaxError )
2070{
2071 VECTOR2I start, end;
2072
2073 if( !aShape->GetLineEndingEndpoints( start, end ) )
2074 {
2075 clearLineEndings( aShape );
2076 return;
2077 }
2078
2079 SHAPE_POLY_SET ink;
2080 aShape->TransformWithLineEndingsToPolygon( ink, 0, aMaxError, ERROR_INSIDE );
2081 ink.Simplify();
2082 ink.Fracture();
2083 EDA_GROUP* group = aShape->GetParentGroup();
2084 BOARD_ITEM_CONTAINER* parent = aShape->GetParent();
2085
2086 for( int outline = 0; outline < ink.OutlineCount(); ++outline )
2087 {
2088 PCB_SHAPE* polygon = outline == 0 ? aShape : new PCB_SHAPE( *aShape );
2089 SHAPE_POLY_SET poly;
2090 poly.AddOutline( ink.COutline( outline ) );
2091 polygon->SetShape( SHAPE_T::POLY );
2092 polygon->SetPolyShape( poly );
2093 polygon->SetWidth( 0 );
2095 polygon->SetLineStyle( LINE_STYLE::SOLID );
2096 clearLineEndings( polygon );
2097
2098 if( outline > 0 )
2099 {
2100 const_cast<KIID&>( polygon->m_Uuid ) = KIID();
2101 polygon->SetParentGroup( nullptr );
2102 parent->Add( polygon );
2103
2104 if( group )
2105 group->AddItem( polygon );
2106 }
2107 }
2108
2109 if( ink.OutlineCount() == 0 )
2110 clearLineEndings( aShape );
2111}
2112
2113
2114static void transformLineEndingsFp( FOOTPRINT* aFootprint, bool aOmit )
2115{
2116 std::vector<PCB_SHAPE*> shapes;
2117 visitFootprintItems( aFootprint,
2118 [&]( BOARD_ITEM* aItem )
2119 {
2120 if( hasLineEndings( aItem ) )
2121 shapes.push_back( static_cast<PCB_SHAPE*>( aItem ) );
2122 } );
2123
2124 for( PCB_SHAPE* shape : shapes )
2125 {
2126 if( aOmit )
2127 clearLineEndings( shape );
2128 else if( !unsafeLineEndings( shape ) )
2129 lowerLineEndingShape( shape, footprintMaxError( aFootprint ) );
2130 }
2131}
2132
2133
2134static void transformLineEndings( BOARD* aBoard, bool aOmit )
2135{
2136 std::vector<PCB_SHAPE*> shapes;
2137 visitBoardItems( aBoard,
2138 [&]( BOARD_ITEM* aItem )
2139 {
2140 if( hasLineEndings( aItem ) )
2141 shapes.push_back( static_cast<PCB_SHAPE*>( aItem ) );
2142 } );
2143
2144 for( PCB_SHAPE* shape : shapes )
2145 {
2146 if( aOmit )
2147 clearLineEndings( shape );
2148 else if( !unsafeLineEndings( shape ) )
2150 }
2151}
2152
2153
2154static int titleFieldIndex( const wxString& aName )
2155{
2156 if( aName == wxT( "TITLE" ) )
2157 return 0;
2158 if( aName == wxT( "ISSUE_DATE" ) )
2159 return 1;
2160 if( aName == wxT( "REVISION" ) )
2161 return 2;
2162 if( aName == wxT( "COMPANY" ) )
2163 return 3;
2164 if( aName.length() == 8 && aName.StartsWith( wxT( "COMMENT" ) ) && aName[7] >= '1' && aName[7] <= '9' )
2165 return 4 + aName[7].GetValue() - '1';
2166 return -1;
2167}
2168
2169
2170static const wxString& titleFieldText( const TITLE_BLOCK& aTitle, int aIndex )
2171{
2172 switch( aIndex )
2173 {
2174 case 0: return aTitle.GetTitle();
2175 case 1: return aTitle.GetDate();
2176 case 2: return aTitle.GetRevision();
2177 case 3: return aTitle.GetCompany();
2178 default: return aTitle.GetComment( aIndex - 4 );
2179 }
2180}
2181
2182
2183static bool postTargetText( const wxString& aText, const BOARD* aBoard, int aTargetVersion,
2184 std::set<wxString>& aDependencies, int aDepth = 0 )
2185{
2186 if( aDepth > 32 )
2187 return true;
2188
2189 for( size_t pos = 0; pos + 1 < aText.length(); ++pos )
2190 {
2191 if( aText[pos] == '\\'
2192 && pos + 2 < aText.length()
2193 && ( aText[pos + 1] == '$' || aText[pos + 1] == '@' )
2194 && aText[pos + 2] == '{' )
2195 {
2196 if( aTargetVersion <= PCB_WRITER_V9::FORMAT_VERSION )
2197 return true;
2198
2199 int braces = 1;
2200 pos += 3;
2201
2202 for( ; pos < aText.length() && braces; ++pos )
2203 braces += aText[pos] == '{' ? 1 : ( aText[pos] == '}' ? -1 : 0 );
2204
2205 --pos;
2206 continue;
2207 }
2208
2209 if( aText[pos] == '@'
2210 && aText[pos + 1] == '{'
2211 && ( aTargetVersion <= PCB_WRITER_V9::FORMAT_VERSION || aText.Mid( pos ).Contains( wxT( "mils" ) ) ) )
2212 {
2213 return true;
2214 }
2215
2216 if( aText[pos] == '$'
2217 && aText[pos + 1] == '{'
2218 && aText.Mid( pos + 2 ).BeforeFirst( '}' ).AfterFirst( ':' ).StartsWith( wxT( "PROPERTY." ) ) )
2219 {
2220 return true;
2221 }
2222 }
2223
2224 for( const auto& reference : ExtractTextVarReferences( aText ) )
2225 {
2226 const wxString& name = reference.primary;
2227
2228 if( name.StartsWith( wxT( "PROPERTY." ) )
2229 || reference.secondary.StartsWith( wxT( "PROPERTY." ) )
2230 || name == wxT( "DRILL_OPERATIONS" )
2231 || name == wxT( "DRILL_SITES" )
2232 || name == wxT( "DRILL_GROUPS" ) )
2233 {
2234 return true;
2235 }
2236
2237 if( aBoard && titleFieldIndex( name ) >= 0 )
2238 {
2239 wxString resolved = name;
2240
2241 if( aBoard->GetTitleBlock().TextVarResolver( &resolved, aBoard->GetProject(), INTERNAL ) )
2242 {
2244
2245 if( aDependencies.insert( name ).second
2246 && postTargetText( resolved, aBoard, aTargetVersion, aDependencies, aDepth + 1 ) )
2247 return true;
2248
2249 continue;
2250 }
2251 }
2252
2253 if( aBoard )
2254 {
2255 const wxString* value = nullptr;
2256
2257 if( aBoard->GetProject() )
2258 {
2259 const std::map<wxString, wxString>& variables = aBoard->GetProject()->GetTextVars();
2260 auto variable = variables.find( name );
2261
2262 if( variable != variables.end() )
2263 value = &variable->second;
2264 }
2265
2266 if( !value )
2267 {
2268 const std::map<wxString, wxString>& properties = aBoard->GetProperties();
2269 auto property = properties.find( name );
2270
2271 if( property != properties.end() )
2272 value = &property->second;
2273 }
2274
2275 if( value
2276 && aDependencies.insert( name ).second
2277 && postTargetText( *value, aBoard, aTargetVersion, aDependencies, aDepth + 1 ) )
2278 {
2279 return true;
2280 }
2281 }
2282 }
2283
2284 return false;
2285}
2286
2287
2288static bool postTargetText( const wxString& aText, const BOARD* aBoard, int aTargetVersion )
2289{
2290 std::set<wxString> dependencies;
2291 return postTargetText( aText, aBoard, aTargetVersion, dependencies );
2292}
2293
2294
2296{
2297 std::vector<std::pair<BOARD_ITEM*, wxString>> m_replacements;
2298 std::vector<std::pair<int, wxString>> m_titleReplacements;
2299 BOARD* m_board = nullptr;
2300 int m_blocked = 0;
2301};
2302
2303
2304static bool hasActiveExpression( const wxString& aText )
2305{
2306 for( size_t pos = 0; pos + 1 < aText.length(); ++pos )
2307 {
2308 if( aText[pos] == '\\' && pos + 2 < aText.length() && ( aText[pos + 1] == '$' || aText[pos + 1] == '@' )
2309 && aText[pos + 2] == '{' )
2310 {
2311 int braces = 1;
2312 pos += 3;
2313
2314 for( ; pos < aText.length() && braces; ++pos )
2315 braces += aText[pos] == '{' ? 1 : ( aText[pos] == '}' ? -1 : 0 );
2316
2317 --pos;
2318 }
2319 else if( aText[pos] == '@' && aText[pos + 1] == '{' )
2320 {
2321 return true;
2322 }
2323 }
2324
2325 return false;
2326}
2327
2328
2329static void planTextItem( BOARD_ITEM* aItem, int aTargetVersion, TEXT_DOWNGRADE_PLAN& aPlan,
2330 const std::function<bool( wxString* )>* aResolver = nullptr )
2331{
2332 if( !aItem )
2333 return;
2334
2335 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
2336 PCB_BARCODE* barcode = dynamic_cast<PCB_BARCODE*>( aItem );
2337 PCB_DIMENSION_BASE* dimension = dynamic_cast<PCB_DIMENSION_BASE*>( aItem );
2338
2339 if( !text && !barcode )
2340 return;
2341
2342 if( dimension && !dimension->GetOverrideTextEnabled() )
2343 return;
2344
2345 const wxString raw = dimension ? dimension->GetOverrideText() : ( text ? text->GetText() : barcode->GetText() );
2346 BOARD* board = aItem->GetBoard();
2347
2348 if( !postTargetText( raw, board, aTargetVersion ) )
2349 return;
2350
2351 std::function<bool( wxString* )> resolver =
2352 [&]( wxString* aToken )
2353 {
2354 if( aResolver )
2355 return ( *aResolver )( aToken );
2356
2357 if( *aToken == wxT( "LAYER" ) && board )
2358 {
2359 *aToken = board->GetLayerName( aItem->GetLayer() );
2360 return true;
2361 }
2362
2363 FOOTPRINT* fp = aItem->GetParentFootprint();
2364 return ( fp && fp->ResolveTextVar( aToken, 0 ) ) || ( board && board->ResolveTextVar( aToken, 0 ) );
2365 };
2366
2367 wxString result;
2368 bool blocked = false;
2369
2370 for( size_t pos = 0; pos < raw.length(); )
2371 {
2372 size_t start = pos;
2373 bool escaped = raw[pos] == '\\'
2374 && pos + 2 < raw.length()
2375 && ( raw[pos + 1] == '$' || raw[pos + 1] == '@' )
2376 && raw[pos + 2] == '{';
2377 size_t marker = escaped ? pos + 1 : pos;
2378
2379 if( marker + 1 >= raw.length() || ( raw[marker] != '$' && raw[marker] != '@' ) || raw[marker + 1] != '{' )
2380 {
2381 result += raw[pos++];
2382 continue;
2383 }
2384
2385 int braces = 1;
2386 pos = marker + 2;
2387
2388 while( pos < raw.length() && braces )
2389 {
2390 braces += raw[pos] == '{' ? 1 : ( raw[pos] == '}' ? -1 : 0 );
2391 ++pos;
2392 }
2393
2394 wxString block = raw.Mid( start, pos - start );
2395
2396 if( escaped || !postTargetText( block, board, aTargetVersion ) )
2397 {
2398 result += block;
2399 continue;
2400 }
2401
2402 if( braces )
2403 {
2404 blocked = true;
2405 break;
2406 }
2407
2408 if( !aResolver )
2409 {
2410 // Resolve only the affected token using the native item-context semantics.
2411 if( auto cell = dynamic_cast<PCB_TABLECELL*>( aItem ) )
2412 {
2413 if( cell->GetRow() < 0 || cell->GetColumn() < 0 )
2414 {
2415 blocked = true;
2416 break;
2417 }
2418
2419 PCB_TABLE table( *static_cast<PCB_TABLE*>( cell->GetParent() ) );
2420 PCB_TABLECELL* temporary = table.GetCell( cell->GetRow(), cell->GetColumn() );
2421
2422 if( !temporary )
2423 {
2424 blocked = true;
2425 break;
2426 }
2427
2428 temporary->SetText( block );
2429 block = temporary->GetUnwrappedShownText( INTERNAL );
2430 }
2431 else
2432 {
2433 PCB_TEXT temporary( aItem->GetParent() );
2434 temporary.SetLayer( aItem->GetLayer() );
2435 temporary.SetText( block );
2436 block = temporary.GetShownText( INTERNAL );
2437 }
2438
2439 if( hasActiveExpression( block ) )
2440 blocked = true;
2441 }
2442 else
2443 {
2444 for( int depth = 0; depth < 32; ++depth )
2445 {
2446 wxString expanded = ExpandTextVars( block, &resolver, INTERNAL );
2447
2448 if( hasActiveExpression( expanded ) )
2449 {
2450 EXPRESSION_EVALUATOR evaluator;
2451 expanded = evaluator.Evaluate( expanded );
2452
2453 if( evaluator.HasErrors() )
2454 {
2455 blocked = true;
2456 break;
2457 }
2458 }
2459
2460 if( expanded == block )
2461 break;
2462
2463 block = expanded;
2464 }
2465
2466 if( hasActiveExpression( block ) )
2467 blocked = true;
2468 }
2469
2471
2472 if( postTargetText( block, board, aTargetVersion )
2473 || block.Contains( wxT( "<Unresolved:" ) )
2474 || block.Contains( wxT( "<Unknown reference:" ) ) )
2475 {
2476 blocked = true;
2477 }
2478
2479 result += block;
2480 }
2481
2482 if( aTargetVersion <= PCB_WRITER_V9::FORMAT_VERSION
2483 && ( result.Contains( wxT( "\\${" ) ) || result.Contains( wxT( "\\@{" ) ) ) )
2484 blocked = true;
2485
2486 if( blocked )
2487 ++aPlan.m_blocked;
2488 else if( result != raw )
2489 aPlan.m_replacements.emplace_back( aItem, result );
2490}
2491
2492
2493static TEXT_DOWNGRADE_PLAN planBoardText( const BOARD* aBoard, int aTargetVersion )
2494{
2496 plan.m_board = const_cast<BOARD*>( aBoard );
2497 visitBoardItems( aBoard,
2498 [&]( BOARD_ITEM* aItem )
2499 {
2500 planTextItem( aItem, aTargetVersion, plan );
2501 } );
2502
2503 std::function<bool( wxString* )> resolver = [&]( wxString* aToken )
2504 {
2505 if( *aToken == wxT( "FILENAME" )
2506 || *aToken == wxT( "FILEPATH" )
2507 || *aToken == wxT( "VARIANT" )
2508 || *aToken == wxT( "VARIANT_DESC" ) )
2509 {
2510 return aBoard->ResolveTextVar( aToken, 0 );
2511 }
2512
2513 return aBoard->GetTitleBlock().TextVarResolver( aToken, aBoard->GetProject(), INTERNAL )
2514 || ( aBoard->GetProject() && aBoard->GetProject()->TextVarResolver( aToken ) );
2515 };
2516
2517 // Worksheet metadata has no footprint context or fixed plotted layer.
2518 for( int field = 0; field < 13; ++field )
2519 {
2520 PCB_TEXT temporary( const_cast<BOARD*>( aBoard ) );
2521 temporary.SetText( titleFieldText( aBoard->GetTitleBlock(), field ) );
2522 TEXT_DOWNGRADE_PLAN title;
2523 planTextItem( &temporary, aTargetVersion, title, &resolver );
2524 plan.m_blocked += title.m_blocked;
2525
2526 if( !title.m_replacements.empty() )
2527 plan.m_titleReplacements.emplace_back( field, title.m_replacements.front().second );
2528 }
2529
2530 return plan;
2531}
2532
2533
2534static void applyTextPlan( const TEXT_DOWNGRADE_PLAN& aPlan )
2535{
2536 for( const auto& [item, value] : aPlan.m_replacements )
2537 {
2538 if( auto dimension = dynamic_cast<PCB_DIMENSION_BASE*>( item ) )
2539 dimension->ChangeOverrideText( value );
2540 else if( auto text = dynamic_cast<EDA_TEXT*>( item ) )
2541 text->SetText( value );
2542 else if( auto barcode = dynamic_cast<PCB_BARCODE*>( item ) )
2543 barcode->SetBarcodeText( value );
2544 }
2545
2546 for( const auto& [field, value] : aPlan.m_titleReplacements )
2547 {
2548 TITLE_BLOCK& title = aPlan.m_board->GetTitleBlock();
2549
2550 switch( field )
2551 {
2552 case 0: title.SetTitle( value ); break;
2553 case 1: title.SetDate( value ); break;
2554 case 2: title.SetRevision( value ); break;
2555 case 3: title.SetCompany( value ); break;
2556 default: title.SetComment( field - 4, value ); break;
2557 }
2558 }
2559}
2560
2561
2562// One row per feature. The report and the transform both walk this list, so they cannot drift.
2563// To add a feature, add a row. A null apply means the feature blocks the export.
2565{
2568 wxString m_feature;
2569 wxString m_detail;
2570 std::function<int( const BOARD* )> m_count;
2571 std::function<void( BOARD* )> m_apply;
2572 std::function<void( FOOTPRINT* )> m_applyFp = {};
2573 std::function<void( BOARD* )> m_omit = {};
2574 std::function<void( FOOTPRINT* )> m_omitFp = {};
2575 wxString m_omitDetail = {};
2576
2580};
2581
2582
2583static std::vector<BOARD_RULE> boardRules()
2584{
2585 // clang-format off
2586 return {
2587 { VER_CONSTRAINTS, DOWNGRADE_BUCKET::BLOCK, _( "Unreviewed generated objects" ),
2588 _( "Unknown generator kinds or detached generated children cannot be safely exported." ),
2589 countUnknownGenerators, nullptr },
2590 { VER_CONSTRAINTS, DOWNGRADE_BUCKET::BLOCK, _( "Unreviewed tuning properties" ),
2591 _( "The tuning generator contains properties outside the reviewed historical schemas." ),
2593 { VER_TIME_DOMAIN_TUNING, DOWNGRADE_BUCKET::DROP, _( "Time-domain tuning patterns" ),
2594 _( "Generator editability is removed. Existing generated copper and group membership are kept." ),
2596 { VER_GROUP_PLACEMENT, DOWNGRADE_BUCKET::DROP, _( "Group placement areas" ),
2597 _( "Group placement linkage is removed. Zone geometry, keepout settings, groups, and copper are kept." ),
2599 { VER_CONSTRAINTS, DOWNGRADE_BUCKET::BLOCK, _( "Unreviewed board graphics" ),
2600 _( "An unrecognized graphic item cannot be safely represented by the target writer." ),
2601 countUnreviewedDrawings, nullptr },
2602 { VER_CONSTRAINTS, DOWNGRADE_BUCKET::DROP, _( "Geometric constraints" ),
2603 _( "Solver relationships are removed. Existing geometry is kept in its current position." ), countConstraints,
2605 { VER_GRIDS, DOWNGRADE_BUCKET::DROP, _( "Local grids" ),
2606 _( "Placement, snapping, and routing grid configuration is removed. Copper is unchanged." ), countGrids,
2607 dropGrids },
2608 { VER_VIA_STITCH, DOWNGRADE_BUCKET::DROP, _( "Via stitching and guarding" ),
2609 _( "Generator editability is removed. Existing generated copper and group membership are kept." ),
2610 []( const BOARD* b )
2611 {
2612 return countGenerators( b, wxT( "via_stitch" ) );
2613 },
2614 []( BOARD* b )
2615 {
2616 dropGenerators( b, wxT( "via_stitch" ) );
2617 } },
2618 { VER_VIA_STACK, DOWNGRADE_BUCKET::DROP, _( "Microvia stack generators" ),
2619 _( "Generator editability is removed. Existing generated copper and group membership are kept." ),
2620 []( const BOARD* b )
2621 {
2622 return countGenerators( b, wxT( "via_stack" ) );
2623 },
2624 []( BOARD* b )
2625 {
2626 dropGenerators( b, wxT( "via_stack" ) );
2627 } },
2628 { VER_LINE_ENDINGS, DOWNGRADE_BUCKET::LOWER, _( "Graphic line endings" ),
2629 _( "Converted to printed polygons with the shortened line body. Ending editability is lost." ),
2630 []( const BOARD* b )
2631 {
2632 return countLineEndings( b );
2633 },
2634 []( BOARD* b )
2635 {
2636 transformLineEndings( b, false );
2637 },
2638 []( FOOTPRINT* f )
2639 {
2640 transformLineEndingsFp( f, false );
2641 },
2642 []( BOARD* b )
2643 {
2644 transformLineEndings( b, true );
2645 },
2646 []( FOOTPRINT* f )
2647 {
2648 transformLineEndingsFp( f, true );
2649 },
2650 _( "Endpoint embellishments are removed. The supported line body and its style are kept." ) },
2651 { VER_SIM_EXCLUSION, DOWNGRADE_BUCKET::DROP, _( "Footprint simulation exclusions" ),
2652 _( "Simulation exclusions are removed. The target may include these footprints in simulation." ),
2654 { VER_CUSTOM_PROPERTIES, DOWNGRADE_BUCKET::DROP, _( "Custom properties" ),
2655 _( "Custom item metadata is removed after affected displayed text has been resolved." ),
2657 { VER_DRILL_DRAWINGS, DOWNGRADE_BUCKET::DROP, _( "Drill charts and maps" ),
2658 _( "Drill chart tables and drill map symbols are removed from the fabrication drawings. Holes are kept." ),
2660 { VER_DRILL_DRAWINGS, DOWNGRADE_BUCKET::DROP, _( "Drill symbol configuration" ),
2661 _( "Drill symbol assignments and display settings are removed. Holes are unchanged." ),
2663 { VER_LINKED_PLACEMENT, DOWNGRADE_BUCKET::DROP, _( "Linked placement rule areas" ),
2664 _( "Links between placement rule areas and their saved repeat layout options are removed. "
2665 "The copied layouts are kept." ),
2667 { VER_IMAGE_ROTATION, DOWNGRADE_BUCKET::BLOCK, _( "Rotated reference images" ),
2668 _( "The target cannot store an image rotation." ),
2670 { VER_IMAGE_OPACITY, DOWNGRADE_BUCKET::DROP, _( "Reference image opacity" ),
2671 _( "The target cannot store an image opacity. Opacity will be ignored." ),
2673 { VER_NET_CHAINS, DOWNGRADE_BUCKET::DROP, _( "Net chains" ),
2674 _( "No equivalent in the target. Connectivity will change." ),
2676 { VER_ELLIPSE, DOWNGRADE_BUCKET::LOWER, _( "Ellipse graphics" ),
2677 _( "Approximated by a polygon." ),
2679 _( "Ellipse graphics are omitted instead of approximated." ) },
2680 { VER_EXTRUDED_BODY, DOWNGRADE_BUCKET::DROP, _( "Extruded 3D bodies" ),
2681 _( "No equivalent in the target. The 3D body is left out." ),
2683 { VER_FOOTPRINT_HEIGHT, DOWNGRADE_BUCKET::DROP, _( "Footprint overall height" ),
2684 _( "No equivalent in the target. The footprint height is left out." ),
2686 { VER_THIEVING, DOWNGRADE_BUCKET::BLOCK, _( "Copper-thieving zones" ),
2687 _( "A refill in the target would turn the pattern into solid copper." ),
2688 countThievingZones, nullptr },
2689 { VER_PAD_SIM, DOWNGRADE_BUCKET::DROP, _( "Pad simulation types" ),
2690 _( "The simulation electrical type is left out." ),
2692 { VER_FP_UNITS, DOWNGRADE_BUCKET::DROP, _( "Footprint unit metadata" ),
2693 _( "Pin-to-unit assignments used for gate swapping are removed." ),
2695 { VER_BARCODE, DOWNGRADE_BUCKET::LOWER, _( "Barcodes" ),
2696 _( "Converted to the printed polygons. No longer editable as a barcode." ),
2698 _( "Barcodes are omitted instead of converted to polygons." ) },
2699 { FIRST_PCB_VARIANTS, DOWNGRADE_BUCKET::DROP, _( "PCB variants" ),
2700 _( "Variant registry and per-footprint overrides are removed." ),
2702 { VER_JUMPER_PADS, DOWNGRADE_BUCKET::DROP, _( "Jumper pad groups" ),
2703 _( "Jumper pad groupings are removed." ),
2705 { VER_BACKDRILL, DOWNGRADE_BUCKET::BLOCK, _( "Backdrill / tertiary drill" ),
2706 _( "Backdrilling changes copper connectivity and cannot be represented." ),
2707 countBackdrills, nullptr },
2708 { VER_POST_MACHINING, DOWNGRADE_BUCKET::DROP, _( "Counterbore / countersink" ),
2709 _( "Drill post-machining is removed." ), countPostMachining, dropPostMachining, dropPostMachiningFp },
2710 { VER_PRESSFIT, DOWNGRADE_BUCKET::DROP, _( "Press-fit pads" ),
2711 _( "The press-fit fabrication property is removed." ),
2713 { VER_SKIP_VIA, DOWNGRADE_BUCKET::LOWER, _( "Skip vias" ),
2714 _( "Approximated by keeping only the start and end layers." ),
2717 {},
2718 {},
2719 {},
2720 {},
2721 true },
2722 { VER_VIA_SPLIT, DOWNGRADE_BUCKET::LOWER, _( "Buried vias" ),
2723 _( "Written with the older combined blind type." ),
2726 {},
2727 {},
2728 {},
2729 {},
2730 true },
2731 { VER_ZONE_DEFAULTS, DOWNGRADE_BUCKET::DROP, _( "Zone layer defaults" ),
2732 _( "Per-layer zone defaults are removed." ),
2734 { VER_PAD_DIE_DELAY, DOWNGRADE_BUCKET::DROP, _( "Pad-to-die delays" ),
2735 _( "Pad-to-die delay is removed." ),
2737 { VER_SHAPE_HATCH, DOWNGRADE_BUCKET::LOWER, _( "Hatched shape fills" ),
2738 _( "Converted to a solid fill." ),
2739 []( const BOARD* b )
2740 {
2742 },
2743 []( BOARD* b )
2744 {
2746 },
2747 []( FOOTPRINT* f )
2748 {
2750 },
2751 []( BOARD* b )
2752 {
2754 },
2755 []( FOOTPRINT* f )
2756 {
2758 },
2759 _( "The hatch fill is omitted. The supported outline is kept." ) },
2760 { VER_SHAPE_HATCH, DOWNGRADE_BUCKET::LOWER, _( "Hatched copper fills" ),
2761 _( "Converted to a solid fill. Copper geometry may change." ),
2762 []( const BOARD* b )
2763 {
2765 },
2766 []( BOARD* b )
2767 {
2769 },
2770 []( FOOTPRINT* f )
2771 {
2773 },
2774 {},
2775 {},
2776 {},
2777 true },
2778 { VER_PCB_POINTS, DOWNGRADE_BUCKET::DROP, _( "PCB points" ),
2779 _( "No equivalent in the target. Points are removed." ),
2781 { VER_VIA_PROTECTION, DOWNGRADE_BUCKET::DROP, _( "Via protection" ),
2782 _( "IPC-4761 covering, plugging, capping, and filling are removed." ),
2784 { VER_DIELECTRIC, DOWNGRADE_BUCKET::DROP, _( "Dielectric frequency models" ),
2785 _( "The frequency-dependent dielectric model is removed." ),
2787 { VER_GROUP_LIB_ID, DOWNGRADE_BUCKET::DROP, _( "Group design block links" ),
2788 _( "The link from a group to its design block is removed." ),
2790 { VER_TEXTBOX_KNOCKOUT, DOWNGRADE_BUCKET::LOWER, _( "Knockout text boxes" ),
2791 _( "Drawn as plain text boxes." ),
2794 _( "Knockout text boxes are omitted instead of drawn as plain text." ) },
2795 { VER_ROUNDED_RECT, DOWNGRADE_BUCKET::LOWER, _( "Rounded rectangles" ),
2796 _( "Approximated by a polygon." ),
2798 _( "Rounded rectangles and rounded custom pad primitives are omitted. Copper geometry may change." ) },
2799 { VER_CELL_KNOCKOUT, DOWNGRADE_BUCKET::LOWER, _( "Knockout table cells" ),
2800 _( "Drawn as plain cells." ),
2803 _( "Knockout cell contents are omitted; empty cells and the table grid are kept." ) },
2804 { VER_ZONE_DEFAULTS, DOWNGRADE_BUCKET::DROP, _( "Per-zone layer properties" ),
2805 _( "The per-layer overrides are removed." ),
2807 { VER_USER_LAYERS, DOWNGRADE_BUCKET::BLOCK, _( "User layers past User_9" ),
2808 _( "The target supports only 9 user layers." ),
2809 countHighUserLayers, nullptr },
2810 { FIRST_FP_CUSTOM_STACKUP, DOWNGRADE_BUCKET::BLOCK, _( "Custom footprint stackups" ),
2811 _( "The target would expand the footprint's inner layers and change its connectivity." ),
2812 countCustomFpStackups, nullptr },
2813 { FIRST_FP_AFFINE_TRANSFORM, DOWNGRADE_BUCKET::BLOCK, _( "Scaled footprints" ),
2814 _( "The target cannot represent a scaled footprint." ),
2815 countScaledFootprints, nullptr },
2816 };
2817 // clang-format on
2818}
2819
2820
2822 bool aDropInsteadOfApproximate )
2823{
2824 // The rules were written against a specific format. Warn developers when the format has
2825 // moved on, since a frozen writer silently omits features that have no rule yet.
2827 wxT( "Board format changed. Review the downgrade rules and denylist, then update "
2828 "BOARD_DOWNGRADE_COVERED." ) );
2829
2830 COMPATIBILITY_REPORT report;
2831
2833 {
2834 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Unreviewed board format" ),
2835 _( "The current board format has changed since the downgrade rules were reviewed." ), 1 );
2836 }
2837
2838 if( aBoard->GetPlotOptions().GetFormat() == PLOT_FORMAT::PNG )
2839 {
2840 report.Add( DOWNGRADE_BUCKET::DROP, _( "PNG plot selection" ),
2841 _( "The unsupported plot format is reset to Gerber." ), 1 );
2842 }
2843
2845 {
2846 const TEXT_DOWNGRADE_PLAN text = planBoardText( aBoard, aTarget.m_boardVersion );
2847
2848 if( text.m_blocked )
2849 {
2850 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Unresolved post-target text" ),
2851 _( "Affected property variables or expressions could not be safely resolved." ),
2852 text.m_blocked );
2853 }
2854
2855 if( !text.m_replacements.empty() || !text.m_titleReplacements.empty() )
2856 {
2857 report.Add( DOWNGRADE_BUCKET::DROP, _( "Post-target text variables" ),
2858 _( "Affected displayed text is resolved to its current value and loses dynamic evaluation." ),
2859 text.m_replacements.size() + text.m_titleReplacements.size() );
2860 }
2861 }
2862
2863 if( aTarget.m_boardVersion < VER_LINE_ENDINGS && !aDropInsteadOfApproximate )
2864 {
2865 if( int unsafe = countLineEndings( aBoard, true ); unsafe > 0 )
2866 {
2867 report.Add( DOWNGRADE_BUCKET::BLOCK, _( "Styled graphic line endings" ),
2868 _( "Dashed, colored, or unresolved-width line endings cannot be faithfully converted. "
2869 "The omission policy can remove just the endpoint embellishments." ),
2870 unsafe );
2871 }
2872 }
2873
2874 for( const BOARD_RULE& rule : boardRules() )
2875 {
2876 if( aTarget.m_boardVersion >= rule.m_introducedIn )
2877 continue;
2878
2879 if( int count = rule.m_count( aBoard ); count > 0 )
2880 {
2881 bool omit = aDropInsteadOfApproximate
2882 && rule.m_bucket == DOWNGRADE_BUCKET::LOWER
2883 && !rule.m_keepUnderOmitPolicy;
2884
2885 report.Add( omit ? DOWNGRADE_BUCKET::DROP : rule.m_bucket,
2886 rule.m_feature,
2887 omit ? rule.m_omitDetail : rule.m_detail,
2888 count );
2889 }
2890 }
2891
2892 return report;
2893}
2894
2895
2896// The stored scale compensates for the truncated pixels/cm PPI that targets before 20260623
2897// compute, so it must be rewound. The catalog keeps images with a silent correction, so this
2898// is not a table rule and adds no report row.
2900{
2901 REFERENCE_IMAGE& ref = aImage->GetReferenceImage();
2902 int legacyPPI = ref.GetImage().GetLegacyPPI();
2903
2904 if( legacyPPI > 0 && ref.GetImage().GetPPI() != legacyPPI )
2905 ref.SetImageScale( ref.GetImageScale() * legacyPPI / ref.GetImage().GetPPI() );
2906}
2907
2908
2910{
2911 for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
2912 {
2913 if( item->Type() == PCB_REFERENCE_IMAGE_T )
2914 compensateImageScale( static_cast<PCB_REFERENCE_IMAGE*>( item ) );
2915 }
2916}
2917
2918
2920{
2921 for( BOARD_ITEM* item : aBoard->Drawings() )
2922 {
2923 if( item->Type() == PCB_REFERENCE_IMAGE_T )
2924 compensateImageScale( static_cast<PCB_REFERENCE_IMAGE*>( item ) );
2925 }
2926
2927 for( FOOTPRINT* fp : aBoard->Footprints() )
2929}
2930
2931
2932void DowngradeBoardInPlace( BOARD* aBoard, const DOWNGRADE_TARGET& aTarget, bool aDropInsteadOfApproximate )
2933{
2934 if( aBoard->GetPlotOptions().GetFormat() == PLOT_FORMAT::PNG )
2935 {
2936 PCB_PLOT_PARAMS options = aBoard->GetPlotOptions();
2937 options.SetFormat( PLOT_FORMAT::GERBER );
2938 aBoard->SetPlotOptions( options );
2939 }
2940
2942 applyTextPlan( planBoardText( aBoard, aTarget.m_boardVersion ) );
2943
2944 if( aTarget.m_boardVersion < VER_IMAGE_PPI )
2946
2947 for( const BOARD_RULE& rule : boardRules() )
2948 {
2949 if( aTarget.m_boardVersion >= rule.m_introducedIn )
2950 continue;
2951
2952 bool omit = aDropInsteadOfApproximate
2953 && rule.m_bucket == DOWNGRADE_BUCKET::LOWER
2954 && !rule.m_keepUnderOmitPolicy;
2955
2956 const std::function<void( BOARD* )>& apply = omit ? rule.m_omit : rule.m_apply;
2957
2958 wxASSERT_MSG( !omit || apply, wxT( "A LOWER rule needs an omission transform." ) );
2959
2960 if( apply )
2961 apply( aBoard );
2962 }
2963}
2964
2965
2966// A library footprint carries the same post-target features as one on a board, but it lives in its
2967// own .kicad_mod, so the board rules never see it. This applies the footprint-scoped subset.
2968void DowngradeFootprintInPlace( FOOTPRINT* aFootprint, const DOWNGRADE_TARGET& aTarget, bool aDropInsteadOfApproximate )
2969{
2971 {
2973 visitFootprintItems( aFootprint,
2974 [&]( BOARD_ITEM* item )
2975 {
2976 planTextItem( item, aTarget.m_boardVersion, text );
2977 } );
2979 }
2980
2981 if( aTarget.m_boardVersion < VER_IMAGE_PPI )
2983
2984 for( const BOARD_RULE& rule : boardRules() )
2985 {
2986 if( aTarget.m_boardVersion >= rule.m_introducedIn )
2987 continue;
2988
2989 bool omit = aDropInsteadOfApproximate
2990 && rule.m_bucket == DOWNGRADE_BUCKET::LOWER
2991 && !rule.m_keepUnderOmitPolicy;
2992
2993 const std::function<void(FOOTPRINT*)>& apply = omit ? rule.m_omitFp : rule.m_applyFp;
2994
2995 wxASSERT_MSG( !omit || !rule.m_applyFp || apply, wxT( "A footprint LOWER rule needs an omission transform." ) );
2996
2997 if( apply )
2998 apply( aFootprint );
2999 }
3000}
3001
3002
3003// Node heads dated by the format that introduced them. The versioned writers cannot emit these,
3004// so this gate is a safety net against a wrong or miswired writer. To add a token, add a row
3005// with its format version.
3006static constexpr DATED_TOKEN BOARD_TOKENS[] = {
3007 { "is_time_domain", VER_TIME_DOMAIN_TUNING },
3008 { "target_delay", VER_TIME_DOMAIN_TUNING },
3009 { "target_delay_min", VER_TIME_DOMAIN_TUNING },
3010 { "target_delay_max", VER_TIME_DOMAIN_TUNING },
3011 { "last_tuning_length", VER_TIME_DOMAIN_TUNING },
3012 { "constraint", VER_CONSTRAINTS },
3013 { "grid_item", VER_GRIDS },
3014 { "start_shape", VER_LINE_ENDINGS },
3015 { "end_shape", VER_LINE_ENDINGS },
3016 { "custom_property", VER_CUSTOM_PROPERTIES },
3017 { "exclude_from_sim", VER_SIM_EXCLUSION },
3018 { "drill_chart", VER_DRILL_DRAWINGS },
3019 { "drill_map", VER_DRILL_DRAWINGS },
3020 { "drill_symbol_profile", VER_DRILL_DRAWINGS },
3021 { "link", VER_LINKED_PLACEMENT },
3022 { "connected_routing", VER_LINKED_PLACEMENT },
3023 { "other_items", VER_LINKED_PLACEMENT },
3024 { "group_items", VER_LINKED_PLACEMENT },
3025 { "locked_items", VER_LINKED_PLACEMENT },
3026 { "templates", VER_VIA_STITCH },
3027 { "hatch_position", VER_ZONE_DEFAULTS },
3028 { "covering", VER_VIA_PROTECTION },
3029 { "plugging", VER_VIA_PROTECTION },
3030 { "capping", VER_VIA_PROTECTION },
3031 { "filling", VER_VIA_PROTECTION },
3032 { "depth", VER_VIA_PROTECTION },
3033 { "zone_defaults", VER_ZONE_DEFAULTS },
3034 { "jumper_pad_groups", VER_JUMPER_PADS },
3035 { "lib_id", VER_GROUP_LIB_ID },
3036 { "start_end_only", VER_SKIP_VIA },
3037 { "point", VER_PCB_POINTS },
3038 { "unit", VER_FP_UNITS },
3039 { "pins", VER_FP_UNITS },
3040 { "barcode", VER_BARCODE },
3041 { "ecc_level", VER_BARCODE },
3042 { "text", VER_BARCODE },
3043 { "text_height", VER_BARCODE },
3044 { "die_delay", VER_PAD_DIE_DELAY },
3045 { "backdrill", VER_BACKDRILL },
3046 { "tertiary_drill", VER_BACKDRILL },
3047 { "front_post_machining", VER_POST_MACHINING },
3048 { "back_post_machining", VER_POST_MACHINING },
3049 { "variant", FIRST_PCB_VARIANTS },
3050 { "variants", FIRST_PCB_VARIANTS },
3051 { "field", FIRST_PCB_VARIANTS },
3052 { "description", FIRST_PCB_VARIANTS },
3053 { "body_pcb_gap", VER_EXTRUDED_BODY },
3054 { "overall_height", VER_EXTRUDED_BODY },
3055 { "gr_ellipse", VER_ELLIPSE },
3056 { "gr_ellipse_arc", VER_ELLIPSE },
3057 { "fp_ellipse", VER_ELLIPSE },
3058 { "fp_ellipse_arc", VER_ELLIPSE },
3059 { "dielectric_model", VER_DIELECTRIC },
3060 { "spec_frequency", VER_DIELECTRIC },
3061 { "constant", VER_DIELECTRIC },
3062 { "net_chain", VER_NET_CHAINS },
3063 { "net_chains", VER_NET_CHAINS },
3064 { "terminal_pad", VER_NET_CHAINS },
3065 { "thieving", VER_THIEVING },
3066 { "rotation_angle", VER_ELLIPSE },
3067 { "stagger", VER_THIEVING },
3068 { "sim_electrical_type", VER_PAD_SIM },
3069 { "transform", FIRST_FP_AFFINE_TRANSFORM },
3070};
3071
3072// Bareword values the target cannot parse. The node scan cannot see these.
3073// Hatch fill values cannot be listed here, unlike the schematic gate. Zones legitimately emit
3074// hatch tokens in every target format, so a hatched shape fill leak is invisible to this gate.
3075static constexpr DATED_TOKEN BOARD_VALUES[] = {
3076 { "exclude_from_sim", VER_SIM_EXCLUSION },
3077 { "via_stitch", VER_VIA_STITCH },
3078 { "via_stack", VER_VIA_STACK },
3079 { "pad_prop_pressfit", VER_PRESSFIT },
3080 { "buried", VER_VIA_SPLIT },
3081};
3082
3083
3088
3089
3090// Each target has its own writer, extracted from that release. A target without a writer writes
3091// nothing, and the fail-closed check downstream refuses the export.
3092void SaveBoardForTarget( BOARD* aBoard, const wxString& aPath, const DOWNGRADE_TARGET& aTarget )
3093{
3095 PCB_WRITER_V10().SaveBoard( aPath, aBoard );
3096 else if( aTarget.m_boardVersion == PCB_WRITER_V9::FORMAT_VERSION )
3097 PCB_WRITER_V9().SaveBoard( aPath, aBoard );
3098 else
3099 wxFAIL_MSG( wxT( "No writer for this downgrade target." ) );
3100}
3101
3102
3103void SaveFootprintForTarget( FOOTPRINT* aFootprint, const wxString& aPath, const DOWNGRADE_TARGET& aTarget )
3104{
3106 PCB_WRITER_V10().SaveFootprintFile( aPath, aFootprint );
3107 else if( aTarget.m_boardVersion == PCB_WRITER_V9::FORMAT_VERSION )
3108 PCB_WRITER_V9().SaveFootprintFile( aPath, aFootprint );
3109 else
3110 wxFAIL_MSG( wxT( "No writer for this downgrade target." ) );
3111}
3112
3113
3114static bool containsGroupPlacementSource( const std::string& aText )
3115{
3116 const std::string text = StripSexprStrings( aText );
3117 std::vector<std::string> path;
3118
3119 for( size_t position = 0; position < text.size(); ++position )
3120 {
3121 if( text[position] == '(' )
3122 {
3123 size_t begin = position + 1;
3124
3125 while( begin < text.size() && std::isspace( static_cast<unsigned char>( text[begin] ) ) )
3126 ++begin;
3127
3128 size_t end = begin;
3129
3130 while( end < text.size()
3131 && ( std::isalnum( static_cast<unsigned char>( text[end] ) ) || text[end] == '_' ) )
3132 ++end;
3133
3134 const std::string head = text.substr( begin, end - begin );
3135
3136 if( head == "group" && path.size() >= 2 && path.back() == "placement" && path[path.size() - 2] == "zone" )
3137 return true;
3138
3139 path.push_back( head );
3140 position = end > begin ? end - 1 : position;
3141 }
3142 else if( text[position] == ')' && !path.empty() )
3143 {
3144 path.pop_back();
3145 }
3146 }
3147
3148 return false;
3149}
3150
3151
3152wxString FindUnsupportedBoardToken( const wxString& aSerialized, const DOWNGRADE_TARGET& aTarget )
3153{
3155 return wxEmptyString;
3156
3157 if( aTarget.m_boardVersion < VER_GROUP_PLACEMENT && containsGroupPlacementSource( aSerialized.ToStdString() ) )
3158 return wxT( "placement/group" );
3159
3160 return FindUnsupportedToken( aSerialized, BOARD_TOKENS, std::size( BOARD_TOKENS ), BOARD_VALUES,
3161 std::size( BOARD_VALUES ), aTarget.m_boardVersion );
3162}
3163
3164
3165void FlattenBoardVariant( BOARD* aBoard, const wxString& aVariantName )
3166{
3167 for( FOOTPRINT* fp : aBoard->Footprints() )
3168 {
3169 fp->SetDNP( fp->GetDNPForVariant( aVariantName ) );
3170 fp->SetExcludedFromBOM( fp->GetExcludedFromBOMForVariant( aVariantName ) );
3171 fp->SetExcludedFromPosFiles( fp->GetExcludedFromPosFilesForVariant( aVariantName ) );
3172 fp->SetExcludedFromSim( fp->GetExcludedFromSimForVariant( aVariantName ) );
3173
3174 if( const FOOTPRINT_VARIANT* variant = fp->GetVariant( aVariantName ) )
3175 {
3176 for( const auto& [name, value] : variant->GetFields() )
3177 {
3178 if( PCB_FIELD* field = fp->GetField( name ) )
3179 field->SetText( value );
3180 }
3181 }
3182 }
3183}
3184
3185
3186DOWNGRADE_FILE_RESULT DowngradeFootprintFileToTemp( const wxString& aSrcFile, const wxString& aTmpFile,
3187 const DOWNGRADE_TARGET& aTarget, bool aDropInsteadOfApproximate )
3188{
3189 PCB_IO_KICAD_SEXPR loader;
3190 wxString nameOut;
3191 std::unique_ptr<FOOTPRINT> owned = loader.ImportFootprint( aSrcFile, nameOut );
3192
3193 if( !owned )
3195
3196 // The scratch board lets the block rules decide, so this stays in step with the rule
3197 // table instead of repeating a subset of it.
3198 BOARD scratch;
3199 FOOTPRINT* fp = owned.get();
3200 scratch.Add( owned.release() );
3201
3202 if( ClassifyBoardForDowngrade( &scratch, aTarget, aDropInsteadOfApproximate ).IsBlocked() )
3204
3205 // Detached before writing, like a real library save. The writers clip a parented
3206 // footprint's zone layers and padstacks to the board's layer set. Remove does not clear
3207 // the parent pointer, so that is explicit.
3208 scratch.Remove( fp );
3209 owned.reset( fp );
3210 fp->SetParent( nullptr );
3211
3212 DowngradeFootprintInPlace( fp, aTarget, aDropInsteadOfApproximate );
3213 SaveFootprintForTarget( fp, aTmpFile, aTarget );
3215}
3216
3217
3218bool ExportBoardToOlderVersion( const wxString& aSrcFile, const wxString& aDestFile, const DOWNGRADE_TARGET& aTarget,
3219 COMPATIBILITY_REPORT& aReport, wxString* aUnsupportedToken, const wxString& aVariant,
3220 bool aDropInsteadOfApproximate )
3221{
3222 if( aUnsupportedToken )
3223 aUnsupportedToken->clear();
3224
3225 SETTINGS_MANAGER projectSettings;
3226 wxFileName projectFile( aSrcFile );
3227 projectFile.SetExt( FILEEXT::ProjectFileExtension );
3228 projectFile.MakeAbsolute();
3229 projectSettings.LoadProject( projectFile.GetFullPath(), false );
3230
3232 std::unique_ptr<BOARD> board( pi.LoadBoard( aSrcFile, nullptr ) );
3233
3234 if( !board )
3235 return false;
3236
3237 // Resolve project variables before replacing dynamic items with fixed geometry.
3238 board->SetProject( projectSettings.GetProject( projectFile.GetFullPath() ), true );
3239
3240 if( !aVariant.IsEmpty() )
3241 FlattenBoardVariant( board.get(), aVariant );
3242
3243 aReport = ClassifyBoardForDowngrade( board.get(), aTarget, aDropInsteadOfApproximate );
3244
3245 if( aReport.IsBlocked() )
3246 return false;
3247
3248 DowngradeBoardInPlace( board.get(), aTarget, aDropInsteadOfApproximate );
3249
3250 // Write to a sibling temp and verify that before it replaces the destination, so a failed
3251 // export never destroys a pre-existing output file. The writer resolves symlinks on purpose,
3252 // so reserve the name exclusively rather than spelling out a guessable one.
3253 const wxString tmpFile = wxFileName::CreateTempFileName( aDestFile + wxT( ".downgrade_tmp." ) );
3254
3255 if( tmpFile.IsEmpty() )
3256 return false;
3257
3258 // Release the reservation so the writer's own exclusive create still sees a new target and
3259 // keeps the permissions an ordinary save would give it.
3260 wxRemoveFile( tmpFile );
3261
3262 try
3263 {
3264 SaveBoardForTarget( board.get(), tmpFile, aTarget );
3265 }
3266 catch( ... )
3267 {
3268 wxRemoveFile( tmpFile );
3269 throw;
3270 }
3271
3272 // Fail closed. Discard the temp if it still uses a token the target cannot parse, or if it
3273 // cannot be read back to verify.
3274 wxString bad;
3275
3276 bool good = VerifyDowngradedFile(
3277 tmpFile, aTarget.m_boardVersion,
3278 [&]( const wxString& aText )
3279 {
3280 return FindUnsupportedBoardToken( aText, aTarget );
3281 },
3282 &bad );
3283
3284 if( !good )
3285 {
3286 wxRemoveFile( tmpFile );
3287
3288 if( aUnsupportedToken )
3289 *aUnsupportedToken = bad;
3290
3291 return false;
3292 }
3293
3294 if( !wxRenameFile( tmpFile, aDestFile, true ) )
3295 {
3296 wxRemoveFile( tmpFile );
3297 return false;
3298 }
3299
3300 return true;
3301}
const char * name
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
static void dropDielectricModels(BOARD *aBoard)
static void dropUnitInfoFp(FOOTPRINT *aFootprint)
COMPATIBILITY_REPORT ClassifyBoardForDowngrade(const BOARD *aBoard, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Report what a board would lose if exported to an older KiCad.
static void dropUnitInfo(BOARD *aBoard)
static void transformLineEndingsFp(FOOTPRINT *aFootprint, bool aOmit)
static constexpr int VER_IMAGE_PPI
static constexpr int VER_USER_LAYERS
static int countLinkedPlacementAreas(const BOARD *aBoard)
static constexpr int VER_JUMPER_PADS
static void dropKnockoutTableCells(BOARD *aBoard)
static void lowerBarcodes(BOARD *aBoard)
static void dropLinkedPlacementAreas(BOARD *aBoard)
static void dropPressFitPadsFp(FOOTPRINT *aFootprint)
static int countDieDelays(const BOARD *aBoard)
static bool hasViaProtection(const PCB_VIA *aVia)
static void detachFromGroup(BOARD_ITEM *aItem)
static void dropTimeDomainTuning(BOARD *aBoard)
static void dropJumperPadsFp(FOOTPRINT *aFootprint)
static void dropCustomProperties(BOARD *aBoard)
static int countCustomFpStackups(const BOARD *aBoard)
static int countFootprintHeights(const BOARD *aBoard)
static constexpr int VER_DRILL_DRAWINGS
static int countHatchedFills(const BOARD *aBoard, const std::function< bool(const BOARD_ITEM *)> &aMatches)
static constexpr int VER_THIEVING
static bool isDrillDrawing(const BOARD_ITEM *aItem)
static void lowerRoundedRects(BOARD *aBoard)
void SaveFootprintForTarget(FOOTPRINT *aFootprint, const wxString &aPath, const DOWNGRADE_TARGET &aTarget)
Save a single .kicad_mod footprint file for the target.
static constexpr int VER_DIELECTRIC
static constexpr int BOARD_DOWNGRADE_COVERED
static int countKnockoutTextBoxesFp(const FOOTPRINT *aFootprint)
static void dropKnockoutCellsIn(BOARD_ITEM *aItem)
static void omitBarcodesFp(FOOTPRINT *aFootprint)
static constexpr int VER_IMAGE_ROTATION
static int countLineEndings(const BOARD *aBoard, bool aUnsafeOnly=false)
static void lowerBuriedVias(BOARD *aBoard)
static void dropPostMachiningFp(FOOTPRINT *aFootprint)
static int countPcbPoints(const BOARD *aBoard)
static int countGrids(const BOARD *aBoard)
static void omitGraphicsFp(FOOTPRINT *aFootprint, const std::function< bool(const BOARD_ITEM *)> &aMatches)
static constexpr int VER_PRESSFIT
static void dropKnockoutTextBoxes(BOARD *aBoard)
int BoardDowngradeCoveredVersion()
The format the denylist was generated against. A format bump forces a review.
static void forEachPadPrimitive(FOOTPRINT *aFootprint, const std::function< void(PCB_SHAPE *)> &aFunc)
static bool isBarcode(const BOARD_ITEM *aItem)
static bool hasActiveExpression(const wxString &aText)
static int countZoneDefaults(const BOARD *aBoard)
static bool isUnreviewedDrawing(const BOARD_ITEM *aItem)
static void dropViaProtection(BOARD *aBoard)
static void omitKnockoutTextBoxesFp(FOOTPRINT *aFootprint)
static void dropJumperPads(BOARD *aBoard)
static void dropGrids(BOARD *aBoard)
static constexpr int VER_SIM_EXCLUSION
static int countJumperPads(const BOARD *aBoard)
static int countPostMachining(const BOARD *aBoard)
static int countSkipVias(const BOARD *aBoard)
static int countPadSimTypes(const BOARD *aBoard)
static void planTextItem(BOARD_ITEM *aItem, int aTargetVersion, TEXT_DOWNGRADE_PLAN &aPlan, const std::function< bool(wxString *)> *aResolver=nullptr)
static const wxString & titleFieldText(const TITLE_BLOCK &aTitle, int aIndex)
static void dropFootprintHeights(BOARD *aBoard)
static void roundedRectToPolygon(PCB_SHAPE *aShape, int aMaxError)
static int countHighUserLayers(const BOARD *aBoard)
static bool hasBackdrill(const PADSTACK &aPadstack)
static void dropZoneLayerProperties(BOARD *aBoard)
static int countBackdrills(const BOARD *aBoard)
static int countNetChains(const BOARD *aBoard)
static void setHatchedFills(BOARD *aBoard, const std::function< bool(const BOARD_ITEM *)> &aMatches, FILL_T aFill)
static std::vector< BOARD_RULE > boardRules()
static void dropConstraints(BOARD *aBoard)
static int countKnockoutCellsIn(const BOARD_ITEM *aItem)
static constexpr int VER_ZONE_DEFAULTS
static constexpr int VER_SHAPE_HATCH
static int countRoundedRectsFp(const FOOTPRINT *aFootprint)
static void omitGraphics(BOARD *aBoard, const std::function< bool(const BOARD_ITEM *)> &aMatches)
void SaveBoardForTarget(BOARD *aBoard, const wxString &aPath, const DOWNGRADE_TARGET &aTarget)
Save a board stamped for the target, using the writer for that version.
static int countGroupDesignBlocks(const BOARD *aBoard)
static void omitEllipses(BOARD *aBoard)
static int countScaledFootprints(const BOARD *aBoard)
static int countSimulationExclusions(const BOARD *aBoard)
static int footprintMaxError(const FOOTPRINT *aFootprint)
static bool isGrid(const BOARD_ITEM *aItem)
void FlattenBoardVariant(BOARD *aBoard, const wxString &aVariantName)
Bake a variant's overrides into the base footprint attributes.
static void dropPcbVariantsFp(FOOTPRINT *aFootprint)
static int countZoneLayerProperties(const BOARD *aBoard)
static void dropConstraintsFp(FOOTPRINT *aFootprint)
static void omitKnockoutTableCells(BOARD *aBoard)
static void dropExtrudedBodiesFp(FOOTPRINT *aFootprint)
static void setHatchedFillsFp(FOOTPRINT *aFootprint, const std::function< bool(const BOARD_ITEM *)> &aMatches, FILL_T aFill)
static int countGroupPlacementAreas(const BOARD *aBoard)
static void transformLineEndings(BOARD *aBoard, bool aOmit)
static constexpr int VER_PAD_DIE_DELAY
static constexpr int VER_VIA_SPLIT
static int countUnreviewedDrawings(const BOARD *aBoard)
static constexpr int VER_VIA_STITCH
static void omitEllipsesFp(FOOTPRINT *aFootprint)
static void dropGroupPlacementLinkage(ZONE *aZone)
static void dropDieDelaysFp(FOOTPRINT *aFootprint)
static constexpr int VER_NET_CHAINS
static void dropExtrudedBodies(BOARD *aBoard)
static bool isHatchedShape(const BOARD_ITEM *aItem)
static constexpr DATED_TOKEN BOARD_VALUES[]
static int countBuriedVias(const BOARD *aBoard)
static void dropNetChains(BOARD *aBoard)
static constexpr int VER_TIME_DOMAIN_TUNING
static bool isKnockoutTextBox(const BOARD_ITEM *aItem)
static int countUnknownGenerators(const BOARD *aBoard)
static constexpr int VER_VIA_STACK
static constexpr int VER_ELLIPSE
static int countDrillSymbolConfiguration(const BOARD *aBoard)
static void omitRoundedRectsFp(FOOTPRINT *aFootprint)
static void dropDrillDrawings(BOARD *aBoard)
static bool isTimeDomainTuning(const PCB_GENERATOR *aGenerator)
static constexpr int VER_CELL_KNOCKOUT
static constexpr int VER_GRIDS
static constexpr int VER_PROPERTY_VARIABLES
static void dropZoneDefaults(BOARD *aBoard)
static void dropReferenceImageOpacities(BOARD *aBoard)
static void dropFootprintHeightsFp(FOOTPRINT *aFootprint)
static int countTimeDomainTuning(const BOARD *aBoard)
static int countCustomProperties(const BOARD *aBoard)
static void dropPadSimTypesFp(FOOTPRINT *aFootprint)
static bool isHatchedCopperShape(const BOARD_ITEM *aItem)
static constexpr int VER_TEXTBOX_KNOCKOUT
bool ExportBoardToOlderVersion(const wxString &aSrcFile, const wxString &aDestFile, const DOWNGRADE_TARGET &aTarget, COMPATIBILITY_REPORT &aReport, wxString *aUnsupportedToken, const wxString &aVariant, bool aDropInsteadOfApproximate)
Load a board, downgrade the copy, and write it stamped for the target.
static void dropPcbVariants(BOARD *aBoard)
static void dropGroupPlacementAreas(BOARD *aBoard)
static constexpr int VER_FOOTPRINT_HEIGHT
static void dropPressFitPads(BOARD *aBoard)
static void dropPostMachining(BOARD *aBoard)
static TEXT_DOWNGRADE_PLAN planBoardText(const BOARD *aBoard, int aTargetVersion)
static constexpr int VER_FP_UNITS
static int countBarcodes(const BOARD *aBoard)
static void lowerLineEndingShape(PCB_SHAPE *aShape, int aMaxError)
static void dropSimulationExclusions(BOARD *aBoard)
static bool hasLineEndings(const BOARD_ITEM *aItem)
static constexpr int VER_IMAGE_OPACITY
static int countGenerators(const BOARD *aBoard, const wxString &aType)
static constexpr int VER_GROUP_LIB_ID
static int countEllipses(const BOARD *aBoard)
static void ellipseToPolygon(PCB_SHAPE *aShape, int aMaxError)
static void dropSimulationExclusionsFp(FOOTPRINT *aFootprint)
static int countExtrudedBodies(const BOARD *aBoard)
static void lowerSkipVias(BOARD *aBoard)
static void dropPcbPoints(BOARD *aBoard)
static void visitBoardItems(const BOARD *aBoard, const std::function< void(BOARD_ITEM *)> &aVisit)
static constexpr int VER_LINE_ENDINGS
static int countViaProtection(const BOARD *aBoard)
static int countUnitInfo(const BOARD *aBoard)
static constexpr int VER_CONSTRAINTS
static int countDrawings(const BOARD *aBoard, const std::function< bool(const BOARD_ITEM *)> &aMatches)
static bool postTargetText(const wxString &aText, const BOARD *aBoard, int aTargetVersion, std::set< wxString > &aDependencies, int aDepth=0)
static void compensateImageScale(PCB_REFERENCE_IMAGE *aImage)
static void omitKnockoutTextBoxes(BOARD *aBoard)
static int countReferenceImageRotations(const BOARD *aBoard)
static void dropKnockoutTextBoxesFp(FOOTPRINT *aFootprint)
static void clearLineEndings(PCB_SHAPE *aShape)
static int countReferenceImageOpacities(const BOARD *aBoard)
static void lowerEllipses(BOARD *aBoard)
static void dropDieDelays(BOARD *aBoard)
static constexpr int VER_PCB_POINTS
static void omitKnockoutTableCellsFp(FOOTPRINT *aFootprint)
static bool unsafeLineEndings(const PCB_SHAPE *aShape)
static std::vector< PCB_SHAPE * > barcodeToShapes(PCB_BARCODE *aBarcode, BOARD_ITEM_CONTAINER *aParent)
static constexpr int VER_BACKDRILL
static void omitBarcodes(BOARD *aBoard)
static void dropGroupDesignBlocks(BOARD *aBoard)
static int countDrillDrawings(const BOARD *aBoard)
static bool hasPostMachining(const PADSTACK &aPadstack)
static void lowerEllipsesFp(FOOTPRINT *aFootprint)
static void dropPcbPointsFp(FOOTPRINT *aFootprint)
static void lowerRoundedRectsFp(FOOTPRINT *aFootprint)
static bool isEllipse(const BOARD_ITEM *aItem)
static void lowerBarcodesFp(FOOTPRINT *aFootprint)
static void compensateReferenceImageScales(BOARD *aBoard)
static void visitFootprintItems(const FOOTPRINT *aFootprint, const std::function< void(BOARD_ITEM *)> &aVisit)
static constexpr int VER_VIA_PROTECTION
static bool isRoundedRect(const BOARD_ITEM *aItem)
DOWNGRADE_FILE_RESULT DowngradeFootprintFileToTemp(const wxString &aSrcFile, const wxString &aTmpFile, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Load one .kicad_mod, classify it against the rule table, and write the downgraded copy to aTmpFile.
static int countPcbVariants(const BOARD *aBoard)
static bool containsGroupPlacementSource(const std::string &aText)
static void applyTextPlan(const TEXT_DOWNGRADE_PLAN &aPlan)
void DowngradeBoardInPlace(BOARD *aBoard, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Approximate or remove, in place, what the target cannot represent.
static int countConstraints(const BOARD *aBoard)
static void dropGroupDesignBlocksFp(FOOTPRINT *aFootprint)
static int countKnockoutTableCells(const BOARD *aBoard)
static constexpr int VER_POST_MACHINING
static int countUnreviewedTuningProperties(const BOARD *aBoard)
void DowngradeFootprintInPlace(FOOTPRINT *aFootprint, const DOWNGRADE_TARGET &aTarget, bool aDropInsteadOfApproximate)
Footprint-scoped downgrade for standalone .kicad_mod files the board pass never sees.
static int countPressFitPads(const BOARD *aBoard)
static constexpr int VER_PAD_SIM
static constexpr int VER_BARCODE
static void omitKnockoutCellContents(BOARD_ITEM *aItem)
static void dropZoneLayerPropertiesFp(FOOTPRINT *aFootprint)
static bool hasGroupPlacementLinkage(const ZONE *aZone)
static int countThievingZones(const BOARD *aBoard)
static int countKnockoutTextBoxes(const BOARD *aBoard)
static void omitPadPrimitives(FOOTPRINT *aFootprint, const std::function< bool(const BOARD_ITEM *)> &aMatches)
static constexpr int VER_CUSTOM_PROPERTIES
static void dropCustomPropertiesFp(FOOTPRINT *aFootprint)
static constexpr int VER_EXTRUDED_BODY
static int countDielectricModels(const BOARD *aBoard)
static int countRoundedRects(const BOARD *aBoard)
static void dropPadSimTypes(BOARD *aBoard)
static constexpr int VER_ROUNDED_RECT
static void omitRoundedRects(BOARD *aBoard)
static int titleFieldIndex(const wxString &aName)
static void dropGroupPlacementAreasFp(FOOTPRINT *aFootprint)
static constexpr int VER_SKIP_VIA
static constexpr int VER_LINKED_PLACEMENT
static constexpr DATED_TOKEN BOARD_TOKENS[]
static bool isHatchedInkShape(const BOARD_ITEM *aItem)
static void compensateReferenceImageScalesFp(FOOTPRINT *aFootprint)
static void dropDrillSymbolConfiguration(BOARD *aBoard)
static void dropGenerators(BOARD *aBoard, const std::function< bool(const PCB_GENERATOR *)> &aMatches)
wxString FindUnsupportedBoardToken(const wxString &aSerialized, const DOWNGRADE_TARGET &aTarget)
Scan serialized board text for tokens the target cannot parse.
static constexpr int VER_GROUP_PLACEMENT
static void dropKnockoutTableCellsFp(FOOTPRINT *aFootprint)
constexpr int FIRST_FP_CUSTOM_STACKUP
First board format that stores custom footprint stackups.
constexpr int FIRST_PCB_VARIANTS
First board format that stores variants.
@ BS_ITEM_TYPE_DIELECTRIC
int GetPPI() const
int GetLegacyPPI() const
Recompute the PPI the way it was computed before the pixels/cm truncation fix.
Container for design settings for a BOARD object.
std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > m_ZoneLayerProperties
DRILL_SYMBOL_PROFILE & GetDrillSymbolProfile()
BOARD_STACKUP & GetStackupDescriptor()
Abstract interface for BOARD_ITEMs capable of storing other items inside.
virtual void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false)=0
Adds an item to the container.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
void SetLocked(bool aLocked) override
Definition board_item.h:427
virtual bool IsKnockout() const
Definition board_item.h:423
bool IsLocked() const override
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:383
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:267
Manage one layer needed to make a physical board.
Manage layers needed to make a physical board.
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
void SetPlotOptions(const PCB_PLOT_PARAMS &aOptions)
Definition board.h:1024
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1217
void SetCurrentVariant(const wxString &aVariant)
Definition board.cpp:2969
void SetVariantNames(const std::vector< wxString > &aNames)
Definition board.h:534
const std::vector< wxString > & GetVariantNames() const
Definition board.h:533
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1520
void SetNetChainColor(const wxString &aChain, const KIGFX::COLOR4D &aColor)
Per-net-chain colour override (empty COLOR4D::UNSPECIFIED = no override).
Definition board.h:1336
const GENERATORS & Generators() const
Definition board.h:485
const PCB_POINTS & Points() const
Definition board.h:499
const std::map< wxString, KIGFX::COLOR4D > & GetNetChainColors() const
Definition board.h:1350
const ZONES & Zones() const
Definition board.h:469
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition board.h:518
bool ResolveTextVar(wxString *token, int aDepth) const
Definition board.cpp:680
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition board.cpp:3724
TITLE_BLOCK & GetTitleBlock()
Definition board.h:1026
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:526
const FOOTPRINTS & Footprints() const
Definition board.h:465
const TRACKS & Tracks() const
Definition board.h:463
const CONSTRAINTS & Constraints() const
Geometric constraints (#2329) owned by this board.
Definition board.h:522
const PCB_PLOT_PARAMS & GetPlotOptions() const
Definition board.h:1023
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition board.cpp:932
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
Definition board.cpp:837
PROJECT * GetProject() const
Definition board.h:774
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1174
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1691
bool IsItemIndexedById(const BOARD_ITEM *aItem) const
Return true if aItem is currently indexed.
Definition board.h:1713
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
const DRAWINGS & Drawings() const
Definition board.h:467
What an export to a given target will keep, approximate, drop, or block.
void Add(DOWNGRADE_BUCKET aBucket, const wxString &aFeature, const wxString &aDetail=wxEmptyString, int aCount=1)
bool IsBlocked() const
True if some feature makes the target impossible. The user cannot export anyway.
Grouping rules and symbol assignments, shared by reference so a chart and its map can never disagree ...
bool IsZero() const
Definition eda_angle.h:135
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:43
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void ClearCustomProperties()
Definition eda_item.h:270
const KIID m_Uuid
Definition eda_item.h:599
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition eda_item.h:115
bool HasCustomProperties() const
Definition eda_item.h:269
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
int GetEllipseMinorRadius() const
Definition eda_shape.h:403
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:385
void SetEndEnding(const LINE_ENDING &aEnding)
Definition eda_shape.h:189
int GetEllipseMajorRadius() const
Definition eda_shape.h:394
virtual int GetEffectiveWidth() const
Definition eda_shape.h:166
void TransformWithLineEndingsToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the shape body shortened for line endings plus line-ending geometry to polygons.
void SetLineStyle(const LINE_STYLE aStyle)
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:412
SHAPE_T GetShape() const
Definition eda_shape.h:177
void SetStartEnding(const LINE_ENDING &aEnding)
Definition eda_shape.h:183
bool GetLineEndingEndpoints(VECTOR2I &aStartPoint, VECTOR2I &aEndPoint) const
Return the source endpoints used to place line endings.
LINE_ENDING_STYLE GetStartEndingStyle() const
Definition eda_shape.h:191
LINE_STYLE GetLineStyle() const
std::vector< VECTOR2I > GetRectCorners() const
const LINE_ENDING & GetStartEnding() const
Definition eda_shape.h:179
const LINE_ENDING & GetEndEnding() const
Definition eda_shape.h:185
LINE_ENDING_STYLE GetEndEndingStyle() const
Definition eda_shape.h:194
int GetCornerRadius() const
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
High-level wrapper for evaluating mathematical and string expressions in wxString format.
wxString Evaluate(const wxString &aInput)
Main evaluation function - processes input string and evaluates all '@{}' expressions.
bool HasErrors() const
Check if the last evaluation had errors.
Variant information for a footprint.
Definition footprint.h:178
void SetExcludedFromSim(bool aExclude)
Definition footprint.h:199
const CASE_INSENSITIVE_MAP< FOOTPRINT_VARIANT > & GetVariants() const
Get all variants.
Definition footprint.h:1011
ZONES & Zones()
Definition footprint.h:361
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
PCB_POINTS & Points()
Definition footprint.h:370
void SetExcludedFromSim(bool aExclude=true)
Definition footprint.h:939
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
const FOOTPRINT_VARIANT * GetVariant(const wxString &aVariantName) const
Get a variant by name.
void SetDuplicatePadNumbersAreJumpers(bool aEnabled)
Definition footprint.h:1134
CONSTRAINTS & Constraints()
Definition footprint.h:367
std::deque< PAD * > & Pads()
Definition footprint.h:355
bool ResolveTextVar(wxString *aToken, int aDepth=0) const
Resolve any references to system tokens supported by the component.
void DeleteVariant(const wxString &aVariantName)
Delete a variant by name.
void SetHeight(int aHeight)
Definition footprint.h:439
JUMPER_GROUP_SET & JumperPadGroups()
Each jumper pad group is a set of pad numbers that should be treated as internally connected.
Definition footprint.h:1140
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
void SetUnitInfo(const std::vector< FP_UNIT_INFO > &aUnits)
Definition footprint.h:908
GROUPS & Groups()
Definition footprint.h:364
void ClearBody()
DRAWINGS & GraphicalItems()
Definition footprint.h:358
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
Definition kiid.h:46
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
Decorative shape (arrowhead, circle, square) at the start or end of a graphic line,...
Definition line_ending.h:62
const STROKE_PARAMS & GetStroke() const
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
Handle the data for a net.
Definition netinfo.h:50
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
Definition padstack.h:158
MASK_LAYER_PROPS & FrontOuterLayers()
Definition padstack.h:407
void SetUnconnectedLayerMode(UNCONNECTED_LAYER_MODE aMode)
Definition padstack.h:402
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:369
UNCONNECTED_LAYER_MODE UnconnectedLayerMode() const
Definition padstack.h:401
DRILL_PROPS & TertiaryDrill()
Definition padstack.h:366
DRILL_PROPS & Drill()
Definition padstack.h:360
DRILL_PROPS & SecondaryDrill()
Definition padstack.h:363
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:372
MASK_LAYER_PROPS & BackOuterLayers()
Definition padstack.h:410
Definition pad.h:61
wxString GetText() const
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the barcode (text + symbol shapes) to polygonal geometry suitable for filling/collision tests...
A geometric constraint between board items (issue #2329).
Abstract dimension API.
wxString GetOverrideText() const
bool GetOverrideTextEnabled() const
virtual wxString GetGeneratorType() const
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
A PCB_IO derivation for saving and loading Pcbnew s-expression formatted files.
std::unique_ptr< FOOTPRINT > ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
std::unique_ptr< BOARD > LoadBoard(const wxString &aFileName, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr)
Load information from some input file format that this PCB_IO implementation knows about into new BOA...
Definition pcb_io.cpp:72
Parameters and options when plotting/printing a board.
PLOT_FORMAT GetFormat() const
void SetFormat(PLOT_FORMAT aFormat)
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
Object to handle a bitmap image that can be inserted in a PCB.
void SetOpacity(double aOpacity)
REFERENCE_IMAGE & GetReferenceImage()
void SetCornerRadius(int aCornerRadius) override
void SetWidth(int aWidth) override
int GetWidth() const override
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:212
void SetPolyShape(const SHAPE_POLY_SET &aShape) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
wxString GetUnwrappedShownText(RESOLUTION_CONTEXT aContext, int aDepth=0) const
GetShownText() before it is line-broken to the cell's width, i.e.
wxString GetShownText(RESOLUTION_CONTEXT aContext, int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition pcb_text.cpp:178
const PADSTACK & Padstack() const
Definition pcb_track.h:431
VIATYPE GetViaType() const
Definition pcb_track.h:422
Writes a board or footprint in the KiCad 10.0 file format.
static constexpr int FORMAT_VERSION
void SaveFootprintFile(const wxString &aFileName, FOOTPRINT *aFootprint)
void SaveBoard(const wxString &aFileName, BOARD *aBoard)
Writes a board or footprint in the KiCad 9.0 file format.
static constexpr int FORMAT_VERSION
void SaveFootprintFile(const wxString &aFileName, FOOTPRINT *aFootprint)
void SaveBoard(const wxString &aFileName, BOARD *aBoard)
virtual bool TextVarResolver(wxString *aToken) const
Definition project.cpp:178
virtual std::map< wxString, wxString > & GetTextVars() const
Definition project.cpp:223
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
EDA_ANGLE GetOrientation() const
double GetOpacity() const
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:38
const wxString & GetCompany() const
Definition title_block.h:93
void SetRevision(const wxString &aRevision)
Definition title_block.h:78
void SetComment(int aIdx, const wxString &aComment)
Definition title_block.h:98
bool TextVarResolver(wxString *aToken, const PROJECT *aProject, RESOLUTION_CONTEXT aContext) const
const wxString & GetRevision() const
Definition title_block.h:83
void SetTitle(const wxString &aTitle)
Definition title_block.h:55
const wxString & GetDate() const
Definition title_block.h:73
const wxString & GetComment(int aIdx) const
void SetCompany(const wxString &aCompany)
Definition title_block.h:88
const wxString & GetTitle() const
Definition title_block.h:60
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:68
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:832
wxString GetPlacementAreaSource() const
Definition zone.h:837
void SetPlacementAreaSource(const wxString &aSource)
Definition zone.h:838
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:839
void SetPlacementAreaSourceType(PLACEMENT_SOURCE_T aType)
Definition zone.h:840
bool GetPlacementAreaEnabled() const
Definition zone.h:834
void SetPlacementAreaEnabled(bool aEnabled)
Definition zone.h:835
void FinalizeTextVarExpansion(wxString &aText, RESOLUTION_CONTEXT aContext)
Definition common.cpp:88
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, RESOLUTION_CONTEXT aContext)
Definition common.cpp:60
std::vector< TEXT_VAR_REF_KEY > ExtractTextVarReferences(const wxString &aSource)
Lex-scan aSource and return every ${...} reference that appears, without resolving.
Definition common.cpp:468
@ INTERNAL
Definition common.h:96
DOWNGRADE_BUCKET
What happens to a feature when we export to an older KiCad.
@ BLOCK
Cannot be represented at all, export to this target is not possible.
@ LOWER
Approximated down to a simpler equivalent.
@ DROP
No equivalent, left out.
wxString FindUnsupportedToken(const wxString &aSerialized, const DATED_TOKEN *aNodes, size_t aNodeCount, const DATED_TOKEN *aValues, size_t aValueCount, int aTargetVersion)
Scan serialized s-expression text against dated node and value denylists.
std::string StripSexprStrings(const std::string &aText)
Drop quoted strings so a token inside a text value cannot cause a false refusal.
DOWNGRADE_FILE_RESULT
What a per-file converter did with one library file.
@ SKIPPED
Not a convertible file, leave it alone.
@ REFUSED
The target cannot represent this file.
@ CONVERTED
The temp file holds the downgraded copy.
bool VerifyDowngradedFile(const wxString &aPath, int aStampVersion, const std::function< wxString(const wxString &)> &aFindForbidden, wxString *aForbidden=nullptr)
Read back a downgraded output and require the target stamp plus a clean token scan.
#define _(s)
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
@ RECURSE
Definition eda_item.h:51
SHAPE_T
Definition eda_shape.h:54
@ ELLIPSE
Definition eda_shape.h:62
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:111
static const std::string ProjectFileExtension
KIID niluuid(0)
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:744
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ User_45
Definition layer_ids.h:164
@ User_10
Definition layer_ids.h:129
static std::vector< PENDING_PROPERTY > plan(const EDA_ITEM &aSource, const EDA_ITEM &aTarget, const std::set< wxString > &aEnabledKeys)
Target properties to write, paired with values read off the source.
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:124
@ NONE
no special fabrication property
Definition padstack.h:116
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
#define FIRST_FP_AFFINE_TRANSFORM
First version that stores footprint children in library frame.
#define SEXPR_BOARD_FILE_VERSION
Current s-expression file format version. 2 was the last legacy format version.
static int countCustomProperties(const SCH_SCREEN *aScreen)
static void lowerEllipses(SCH_SCREEN *aScreen)
static void omitEllipses(SCH_SCREEN *aScreen)
static bool isEllipse(const SCH_ITEM *aItem)
static void lowerRoundedRects(SCH_SCREEN *aScreen)
static bool isRoundedRect(const SCH_ITEM *aItem)
static int countRoundedRects(const SCH_SCREEN *aScreen)
static void omitRoundedRects(SCH_SCREEN *aScreen)
static void dropCustomProperties(SCH_SCREEN *aScreen)
static int countEllipses(const SCH_SCREEN *aScreen)
std::function< void(BOARD *)> m_omit
std::function< void(BOARD *)> m_apply
DOWNGRADE_BUCKET m_bucket
std::function< void(FOOTPRINT *)> m_applyFp
Footprint-scoped part, for .kicad_mod files.
std::function< void(FOOTPRINT *)> m_omitFp
wxString m_omitDetail
std::function< int(const BOARD *)> m_count
bool m_keepUnderOmitPolicy
An approximation the omission preference must not remove, because removing it would change the board ...
A token dated by the format that introduced it, for the fail-closed downgrade gates.
An older KiCad release we can export a project back to.
int m_boardVersion
SEXPR_BOARD_FILE_VERSION for the target.
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
Definition padstack.h:275
std::optional< bool > is_capped
True if the drill hole should be capped.
Definition padstack.h:281
std::optional< bool > is_filled
True if the drill hole should be filled completely.
Definition padstack.h:280
std::optional< bool > has_covering
True if the pad on this side should have covering.
Definition padstack.h:263
std::optional< bool > has_plugging
True if the drill hole should be plugged on this side.
Definition padstack.h:264
Options for copying a placement rule area onto the others linked to it.
std::vector< std::pair< int, wxString > > m_titleReplacements
std::map< EDA_TEXT *, wxString > m_replacements
const std::string table
netlist clear()
std::string path
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DRILL_MAP_T
class PCB_DRILL_MAP, drill symbols drawn at the holes
Definition typeinfo.h:240
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:99
@ PCB_GRID_ITEM_T
a subgrid placed on a board
Definition typeinfo.h:238
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
@ PCB_DRILL_CHART_T
class PCB_DRILL_CHART, a live drill chart derived from PCB_TABLE
Definition typeinfo.h:239
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.
Class ZONE_SETTINGS used to handle zones parameters in dialogs.