11#!/usr/bin/python3
2- # Copyright (c) 2020-2024 Salvador E. Tropea
3- # Copyright (c) 2020-2024 Instituto Nacional de Tecnología Industrial
2+ # Copyright (c) 2020-2025 Salvador E. Tropea
3+ # Copyright (c) 2020-2025 Instituto Nacional de Tecnología Industrial
44# License: GPL-2.0
55# Project: KiCad Diff
66# Adapted from: https://github.com/obra/kicad-tools
2525
2626"""
2727__author__ = 'Salvador E. Tropea'
28- __copyright__ = 'Copyright 2020-2024 , INTI/' + __author__
28+ __copyright__ = 'Copyright 2020-2025 , INTI/' + __author__
2929__credits__ = ['Salvador E. Tropea' , 'Jesse Vincent' ]
3030__license__ = 'GPL 2.0'
31- __version__ = '2.5.5 '
31+ __version__ = '2.5.6 '
32323333__status__ = 'beta'
3434__url__ = 'https://github.com/INTI-CMNB/KiDiff/'
107107 NO_DRILL_SHAPE = pcbnew .PCB_PLOT_PARAMS .NO_DRILL_SHAPE
108108 SMALL_DRILL_SHAPE = pcbnew .PCB_PLOT_PARAMS .SMALL_DRILL_SHAPE
109109 FULL_DRILL_SHAPE = pcbnew .PCB_PLOT_PARAMS .FULL_DRILL_SHAPE
110+ LA_KI8_2_KI9 = {0 : 0 , 1 : 4 , 2 : 6 , 3 : 8 , 4 : 10 , 5 : 12 , 6 : 14 , 7 : 16 , 8 : 18 , 9 : 20 , 10 : 22 , 11 : 24 , 12 : 26 , 13 : 28 , 14 : 30 ,
111+ 15 : 32 , 16 : 34 , 17 : 36 , 18 : 38 , 19 : 40 , 20 : 42 , 21 : 44 , 22 : 46 , 23 : 48 , 24 : 50 , 25 : 52 , 26 : 54 , 27 : 56 ,
112+ 28 : 58 , 29 : 60 , 30 : 62 , 31 : 2 , 32 : 11 , 33 : 9 , 34 : 15 , 35 : 13 , 36 : 7 , 37 : 5 , 38 : 3 , 39 : 1 , 40 : 17 , 41 : 19 ,
113+ 42 : 21 , 43 : 23 , 44 : 25 , 45 : 27 , 46 : 29 , 47 : 31 , 48 : 33 , 49 : 35 , 50 : 39 , 51 : 41 , 52 : 43 , 53 : 45 , 54 : 47 ,
114+ 55 : 49 , 56 : 51 , 57 : 53 , 58 : 55 , 59 : 37 }
110115
111116
112117def SetExcludeEdgeLayer (po , exclude_edge_layer , layer ):
@@ -164,9 +169,8 @@ def CheckOptions(name, cur_ops):
164169 return res
165170
166171
167- def GenPCBImages (file , file_hash , hash_dir , file_no_ext , layer_names , wanted_layers , kiri_mode , zones_ops ):
172+ def GenPCBImages (board , file_hash , hash_dir , file_no_ext , layer_names , wanted_layers , kiri_mode , zones_ops ):
168173 # Setup the KiCad plotter
169- board = LoadBoard (file )
170174 if hasattr (pcbnew , 'LAYER_HIDDEN_TEXT' ):
171175 # KiCad 8.0.2 crazyness: hidden text affects scaling, even when not plotted
172176 # So a PRL can affect the plot mechanism
@@ -186,7 +190,8 @@ def GenPCBImages(file, file_hash, hash_dir, file_no_ext, layer_names, wanted_lay
186190 SetExcludeEdgeLayer (popt , False , board .GetLayerID ('Edge.Cuts' ))
187191 popt .SetUseAuxOrigin (False )
188192 popt .SetSkipPlotNPTH_Pads (False )
189- popt .SetPlotViaOnMaskLayer (True )
193+ if kicad_version_major < 9 :
194+ popt .SetPlotViaOnMaskLayer (True )
190195 popt .SetSubtractMaskFromSilk (False )
191196
192197 if zones_ops != 'none' :
@@ -359,10 +364,14 @@ def GenImages(file, file_hash, all, zones, kiri_mode=False):
359364
360365 # Read the layer names from the file
361366 if is_pcb :
367+ board = LoadBoard (file )
368+ # This code exposes the fails in KiCad API for tests/board_samples/kicad_8/light_control.kicad_pcb
369+ # for la in board.GetEnabledLayers().Seq():
370+ # logger.debug(f'{la} -> {board.GetLayerName(la)} ({board.GetStandardLayerName(la)})')
362371 layer_names , wanted_layers = load_layer_names (file , hash_dir , kiri_mode )
363372 logger .debug ('Layers list: ' + str (layer_names ))
364373 logger .debug ('Wanted layers: ' + str (wanted_layers ))
365- res = GenPCBImages (file , file_hash , hash_dir , file_no_ext , layer_names , wanted_layers , kiri_mode , zones )
374+ res = GenPCBImages (board , file_hash , hash_dir , file_no_ext , layer_names , wanted_layers , kiri_mode , zones )
366375 else :
367376 layer_names = {0 : 'Schematic_all' if args .all_pages else 'Schematic' }
368377 GenSCHImage (file , file_hash , hash_dir , file_no_ext , layer_names , all , kiri_mode )
@@ -691,6 +700,8 @@ def save_layers_to_cache(layers_file, all_layers, kiri_mode):
691700
692701
693702def load_layers_from_pcb (pcb_file , layers_file , kiri_mode ):
703+ # We get the layers from the PCB because BOARD.GetLayerName(id) and BOARD.GetStandardLayerName(id) returns the same
704+ # even for files using the KiCad 5 names as user names
694705 layer_names = {}
695706 name_to_id = {}
696707 all_layers = []
@@ -699,6 +710,9 @@ def load_layers_from_pcb(pcb_file, layers_file, kiri_mode):
699710 collect_layers = False
700711 re_layer = re .compile (r'\s+\((\d+)\s+(\S+)' )
701712 re_layer_user = re .compile (r'\s+\((\d+)\s+(\S+)\s+user\s+"([^"]+)"' )
713+ re_version = re .compile (r'\(version (\d+)\)' )
714+ version = None
715+ convert_layers = False
702716 for line in file_file :
703717 if collect_layers :
704718 z = re_layer .match (line )
@@ -708,8 +722,11 @@ def load_layers_from_pcb(pcb_file, layers_file, kiri_mode):
708722 if lname [0 ] == '"' :
709723 lname = lname [1 :- 1 ]
710724 lnum = res [0 ]
711- logger .debug (lname + '->' + lnum )
712725 ilnum = int (lnum )
726+ if convert_layers :
727+ ilnum = LA_KI8_2_KI9 [ilnum ]
728+ lnum = str (ilnum )
729+ logger .debug (lname + '->' + lnum )
713730 name_to_id [lname ] = ilnum
714731 # Check if the user renamed this layer
715732 z = re_layer_user .match (line )
@@ -729,6 +746,12 @@ def load_layers_from_pcb(pcb_file, layers_file, kiri_mode):
729746 if re .search (r'^\s+\)$' , line ):
730747 break
731748 else :
749+ if not version :
750+ z = re_version .search (line )
751+ if z :
752+ version = int (z .group (1 ))
753+ logger .debug (f'PCB version { version } ' )
754+ convert_layers = version < 20241228 and kicad_version_major >= 9
732755 if re .search (r'\s+\(layers' , line ):
733756 collect_layers = True
734757 save_layers_to_cache (layers_file , all_layers , kiri_mode )
@@ -814,9 +837,14 @@ def get_layer(line):
814837 args = parser .parse_args ()
815838
816839 # Fill the names for the inner layers
817- for i in range (1 , 30 ):
818- name = 'In' + str (i )+ '.Cu'
819- DEFAULT_LAYER_NAMES [pcbnew .In1_Cu + i - 1 ] = name
840+ if kicad_version_major >= 9 :
841+ for i in range (1 , 30 ):
842+ name = 'In' + str (i )+ '.Cu'
843+ DEFAULT_LAYER_NAMES [(i + 1 )* 2 ] = name
844+ else :
845+ for i in range (1 , 30 ):
846+ name = 'In' + str (i )+ '.Cu'
847+ DEFAULT_LAYER_NAMES [pcbnew .In1_Cu + i - 1 ] = name
820848
821849 # Create a logger with the specified verbosity
822850 if args .verbose >= 2 :
0 commit comments