/**
 * @file DXF_plotter.cpp
 * @brief Kicad: specialized plotter for DXF files format
 */
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <ranges>
#include <plotters/plotter_dxf.h>
#include <macros.h>
#include <string_utils.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_rect.h>
#include <trigo.h>
#include <fmt/core.h>
#include <algorithm>

/**
 * Oblique angle for DXF native text
 * (I don't remember if 15 degrees is the ISO value... it looks nice anyway)
 */
static const double DXF_OBLIQUE_ANGLE = 15;

// No support for linewidths in DXF
#define DXF_LINE_WIDTH DO_NOT_SET_LINE_WIDTH

/**
 * The layer/colors palette.
 *
 * The acad/DXF palette is divided in 3 zones:
 *
 *  - The primary colors (1 - 9)
 *  - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
 *  - Greys (251 - 255)

 * There is *no* black... the white does it on paper, usually, and anyway it depends on the
 * plotter configuration, since DXF colors are meant to be logical only (they represent *both*
 * line color and width); later version with plot styles only complicate the matter!
 *
 * As usual, brown and magenta/purple are difficult to place since they are actually variations
 * of other colors.
 */
static const struct
{
    const char* name;
    int         index;
} acad_dxf_color_names[]=
{
    { "BLACK",             250 },
    { "RED",               14  },
    { "YELLOW",            52  },
    { "GREEN",             94  },
    { "CYAN",              134 },
    { "BLUE",              174 },
    { "MAGENTA",           214 },
    { "WHITE",             250 },
    { "BROWN",             54  },
    { "ORANGE",            32  },
    { "LIGHTRED",          12  },
    { "LIGHTYELLOW",       51  },
    { "LIGHTGREEN",        92  },
    { "LIGHTCYAN",         132 },
    { "LIGHTBLUE",         172 },
    { "LIGHTMAGENTA",      212 },
    { "LIGHTORANGE",       30  },
    { "LIGHTGRAY",         9   },
    { "DARKRED",           16  },
    { "DARKYELLOW",        41  },
    { "DARKGREEN",         96  },
    { "DARKCYAN",          136 },
    { "DARKBLUE",          176 },
    { "DARKMAGENTA",       216 },
    { "DARKBROWN",         56  },
    { "DARKORANGE",        34  },
    { "DARKGRAY",          8   },
    { "PURERED",           1   },
    { "PUREYELLOW",        2   },
    { "PUREGREEN",         3   },
    { "PURECYAN",          4   },
    { "PUREBLUE",          5   },
    { "PUREMAGENTA",       6   },
    { "PUREORANGE",        40  },
    { "PUREGRAY",          57  },
    { "REDONE",            11  },
    { "REDTWO",            13  },
    { "REDTHREE",          15  },
    { "REDFOUR",           17  },
    { "REDFIVE",           18  },
    { "REDSIX",            19  },
    { "ORANGEONE",         20  },
    { "ORANGETWO",         21  },
    { "ORANGETHREE",       22  },
    { "ORANGEFOUR",        23  },
    { "ORANGEFIVE",        24  },
    { "REDSEVEN",          25  },
    { "REDEIGHT",          26  },
    { "ORANGESIX",         27  },
    { "REDNINE",           28  },
    { "ORANGESEVEN",       29  },
    { "ORANGEEIGHT",       31  },
    { "ORANGENINE",        33  },
    { "ORANGETEN",         35  },
    { "ORANGEELEVEN",      36  },
    { "ORANGETWELVE",      37  },
    { "ORANGETHIRTEEN",    38  },
    { "ORANGEFOURTEEN",    39  },
    { "YELLOWONE",         42  },
    { "YELLOWTWO",         43  },
    { "YELLOWTHREE",       44  },
    { "YELLOWFOUR",        45  },
    { "YELLOWFIVE",        46  },
    { "YELLOWSIX",         47  },
    { "YELLOWSEVEN",       48  },
    { "YELLOWEIGHT",       49  },
    { "YELLOWNINE",        53  },
    { "YELLOWTEN",         55  },
    { "YELLOWELEVEN",      58  },
    { "YELLOWTWELVE",      59  },
    { "YELLOWTHIRTEEN",    60  },
    { "YELLOWFOURTEEN",    61  },
    { "GREENONE",          62  },
    { "GREENTWO",          63  },
    { "GREENTHREE",        64  },
    { "GREENFOUR",         65  },
    { "GREENFIVE",         66  },
    { "GREENSIX",          67  },
    { "GREENSEVEN",        68  },
    { "GREENEIGHT",        69  },
    { "GREENNINE",         70  },
    { "GREENTEN",          71  },
    { "GREENELEVEN",       72  },
    { "GREENTWELVE",       73  },
    { "GREENTHIRTEEN",     74  },
    { "GREENFOURTEEN",     75  },
    { "GREENFIFTEEN",      76  },
    { "GREENSIXTEEN",      77  },
    { "GREENSEVENTEEN",    78  },
    { "GREENEIGHTEEN",     79  },
    { "GREENNINETEEN",     80  },
    { "GREENTWENTY",       81  },
    { "GREENTWENTYONE",    82  },
    { "GREENTWENTYTWO",    83  },
    { "GREENTWENTYTHREE",  84  },
    { "GREENTWENTYFOUR",   85  },
    { "GREENTWENTYFIVE",   86  },
    { "GREENTWENTYSIX",    87  },
    { "GREENTWENTYSEVEN",  88  },
    { "GREENTWENTYEIGHT",  89  },
    { "GREENTWENTYNINE",   90  },
    { "GREENTHIRTY",       91  },
    { "GREENTHIRTYONE",    93  },
    { "GREENTHIRTYTWO",    95  },
    { "GREENTHIRTYTHREE",  97  },
    { "GREENTHIRTYFOUR",   98  },
    { "GREENTHIRTYFIVE",   99  },
    { "GREENTHIRTYSIX",    100 },
    { "GREENTHIRTYSEVEN",  101 },
    { "GREENTHIRTYEIGHT",  102 },
    { "GREENTHIRTYNINE",   103 },
    { "GREENFORTY",        104 },
    { "GREENFORTYONE",     105 },
    { "GREENFORTYTWO",     106 },
    { "GREENFORTYTHREE",   107 },
    { "GREENFORTYFOUR",    108 },
    { "GREENFORTYFIVE",    109 },
    { "GREENFORTYSIX",     110 },
    { "GREENFORTYSEVEN",   111 },
    { "GREENFORTYEIGHT",   112 },
    { "GREENFORTYNINE",    113 },
    { "GREENFIFTY",        114 },
    { "GREENFIFTYONE",     115 },
    { "GREENFIFTYTWO",     116 },
    { "GREENFIFTYTHREE",   117 },
    { "GREENFIFTYFOUR",    118 },
    { "GREENFIFTYFIVE",    119 },
    { "GREENFIFTYSIX",     120 },
    { "GREENFIFTYSEVEN",   121 },
    { "GREENFIFTYEIGHT",   122 },
    { "GREENFIFTYNINE",    123 },
    { "GREENSIXTY",        124 },
    { "GREENSIXTYONE",     125 },
    { "GREENSIXTYTWO",     126 },
    { "GREENSIXTYTHREE",   127 },
    { "GREENSIXTYFOUR",    128 },
    { "GREENSIXTYFIVE",    129 },
    { "CYANONE",           131 },
    { "CYANTWO",           133 },
    { "CYANTHREE",         135 },
    { "CYANFOUR",          137 },
    { "CYANFIVE",          138 },
    { "CYANSIX",           139 },
    { "CYANSEVEN",         140 },
    { "BLUEONE",           141 },
    { "BLUETWO",           142 },
    { "BLUETHREE",         143 },
    { "BLUEFOUR",          144 },
    { "BLUEFIVE",          145 },
    { "BLUESIX",           146 },
    { "BLUESEVEN",         147 },
    { "BLUEEIGHT",         148 },
    { "BLUENINE",          149 },
    { "BLUETEN",           150 },
    { "BLUEELEVEN",        151 },
    { "BLUETWELVE",        152 },
    { "BLUETHIRTEEN",      153 },
    { "BLUEFOURTEEN",      154 },
    { "BLUEFIFTEEN",       155 },
    { "BLUESIXTEEN",       156 },
    { "BLUESEVENTEEN",     157 },
    { "BLUEEIGHTEEN",      158 },
    { "BLUENINETEEN",      159 },
    { "BLUETWENTY",        160 },
    { "BLUETWENTYONE",     161 },
    { "BLUETWENTYTWO",     162 },
    { "BLUETWENTYTHREE",   163 },
    { "BLUETWENTYFOUR",    164 },
    { "BLUETWENTYFIVE",    165 },
    { "BLUETWENTYSIX",     166 },
    { "BLUETWENTYSEVEN",   167 },
    { "BLUETWENTYEIGHT",   168 },
    { "BLUETWENTYNINE",    169 },
    { "BLUETHIRTY",        170 },
    { "BLUETHIRTYONE",     171 },
    { "BLUETHIRTYTWO",     177 },
    { "BLUETHIRTYETHREE",  178 },
    { "BLUETHIRTYFOUR",    179 },
    { "VIOLETONE",         180 },
    { "VIOLETTWO",         181 },
    { "VIOLETTHREE",       182 },
    { "VIOLETFOUR",        183 },
    { "VIOLETFIVE",        184 },
    { "VIOLETSIX",         185 },
    { "VIOLETSEVEN",       186 },
    { "VIOLETEIGHT",       187 },
    { "VIOLETNINE",        188 },
    { "VIOLETTEN",         189 },
    { "VIOLETELEVEN",      190 },
    { "VIOLETTWELVE",      191 },
    { "VIOLETTHIRTEEN",    192 },
    { "VIOLETFOURTEEN",    193 },
    { "VIOLETFIFTEEN",     194 },
    { "VIOLETSIXTEEN",     195 },
    { "VIOLETSEVENTEEN",   196 },
    { "VIOLETEIGHTEEN",    197 },
    { "VIOLETNINETEEN",    198 },
    { "VIOLETTWENTY",      199 },
    { "VIOLETTWENTYONE",   200 },
    { "VIOLETTWENTYTWO",   201 },
    { "VIOLETTWENTYTHREE", 202 },
    { "VIOLETTWENTYFOUR",  203 },
    { "VIOLETTWENTYFIVE",  204 },
    { "VIOLETTWENTYSIX",   205 },
    { "VIOLETTWENTYSEVEN", 206 },
    { "VIOLETTWENTYEIGHT", 207 },
    { "VIOLETTWENTYNINE",  208 },
    { "VIOLETTHIRTY",      209 },
    { "MAGENTAONE",        210 },
    { "MAGENTATWO",        211 },
    { "MAGENTATHREE",      213 },
    { "MAGENTAFOUR",       215 },
    { "MAGENTAFIVE",       217 },
    { "MAGENTASIX",        218 },
    { "MAGENTASEVEN",      219 },
    { "MAGENTAEIGHT",      220 },
    { "MAGENTANINE",       221 },
    { "MAGENTATEN",        222 },
    { "MAGENTAELEVEN",     223 },
    { "MAGENTATWELVE",     224 },
    { "MAGENTATHIRTEEN",   225 },
    { "MAGENTAFOURTEEN",   226 },
    { "REDTEN",            227 },
    { "REDELEVEN",         228 },
    { "VIOLETFIFTEEN",     229 },
    { "REDTWELVE",         230 },
    { "REDTHIRTEEN",       231 },
    { "REDFOURTEEN",       232 },
    { "REDFIFTEEN",        233 },
    { "REDSIXTEEN",        234 },
    { "REDSEVENTEEN",      235 },
    { "REDEIGHTEEN",       236 },
    { "REDNINETEEN",       237 },
    { "REDTWENTY",         238 },
    { "REDTWENTYONE",      239 },
    { "REDTWENTYTWO",      240 },
    { "REDTWENTYTHREE",    241 },
    { "REDTWENTYFOUR",     242 },
    { "REDTWENTYFIVE",     243 },
    { "REDTWENTYSIX",      244 },
    { "REDTWENTYSEVEN",    245 },
    { "REDTWENTYEIGHT",    246 },
    { "REDTWENTYNINE",     247 },
    { "REDTHIRTY",         248 },
    { "REDTHIRTYONE",      249 },
    { "GRAYONE",           251 },
    { "GRAYTWO",           252 },
    { "GRAYTHREE",         253 },
    { "GRAYFOUR",          254 }
};

