From 794e7eacad577899a4e6f25a490484840f280f1b Mon Sep 17 00:00:00 2001 From: Colin Norris Date: Thu, 15 Jun 2023 09:05:58 +0100 Subject: [PATCH] Initial upload --- .gitignore | 34 ++++++++++ Program.cs | 127 +++++++++++++++++++++++++++++++++++ wks_converter.code-workspace | 8 +++ wks_converter.csproj | 10 +++ 4 files changed, 179 insertions(+) create mode 100644 .gitignore create mode 100644 Program.cs create mode 100644 wks_converter.code-workspace create mode 100644 wks_converter.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3dcc2fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# From .NET Core 3.0 you can use the command: `dotnet new gitignore` to generate a customizable .gitignore file + +*.swp +*.*~ +project.lock.json +.DS_Store +*.pyc + +# Visual Studio Code +.vscode + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +msbuild.log +msbuild.err +msbuild.wrn + +# Visual Studio 2015 +.vs/ \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..981cab1 --- /dev/null +++ b/Program.cs @@ -0,0 +1,127 @@ +/* + * This program takes a pcbnew file, and converts the straight lines and text on + * the User.Drawings layer to the kicad_wks format. So you could import a DXF + * format template/title block to pcbnew, then save the file and use this + * program to convert it for use in eeschema and pcbnew. You will need to add + * fields manually using pl_editor still. + * + * Curves are ignored because pl_editor doesn't support them, I ended up + * replacing the curves with straight segments in a separate program and + * inserting the result into pcbnew. Naturally it would be better if this + * program could do that for you. + * + * Only suitable for KiCad 7.0 files and possibly later versions. + */ + +using System.Text.RegularExpressions; + +namespace wks_converter +{ + class Program + { + const int decimalPlacesSupportedInWKSFile = 4; + const float lineWidth = 0.075f; // TODO make this an arg + const float textLineWidth = 0.075f; // TODO make this an arg + + const int pageWidthMM = 594; // TODO make this an arg or parse the page size from the pcbnew file and look it up + const int pageHeightMM = 420; + + static readonly Regex graphicalLineRegex = new Regex(@"\(gr_line \(start ([\d.]+) ([\d.]+)\) \(end ([\d.]+) ([\d.]+)\)"); + static readonly Regex graphicalTextRegex = new Regex(@"\(gr_text \""([^""]+)\"" \(at ([\d.]+) ([\d.]+)\) \(layer ""Dwgs.User""\) \(tstamp [\d\w-]+\)[\t\n\r ]+\(effects \(font \(size ([\d.]+) ([\d.]+)\) \(thickness ([\d.]+)\) *([\w ]*)\) \(justify ([\w ]+)\)\)[\t\n\r ]+\)"); + + static void WriteOutputFileHeader(StreamWriter writer) + { + writer.WriteLine($@"(kicad_wks (version 20220228) (generator pl_editor)"); + writer.WriteLine($@" (setup (textsize 1.5 1.5)(linewidth {lineWidth})(textlinewidth {textLineWidth})"); + writer.WriteLine($@" (left_margin 0)(right_margin 0)(top_margin 0)(bottom_margin 0))"); + } + + static void WriteOutputFileFooter(StreamWriter writer) + { + writer.WriteLine($@")"); + } + + static double ParseDouble(string input) + { + double v = Double.Parse(input); + return Math.Round(v, decimalPlacesSupportedInWKSFile); + } + + static (double X, double Y) ParseVector2(string inputX, string inputY) + { + return (ParseDouble(inputX), ParseDouble(inputY)); + } + + static (double X, double Y) ParsePageCoords(string inputX, string inputY) + { + return (pageWidthMM - ParseDouble(inputX), pageHeightMM - ParseDouble(inputY)); + } + + static void ParseGraphicalLines(string fileContents, StreamWriter writer) + { + MatchCollection matches = graphicalLineRegex.Matches(fileContents); + + foreach (Match match in matches) + { + GroupCollection groups = match.Groups; + + var start = ParsePageCoords(groups[1].Value, groups[2].Value); + var end = ParsePageCoords(groups[3].Value, groups[4].Value); + + writer.WriteLine($@" (line (name """") (start {start.X} {start.Y}) (end {end.X} {end.Y}))"); + } + } + + static void ParseGraphicalTextItems(string fileContents, StreamWriter writer) + { + MatchCollection matches = graphicalTextRegex.Matches(fileContents); + + foreach (Match match in matches) + { + GroupCollection groups = match.Groups; + + string text = groups[1].Value; + + var start = ParsePageCoords(groups[2].Value, groups[3].Value); + var fontSize = ParseVector2(groups[5].Value, groups[4].Value); + var fontThickness = ParseDouble(groups[6].Value); + string style = groups[7].Value; + string justification = groups[8].Value; + + text = text.Replace("{dblquote}", "\\\""); + + writer.WriteLine($@" (tbtext ""{text}"" (name """") (pos {start.X} {start.Y})(font (size {fontSize.X} {fontSize.Y}) {style}) (justify {justification}))"); + } + } + + static void Main(string[] args) + { + if (args.Length != 2) + { + Console.WriteLine("Specify input file path, then output file path. e.g. infile.kicad_pcb outfile.kicad_wks"); + return; + } + + string inputFilePath = args[0]; + string outputFilePath = args[1]; + + string fileContents = File.ReadAllText(inputFilePath); + + using (StreamWriter writer = new StreamWriter(outputFilePath)) + { + WriteOutputFileHeader(writer); + + /* I will parse the whole file in one go rather than line by + line, because some records take up more than one line. */ + + ParseGraphicalLines(fileContents, writer); + ParseGraphicalTextItems(fileContents, writer); + + WriteOutputFileFooter(writer); + writer.Close(); + } + + Console.WriteLine("Finished"); + } + } +} \ No newline at end of file diff --git a/wks_converter.code-workspace b/wks_converter.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/wks_converter.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/wks_converter.csproj b/wks_converter.csproj new file mode 100644 index 0000000..f02677b --- /dev/null +++ b/wks_converter.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + +