nextjlc/
dcode.rs

1/* src/dcode.rs */
2
3/* SPDX-License-Identifier: Apache-2.0 */
4/*
5 * Author HalfSweet <halfsweet@halfsweet.cn>
6 * Author Canmi <t@canmi.icu>
7 */
8
9use once_cell::sync::Lazy;
10use regex::Regex;
11
12// This regex matches any standard D-code (Dxx*).
13static DCODE_REGEX: Lazy<Regex> =
14    Lazy::new(|| Regex::new(r"(D\d{2,4}\*)").expect("Failed to compile D-Code regex"));
15
16/// Gerber flavor for D-code processing
17pub enum GerberFlavor {
18    KiCad,
19    Altium,
20}
21
22/// Process Gerber data to prepend "G54" to D-codes according to the CAD flavor.
23///
24/// KiCad: process all non-%ADD, non-G54D D-codes.
25/// Altium: skip D-codes on lines starting with G01/G02/G36/G37 with coordinates
26/// or lines that only contain a single Dxx*; other D-codes (including %ADD/%AM/G04) are processed.
27///
28/// # Arguments
29/// * `gerber_data` - Raw Gerber file content
30/// * `flavor` - KiCad or Altium
31///
32/// # Returns
33/// Processed Gerber content with appropriate D-codes prefixed with "G54"
34pub fn process_d_codes(gerber_data: String, flavor: GerberFlavor) -> String {
35    let input_lines: Vec<&str> = gerber_data.split('\n').collect();
36    let mut processed_lines = Vec::with_capacity(input_lines.len());
37
38    for line in input_lines {
39        let mut should_skip = false;
40
41        match flavor {
42            GerberFlavor::KiCad => {
43                if line.contains("%ADD") || line.contains("G54D") {
44                    should_skip = true;
45                }
46            }
47            GerberFlavor::Altium => {
48                // Skip if line already contains G54D
49                if line.contains("G54D") {
50                    should_skip = true;
51                }
52
53                // Skip lines starting with G01/G02/G36/G37 with coordinates or single Dxx*
54                if line.starts_with("G01")
55                    || line.starts_with("G02")
56                    || line.starts_with("G36")
57                    || line.starts_with("G37")
58                {
59                    let trimmed = line.trim();
60                    // Skip lines that are only Dxx* or contain coordinates
61                    if trimmed == "D01*"
62                        || trimmed == "D02*"
63                        || trimmed == "D03*"
64                        || trimmed.contains("X")
65                        || trimmed.contains("Y")
66                    {
67                        should_skip = true;
68                    }
69                }
70            }
71        }
72
73        if should_skip {
74            processed_lines.push(line.to_string());
75        } else {
76            let modified_line = DCODE_REGEX.replace_all(line, "G54$1");
77            processed_lines.push(modified_line.to_string());
78        }
79    }
80
81    processed_lines.join("\n")
82}