// Array of predefined DXF color values, each entry containing blue, green, red components and a corresponding color number.
static const struct
{
    int         blue;
    int         green;
    int         red;
    DXF_COLOR_T colorNumber;
} acad_dxf_color_values[] =
{
    { 0,    0,    0,    DXF_COLOR_T::BLACK              },
    { 0,    0,    127,  DXF_COLOR_T::RED,               },
    { 0,    165,  165,  DXF_COLOR_T::YELLOW,            },
    { 0,    127,  0,    DXF_COLOR_T::GREEN,             },
    { 127,  127,  0,    DXF_COLOR_T::CYAN,              },
    { 127,  0,    0,    DXF_COLOR_T::BLUE,              },
    { 127,  0,    127,  DXF_COLOR_T::MAGENTA,           },
    { 255,  255,  255,  DXF_COLOR_T::WHITE,             },
    { 0,    127,  127,  DXF_COLOR_T::BROWN,             },
    { 0,    82,   165,  DXF_COLOR_T::ORANGE,            },
    { 0,    0,    165,  DXF_COLOR_T::LIGHTRED,          },
    { 127,  255,  255,  DXF_COLOR_T::LIGHTYELLOW,       },
    { 0,    165,  0,    DXF_COLOR_T::LIGHTGREEN,        },
    { 165,  165,  0,    DXF_COLOR_T::LIGHTCYAN,         },
    { 165,  0,    0,    DXF_COLOR_T::LIGHTBLUE,         },
    { 165,  0,    165,  DXF_COLOR_T::LIGHTMAGENTA,      },
    { 0,    127,  255,  DXF_COLOR_T::LIGHTORANGE,       },
    { 192,  192,  192,  DXF_COLOR_T::LIGHTGRAY,         },
    { 0,    0,    76,   DXF_COLOR_T::DARKRED,           },
    { 127,  223,  255,  DXF_COLOR_T::DARKYELLOW,        },
    { 0,    76,   0,    DXF_COLOR_T::DARKGREEN,         },
    { 76,   76,   0,    DXF_COLOR_T::DARKCYAN,          },
    { 76,   0,    0,    DXF_COLOR_T::DARKBLUE,          },
    { 76,   0,    76,   DXF_COLOR_T::DARKMAGENTA,       },
    { 0,    76,   76,   DXF_COLOR_T::DARKBROWN,         },
    { 0,    63,   127,  DXF_COLOR_T::DARKORANGE,        },
    { 128,  128,  128,  DXF_COLOR_T::DARKGRAY,          },
    { 0,    0,    255,  DXF_COLOR_T::PURERED,           },
    { 0,    255,  255,  DXF_COLOR_T::PUREYELLOW,        },
    { 0,    255,  0,    DXF_COLOR_T::PUREGREEN,         },
    { 255,  255,  0,    DXF_COLOR_T::PURECYAN,          },
    { 255,  0,    0,    DXF_COLOR_T::PUREBLUE,          },
    { 255,  0,    255,  DXF_COLOR_T::PUREMAGENTA,       },
    { 0,    191,  255,  DXF_COLOR_T::PUREORANGE,        },
    { 38,   76,   76,   DXF_COLOR_T::PUREGRAY,          },
    { 127,  127,  255,  DXF_COLOR_T::REDONE,            },
    { 82,   82,   165,  DXF_COLOR_T::REDTWO,            },
    { 63,   63,   127,  DXF_COLOR_T::REDTHREE,          },
    { 38,   38,   76,   DXF_COLOR_T::REDFOUR,           },
    { 0,    0,    38,   DXF_COLOR_T::REDFIVE,           },
    { 19,   19,   38,   DXF_COLOR_T::REDSIX,            },
    { 0,    63,   255,  DXF_COLOR_T::ORANGEONE,         },
    { 127,  159,  255,  DXF_COLOR_T::ORANGETWO,         },
    { 0,    41,   165,  DXF_COLOR_T::ORANGETHREE,       },
    { 82,   103,  165,  DXF_COLOR_T::ORANGEFOUR,        },
    { 0,    31,   127,  DXF_COLOR_T::ORANGEFIVE,        },
    { 63,   79,   127,  DXF_COLOR_T::REDSEVEN,          },
    { 0,    19,   76,   DXF_COLOR_T::REDEIGHT,          },
    { 38,   47,   76,   DXF_COLOR_T::ORANGESIX,         },
    { 0,    9,    38,   DXF_COLOR_T::REDNINE,           },
    { 19,   23,   38,   DXF_COLOR_T::ORANGESEVEN,       },
    { 127,  191,  255,  DXF_COLOR_T::ORANGEEIGHT,       },
    { 82,   124,  165,  DXF_COLOR_T::ORANGENINE,        },
    { 63,   95,   127,  DXF_COLOR_T::ORANGETEN,         },
    { 0,    38,   76,   DXF_COLOR_T::ORANGEELEVEN,      },
    { 38,   57,   76,   DXF_COLOR_T::ORANGETWELVE,      },
    { 0,    19,   38,   DXF_COLOR_T::ORANGETHIRTEEN,    },
    { 19,   28,   38,   DXF_COLOR_T::ORANGEFOURTEEN,    },
    { 0,    124,  165,  DXF_COLOR_T::YELLOWONE,         },
    { 82,   145,  165,  DXF_COLOR_T::YELLOWTWO,         },
    { 0,    95,   127,  DXF_COLOR_T::YELLOWTHREE,       },
    { 63,   111,  127,  DXF_COLOR_T::YELLOWFOUR,        },
    { 0,    57,   76,   DXF_COLOR_T::YELLOWFIVE,        },
    { 38,   66,   76,   DXF_COLOR_T::YELLOWSIX,         },
    { 0,    28,   38,   DXF_COLOR_T::YELLOWSEVEN,       },
    { 19,   33,   38,   DXF_COLOR_T::YELLOWEIGHT,       },
    { 82,   165,  165,  DXF_COLOR_T::YELLOWNINE,        },
    { 63,   127,  127,  DXF_COLOR_T::YELLOWTEN,         },
    { 0,    38,   38,   DXF_COLOR_T::YELLOWELEVEN,      },
    { 19,   38,   38,   DXF_COLOR_T::YELLOWTWELVE,      },
    { 0,    255,  191,  DXF_COLOR_T::YELLOWTHIRTEEN,    },
    { 127,  255,  223,  DXF_COLOR_T::YELLOWFOURTEEN,    },
    { 0,    165,  124,  DXF_COLOR_T::GREENONE,          },
    { 82,   165,  145,  DXF_COLOR_T::GREENTWO,          },
    { 0,    127,  95,   DXF_COLOR_T::GREENTHREE,        },
    { 63,   127,  111,  DXF_COLOR_T::GREENFOUR,         },
    { 0,    76,   57,   DXF_COLOR_T::GREENFIVE,         },
    { 38,   76,   66,   DXF_COLOR_T::GREENSIX,          },
    { 0,    38,   28,   DXF_COLOR_T::GREENSEVEN,        },
    { 19,   38,   33,   DXF_COLOR_T::GREENEIGHT,        },
    { 0,    255,  127,  DXF_COLOR_T::GREENNINE,         },
    { 127,  255,  191,  DXF_COLOR_T::GREENTEN,          },
    { 0,    165,  82,   DXF_COLOR_T::GREENELEVEN,       },
    { 82,   165,  124,  DXF_COLOR_T::GREENTWELVE,       },
    { 0,    127,  63,   DXF_COLOR_T::GREENTHIRTEEN,     },
    { 63,   127,  95,   DXF_COLOR_T::GREENFOURTEEN,     },
    { 0,    76,   38,   DXF_COLOR_T::GREENFIFTEEN,      },
    { 38,   76,   57,   DXF_COLOR_T::GREENSIXTEEN,      },
    { 0,    38,   19,   DXF_COLOR_T::GREENSEVENTEEN,    },
    { 19,   38,   28,   DXF_COLOR_T::GREENEIGHTEEN,     },
    { 0,    255,  63,   DXF_COLOR_T::GREENNINETEEN,     },
    { 127,  255,  159,  DXF_COLOR_T::GREENTWENTY,       },
    { 0,    165,  41,   DXF_COLOR_T::GREENTWENTYONE,    },
    { 82,   165,  103,  DXF_COLOR_T::GREENTWENTYTWO,    },
    { 0,    127,  31,   DXF_COLOR_T::GREENTWENTYTHREE,  },
    { 63,   127,  79,   DXF_COLOR_T::GREENTWENTYFOUR,   },
    { 0,    76,   19,   DXF_COLOR_T::GREENTWENTYFIVE,   },
    { 38,   76,   47,   DXF_COLOR_T::GREENTWENTYSIX,    },
    { 0,    38,   9,    DXF_COLOR_T::GREENTWENTYSEVEN,  },
    { 19,   38,   23,   DXF_COLOR_T::GREENTWENTYEIGHT,  },
    { 0,    255,  0,    DXF_COLOR_T::GREENTWENTYNINE,   },
    { 127,  255,  127,  DXF_COLOR_T::GREENTHIRTY,       },
    { 82,   165,  82,   DXF_COLOR_T::GREENTHIRTYONE,    },
    { 63,   127,  63,   DXF_COLOR_T::GREENTHIRTYTWO,    },
    { 38,   76,   38,   DXF_COLOR_T::GREENTHIRTYTHREE,  },
    { 0,    38,   0,    DXF_COLOR_T::GREENTHIRTYFOUR,   },
    { 19,   38,   19,   DXF_COLOR_T::GREENTHIRTYFIVE,   },
    { 63,   255,  0,    DXF_COLOR_T::GREENTHIRTYSIX,    },
    { 159,  255,  127,  DXF_COLOR_T::GREENTHIRTYSEVEN,  },
    { 41,   165,  0,    DXF_COLOR_T::GREENTHIRTYEIGHT,  },
    { 103,  165,  82,   DXF_COLOR_T::GREENTHIRTYNINE,   },
    { 31,   127,  0,    DXF_COLOR_T::GREENFORTY,        },
    { 79,   127,  63,   DXF_COLOR_T::GREENFORTYONE,     },
    { 19,   76,   0,    DXF_COLOR_T::GREENFORTYTWO,     },
    { 47,   76,   38,   DXF_COLOR_T::GREENFORTYTHREE,   },
    { 9,    38,   0,    DXF_COLOR_T::GREENFORTYFOUR,    },
    { 23,   88,   19,   DXF_COLOR_T::GREENFORTYFIVE,    },
    { 127,  255,  0,    DXF_COLOR_T::GREENFORTYSIX,     },
    { 191,  255,  127,  DXF_COLOR_T::GREENFORTYSEVEN,   },
    { 82,   165,  0,    DXF_COLOR_T::GREENFORTYEIGHT,   },
    { 95,   127,  63,   DXF_COLOR_T::GREENFORTYNINE,    },
    { 63,   127,  0,    DXF_COLOR_T::GREENFIFTY,        },
    { 95,   127,  63,   DXF_COLOR_T::GREENFIFTYONE,     },
    { 38,   76,   0,    DXF_COLOR_T::GREENFIFTYTWO,     },
    { 57,   76,   38,   DXF_COLOR_T::GREENFIFTYTHREE,   },
    { 19,   38,   0,    DXF_COLOR_T::GREENFIFTYFOUR,    },
    { 28,   88,   19,   DXF_COLOR_T::GREENFIFTYFIVE,    },
    { 191,  255,  0,    DXF_COLOR_T::GREENFIFTYSIX,     },
    { 223,  255,  127,  DXF_COLOR_T::GREENFIFTYSEVEN,   },
    { 124,  165,  0,    DXF_COLOR_T::GREENFIFTYEIGHT,   },
    { 145,  165,  82,   DXF_COLOR_T::GREENFIFTYNINE,    },
    { 95,   127,  0,    DXF_COLOR_T::GREENSIXTY,        },
    { 111,  127,  63,   DXF_COLOR_T::GREENSIXTYONE,     },
    { 57,   76,   0,    DXF_COLOR_T::GREENSIXTYTWO,     },
    { 66,   76,   38,   DXF_COLOR_T::GREENSIXTYTHREE,   },
    { 28,   38,   0,    DXF_COLOR_T::GREENSIXTYFOUR,    },
    { 88,   88,   19,   DXF_COLOR_T::GREENSIXTYFIVE,    },
    { 255,  255,  127,  DXF_COLOR_T::CYANONE,           },
    { 165,  165,  82,   DXF_COLOR_T::CYANTWO,           },
    { 127,  127,  63,   DXF_COLOR_T::CYANTHREE,         },
    { 76,   76,   38,   DXF_COLOR_T::CYANFOUR,          },
    { 38,   38,   0,    DXF_COLOR_T::CYANFIVE,          },
    { 88,   88,   19,   DXF_COLOR_T::CYANSIX,           },
    { 255,  191,  0,    DXF_COLOR_T::CYANSEVEN,         },
    { 255,  223,  127,  DXF_COLOR_T::BLUEONE,           },
    { 165,  124,  0,    DXF_COLOR_T::BLUETWO,           },
    { 165,  145,  82,   DXF_COLOR_T::BLUETHREE,         },
    { 127,  95,   0,    DXF_COLOR_T::BLUEFOUR,          },
    { 127,  111,  63,   DXF_COLOR_T::BLUEFIVE,          },
    { 76,   57,   0,    DXF_COLOR_T::BLUESIX,           },
    { 126,  66,   38,   DXF_COLOR_T::BLUESEVEN,         },
    { 38,   28,   0,    DXF_COLOR_T::BLUEEIGHT,         },
    { 88,   88,   19,   DXF_COLOR_T::BLUENINE,          },
    { 255,  127,  0,    DXF_COLOR_T::BLUETEN,           },
    { 255,  191,  127,  DXF_COLOR_T::BLUEELEVEN,        },
    { 165,  82,   0,    DXF_COLOR_T::BLUETWELVE,        },
    { 165,  124,  82,   DXF_COLOR_T::BLUETHIRTEEN,      },
    { 127,  63,   0,    DXF_COLOR_T::BLUEFOURTEEN,      },
    { 127,  95,   63,   DXF_COLOR_T::BLUEFIFTEEN,       },
    { 76,   38,   0,    DXF_COLOR_T::BLUESIXTEEN,       },
    { 126,  57,   38,   DXF_COLOR_T::BLUESEVENTEEN,     },
    { 38,   19,   0,    DXF_COLOR_T::BLUEEIGHTEEN,      },
    { 88,   28,   19,   DXF_COLOR_T::BLUENINETEEN,      },
    { 255,  63,   0,    DXF_COLOR_T::BLUETWENTY,        },
    { 255,  159,  127,  DXF_COLOR_T::BLUETWENTYONE,     },
    { 165,  41,   0,    DXF_COLOR_T::BLUETWENTYTWO,     },
    { 165,  103,  82,   DXF_COLOR_T::BLUETWENTYTHREE,   },
    { 127,  31,   0,    DXF_COLOR_T::BLUETWENTYFOUR,    },
    { 127,  79,   63,   DXF_COLOR_T::BLUETWENTYFIVE,    },
    { 76,   19,   0,    DXF_COLOR_T::BLUETWENTYSIX,     },
    { 126,  47,   38,   DXF_COLOR_T::BLUETWENTYSEVEN,   },
    { 38,   9,    0,    DXF_COLOR_T::BLUETWENTYEIGHT,   },
    { 88,   23,   19,   DXF_COLOR_T::BLUETWENTYNINE,    },
    { 255,  0,    0,    DXF_COLOR_T::BLUETHIRTY,        },
    { 255,  127,  127,  DXF_COLOR_T::BLUETHIRTYONE,     },
    { 126,  38,   38,   DXF_COLOR_T::BLUETHIRTYTWO,     },
    { 38,   0,    0,    DXF_COLOR_T::BLUETHIRTYETHREE,  },
    { 88,   19,   19,   DXF_COLOR_T::BLUETHIRTYFOUR,    },
    { 255,  0,    63,   DXF_COLOR_T::VIOLETONE,         },
    { 255,  127,  159,  DXF_COLOR_T::VIOLETTWO,         },
    { 165,  0,    41,   DXF_COLOR_T::VIOLETTHREE,       },
    { 165,  82,   103,  DXF_COLOR_T::VIOLETFOUR,        },
    { 127,  0,    31,   DXF_COLOR_T::VIOLETFIVE,        },
    { 127,  63,   79,   DXF_COLOR_T::VIOLETSIX,         },
    { 76,   0,    19,   DXF_COLOR_T::VIOLETSEVEN,       },
    { 126,  38,   47,   DXF_COLOR_T::VIOLETEIGHT,       },
    { 38,   0,    9,    DXF_COLOR_T::VIOLETNINE,        },
    { 88,   19,   23,   DXF_COLOR_T::VIOLETTEN,         },
    { 255,  0,    127,  DXF_COLOR_T::VIOLETELEVEN,      },
    { 255,  127,  191,  DXF_COLOR_T::VIOLETTWELVE,      },
    { 165,  0,    82,   DXF_COLOR_T::VIOLETTHIRTEEN,    },
    { 165,  82,   124,  DXF_COLOR_T::VIOLETFOURTEEN,    },
    { 127,  0,    63,   DXF_COLOR_T::VIOLETFIFTEEN,     },
    { 127,  63,   95,   DXF_COLOR_T::VIOLETSIXTEEN,     },
    { 76,   0,    38,   DXF_COLOR_T::VIOLETSEVENTEEN,   },
    { 126,  38,   57,   DXF_COLOR_T::VIOLETEIGHTEEN,    },
    { 38,   0,    19,   DXF_COLOR_T::VIOLETNINETEEN,    },
    { 88,   19,   28,   DXF_COLOR_T::VIOLETTWENTY,      },
    { 255,  0,    191,  DXF_COLOR_T::VIOLETTWENTYONE,   },
    { 255,  127,  223,  DXF_COLOR_T::VIOLETTWENTYTWO,   },
    { 165,  0,    124,  DXF_COLOR_T::VIOLETTWENTYTHREE, },
    { 165,  82,   145,  DXF_COLOR_T::VIOLETTWENTYFOUR,  },
    { 127,  0,    95,   DXF_COLOR_T::VIOLETTWENTYFIVE,  },
    { 127,  63,   111,  DXF_COLOR_T::VIOLETTWENTYSIX,   },
    { 76,   0,    57,   DXF_COLOR_T::VIOLETTWENTYSEVEN, },
    { 76,   38,   66,   DXF_COLOR_T::VIOLETTWENTYEIGHT, },
    { 38,   0,    28,   DXF_COLOR_T::VIOLETTWENTYNINE,  },
    { 88,   19,   88,   DXF_COLOR_T::VIOLETTHIRTY,      },
    { 255,  0,    255,  DXF_COLOR_T::MAGENTAONE,        },
    { 255,  127,  255,  DXF_COLOR_T::MAGENTATWO,        },
    { 165,  82,   165,  DXF_COLOR_T::MAGENTATHREE,      },
    { 127,  63,   127,  DXF_COLOR_T::MAGENTAFOUR,       },
    { 76,   38,   76,   DXF_COLOR_T::MAGENTAFIVE,       },
    { 38,   0,    38,   DXF_COLOR_T::MAGENTASIX,        },
    { 88,   19,   88,   DXF_COLOR_T::MAGENTASEVEN,      },
    { 191,  0,    255,  DXF_COLOR_T::MAGENTAEIGHT,      },
    { 223,  127,  255,  DXF_COLOR_T::MAGENTANINE,       },
    { 124,  0,    165,  DXF_COLOR_T::MAGENTATEN,        },
    { 145,  82,   165,  DXF_COLOR_T::MAGENTAELEVEN,     },
    { 95,   0,    127,  DXF_COLOR_T::MAGENTATWELVE,     },
    { 111,  63,   127,  DXF_COLOR_T::MAGENTATHIRTEEN,   },
    { 57,   0,    76,   DXF_COLOR_T::MAGENTAFOURTEEN,   },
    { 66,   38,   76,   DXF_COLOR_T::REDTEN,            },
    { 28,   0,    38,   DXF_COLOR_T::REDELEVEN,         },
    { 88,   19,   88,   DXF_COLOR_T::VIOLETFIFTEEN,     },
    { 127,  0,    255,  DXF_COLOR_T::REDTWELVE,         },
    { 191,  127,  255,  DXF_COLOR_T::REDTHIRTEEN,       },
    { 82,   0,    165,  DXF_COLOR_T::REDFOURTEEN,       },
    { 124,  82,   165,  DXF_COLOR_T::REDFIFTEEN,        },
    { 63,   0,    127,  DXF_COLOR_T::REDSIXTEEN,        },
    { 95,   63,   127,  DXF_COLOR_T::REDSEVENTEEN,      },
    { 38,   0,    76,   DXF_COLOR_T::REDEIGHTEEN,       },
    { 57,   38,   76,   DXF_COLOR_T::REDNINETEEN,       },
    { 19,   0,    38,   DXF_COLOR_T::REDTWENTY,         },
    { 28,   19,   88,   DXF_COLOR_T::REDTWENTYONE,      },
    { 63,   0,    255,  DXF_COLOR_T::REDTWENTYTWO,      },
    { 159,  127,  255,  DXF_COLOR_T::REDTWENTYTHREE,    },
    { 41,   0,    165,  DXF_COLOR_T::REDTWENTYFOUR,     },
    { 103,  82,   165,  DXF_COLOR_T::REDTWENTYFIVE,     },
    { 31,   0,    127,  DXF_COLOR_T::REDTWENTYSIX,      },
    { 79,   63,   127,  DXF_COLOR_T::REDTWENTYSEVEN,    },
    { 19,   0,    76,   DXF_COLOR_T::REDTWENTYEIGHT,    },
    { 47,   38,   76,   DXF_COLOR_T::REDTWENTYNINE,     },
    { 9,    0,    38,   DXF_COLOR_T::REDTHIRTY,         },
    { 23,   19,   88,   DXF_COLOR_T::REDTHIRTYONE,      },
    { 101,  101,  101,  DXF_COLOR_T::GRAYONE,           },
    { 102,  102,  102,  DXF_COLOR_T::GRAYTWO,           },
    { 153,  153,  153,  DXF_COLOR_T::GRAYTHREE,         },
    { 204,  204,  204,  DXF_COLOR_T::GRAYFOUR,          }
};

static const char* getDXFLineType( LINE_STYLE aType )
{
    switch( aType )
    {
    case LINE_STYLE::DEFAULT:
    case LINE_STYLE::SOLID:
        return "CONTINUOUS";
    case LINE_STYLE::DASH:
        return "DASHED";
    case LINE_STYLE::DOT:
        return "DOTTED";
    case LINE_STYLE::DASHDOT:
        return "DASHDOT";
    case LINE_STYLE::DASHDOTDOT:
        return "DIVIDE";
    default:
        wxFAIL_MSG( "Unhandled LINE_STYLE" );
        return "CONTINUOUS";
    }
}

int DXF_PLOTTER::FindNearestLegacyColor( int aR, int aG, int aB )
{
    int nearestColorValueIndex = static_cast<int>(DXF_COLOR_T::BLACK);

    int nearestDistance = std::numeric_limits<int>::max();

    for( int trying = static_cast<int>(DXF_COLOR_T::BLACK); trying < static_cast<int>(DXF_COLOR_T::NBCOLORS); trying++ )
    {
        auto c = acad_dxf_color_values[trying];
        int  distance = ( aR - c.red ) * ( aR - c.red ) + ( aG - c.green ) * ( aG - c.green )
                       + ( aB - c.blue ) * ( aB - c.blue );

        if( distance < nearestDistance )
        {
            nearestDistance = distance;
            nearestColorValueIndex = trying;
        }
    }

    return nearestColorValueIndex;
}


/**
 * @brief Retrieves the current layer name or layer color name for DXF plotting.
 *
 * This function returns the appropriate layer name or layer color name depending on the specified
 * DXF_LAYER_OUTPUT_MODE. DXF files do not use RGB definitions for colors, so this function converts
 * the color to the nearest legacy color name acceptable in DXF files.
 *
 * @param mode The mode determining whether to return the layer name or layer color name.
 * @param layerId Optional parameter specifying the layer ID to use. If not provided, the current layer ID is used.
 * @return The layer name or color name as a wxString.
 *
 * The function operates in two main modes:
 * 1. Layer_Name or Current_Layer_Name: Returns the name of the specified layer or the current layer.
 *    - Searches through the `m_layersToExport` list to find the matching layer ID.
 *    - If the layer ID is found, returns the corresponding layer name.
 *    - If not found, defaults to "BLACK".
 *
 * 2. Layer_Color_Name or Current_Layer_Color_Name: Returns the color name of the specified layer or the current layer.
 *    - Retrieves the color of the layer from the render settings.
 *    - Finds the nearest legacy color that matches the layer's color.
 *    - Returns the name of the nearest legacy color.
 *
 * If the mode is unknown, returns "Unknown Mode".
 */
wxString DXF_PLOTTER::GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE aMode, std::optional<PCB_LAYER_ID> alayerId )
{
    PCB_LAYER_ID actualLayerId = ( alayerId.has_value() ) ? alayerId.value() : m_layer;

    switch( aMode )
    {
    case DXF_LAYER_OUTPUT_MODE::Layer_Name:
    case DXF_LAYER_OUTPUT_MODE::Current_Layer_Name:
    {
        if( m_layersToExport.empty() )
        {
            COLOR4D layerColor = ( aMode == DXF_LAYER_OUTPUT_MODE::Current_Layer_Name )
                                         ? m_currentColor
                                         : RenderSettings()->GetLayerColor( actualLayerId );

            int color = FindNearestLegacyColor(
                    int( layerColor.r * 255 ), int( layerColor.g * 255 ), int( layerColor.b * 255 ) );
            return wxString( acad_dxf_color_names[color].name );
        }

        auto it = std::find_if( m_layersToExport.begin(), m_layersToExport.end(),
                                [actualLayerId]( const std::pair<int, wxString>& element )
                                {
                                    return element.first == actualLayerId;
                                } );

        return ( it != m_layersToExport.end() ) ? it->second : wxString( "BLACK" );
    }

    case DXF_LAYER_OUTPUT_MODE::Layer_Color_Name:
    case DXF_LAYER_OUTPUT_MODE::Current_Layer_Color_Name:
    {
        COLOR4D layerColor = ( aMode == DXF_LAYER_OUTPUT_MODE::Current_Layer_Color_Name )
                                     ? RenderSettings()->GetLayerColor( GetLayer() )
                                     : RenderSettings()->GetLayerColor( actualLayerId );

        int color = FindNearestLegacyColor(
                int( layerColor.r * 255 ), int( layerColor.g * 255 ), int( layerColor.b * 255 ) );
        wxString cname( acad_dxf_color_names[color].name );
        return cname;
    }

    default: return wxString( "Unknown Mode" );
    }
}


void DXF_PLOTTER::SetUnits( DXF_UNITS aUnit )
{
    m_plotUnits = aUnit;

    switch( aUnit )
    {
    case DXF_UNITS::MM:
        m_unitScalingFactor = 0.00254;
        m_measurementDirective = 1;
        m_insUnits = 4;
        break;

    case DXF_UNITS::INCH:
    default:
        m_unitScalingFactor = 0.0001;
        m_measurementDirective = 0;
        m_insUnits = 1;
    }
}


// convert aValue to a string, and remove trailing zeros
// In DXF files coordinates need a high precision: at least 9 digits when given
// in inches and 7 digits when in mm.
// So we use 16 digits and remove trailing 0 (if any)
static std::string formatCoord( double aValue )
{
    std::string buf;

    buf = fmt::format( "{:.16f}", aValue );

    // remove trailing zeros
    while( !buf.empty() && buf[buf.size() - 1] == '0' )
    {
        buf.pop_back();
    }

    return buf;
}


void DXF_PLOTTER::SetViewport( const VECTOR2I& aOffset, double aIusPerDecimil,
                               double aScale, bool aMirror )
{
    m_plotOffset  = aOffset;
    m_plotScale   = aScale;

    /* DXF paper is 'virtual' so there is no need of a paper size.
       Also this way we can handle the aux origin which can be useful
       (for example when aligning to a mechanical drawing) */
    m_paperSize.x = 0;
    m_paperSize.y = 0;

    /* Like paper size DXF units are abstract too. Anyway there is a
     * system variable (MEASUREMENT) which will be set to 0 to indicate
     * english units */
    m_IUsPerDecimil = aIusPerDecimil;
    m_iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils
    m_iuPerDeviceUnit *= GetUnitScaling();    // Get the scaling factor for the current units

    m_plotMirror = false;                     // No mirroring on DXF
    m_currentColor = COLOR4D::BLACK;
}


bool DXF_PLOTTER::StartPlot( const wxString& aPageNumber )
{
    wxASSERT( m_outputFile );

    // DXF HEADER - Boilerplate
    // Defines the minimum for drawing i.e. the angle system and the
    // 4 linetypes (CONTINUOUS, DOTDASH, DASHED and DOTTED)
    fmt::print( m_outputFile,
                "  0\n"
                "SECTION\n"
                "  2\n"
                "HEADER\n"
                "  9\n"
                "$ANGBASE\n"
                "  50\n"
                "0.0\n"
                "  9\n"
                "$ANGDIR\n"
                "  70\n"
                "1\n"
                "  9\n"
                "$MEASUREMENT\n"
                "  70\n"
                "{}\n"
                "  9\n"
                "$INSUNITS\n"
                "  70\n"
                "{}\n"
                "  0\n"
                "ENDSEC\n"
                "  0\n"
                "SECTION\n"
                "  2\n"
                "TABLES\n"
                "  0\n"
                "TABLE\n"
                "  2\n"
                "LTYPE\n"
                "  70\n"
                "4\n"
                "  0\n"
                "LTYPE\n"
                "  5\n"
                "40F\n"
                "  2\n"
                "CONTINUOUS\n"
                "  70\n"
                "0\n"
                "  3\n"
                "Solid line\n"
                "  72\n"
                "65\n"
                "  73\n"
                "0\n"
                "  40\n"
                "0.0\n"
                "  0\n"
                "LTYPE\n"
                "  5\n"
                "410\n"
                "  2\n"
                "DASHDOT\n"
                " 70\n"
                "0\n"
                "  3\n"
                "Dash Dot ____ _ ____ _\n"
                " 72\n"
                "65\n"
                " 73\n"
                "4\n"
                " 40\n"
                "2.0\n"
                " 49\n"
                "1.25\n"
                " 49\n"
                "-0.25\n"
                " 49\n"
                "0.25\n"
                " 49\n"
                "-0.25\n"
                "  0\n"
                "LTYPE\n"
                "  5\n"
                "411\n"
                "  2\n"
                "DASHED\n"
                " 70\n"
                "0\n"
                "  3\n"
                "Dashed __ __ __ __ __\n"
                " 72\n"
                "65\n"
                " 73\n"
                "2\n"
                " 40\n"
                "0.75\n"
                " 49\n"
                "0.5\n"
                " 49\n"
                "-0.25\n"
                "  0\n"
                "LTYPE\n"
                "  5\n"
                "43B\n"
                "  2\n"
                "DOTTED\n"
                " 70\n"
                "0\n"
                "  3\n"
                "Dotted .  .  .  .\n"
                " 72\n"
                "65\n"
                " 73\n"
                "2\n"
                " 40\n"
                "0.2\n"
                " 49\n"
                "0.0\n"
                " 49\n"
                "-0.2\n"
                "  0\n"
                "ENDTAB\n",
                GetMeasurementDirective(), GetInsUnits() );

    // Text styles table
    // Defines 4 text styles, one for each bold/italic combination
    fmt::print( m_outputFile,
	            "  0\n"
	            "TABLE\n"
	            "  2\n"
	            "STYLE\n"
	            "  70\n"
	            "4\n" );

    static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"};

    for(int i = 0; i < 4; i++ )
    {
        fmt::print( m_outputFile,
                 "  0\n"
                 "STYLE\n"
                 "  2\n"
                 "{}\n"         // Style name
                 "  70\n"
                 "0\n"          // Standard flags
                 "  40\n"
                 "0\n"          // Non-fixed height text
                 "  41\n"
                 "1\n"          // Width factor (base)
                 "  42\n"
                 "1\n"          // Last height (mandatory)
                 "  50\n"
                 "{:g}\n"         // Oblique angle
                 "  71\n"
                 "0\n"          // Generation flags (default)
                 "  3\n"
                 // The standard ISO font (when kicad is build with it
                 // the dxf text in acad matches *perfectly*)
                 "isocp.shx\n", // Font name (when not bigfont)
                 // Apply a 15 degree angle to italic text
                 style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
    }

    int numLayers = static_cast<int>( !m_layersToExport.empty() ? m_layersToExport.size() : static_cast<int>(DXF_COLOR_T::NBCOLORS) );

    // If printing in monochrome, only output the black layer
    if( !GetColorMode() && m_layersToExport.empty() )
        numLayers = 1;


    // Layer table - one layer per color
    fmt::print( m_outputFile,
             "  0\n"
             "ENDTAB\n"
             "  0\n"
             "TABLE\n"
             "  2\n"
             "LAYER\n"
             "  70\n"
             "{}\n", (int)numLayers );

    /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:

       - The primary colors (1 - 9)
       - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
       - Greys (251 - 255)
     */

    wxString layerName;
    int colorNumber;

    bool hasActualColor = false;
    COLOR4D actualColor;

    for( int i = 0; i < numLayers; i++ )
    {
        if( !m_layersToExport.empty() )
        {
            layerName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Layer_Name, m_layersToExport.at( i ).first );
            wxString colorName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Layer_Color_Name, m_layersToExport.at( i ).first );

            auto it = std::find_if( std::begin( acad_dxf_color_names ), std::end( acad_dxf_color_names ),
                                      [colorName](const auto& layer)
                                      {
                                          return std::strcmp( layer.name, colorName.ToStdString().c_str() ) == 0;
                                      });

            if( it != std::end( acad_dxf_color_names ) )
                colorNumber = it->index;
            else
                colorNumber = 7; // Default to white/black

            actualColor = RenderSettings()->GetLayerColor( m_layersToExport.at( i ).first );
            hasActualColor = true;
        }
        else
        {
            layerName = wxString( acad_dxf_color_names[i].name );
            colorNumber = acad_dxf_color_names[i].index;
        }

        fmt::print( m_outputFile,
                    "  0\n"
                    "LAYER\n"
                    "  2\n"
                    "{}\n"         // Layer name
                    "  70\n"
                    "0\n"          // Standard flags
                    "  62\n"
                    "{}\n",        // Color number
                    TO_UTF8( layerName ),
                    colorNumber );

        if( hasActualColor )
        {
            // Add the true color value as an extended data entry
            int r = static_cast<int>( actualColor.r * 255 );
            int g = static_cast<int>( actualColor.g * 255 );
            int b = static_cast<int>( actualColor.b * 255 );

            int trueColorValue = ( r << 16 ) | ( g << 8 ) | b;

            fmt::print( m_outputFile,
                        "  420\n"
                        "{}\n",
                        trueColorValue );
        }

        fmt::print( m_outputFile,
                    "  6\n"
                    "CONTINUOUS\n");// Linetype name
    }

    // End of layer table, begin entities
    fmt::print( m_outputFile,
           "  0\n"
           "ENDTAB\n"
           "  0\n"
           "ENDSEC\n"
           "  0\n"
           "SECTION\n"
           "  2\n"
           "ENTITIES\n" );

    return true;
}


bool DXF_PLOTTER::EndPlot()
{
    wxASSERT( m_outputFile );

    // DXF FOOTER
    fmt::print( m_outputFile,
            "  0\n"
           "ENDSEC\n"
           "  0\n"
           "EOF\n" );
    fclose( m_outputFile );
    m_outputFile = nullptr;

    return true;
}


void DXF_PLOTTER::SetColor( const COLOR4D& color )
{
    if( ( m_colorMode )
       || ( color == COLOR4D::BLACK )
       || ( color == COLOR4D::WHITE ) )
    {
        m_currentColor = color;
    }
    else
    {
        m_currentColor = COLOR4D::BLACK;
    }
}


void DXF_PLOTTER::Rect( const VECTOR2I& p1, const VECTOR2I& p2, FILL_T fill, int width,
                        int aCornerRadius )
{
    wxASSERT( m_outputFile );

    if( aCornerRadius > 0 )
    {
        BOX2I box( p1, VECTOR2I( p2.x - p1.x, p2.y - p1.y ) );
        box.Normalize();
        SHAPE_RECT rect( box );
        rect.SetRadius( aCornerRadius );
        PlotPoly( rect.Outline(), fill, width, nullptr );
        return;
    }

    if( p1 != p2 )
    {
        MoveTo( p1 );
        LineTo( VECTOR2I( p1.x, p2.y ) );
        LineTo( VECTOR2I( p2.x, p2.y ) );
        LineTo( VECTOR2I( p2.x, p1.y ) );
        FinishTo( VECTOR2I( p1.x, p1.y ) );
    }
    else
    {
        // Draw as a point
        wxString cLayerName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Current_Layer_Name );

        VECTOR2D point_dev = userToDeviceCoordinates( p1 );

        fmt::print( m_outputFile, "0\nPOINT\n8\n{}\n10\n{}\n20\n",
                    TO_UTF8( cLayerName ),
                    formatCoord( point_dev.x ),
                    formatCoord( point_dev.y ) );
    }
}


void DXF_PLOTTER::Circle( const VECTOR2I& centre, int diameter, FILL_T fill, int width )
{
    wxASSERT( m_outputFile );
    double   radius = userToDeviceSize( diameter / 2 );
    VECTOR2D centre_dev = userToDeviceCoordinates( centre );

    wxString cLayerName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Current_Layer_Name );

    if( radius > 0 )
    {
        if( fill == FILL_T::NO_FILL )
        {
            fmt::print( m_outputFile, "0\nCIRCLE\n8\n{}\n10\n{}\n20\n{}\n40\n{}\n",
                        TO_UTF8( cLayerName ),
                        formatCoord( centre_dev.x ),
                        formatCoord( centre_dev.y ),
                        formatCoord( radius ) );
        }
        else if( fill == FILL_T::FILLED_SHAPE )
        {
            double r = radius * 0.5;
            fmt::print( m_outputFile, "0\nPOLYLINE\n" );
            fmt::print( m_outputFile, "8\n{}\n66\n1\n70\n1\n", TO_UTF8( cLayerName ) );
            fmt::print( m_outputFile, "40\n{}\n41\n{}\n",
                                        formatCoord( radius ),
                                        formatCoord( radius ) );
            fmt::print( m_outputFile, "0\nVERTEX\n8\n{}\n", TO_UTF8( cLayerName ) );
            fmt::print( m_outputFile, "10\n{}\n 20\n{}\n42\n1.0\n",
                                        formatCoord( centre_dev.x-r ),
                                        formatCoord( centre_dev.y ) );
            fmt::print( m_outputFile, "0\nVERTEX\n8\n{}\n", TO_UTF8( cLayerName ) );
            fmt::print( m_outputFile, "10\n{}\n 20\n{}\n42\n1.0\n",
                                        formatCoord( centre_dev.x+r ),
                                        formatCoord( centre_dev.y ) );
            fmt::print( m_outputFile, "0\nSEQEND\n" );
        }
    }
    else
    {
        // Draw as a point
        fmt::print( m_outputFile, "0\nPOINT\n8\n{}\n10\n{}\n20\n{}\n", TO_UTF8( cLayerName ),
                    formatCoord( centre_dev.x ),
                    formatCoord( centre_dev.y ) );
    }
}


void DXF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFill, int aWidth,
                            void* aData )
{
    if( aCornerList.size() <= 1 )
        return;

    unsigned last = aCornerList.size() - 1;

    // Plot outlines with lines (thickness = 0) to define the polygon
    if( aWidth <= 0 || aFill == FILL_T::NO_FILL  )
    {
        MoveTo( aCornerList[0] );

        for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
            LineTo( aCornerList[ii] );

        // Close polygon if 'fill' requested
        if( aFill != FILL_T::NO_FILL )
        {
            if( aCornerList[last] != aCornerList[0] )
                LineTo( aCornerList[0] );
        }

        PenFinish();
        return;
    }

    // The polygon outline has thickness, and is filled
    // Build and plot the polygon which contains the initial
    // polygon and its thick outline
    SHAPE_POLY_SET  bufferOutline;
    SHAPE_POLY_SET  bufferPolybase;

    bufferPolybase.NewOutline();

    // enter outline as polygon:
    for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
    {
        TransformOvalToPolygon( bufferOutline, aCornerList[ ii - 1 ], aCornerList[ ii ],
            aWidth, GetPlotterArcHighDef(), ERROR_INSIDE );
    }

    // enter the initial polygon:
    for( const VECTOR2I& corner : aCornerList )
        bufferPolybase.Append( corner );

    // Merge polygons to build the polygon which contains the initial
    // polygon and its thick outline

    // create the outline which contains thick outline:
    bufferPolybase.BooleanAdd( bufferOutline );
    bufferPolybase.Fracture();

    if( bufferPolybase.OutlineCount() < 1 )      // should not happen
        return;

    const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );

    if( path.PointCount() < 2 )           // should not happen
        return;

    // Now, output the final polygon to DXF file:
    last = path.PointCount() - 1;
    VECTOR2I point = path.CPoint( 0 );

    VECTOR2I startPoint( point.x, point.y );
    MoveTo( startPoint );

    for( int ii = 1; ii < path.PointCount(); ii++ )
    {
        point = path.CPoint( ii );
        LineTo( VECTOR2I( point.x, point.y ) );
    }

    // Close polygon, if needed
    point = path.CPoint( last );
    VECTOR2I endPoint( point.x, point.y );

    if( endPoint != startPoint )
        LineTo( startPoint );

    PenFinish();
}


std::vector<VECTOR2I> arcPts( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
                              const EDA_ANGLE& aAngle, double aRadius )
{
    std::vector<VECTOR2I> pts;

    /*
     * Arcs are not so easily approximated by beziers (in the general case), so we approximate
     * them in the old way
     */
    EDA_ANGLE       startAngle = -aStartAngle;
    EDA_ANGLE       endAngle = startAngle - aAngle;
    VECTOR2I        start;
    VECTOR2I        end;
    const EDA_ANGLE delta( 5, DEGREES_T );   // increment to draw circles

    if( startAngle > endAngle )
        std::swap( startAngle, endAngle );

    // Usual trig arc plotting routine...
    start.x = KiROUND( aCenter.x + aRadius * ( -startAngle ).Cos() );
    start.y = KiROUND( aCenter.y + aRadius * ( -startAngle ).Sin() );
    pts.emplace_back( start );

    for( EDA_ANGLE ii = startAngle + delta; ii < endAngle; ii += delta )
    {
        end.x = KiROUND( aCenter.x + aRadius * ( -ii ).Cos() );
        end.y = KiROUND( aCenter.y + aRadius * ( -ii ).Sin() );
        pts.emplace_back( end );
    }

    end.x = KiROUND( aCenter.x + aRadius * ( -endAngle ).Cos() );
    end.y = KiROUND( aCenter.y + aRadius * ( -endAngle ).Sin() );
    pts.emplace_back( end );

    return pts;
}


void DXF_PLOTTER::PlotPoly( const SHAPE_LINE_CHAIN& aLineChain, FILL_T aFill, int aWidth, void* aData )
{
    std::set<size_t>      handledArcs;
    std::vector<VECTOR2I> cornerList;

    for( int ii = 0; ii < aLineChain.SegmentCount(); ++ii )
    {
        if( aLineChain.IsArcSegment( ii ) )
        {
            size_t arcIndex = aLineChain.ArcIndex( ii );

            if( !handledArcs.contains( arcIndex ) )
            {
                handledArcs.insert( arcIndex );
                const SHAPE_ARC& arc( aLineChain.Arc( arcIndex ) );
                std::vector<VECTOR2I> pts = arcPts( arc.GetCenter(), arc.GetStartAngle(),
                                                    arc.GetCentralAngle(), arc.GetRadius() );

                for( const VECTOR2I& pt : std::ranges::reverse_view( pts ) )
                    cornerList.emplace_back( pt );
            }
        }
        else
        {
            const SEG& seg( aLineChain.Segment( ii ) );
            cornerList.emplace_back( seg.A );
            cornerList.emplace_back( seg.B );
        }
    }

    if( aLineChain.IsClosed() && cornerList.front() != cornerList.back() )
        cornerList.emplace_back( aLineChain.CPoint( 0 ) );

    PlotPoly( cornerList, aFill, aWidth, aData );
}


void DXF_PLOTTER::PenTo( const VECTOR2I& pos, char plume )
{
    wxASSERT( m_outputFile );

    if( plume == 'Z' )
    {
        return;
    }

    VECTOR2D pos_dev = userToDeviceCoordinates( pos );
    VECTOR2D pen_lastpos_dev = userToDeviceCoordinates( m_penLastpos );

    if( m_penLastpos != pos && plume == 'D' )
    {
        wxASSERT( m_currentLineType >= LINE_STYLE::FIRST_TYPE
                  && m_currentLineType <= LINE_STYLE::LAST_TYPE );

        // DXF LINE
        wxString cLayerName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Current_Layer_Name );

        const char* lname = getDXFLineType( static_cast<LINE_STYLE>( m_currentLineType ) );
        fmt::print( m_outputFile, "0\nLINE\n8\n{}\n6\n{}\n10\n{}\n20\n{}\n11\n{}\n21\n{}\n",
                    TO_UTF8( cLayerName ), lname,
                    formatCoord( pen_lastpos_dev.x ),
                    formatCoord( pen_lastpos_dev.y ),
                    formatCoord( pos_dev.x ),
                    formatCoord( pos_dev.y ) );
    }

    m_penLastpos = pos;
}


void DXF_PLOTTER::SetDash( int aLineWidth, LINE_STYLE aLineStyle )
{
    wxASSERT( aLineStyle >= LINE_STYLE::FIRST_TYPE
                && aLineStyle <= LINE_STYLE::LAST_TYPE );

    m_currentLineType = aLineStyle;
}


void DXF_PLOTTER::Arc( const VECTOR2D& aCenter, const EDA_ANGLE& aStartAngle,
                       const EDA_ANGLE& aAngle, double aRadius, FILL_T aFill, int aWidth )
{
    wxASSERT( m_outputFile );

    if( aRadius <= 0 )
        return;

    EDA_ANGLE startAngle = -aStartAngle;
    EDA_ANGLE endAngle = startAngle - aAngle;

    // In DXF, arcs are drawn CCW.
    // If startAngle > endAngle, it is CW. So transform it to CCW
    if( endAngle < startAngle )
        std::swap( startAngle, endAngle );

    VECTOR2D centre_device = userToDeviceCoordinates( aCenter );
    double   radius_device = userToDeviceSize( aRadius );

    // Emit a DXF ARC entity
    wxString cLayerName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Current_Layer_Name );
    fmt::print( m_outputFile,
                "0\nARC\n8\n{}\n10\n{}\n20\n{}\n40\n{}\n50\n{:.8f}\n51\n{:.8f}\n",
                TO_UTF8( cLayerName ),
                formatCoord( centre_device.x ),
                formatCoord( centre_device.y ),
                formatCoord( radius_device ),
                startAngle.AsDegrees(),
                endAngle.AsDegrees() );
}


void DXF_PLOTTER::ThickSegment( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aWidth, void* aData )
{
    const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );

    if( cfg && cfg->GetDXFPlotMode() == SKETCH )
    {
        std::vector<VECTOR2I> cornerList;
        SHAPE_POLY_SET outlineBuffer;
        TransformOvalToPolygon( outlineBuffer, aStart, aEnd, aWidth, GetPlotterArcHighDef(),
                                ERROR_INSIDE );
        const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline( 0 );

        cornerList.reserve( path.PointCount() );

        for( int jj = 0; jj < path.PointCount(); jj++ )
            cornerList.emplace_back( path.CPoint( jj ).x, path.CPoint( jj ).y );

        // Ensure the polygon is closed
        if( cornerList[0] != cornerList[cornerList.size() - 1] )
            cornerList.push_back( cornerList[0] );

        PlotPoly( cornerList, FILL_T::NO_FILL, DXF_LINE_WIDTH );
    }
    else
    {
        MoveTo( aStart );
        FinishTo( aEnd );
    }
}


void DXF_PLOTTER::ThickArc( const VECTOR2D& centre, const EDA_ANGLE& aStartAngle,
                            const EDA_ANGLE& aAngle, double aRadius, int aWidth, void* aData )
{
    const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );

    if( cfg && cfg->GetDXFPlotMode() == SKETCH )
    {
        Arc( centre, aStartAngle, aAngle, aRadius - aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
        Arc( centre, aStartAngle, aAngle, aRadius + aWidth/2, FILL_T::NO_FILL, DXF_LINE_WIDTH );
    }
    else
    {
        Arc( centre, aStartAngle, aAngle, aRadius, FILL_T::NO_FILL, DXF_LINE_WIDTH );
    }
}


void DXF_PLOTTER::ThickRect( const VECTOR2I& p1, const VECTOR2I& p2, int width, void* aData )
{
    const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );

    if( cfg && cfg->GetDXFPlotMode() == SKETCH )
    {
        VECTOR2I offsetp1( p1.x - width/2, p1.y - width/2 );
        VECTOR2I offsetp2( p2.x + width/2, p2.y + width/2 );
        Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );

        offsetp1.x += width;
        offsetp1.y += width;
        offsetp2.x -= width;
        offsetp2.y -= width;
        Rect( offsetp1, offsetp2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
    }
    else
    {
        Rect( p1, p2, FILL_T::NO_FILL, DXF_LINE_WIDTH, 0 );
    }
}


void DXF_PLOTTER::ThickCircle( const VECTOR2I& pos, int diametre, int width, void* aData )
{
    const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );

    if( cfg && cfg->GetDXFPlotMode() == SKETCH )
    {
        Circle( pos, diametre - width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
        Circle( pos, diametre + width, FILL_T::NO_FILL, DXF_LINE_WIDTH );
    }
    else
    {
        Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
    }
}


void DXF_PLOTTER::FilledCircle( const VECTOR2I& pos, int diametre, void* aData )
{
    const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );

    if( cfg && cfg->GetDXFPlotMode() == SKETCH )
        Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
    else
        Circle( pos, diametre, FILL_T::FILLED_SHAPE, 0 );
}


void DXF_PLOTTER::ThickPoly( const SHAPE_POLY_SET& aPoly, int aWidth, void* aData )
{
    const PLOT_PARAMS* cfg = static_cast<const PLOT_PARAMS*>( aData );

    if( cfg && cfg->GetDXFPlotMode() == SKETCH )
    {
        SHAPE_POLY_SET outline = aPoly.CloneDropTriangulation();
        outline.Inflate( aWidth / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, GetPlotterArcHighDef() );
        PLOTTER::PlotPoly( outline.COutline( 0 ), FILL_T::NO_FILL, DXF_LINE_WIDTH, aData );

        outline = aPoly.CloneDropTriangulation();
        outline.Deflate( aWidth / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, GetPlotterArcHighDef() );
        PLOTTER::PlotPoly( outline.COutline( 0 ), FILL_T::NO_FILL, DXF_LINE_WIDTH, aData );
    }
    else
    {
        PLOTTER::PlotPoly( aPoly.COutline( 0 ), FILL_T::NO_FILL, aWidth, aData );
    }
}


void DXF_PLOTTER::FlashPadOval( const VECTOR2I& aPos, const VECTOR2I& aSize,
                                const EDA_ANGLE& aOrient, void* aData )
{
    wxASSERT( m_outputFile );

    VECTOR2I  size( aSize );
    EDA_ANGLE orient( aOrient );

    /* The chip is reduced to an oval tablet with size.y > size.x
     * (Oval vertical orientation 0) */
    if( size.x > size.y )
    {
        std::swap( size.x, size.y );
        orient += ANGLE_90;
    }

    ThickOval( aPos, size, orient, DXF_LINE_WIDTH, aData );
}


void DXF_PLOTTER::FlashPadCircle( const VECTOR2I& pos, int diametre, void* aData )
{
    wxASSERT( m_outputFile );
    Circle( pos, diametre, FILL_T::NO_FILL, DXF_LINE_WIDTH );
}


void DXF_PLOTTER::FlashPadRect( const VECTOR2I& aPos, const VECTOR2I& aPadSize,
                                const EDA_ANGLE& aOrient, void* aData )
{
    wxASSERT( m_outputFile );

    VECTOR2I size, start, end;

    size.x = aPadSize.x / 2;
    size.y = aPadSize.y / 2;

    if( size.x < 0 )
        size.x = 0;

    if( size.y < 0 )
        size.y = 0;

    // If a dimension is zero, the trace is reduced to 1 line
    if( size.x == 0 )
    {
        start = VECTOR2I( aPos.x, aPos.y - size.y );
        end = VECTOR2I( aPos.x, aPos.y + size.y );
        RotatePoint( start, aPos, aOrient );
        RotatePoint( end, aPos, aOrient );
        MoveTo( start );
        FinishTo( end );
        return;
    }

    if( size.y == 0 )
    {
        start = VECTOR2I( aPos.x - size.x, aPos.y );
        end = VECTOR2I( aPos.x + size.x, aPos.y );
        RotatePoint( start, aPos, aOrient );
        RotatePoint( end, aPos, aOrient );
        MoveTo( start );
        FinishTo( end );
        return;
    }

    start = VECTOR2I( aPos.x - size.x, aPos.y - size.y );
    RotatePoint( start, aPos, aOrient );
    MoveTo( start );

    end = VECTOR2I( aPos.x - size.x, aPos.y + size.y );
    RotatePoint( end, aPos, aOrient );
    LineTo( end );

    end = VECTOR2I( aPos.x + size.x, aPos.y + size.y );
    RotatePoint( end, aPos, aOrient );
    LineTo( end );

    end = VECTOR2I( aPos.x + size.x, aPos.y - size.y );
    RotatePoint( end, aPos, aOrient );
    LineTo( end );

    FinishTo( start );
}


void DXF_PLOTTER::FlashPadRoundRect( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
                                     int aCornerRadius, const EDA_ANGLE& aOrient, void* aData )
{
    SHAPE_POLY_SET outline;
    TransformRoundChamferedRectToPolygon( outline, aPadPos, aSize, aOrient, aCornerRadius, 0.0, 0,
                                          0, GetPlotterArcHighDef(), ERROR_INSIDE );

    // TransformRoundRectToPolygon creates only one convex polygon
    SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );

    MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );

    for( int ii = 1; ii < poly.PointCount(); ++ii )
        LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );

    FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
}


void DXF_PLOTTER::FlashPadCustom( const VECTOR2I& aPadPos, const VECTOR2I& aSize,
                                  const EDA_ANGLE& aOrient, SHAPE_POLY_SET* aPolygons,
                                  void* aData )
{
    for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
    {
        SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );

        MoveTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );

        for( int ii = 1; ii < poly.PointCount(); ++ii )
            LineTo( VECTOR2I( poly.CPoint( ii ).x, poly.CPoint( ii ).y ) );

        FinishTo( VECTOR2I( poly.CPoint( 0 ).x, poly.CPoint( 0 ).y ) );
    }
}


void DXF_PLOTTER::FlashPadTrapez( const VECTOR2I& aPadPos, const VECTOR2I* aCorners,
                                  const EDA_ANGLE& aPadOrient, void* aData )
{
    wxASSERT( m_outputFile );
    VECTOR2I coord[4]; /* coord actual corners of a trapezoidal trace */

    for( int ii = 0; ii < 4; ii++ )
    {
        coord[ii] = aCorners[ii];
        RotatePoint( coord[ii], aPadOrient );
        coord[ii] += aPadPos;
    }

    // Plot edge:
    MoveTo( coord[0] );
    LineTo( coord[1] );
    LineTo( coord[2] );
    LineTo( coord[3] );
    FinishTo( coord[0] );
}


void DXF_PLOTTER::FlashRegularPolygon( const VECTOR2I& aShapePos, int aRadius, int aCornerCount,
                                       const EDA_ANGLE& aOrient, void* aData )
{
    // Do nothing
    wxASSERT( 0 );
}


/**
 * Check if a given string contains non-ASCII characters.
 *
 * @param string String to check.
 * @return true if it contains some non-ASCII character, false if all characters are
 *         inside ASCII range (<=255).
 */
bool containsNonAsciiChars( const wxString& string )
{
    for( unsigned i = 0; i < string.length(); i++ )
    {
        wchar_t ch = string[i];

        if( ch > 255 )
            return true;
    }
    return false;
}


void DXF_PLOTTER::Text( const VECTOR2I&        aPos,
                        const COLOR4D&         aColor,
                        const wxString&        aText,
                        const EDA_ANGLE&       aOrient,
                        const VECTOR2I&        aSize,
                        enum GR_TEXT_H_ALIGN_T aH_justify,
                        enum GR_TEXT_V_ALIGN_T aV_justify,
                        int                    aWidth,
                        bool                   aItalic,
                        bool                   aBold,
                        bool                   aMultilineAllowed,
                        KIFONT::FONT*          aFont,
                        const KIFONT::METRICS& aFontMetrics,
                        void*                  aData )
{
    // Fix me: see how to use DXF text mode for multiline texts
    if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
        aMultilineAllowed = false;  // the text has only one line.

    bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );

    if( m_textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed || processSuperSub )
    {
        // output text as graphics.
        // Perhaps multiline texts could be handled as DXF text entity
        // but I do not want spend time about this (JPC)
        PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify, aWidth, aItalic,
                       aBold, aMultilineAllowed, aFont, aFontMetrics, aData );
    }
    else
    {
        TEXT_ATTRIBUTES attrs;
        attrs.m_Halign = aH_justify;
        attrs.m_Valign =aV_justify;
        attrs.m_StrokeWidth = aWidth;
        attrs.m_Angle = aOrient;
        attrs.m_Italic = aItalic;
        attrs.m_Bold = aBold;
        attrs.m_Mirrored = aSize.x < 0;
        attrs.m_Multiline = false;
        plotOneLineOfText( aPos, aColor, aText, attrs );
    }
}


void DXF_PLOTTER::PlotText( const VECTOR2I&        aPos,
                            const COLOR4D&         aColor,
                            const wxString&        aText,
                            const TEXT_ATTRIBUTES& aAttributes,
                            KIFONT::FONT*          aFont,
                            const KIFONT::METRICS& aFontMetrics,
                            void*                  aData )
{
    TEXT_ATTRIBUTES attrs = aAttributes;

    // Fix me: see how to use DXF text mode for multiline texts
    if( attrs.m_Multiline && !aText.Contains( wxT( "\n" ) ) )
        attrs.m_Multiline = false;  // the text has only one line.

    bool processSuperSub = aText.Contains( wxT( "^{" ) ) || aText.Contains( wxT( "_{" ) );

    if( m_textAsLines || containsNonAsciiChars( aText ) || attrs.m_Multiline || processSuperSub )
    {
        // output text as graphics.
        // Perhaps multiline texts could be handled as DXF text entity
        // but I do not want spend time about that (JPC)
        PLOTTER::PlotText( aPos, aColor, aText, aAttributes, aFont, aFontMetrics, aData );
    }
    else
    {
        plotOneLineOfText( aPos, aColor, aText, attrs );
    }
}


void DXF_PLOTTER::plotOneLineOfText( const VECTOR2I& aPos, const COLOR4D& aColor,
                                     const wxString& aText, const TEXT_ATTRIBUTES& aAttributes )
{
    /* Emit text as a text entity. This loses formatting and shape but it's
       more useful as a CAD object */
    VECTOR2D origin_dev = userToDeviceCoordinates( aPos );
    SetColor( aColor );
    wxString cLayerName = GetCurrentLayerName( DXF_LAYER_OUTPUT_MODE::Current_Layer_Name );

    VECTOR2D size_dev = userToDeviceSize( aAttributes.m_Size );
    int h_code = 0, v_code = 0;

    switch( aAttributes.m_Halign )
    {
    case GR_TEXT_H_ALIGN_LEFT:   h_code = 0; break;
    case GR_TEXT_H_ALIGN_CENTER: h_code = 1; break;
    case GR_TEXT_H_ALIGN_RIGHT:  h_code = 2; break;
    case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
    }

    switch( aAttributes.m_Valign )
    {
    case GR_TEXT_V_ALIGN_TOP:    v_code = 3; break;
    case GR_TEXT_V_ALIGN_CENTER: v_code = 2; break;
    case GR_TEXT_V_ALIGN_BOTTOM: v_code = 1; break;
    case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) ); break;
    }

    std::string textStyle = "KICAD";
    if( aAttributes.m_Bold )
    {
        if( aAttributes.m_Italic )
            textStyle = "KICADBI";
        else
            textStyle = "KICADB";
    }
    else if( aAttributes.m_Italic )
        textStyle = "KICADI";

    // Position, size, rotation and alignment
    // The two alignment point usages is somewhat idiot (see the DXF ref)
    // Anyway since we don't use the fit/aligned options, they're the same
    fmt::print( m_outputFile,
             "  0\n"
             "TEXT\n"
             "  7\n"
             "{}\n"          // Text style
             "  8\n"
             "{}\n"          // Layer name
             "  10\n"
             "{}\n"          // First point X
             "  11\n"
             "{}\n"          // Second point X
             "  20\n"
             "{}\n"          // First point Y
             "  21\n"
             "{}\n"          // Second point Y
             "  40\n"
             "{}\n"          // Text height
             "  41\n"
             "{}\n"          // Width factor
             "  50\n"
             "{:.8f}\n"        // Rotation
             "  51\n"
             "{:.8f}\n"        // Oblique angle
             "  71\n"
             "{}\n"          // Mirror flags
             "  72\n"
             "{}\n"          // H alignment
             "  73\n"
             "{}\n",         // V alignment
             aAttributes.m_Bold ? ( aAttributes.m_Italic ? "KICADBI" : "KICADB" )
                                : ( aAttributes.m_Italic ? "KICADI" : "KICAD" ),
             TO_UTF8( cLayerName ),
             formatCoord( origin_dev.x ),
             formatCoord( origin_dev.x ),
             formatCoord( origin_dev.y ),
             formatCoord( origin_dev.y ),
             formatCoord( size_dev.y ),
             formatCoord( fabs( size_dev.x / size_dev.y ) ),
             aAttributes.m_Angle.AsDegrees(),
             aAttributes.m_Italic ? DXF_OBLIQUE_ANGLE : 0,
             aAttributes.m_Mirrored ? 2 : 0, // X mirror flag
             h_code, v_code );

    /* There are two issue in emitting the text:
       - Our overline character (~) must be converted to the appropriate
       control sequence %%O or %%o
       - Text encoding in DXF is more or less unspecified since depends on
       the DXF declared version, the acad version reading it *and* some
       system variables to be put in the header handled only by newer acads
       Also before R15 unicode simply is not supported (you need to use
       bigfonts which are a massive PITA). Common denominator solution:
       use Latin1 (and however someone could choke on it, anyway). Sorry
       for the extended latin people. If somewant want to try fixing this
       recent version seems to use UTF-8 (and not UCS2 like the rest of
       Windows)

       XXX Actually there is a *third* issue: older DXF formats are limited
       to 255 bytes records (it was later raised to 2048); since I'm lazy
       and text so long is not probable I just don't implement this rule.
       If someone is interested in fixing this, you have to emit the first
       partial lines with group code 3 (max 250 bytes each) and then finish
       with a group code 1 (less than 250 bytes). The DXF refs explains it
       in no more details...
     */

    int braceNesting = 0;
    int overbarDepth = -1;

    fmt::print( m_outputFile, "  1\n" );

    for( unsigned int i = 0; i < aText.length(); i++ )
    {
        /* Here I do a bad thing: writing the output one byte at a time!
           but today I'm lazy and I have no idea on how to coerce a Unicode
           wxString to spit out latin1 encoded text ...

           At least stdio is *supposed* to do output buffering, so there is
           hope is not too slow */
        wchar_t ch = aText[i];

        if( ch > 255 )
        {
            // I can't encode this...
            putc( '?', m_outputFile );
        }
        else
        {
            if( aText[i] == '~' && i+1 < aText.length() && aText[i+1] == '{' )
            {
                fmt::print( m_outputFile, "%%o" );
                overbarDepth = braceNesting;

                // Skip the '{'
                i++;
                continue;
            }
            else if( aText[i] == '{' )
            {
                braceNesting++;
            }
            else if( aText[i] == '}' )
            {
                if( braceNesting > 0 )
                    braceNesting--;

                if( braceNesting == overbarDepth )
                {
                    fmt::print( m_outputFile, "%%O" );
                    overbarDepth = -1;
                    continue;
                }
            }

            putc( ch, m_outputFile );
        }
    }

    fmt::print( m_outputFile, "\n" );
}
