#usage "Ausgabe eines <b>Sch</b>altplanes (oder Boards) als "
"<b>W</b>indows-<b>M</b>eta-Datei (<b>F</b>ile) "
"für bequemen Dokument-Import sowie String-Suchmöglichkeit in PDFs<p>\n"
"Es werden nur die gerade sichtbaren Layer ausgegeben.<br>"
"Kommandozeilenargumente: [Argumente] [Dateiname]<br>\n"
"Argumente siehe SCH2WMF.TXT.\n"
"<hr>"
"<author>Autor: <A href=mailto:henrik.haftmann@e-technik.tu-chemnitz.de>"
"henrik.haftmann@e-technik.tu-chemnitz.de</A>, <br>"
"URL: <A href='http://www.tu-chemnitz.de/~heha/hs_freeware/sch2wmf/'>"
"http://www.tu-chemnitz.de/~heha/hs_freeware/sch2wmf/</A></author>"
// Hinweis: WMF-Dateien können mit Internet Explorer 6+ direkt im WWW als Bild
// angezeigt werden (das wird wohl die Mac-Version nicht betreffen).
// Leider druckt der IE das WMF als Klötzchengrafik (wie auch bei verkleinerten
// Rasterbilddateien). Man kann sie auch nicht im Browser skalieren.
// Ordentliche Betrachter, bspw. ACDSee, skalieren und drucken WMFs sauber als
// Vektorgrafik.
// Zur Analyse von WMF- und EMF-Dateien habe ich im Zuge von SCH2WMF das Programm
// WMFVIEW geschrieben, welches auf meiner Freeware-Seite zur Verfügung steht.
// (Suchmaschinen-Stichworte: haftmann freeware wmfview)
// Versionsgeschichte:
// Oktober 2005: SCH2CGM (Computer Graphics Metafile, kann in MS Office
// importiert werden, sofern Importfilter installiert
// August 2006: SCH2EMF gescheitert
// Oktober 2006: SCH2WMF funktionsfähig
// Dezember 2006: experimentelle BRD-Unterstützung
// Juli 2007: Bugfix: ausgeblendete Ebenen sichtbar bei Schwarzweiß-Ausgabe
// Abschaffung der zwei Läufe (schneller)
// Korrektur bei Bögen, brauchbare Funktion im .BRD
// Mai 2008: Hinweis: Proportional-Schrift in Kupfer wird niemals ersetzt
// Warnung, wenn kein RATSNEST
// Ausgabe pseudotransparent (nur Board) mittels ROP-Kodes
// Bugfix: Fehlermeldung bei TCREAM
// exakte Füllmuster, Textlayer (nicht: Text) obenauf (Board)
// Juni 2008: Schraffuren beim Spiegeln umgedreht
// Ausstanzen von Löchern, Hintergrundfüllung
// im Board stets lagenweise Ausgabe (auch bei Schwarzweiß)
// Überstreichungen entsprechend Eagle 5
// Löcher und Bohrsymbole
// Schraffierte Linien als Achtecke, schraffierte Polygonränder
// Schraffierte Bögen, Bohrsymbole
// LastLayers, Kommandozeile, erweiterter Dialog (d=2)
// Oktober 2008: Bugfix bei Schaltplanausgabe mit schraffiertem Layer
// Umgestaltung der Recordausgabe, kürzere Createfont-Records
// Ausgabe Layer 93 (Pins), selektierbare Ausgabe
// Symbolzeichenausgabe via Symbol-Schriftart
// Symbolischer Dateiname "*" in Kommandozeile
// November 2008: Bugfix: Kreise mit Strichstärke Null sind ausgefüllt
// Bugfix: SMDs mit Abrundungen ausgeben (beides Guido Clausner)
// Dezember 2009: Bugfix: Löcher-Darstellung
// Februar 2012: Bugfix: Eagle6-Kompatibilität, Total-Commander-und-IE-Kompatibilität
// (Nebeneffekt: Fehldarstellung der Schrift in Total Commander 6.03,
// habe mich vorher davon ins Bockshorn jagen lassen …)
// FlatArcReplace: nun Verhältnis zwischen Strichbreite und Bogenradius
// Korrektur der Schriftpositionierung bei Ausrichtung an Oberkante
// Bugfix: Auswahlrechteck-Vorgabe nun in aktuellen Raster-Einheiten
// Dateinamen-Vorgabe beim Board mit Bindestrich bei Buchstaben-Ende
// (Buchstaben stoßen nicht aufeinander, statt boardt.wmf nun board-t.wmf)
// April 2016: Spiegelung vertikal und Rotation dazu (nur bei d=2 zu sehen).
// Damit können alle 8 denkbaren Ausrichtungen ausgespuckt werden.
// Mai 2016: Rechtsbündiger Text mit Ohm-Zeichen nun korrekt
// September 2016:Übereinander gesetzter Text mit n×-Präfix
// PROBLEM: pseudotransparente Ausgabe auf Bildschirm prima, beim PDF Murks
// PROBLEM: Einfügen in .docx führt zu falscher Farbwiedergabe,
// bei Betrieb von Winword im Kompatibilitätsmodus (.doc) alles korrekt.
// PDF-Ausgabe pseudotransparenter Board-Layer: Falschfarben
// Oktober 2021: Bugfix: Ausschlüsse im Supply-Layer
// Zur problemspezifischen Anpassung des Dialogs
int NumSheet; // Anzahl Seiten des Schaltplans
int NumHatched; // Anzahl gestrichelte Layer (bei Schaltplan üblich: Null)
int NumExact; // Anzahl gestrichelte Layer _ohne_ Windows-Entsprechung
int NumFont[]; // Anzahl Vorkommen von Vektor/Prop/Fixed-Fonts
int NumCopperVector; // Anzahl Vorkommen Vektorfonts im Kupfer (nur Board)
int NumFlatArc; // Anzahl Bögen mit flachen Enden (außer Tortenstücke)
int NumPieced; // Anzahl Strichellinien (auch Bögen)
string SName; // Schaltplan- oder Board-Name (mit Pfad)
// **** Dialog ****
// Voreinstellungen
int Debug=0; // Kreis am Text-Ursprung
int PaletteType=palette(-1)+1; // Farbmodell, 2. Parameter für palette()
// PaletteType=0 bedeutet Schwarzweiß-Ausgabe (1 Layer)
int HatchType=1; // Füllstil für gestrichelt definierte Layer (!Polygone)
int AllPages=0; // Automatisch alle Seiten generieren (mehrseitige Schaltpläne)
int Flipped=0; // gespiegelt (Bit 0: an X-Achse, Bit 1: an Y-Achse, Bit 2: X/Y-Tausch, nur für Board)
int Trans=0; // Transparenz-Simulation (mittels ROP-Kodes, wie Eagle<5)
int OutBkgnd=0; // Hintergrund ausgeben
int DoNegate=0; // Negationen (bei Strings) ausgeben
int LineReplace=1; // Schraffierte Linien durch (längliche) Achtecke ersetzen
real FlatArcReplace=0.8;// Bögen mit flachen Enden ersetzen ab Verhältnis Breite:Radius
// (Ab Verhältnis 1,9 wird stets ein Tortenstück ausgegeben)
int PiecedReplace=1; // Strichellinien-Ersetzung (Standard: nur ersetzte Polygone Eagle-stricheln)
int DupStrReplace=1; // Übereinander gesetzten Text nur 1x ausgeben mit Präfix
int Dialog=1; // =2: Erweiterter Dialog (nur für Füchse), 0 = kein Dialog
int GroupOnly=0; // Gruppe (hier ausgewählt durch Rechteck) ausgeben
int GroupRect[]; // Auswahlrechteck (ersetzt fehlendes InGroup(), ab Eagle 5)
string LastLayers; // Zuletzt auszugebende Layer (stets deckend, nur Board)
string FName; // Dateiname (ggf. Kommandozeilenargument)
real Scale=1; // Ausgabe-(Fehl-)Skalierung (besonders für Total Commander und Internet Explorer)
// Die Vorgabe-Schriftersetzung ist, Vektor-Schrift durch Arial fett
// ersetzen zu lassen, um die Ausgabedatei klein und durchsuchbar zu machen.
// Beim ULP-Start wird der Schaltplan nach speziellen Zeichen im Vektorfont,
// bspw. dem Omega-Zeichen, durchsucht.
// (Die meisten Eagle-Anwender werden's wohl gar nicht wissen:-)
// In diesem Fall erfolgt die Ausgabe in Symbol-Schriftart.
// Bei Zeichen, die nicht in der Symbol-Schriftart enthalten sind
// (Rahmenzeichen; extrem selten verwendet) erfolgt standardmäßig
// keine Schriftersetzung, sondern schnöde Linienausgabe.
// Das ist der Nachteil, wenn man WMF (und kein EMF - Unicode) ausgibt!
int FontSel[]={1,1,2}; // Font-Nr. für Vektor-Proportional-Fixed
int FontBold[]={1,0,0};
int FontItalic[]={0,0,0};
string FontSrc[]={"Vektor","Proportional","Fixed"}; // Eagle-Namen
string FontName[]={"Vektor","Arial","Courier New","Times New Roman","Symbol"};
string FontLabel[];
// Um die 32-bit-Koordinaten von Eagle ins .WMF zu pressen (16-bit-Koordinaten),
// müssen alle Koordinatenangaben durch einen gemeinsamen Teiler dividiert werden.
real Teiler=25.4; // µm pro Pixel, Vorgabe: 1 mil WMF-Raster: max. Seitengröße 32767mil=0,83m
real MinBreite=50.8; // Minimale Linienbreite für Ersetzungsvorgänge in µm
// * Strichellinien durch Eagle-Strichelung
// * Schraffierte Linien und Polygonränder durch Achtecke
// * Bögen mit ENDCAP_FLAT durch Polygone
// zum Entdecken mehrfach übereinander platzierter Strings
// Werden mit "2x " usw. ausgegeben.
// Optimal für gleiche Werte im Schaltplan
string DupStrHash[];
int DupStrCount[];
int AnyDupStr() {
for (int i=0; DupStrCount[i]; i++) if (DupStrCount[i]>1) return 1;
return 0;
}
// berechnet aus String LastLayers das Char-Array LastLayers(!)
// meckert und liefert 0 bei Fehler, sonst !=0
int CalcLastLayers(void) {
string A;
int i,j,layer,layere;
for (i=j=0; LastLayers[i]; i++) {
// Startwert einlesen
layer=strtol(strsub(LastLayers,i));
if (layer<=0 || layer>255) break; // Fehler
while ('0'<=LastLayers[i] && LastLayers[i]<='9') i++;
// ggf. Endwert einlesen
if (LastLayers[i]=='-') {
i++;
layere=strtol(strsub(LastLayers,i));
if (layere<=0 || layere>255) break; // Fehler
while ('0'<=LastLayers[i] && LastLayers[i]<='9') i++;
}else layere=layer;
// auf- oder absteigend LastLayers füllen
if (layer<=layere) for (; layer<=layere; layer++) A[j++]=layer;
else for (; layer>=layere; layer--) A[j++]=layer;
// Folgezeichen auswerten
if (!LastLayers[i]) break; // Ende
if (LastLayers[i]!=','
|| !LastLayers[i+1]) break; // Fehler, wenn kein Komma oder ",\0"
}
A[j]=0; // terminieren
if (LastLayers[i]) { // Nicht am Ende angekommen?
string s;
sprintf(s,"!Falsche Syntax für Layer-Angabe!<pre style=color:red><b>%s\n%*s^---hier!",
LastLayers,i,"");
dlgMessageBox(s);
return 0;
}
LastLayers=A;
return 1;
}
/***********************
*** Dialog-Routinen ***
***********************/
// globale Variablen: Farben und Füllstile sowie Name per Layer
int col[],fil[];
string nam[];
// liefert TRUE, wenn Layers aktiv sind, die von unten zu sehen sind
// (d.h. solche, auf denen platzierter Text tatsächlich gespiegelt erscheint)
int AnyBottomLayers() {
return col[LAYER_BOTTOM]
+ col[LAYER_BPLACE]
+ col[LAYER_BORIGINS]
+ col[LAYER_BNAMES]
+ col[LAYER_BVALUES]
+ col[LAYER_BSTOP]
+ col[LAYER_BCREAM]
+ col[LAYER_BFINISH]
+ col[LAYER_BGLUE]
+ col[LAYER_BTEST]
+ col[LAYER_BKEEPOUT]
+ col[LAYER_BRESTRICT]
+ col[LAYER_BDOCU];
}
// Font-Namen nur für SetFontLabel (Leerzeichen führen zu Fehlern)
string FontSty[]={"Verdana","Arial","Courier","Times"};
void SetFontLabel(void) {
for (int idx=0; idx<3; idx++) sprintf(FontLabel[idx],
"<span style=font-family:%s;font-weight:%d;font-style:%s>%s</span>:",
FontSty[FontSel[idx]],
FontBold[idx]?700:400,
FontItalic[idx]?"italic":"normal",
FontSrc[idx]);
}
void FontSubstLine(int idx) {
dlgCell(idx,0) {dlgLabel(FontLabel[idx],1); dlgSpacing(10);}
dlgCell(idx,1) {
dlgComboBox(FontName,FontSel[idx]) {
if (!FontSel[0]) dlgMessageBox(
"Die Vektor-Schriftart ergibt <b style=color:darkgreen>layout-treue</b> Ausgabedateien.<p>"+
"Allerdings ist die WMF-Datei <b style=color:darkred>wesentlich größer</b> "+
"und <b style=color:darkred>nicht durchsuchbar</b> nach Text.<p>"+
"Die Einstellungen <b>Fett</b> und <b>Kursiv</b> sind wirkungslos.");
SetFontLabel();
}
dlgSpacing(10);
}
dlgCell(idx,2) dlgCheckBox("Fett",FontBold[idx]) SetFontLabel();
dlgCell(idx,3) dlgCheckBox("Kursiv",FontItalic[idx]) SetFontLabel();
dlgCell(idx,4) dlgStretch(1);
}
void OnSetPaletteType(void) {
switch (PaletteType) {
case 1: {
OutBkgnd=1;
if (board) Trans=1; // voreinstellen wie Eagle
if (NumExact) HatchType=2;
}break;
case 3: {
OutBkgnd=!Trans; // voreinstellen wie Eagle (wenn Trans=0)
}break;
}
}
// Hilfsvariablen zum Zusammensetzen von Flipped
int TopDown;
int Rotated;
int DialogBox(void) {
string DeckLabel=Trans?"(transparent)":"(deckend)";
status("Dialog");
return dlgDialog("WMF-Export-Optionen") {
dlgLabel("Dateiname: <b>"+filename(SName)+"</b>");
if (FName) dlgLabel("Ausgabe-Datei: <b>"+filename(FName)+"</b>");
if (Dialog>1) dlgGridLayout{
dlgCell(0,0) dlgLabel("WMF-Einheit (alles 16-Bit-Koordinaten): ");
dlgCell(0,1) dlgRealEdit(Teiler,1,300);
dlgCell(0,2) dlgLabel(" µm");
dlgCell(1,0) dlgLabel("Minimale Linienbreite für Ersetzungen: ");
dlgCell(1,1) dlgRealEdit(MinBreite,0.1,3000);
dlgCell(1,2) dlgLabel(" µm");
dlgCell(2,0) dlgLabel("Ausgabe-Skalierung (»falsche« DPI-Angabe): ");
dlgCell(2,1) dlgRealEdit(Scale,0.1,100);
}
dlgGroup("Ausgabe-&Farben (siehe Eagle-Palette)") {
dlgRadioButton("Schwarz auf Weiß (Schwarzweiß)",PaletteType) OnSetPaletteType();
dlgRadioButton("Bunt auf Schwarz",PaletteType) OnSetPaletteType();
dlgRadioButton("Bunt auf Weiß",PaletteType) OnSetPaletteType();
dlgRadioButton("Bunt auf farbigem Hintergrund",PaletteType) OnSetPaletteType();
}
dlgGroup("Schrift-&Ersetzung (verwendete Schriften gelistet)") {
dlgGridLayout{
SetFontLabel();
for (int i=0; i<3; i++) if (NumFont[i]) FontSubstLine(i);
}
if (NumCopperVector) {
dlgSpacing(4);
dlgLabel("<b>Hinweis:</b> Vektorschrift auf Kupfer wird niemals ersetzt.");
}
if (AnyDupStr()) {
dlgSpacing(4);
dlgCheckBox("n×-Präfix für übereinander gesetzten Text",DupStrReplace);
}
}
if (Dialog>1) {
if (NumFlatArc) {
string s;
sprintf(s,"Behandlung von %d B%sgen mit flachen Enden",
NumFlatArc, NumFlatArc==1?"o":"ö");
dlgGroup(s) {
dlgHBoxLayout{
dlgLabel("als Polygon interpolieren ab Breite:Radius: ");
dlgRealEdit(FlatArcReplace,0,2);
}
dlgLabel("<b>0</b> = immer (größere Datei), <b>2</b> = nie (sichtbare »Wurst-Enden«)");
}
}
if (NumPieced) {
string s;
sprintf(s,"Behandlung von %d Strichellinie%s",
NumPieced,NumPieced==1?"":"n");
dlgGroup(s) {
dlgRadioButton("Linien mit Windows-Strichelung ausgeben",PiecedReplace);
dlgRadioButton("Windows für Linien und Bögen, Eagle für Polygone",PiecedReplace);
dlgRadioButton("Eagle-Strichelung verwenden (Einzelsegmente)",PiecedReplace);
}
}
}
if (NumHatched) {
string s;
sprintf(s,"Füll&muster (%d schraffierte%s Layer vorhanden)",
NumHatched,NumHatched==1?"r":"");
dlgGroup(s) {
dlgRadioButton("Ausgefüllt",HatchType) if (Dialog>1) LineReplace=0;
dlgRadioButton("Windows-Füllmuster (hat nur 6 Muster: = || // \\\\\\\\ ++ xx)",HatchType);
dlgHBoxLayout{
dlgRadioButton("Exakte Füllmuster wie in Eagle",HatchType);
dlgLabel(DeckLabel,1); // "transparent" oder "deckend"
dlgStretch(1);
}
// Vorher ermitteln, ob überhaupt problematische Linien oder Polygone
// mit Strichbreite>0 vorhanden sind!?
if (Dialog>1) {
dlgSpacing(4);
dlgGroup("Ersetzung von schraffierten &Linien und Bögen") {
dlgRadioButton("Keine Ersetzung (dann ausgefüllt, nicht schraffiert)", LineReplace);
dlgRadioButton("Linien und Bögen durch Polygone ersetzen", LineReplace);
dlgRadioButton("Auch Polygonränder ersetzen", LineReplace);
}
}
}
}
if (schematic && Dialog>1) {
dlgGroup("Rotation") {
dlgRadioButton("keine",Flipped);
dlgRadioButton("90° nach links",Flipped);
dlgRadioButton("180°",Flipped);
dlgRadioButton("90° nach rechts",Flipped);
}
}
if (board) {
if (AnyBottomLayers() || Dialog>1) {
dlgCheckBox("&Gespiegelt (an Y-Achse, zur Seite)",Flipped);
}
if (Dialog>1) {
dlgCheckBox("Na&ch unten gespiegelt (an X-Achse)",TopDown);
dlgCheckBox("&90° nach links rotiert (nach den Spiegelungen)",Rotated);
}
dlgCheckBox("Simulierte Tr&ansparenz (mit Rasteroperationskodes)",Trans) {
DeckLabel=Trans?"(transparent)":"(deckend)";
if (Trans) switch (PaletteType) {
case 3: dlgMessageBox(
"Bei »Bunt auf farbigem Hintergrund« gibt es in Eagle < 5 keine Transparenz.<p>"
"Hier werden Komplementärfarben ausgegeben.<p>"+
"Hässliches Aussehen bei PostScript- und PDF-Weiterverarbeitung (Windows-Bug)");
break;
}
}
if (Dialog>1 || LastLayers) { // Nur bei "Dialog=2" oder LastLayer-Vorgabe
dlgGroup("&Umordnung der Layer-Ausgabe") {
dlgHBoxLayout{
dlgLabel("Zuletzt ausgeben (stets deckend):");
dlgSpacing(4);
dlgStringEdit(LastLayers);
}
}
}
}
if (DoNegate)
dlgCheckBox(EAGLE_VERSION<5?"&Negationsstriche statt »!« (wie Eagle 5)":"&Negationsstriche (sonst »!«)",DoNegate);
if (GroupOnly==1) dlgCheckBox("Selektierte G&ruppe (sonst ganze Seite)",GroupOnly);
else GroupOnly=0;
dlgCheckBox("&Hintergrund ausmalen (Vektorgrafik nicht transparent)",OutBkgnd);
if (NumSheet>1) {
dlgSpacing(4);
dlgCheckBox("&Alle Seiten (mehrere Dateien)",AllPages) {
if (AllPages) GroupOnly=0;
}
}
dlgSpacing(4);
dlgHBoxLayout {
dlgPushButton(FName?"+O&K":"+&Weiter, zum Speichern ...") {
if (Dialog<2 || CalcLastLayers()) dlgAccept(1);
}
dlgPushButton("Abbrechen") dlgReject();
}
};
}
/* Die Vektor-Schriftart und die (später) möglichen Zeichen-Ersetzungen
Kode Zeichen (CP1252) CP437 Symbol Unicode
80 128 ?
81 129 Raster B1 177
82 130 ?
83 131 ?
84 132 ?
85 133 ?
86 134 ?
87 135 ?
88 136 ?
89 137 ?
8A 138 ?
8B 139 ?
8C 140 ?
8D 141 Rahmen:0-0-1-1 DA 218 U+250C
8E 142 ?
8F 143 größer-gleich F2 242 B3 179
90 144 Block oben DF 223
91 145 ?
92 146 ?
93 147 ?
94 148 ?
95 149 ?
96 150 ?
97 151 ?
98 152 ?
99 153 ?
9A 154 ?
9B 155 ?
9C 156 ?
9D 157 Rahmen:0-0-2-2 C9 201 U+2554
9E 158 ?
9F 159 ?
A0 160 _ (anderer Kode)
A1 161 ¡
A2 162 Rahmen:1-2-0-0 BD 189 U+255C
A3 163 £
A4 164 Rahmen:2-1-2-0 D0 208 U+2567
A5 165 Rahmen:2-1-0-0 BE 190 U+255B
A6 166 Block rechts? DE 222
A7 167 Integral untere Hälfte F5 245
A8 168 Block? unten DC 220
A9 169 Rahmen:2-0-0-1 B8 184 U+2555
AA 170 ª
AB 171 «
AC 172 ¬
AD 173 identisch F0 240 BA 186
AE 174 umgekehrte Negation A9 169
AF 175 Element-von EE 238 CE 206
B0 176 °
B1 177 ±
B2 178 ²
B3 179 hochgestelltes n FC 252
B4 180 Schnittmenge EF 239 C7 199
B5 181 µ
B6 182 Integral obere Hälfte F4 244
B7 183 · (aber tiefer)
B8 184 Rund (zwei Wellen) F7 247 BB 187
B9 185 Wurzel FB 251 D6 214
BA 186 º
BB 187 »
BC 188 ¼
BD 189 ½
BE 190 kleiner-gleich F3 243 A3 163
BF 191 ¿
C0 192 Rahmen:1-0-0-2 B7 183 U+2556
C1 193 Rahmen:2-1-0-1 B5 181 U+2561
C2 194 Rahmen:1-2-0-2 B6 182 U+2562
C3 195 Rahmen:0-2-1-2 C7 199 U+255F
C4 196 Ä
C5 197 Å
C6 198 Æ
C7 199 Ç
C8 200 Rahmen:0-1-2-0 D4 212 U+2558
C9 201 É
CA 202 Rahmen:1-0-1-2 D2 210 U+2565
CB 203 Rahmen:0-2-1-0 D3 211 U+2559
CC 204 Block rechts? DE 222
CD 205 Rahmen:0-0-1-2 D6 214 U+2553
CE 206 Rahmen:1-2-1-2 D7 215 U+256B
CF 207 Rahmen:2-1-2-1 D8 216 U+256A
D0 208 Rahmen:2-0-2-1 D1 209 U+2564
D1 209 Ñ
D2 210 pi E3 227 70 112
D3 211 alpha E0 224 61 97
D4 212 Gamma E2 226 47 71
D5 213 rho E5 229 73 115
D6 214 Ö
D7 215 Peseta 9D?
D8 216 Ø
D9 217 delta EB 235 64 100
DA 218 Theta E9 233 51 81
DB 219 Ohm EA 234 57 87
DC 220 Ü
DD 221 ø (anderer Kode!) ED? 198
DE 222 Phi E8 232 46 70
DF 223 ß
E0 224 à
E1 225 á
E2 226 â
E3 227 Rahmen:0-1-2-1 C6 198 U+255E
E4 228 ä
E5 229 å
E6 230 æ
E7 231 ç
E8 232 è
E9 233 é
EA 234 ê
EB 235 Leerzeichen "nbsp"
EC 236 ì
ED 237 í
EE 238 î
EF 239 ï
F0 240 Rahmen:1-2-1-0 D0 208 U+2568
F1 241 ñ
F2 242 ò
F3 243 ó
F4 244 ô
F5 245 Sigma E4 228 53 83
F6 246 ö
F7 247 ÷
F8 248 ø
F9 249 ù
FA 250 ú
FB 251 û
FC 252 ü
FD 253 unendlich EC 236 A5 165
FE 254 tau E7 231 74 116
FF 255 ÿ
*/
string Problem160= // Ausrufezeichen bei Problemstellen,
"!¡!£!!¦!!!ª«¬º!ΰ±²!ǵ!·»Öº»¼½£¿" // ungleiche Zeichen bei Einsatz
+"!!!!ÄÅÆÇ!É!!!!!!!ÑpaGsÖ!ØdQWÜÆFß" // der Schriftart „Symbol“
+"àáâ!äåæçèéê ìíîï!ñòóôSö÷øùúûü¥tÿ";
int ProblemChars(string s) {
int len=strlen(s);
for (int i=0; i<len; i++) {
char c=s[i];
if (c<32) return 1;
if (c>=128 && c<160 && c!=143) return 1; //immer Problem
if (c>=160 && Problem160[c-160]=='!') return 1; // bspw. Rahmenzeichen
}
return 0;
}
/* liefert TRUE bei problematischem Text:
* Schriftart == Vector
UND Ersatzschrift != Vector
UND enthält Zeichen im Nicht-Latin1-Font
* Ersatzschrift != Vector
UND (Schrift gespiegelt XOR Ausgabe nicht gespiegelt)
UND im Board
--- WMF erlaubt leider keine Ausgabe von gespiegeltem Text ---
*/
int ProblemText(UL_TEXT O) {
if (O.font==FONT_VECTOR/*0*/
&& FontSel[0]
&& ProblemChars(O.value)) return 1;
// Zweiter Fall nicht implementiert
return 0;
}
// Kode für „Symbol“-Zeichensatz ermitteln, 0 wenn keine Symbol-Ersetzung
int GetSymbolChar(char c) {
if (c==143) return 179;
if (c>=160) {
char r=Problem160[c-160];
if (r!='!' && r!=c) return r;
}
return 0;
}
// Test des Strings auf Symbol-Zeichenersetzung
int HasSymbolChars(string s) {
int len=strlen(s);
for (int i=0; i<len; i++) {
if (GetSymbolChar(s[i])) return 1;
}
return 0;
}
string strrev(string s) {
string ret="";
for (int i=strlen(s);i;) ret+=s[--i];
return ret;
}
// liefert einen „Lauf“ gleicher Zeichentypen aus <s> ab <pos>.
// Ist <symb>!=0, dann SYMBOL-Zeichensatz, sonst ANSI-Zeichensatz
// Bei Symbol-Zeichensatz liefert die Funktion die Kodes
// des Symbol-Zeichensatzes.
string GetCharRun(string s, int pos, int symb) {
string ret="";
if (symb) for (;;) {
char c=GetSymbolChar(s[pos++]);
if (!c) break;
ret+=c;
}else for (;;) {
char c=s[pos++];
if (!c) break;
if (GetSymbolChar(c)) break;
ret+=c;
}
return ret;
}
int DupStrFind(UL_TEXT O) {
string hash;
sprintf(hash,"%d %d %f %d %d %d %d %s", // "spin" und "ratio" sind weggelassen
O.x,O.y,O.angle,O.font,O.layer,O.mirror,O.size,O.value);
int i=0;
for (; DupStrCount[i]; i++) {
if (DupStrHash[i]==hash) return i;
}
DupStrHash[i]=hash; // Neuen String einsetzen, Array verlängern
return i;
}
void CountFont(UL_TEXT O) {
if (!col[O.layer]) return; // unsichtbare Schrift nicht mitzählen
if (!strlen(O.value)) return; // Leere Strings nicht mitzählen
NumFont[O.font]++;
DupStrCount[DupStrFind(O)]++; // Dubletten finden
if (ProblemText(O)) FontSel[0]=0; // Vektor-Schrift voreinstellen (Default: Arial)
if (O.font==FONT_VECTOR && FontSel[0] && FontSel[0]==FontSel[1] && HasSymbolChars(O.value)) {
FontBold[0]=FontBold[1]; // Vorgabe: Soll gleich aussehen (typisch für Ohm-Zeichen)
FontItalic[0]=FontItalic[1];
}
if (!DoNegate) {
int i=strchr(O.value,'!'); // Schnellsuche (unexakt)
if (i!=-1 && i!=strlen(O.value)-1) DoNegate++; // Wenigstens Ausrufezeichen am Ende nicht beachten
}
}
int IsPie(UL_ARC O) {
return O.cap==CAP_FLAT && O.width>O.radius*1.9;
}
void CountFlatArc(UL_WIRE O) {
if (O.cap==CAP_FLAT && O.arc && !IsPie(O.arc)) NumFlatArc++;
if (O.style!=WIRE_STYLE_CONTINUOUS) NumPieced++;
}
void CatchGroupRect(UL_RECTANGLE O) {
if (O.angle) return;
GroupOnly++; // Geeignete Rechtecke mitzählen (Auswertung später)
GroupRect[0]=O.x1;
GroupRect[1]=O.y1;
GroupRect[2]=O.x2;
GroupRect[3]=O.y2;
}
/* Suche nach verwendeten Text-Fonts, zur Vorbereitung des Dialogs */
// Weiterhin wird (beim Vektor-Font) nach problematischen Zeichen gesucht.
// Bei Problemen wird die Voreinstellung für Vektor-Font
// von "Arial" auf "Vektor" zurückgesetzt.
// (Für alle, die nicht wissen, worum es geht:
// Eagle unterstützt bspw. das Omega-Zeichen, aber nur in der Vektor-Schrift.)
// In nicht-westeuropäischen Windows-Versionen sind größere Teile der
// Vektor-Schriftart problematisch ...
// Außerdem wird nach Bögen mit CAP_FLAT gesucht - sowie nach einem Auswahlrechteck
void SchCountFonts(UL_SCHEMATIC O) {
O.sheets(O){
string e;
sprintf(e,"Seite %d",O.number);
status(e);
NumSheet++;
O.busses(O) {
O.segments(O) {
O.texts(O) CountFont(O);
O.wires(O) CountFlatArc(O);
}
}
O.nets(O) {
O.segments(O) {
O.texts(O) CountFont(O);
O.wires(O) CountFlatArc(O);
}
}
O.parts(O) {
O.instances(O) {
O.gate.symbol.pins(O) {
O.texts(O) CountFont(O);
O.wires(O) CountFlatArc(O);
}
O.gate.symbol.texts(O) CountFont(O);
O.gate.symbol.wires(O) CountFlatArc(O);
O.texts(O) CountFont(O);
}
}
O.texts(O) CountFont(O);
O.wires(O) CountFlatArc(O);
O.rectangles(Q) if (Q.layer==LAYER_NETS) CatchGroupRect(Q);
}
}
// das gleiche für's Board
void BrdCountFonts(UL_BOARD O) {
O.elements(O) {
O.texts(O) CountFont(O);
O.package.texts(O) CountFont(O);
O.package.wires(O) CountFlatArc(O);
}
O.signals(O) O.wires(O) CountFlatArc(O);
O.texts(O) { // Vektor-Schrift im Kupfer wird NICHT gezählt (bleibt stets Vektor)
if (O.layer>LAYER_BOTTOM || O.font!=FONT_VECTOR) CountFont(O);
else if (col[O.layer]) NumCopperVector++;
}
O.wires(O) CountFlatArc(O);
O.rectangles(Q) if (Q.layer==LAYER_DIMENSION) CatchGroupRect(Q);
}
int IsRatsnestPoly(UL_POLYGON O) {
O.fillings(Q) return 1;
return 0;
}
// Prüft auf Vorhandensein sichtbarer, nicht geRATSNESTer Polygone (nur Kupfer)
int BrdAreRipupPolys(UL_BOARD O) {
O.signals(O) O.polygons(Q)
if (Q.layer<=LAYER_BOTTOM
&& col[Q.layer]
&& !IsRatsnestPoly(Q)) return 1;
return 0;
}
// **** globale Variablen ****
/* für den WMF-Kopf */
int nWords; // Länge WMF-Datei
int nMaxRecSize;// Länge längster WMF-Record
int nHandles;
/* WMF-Daten (Ansammlung im RAM, WORD-Array) */
int FileData[];
int CurLayer;
int LayerColor; // Windows-RGB
int LayerFill; // Windows-Füllstil (Low-Nibble, High-Nibble)
int LayerRop; // aktueller Windows-Rasteroperationskode
int LastX,LastY; // (kleinere Datei, schnelleres Rendern)
int TextColor;
int TextAlign;
int BackgroundColor;
int DrawHoles=0; // Löcher statt Pads/Vias zeichnen
/* Ausgabe 2 Byte in Intel-Notation (Little Endian) */
void PrintWord(int w) {
printf("%c%c",w&0xFF,(w>>8)&0xFF);
}
/* Ausgabe 2 oder 4 Byte in Intel-Notation in Datenpuffer */
void OutWord(int w) {
FileData[nWords++]=w;
}
void OutDword(int d) {
FileData[nWords++]=d;
FileData[nWords++]=d>>16;
}
// <term>=1: String garantiert nullterminieren, sonst nicht zwangsweise nullterminiert
void OutString(string s, int term) {
int i, len=strlen(s)+term;
for (i=0; i<len; i+=2) {
OutWord(s[i]&0xFF|s[i+1]<<8);
}
}
int nRecordStart;
void BeginRecord(int recordtype) {
nRecordStart=nWords; // Anfang merken
nWords+=2; // Platz für Länge lassen
OutWord(recordtype);
}
void EndRecord(void) {
int b=nWords-nRecordStart; // Länge in WORDs
if (nMaxRecSize<b) nMaxRecSize=b;
FileData[nRecordStart]=b; // Länge eintragen
FileData[nRecordStart+1]=b>>16;
}
int Mass(int eaglepixel) {
// 1 Eagle-Pixel sind 0,1 µm (254000 dpi), damit gibts Probleme mit 16 bit.
// (Bei Eagle 6+ gar 1/320 µm, 8128000 dpi)
// Division durch 254 ergibt 25,4 µm als Ersatzraster, 1/1000 Zoll = 1 mil, ergibt 1000 dpi,
// das scheint für alle gängigen Schaltpläne (und auch Boards) ausreichend zu sein.
// Die Maximalausdehnung wäre dann 1 mil * 32767 ~ 0,8 m
// Für 1200 dpi (Laserdrucker) müsste der Teiler 211,66 sein (Eagle 6+: 6773,3), alle krumm.
return round(u2mic(eaglepixel)/Teiler);
}
void OutMassX(int eaglepixel) {
OutWord(Mass(Flipped&1?-eaglepixel:eaglepixel));
}
void OutMassY(int eaglepixel) {
OutWord(Mass(Flipped&2?eaglepixel:-eaglepixel));
}
void OutXY(int x, int y) {
if (Flipped&4) {int t=x; x=y; y=t;}
OutMassY(y); // im WMF verkehrherum weil's auf Pascal-Aufrufkonvention basiert
OutMassX(x);
}
// Ausgabe für Polygone: „richtigherum“, gleich mit Koordinatentransformation
real poly_mx=0; // Verschiebung
real poly_my=0;
real poly_c=1; // Drehmatrix-Elemente | +cos +sin |
real poly_s=0; // | -sin +cos |
void SetPolyTr(real mx,real my,real angle/*in Grad*/) {
angle*=PI/180;
poly_mx=mx;
poly_my=my;
poly_c=cos(angle);
poly_s=sin(angle);
}
void OutPolyXY(real x, real y) {
int xx=poly_mx+poly_c*x-poly_s*y;
int yy=poly_my+poly_s*x+poly_c*y;
if (Flipped&4) {x=xx;xx=yy;yy=x;}
OutMassX(xx); // In richtiger Reihenfolge ausgeben
OutMassY(yy);
}
void OutPolyRA(real r, real a) {
a*=PI/180;
OutPolyXY(r*cos(a),r*sin(a));
}
void OutRop(int NewRop) {
if (NewRop==LayerRop) return;
BeginRecord(0x0104/*META_SETROP2*/);
OutWord(NewRop);
EndRecord();
LayerRop=NewRop;
}
// die Füllstile könnten auch per CreatePatternBrush exakt angepasst werden
// Da jedoch Schaltpläne äußerst selten Gebrauch davon machen, werden hier
// nur die Standard-Windows-Füllstile verwendet.
int FillStyles[]={ // bestmögliche Annäherung an Windows-Füllstile
1/*BS_NULL*/, // 0 Eagle-Füllung:
0/*BS_SOLID*/, // 1
0x02/*HS_HORIZONTAL*/, // 2 breite === 50%
0x32/*HS_BDIAGONAL*/, // 3 dünne /// 10%
0x32/*HS_BDIAGONAL*/, // 4 breite /// 50%
0x22/*HS_FDIAGONAL*/, // 5 breite \\\ 50%
0x22/*HS_FDIAGONAL*/, // 6 dünne \\\\ 10%
0x42/*HS_CROSS*/, // 7 dünne +++ 30%
0x52/*HS_DIAGCROSS*/, // 8 dünne xxx 30%
0x12/*HS_VERTICAL*/, // 9 Punkte 50%
0x12/*HS_VERTICAL*/, // A Punkte 10%
0x12/*HS_VERTICAL*/, // B Punkte 30%
0x02/*HS_HORIZONTAL*/, // C dünne === 50%
0x02/*HS_HORIZONTAL*/, // D dünne === 50%
0x02/*HS_HORIZONTAL*/, // E dünne === 50%
0x02/*HS_HORIZONTAL*/};// F dünne === 50%
// Für genauere Füllstil-Simulation muss man entsprechende Bitmaps laden
// Bei PostScript-Ausgabe werden unschön große Schraffuren ausgegeben
int EaglePattern[]={ // ab Index 2
0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF, // breite waagerechte Linien
0x04,0x08,0x10,0x20,0x40,0x80,0x01,0x02, // HS_BDIAGONAL
0xE0,0xC1,0x83,0x07,0x0E,0x1C,0x38,0x70, // // dick
0x1C,0x0E,0x07,0x83,0xC1,0xE0,0x70,0x38, // \\ dick
0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01, // HS_FDIAGONAL
0x11,0x11,0x11,0xFF,0x11,0x11,0x11,0xFF, // ++
0x48,0x84,0x03,0x03,0x84,0x48,0x30,0x30, // xx
0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55, // Schachbrett
0x00,0x10,0x00,0x01,0x00,0x10,0x00,0x01, // Punkte dünn
0x00,0x44,0x00,0x11,0x00,0x44,0x00,0x11, // Punkte dichter
0x00,0xAA,0x00,0xAA,0x00,0xAA,0x00,0xAA, // Punkte angeordnet
0x00,0x55,0x00,0x55,0x00,0x55,0x00,0x55,
0x55,0x00,0x55,0x00,0x55,0x00,0x55,0x00,
0xAA,0x00,0xAA,0x00,0xAA,0x00,0xAA,0x00};
// Bei PostScript-Ausgabe erscheint eine grobe Klötzchenfüllung.
// Umwandeln RGB in Windows-Farbe
int rgb2bgr(int rgb) {
return ((rgb&0xFF)<<16)|(rgb&0xFF00)|((rgb>>16)&0xFF);
}
int odd(int v) {
while (v&~1) v=v&1^(v>>=1);
return v;
}
// Füllstil-Code für Layer <n> berechnen
int CalcFill(int n) {
int ret;
switch (HatchType) {
case 0: ret=0; break;
case 1: {
ret=FillStyles[fil[n]];
if (odd(Flipped&3)) switch (ret) {
case 0x22/*HS_FDIAGONAL*/:
case 0x32/*HS_BDIAGONAL*/: ret^=0x32^0x22; // Richtungen tauschen
}
}break;
case 2: switch (fil[n]) {
case 0: ret=1/*BS_NULL*/; break;
case 1: ret=0/*BS_SOLID*/; break;
default: ret=((fil[n]-2)<<4)|3;
}break;
}
return ret;
}
// Funktion liefert 0 (FALSE) wenn Eagle-Layer <n> unsichtbar
int SetLayer(int n) {
int c=col[n]; // c=0 wenn Layer nicht sichtbar
if (c) {
int rop=13/*R2_COPYPEN = P*/;
if (Trans) switch (PaletteType) {
case 0:
case 2: rop=9/*R2_MASKPEN = DPa*/; break;
case 1: rop=15/*R2_MERGEPEN = DPo*/; break;
case 3: rop=3/*R2_MASKNOTPEN = DPna*/; break; // Komplementärfarben
}
OutRop(rop); // Für Schrift ist die ROP-Einstellung irrelevant!
int rgb=PaletteType?palette(c,PaletteType-1):0;
if (rop==3 && !rgb) rgb=0xFFFFFF;
LayerColor=rgb2bgr(rgb);
LayerFill=CalcFill(n);
}
return c;
}
// globale Variablen zur Verwaltung der sogenannten GDI-Objekte
// Die Arrays wachsen mit der Verwendung verschiedener Pinsel, Stifte und Schriften.
int OD_type[];
int OD_param1[];
int OD_param2[];
int OD_color[];
int FindObj(int type, int p1, int p2, int color) {
int j;
for (j=0; j<nHandles; j++) {
if (type==OD_type[j]
&& p1==OD_param1[j]
&& p2==OD_param2[j]
&& color==OD_color[j]) return j; // vorhandenes Objekt benutzen
}
OD_type[j] =type; // neues Objekt vermerken (j == nHandles)
OD_param1[j]=p1;
OD_param2[j]=p2;
OD_color[j] =color;
return j;
}
void OutSelectObject(int idx) {
BeginRecord(0x12D/*META_SELECTOBJECT*/);
OutWord(idx);
EndRecord();
}
void OutDeleteObject(int idx) {
BeginRecord(0x1F0/*META_DELETEOBJECT*/);
OutWord(idx);
EndRecord();
}
int ObjInUse[]; // Momentanes verwendetes Objekt aus den drei u.g. Typen
enum {ObjPen,ObjBrush,ObjFont};
void ClearObjects(void) {
// Handletabelle löschen, sonstige GDI-Attribute invalidieren
nHandles=0;
TextColor=-1;
TextAlign=-1;
LastX=INT_MAX; // ungültige Position
LayerRop=13/*R2_COPYPEN*/;
}
// Liefert Index in Handle-Tabelle (für schnelle WMF-Darstellung),
// liefert -1, wenn kein neues CreateXxx erforderlich ist.
int GetNewHandleIndex(int objtype, int h1, int h2, int color) {
int j=FindObj(objtype,h1,h2,color);
if (j==nHandles) { // nicht gefunden: neues Objekt
ObjInUse[objtype]=j;
nHandles++;
return j;
} // gefunden: ggf. als aktuelles Objekt aktivieren
if (ObjInUse[objtype]!=j) {
OutSelectObject(j);
ObjInUse[objtype]=j;
}
return -1;
}
void SetPen(int style, int width, int color) {
width=Mass(width);
if (style==5) width=color=0; // PS_NULL: keine Breite, keine Farbe
int idx=GetNewHandleIndex(ObjPen,style,width,color);
if (idx<0) return;
BeginRecord(0x02FA/*META_CREATEPENINDIRECT*/);
OutWord(style);
OutWord(width);
OutWord(width);
OutDword(color);
EndRecord();
OutSelectObject(idx);
}
void SetTextColor(int color) {
if (TextColor!=color) {
BeginRecord(0x0209/*META_SETTEXTCOLOR*/);
OutDword(color);
EndRecord();
TextColor=color;
}
}
void SetBrush(int style, int hatch, int color) {
if (style<2) hatch=0; // BS_SOLID,BS_NULL: kein Muster
if (style==1) color=0; // BS_NULL: keine Farbe
int idx=GetNewHandleIndex(ObjBrush,style,hatch,color);
if (idx<0) return;
if (style<3) {
BeginRecord(0x02FC/*META_CREATEBRUSHINDIRECT*/);
OutWord(style);
OutDword(color);
OutWord(hatch);
EndRecord();
}else{
int i;
BeginRecord(0x01F9/*META_CREATEPATTERNBRUSH*/);
OutWord(0/*BitmapType ??*/);
OutWord(8); // Breite
OutWord(8); // Höhe
OutWord(2); // Breite in Bytes (muss geradzahlig sein)
OutWord(0x0101); // Planes und Bit pro Pixel
for (i=0; i<11; i++) OutWord(0); // reservierte WORDs
for (i=0; i<8; i++) OutWord(~EaglePattern[hatch*8+(odd(Flipped&3)?7-i:i)]);
EndRecord();
SetTextColor(color);
}
OutSelectObject(idx);
}
void SetFont(int font, int size, int angle, int color, int align) {
int sel=4;
if (font&4) { // Symbol-Bit gesetzt?
font&=~4;
}else{
sel=FontSel[font];
}
if (!sel) return; // VEKTOR - das geht hier nicht!
size=Mass(size*1.6);
int idx=GetNewHandleIndex(ObjFont,
sel|FontBold[font]<<8|FontItalic[font]<<9,size,angle);
if (idx>=0) {
BeginRecord(0x02FB/*META_CREATEFONTINDIRECT*/);
OutWord(size);
OutWord(0);
OutWord(angle);
OutWord(angle); // wird von Win16 ignoriert
OutWord(FontBold[font]?700:400);
OutWord(FontItalic[font]);
OutWord(sel==4?0x200:0); // HIBYTE = LF_CHARSET
OutWord(4/*OUT_TT_PRECIS*/); // sonst kleine Schriften immer 0°
OutWord(0/*FontFamily[font]<<8*/);
OutString(FontName[sel],1);
EndRecord();
OutSelectObject(idx);
}
SetTextColor(color);
if (TextAlign!=align) {
BeginRecord(0x012E/*META_SETTEXTALIGN*/);
OutWord(align);
EndRecord();
TextAlign=align;
}
}
// Koordinaten sortieren und zwei Punktkordinaten ausgeben
void OutRect(int left, int top, int right, int bottom) {
if (!(Flipped&1) && left>right
|| Flipped&1 && left<right) {int t=left; left=right; right=t;}
if (!(Flipped&2) && top<bottom
|| Flipped&2 && top>bottom) {int t=top; top=bottom; bottom=t;}
OutXY(right,bottom);
OutXY(left,top); // rclBox
}
// Gedrehtes Rechteck ausgeben
void OutRectangle(int mx, int my, int x, int y, real angle) {
int w=angle*10;
x/=2; y/=2; // jetzt Abstand vom Ursprung
switch (w) {
case 0:
case 900:
case 1800:
case 2700: {
BeginRecord(0x041B/*META_RECTANGLE*/);
if (w==0 || w==1800) {
OutRect(mx-x,my-y,mx+x,my+y);
}else{ // 90° / 270°:
OutRect(mx-y,my-x,mx+y,my+x);
}
}break;
default: {
// Für Board: vollständige Koordinatentransformation und Ausgabe eines Polygons!
BeginRecord(0x0324/*META_POLYGON*/);
OutWord(4);
SetPolyTr(mx,my,angle);
OutPolyXY( x, y); // I.
OutPolyXY(-x, y); // II.
OutPolyXY(-x,-y); // III.
OutPolyXY( x,-y); // IV. Quadrant
}
}
EndRecord();
}
// Gedrehtes RoundRect ausgeben (r!=0), nur für Board, SMDs
void OutRoundRect(int mx, int my, int x, int y, int r, real angle) {
int w=angle*10;
x/=2; y/=2; // jetzt Abstand vom Ursprung
switch (w) {
case 0:
case 900:
case 1800:
case 2700: {
BeginRecord(0x061C/*META_ROUNDRECT*/);
OutWord(Mass(2*r));
OutWord(Mass(2*r));
if (w==0 || w==1800) {
OutRect(mx-x,my-y,mx+x,my+y);
}else{ // 90° / 270°:
OutRect(mx-y,my-x,mx+y,my+x);
}
}break;
default: {
// Polygonausgabe mit Kreisapproximation: als Zwölfeck (sollte reichen)
real z=1-sin(PI/4); // 45°
BeginRecord(0x0324/*META_POLYGON*/);
OutWord(12);
SetPolyTr(mx,my,angle);
OutPolyXY( x , y-r );
OutPolyXY( x-r*z, y-r*z);
OutPolyXY( x-r , y );
OutPolyXY(-x+r , y );
OutPolyXY(-x+r*z, y-r*z);
OutPolyXY(-x , y-r );
OutPolyXY(-x ,-y+r );
OutPolyXY(-x+r*z,-y+r*z);
OutPolyXY(-x+r ,-y );
OutPolyXY( x-r ,-y );
OutPolyXY( x-r*z,-y+r*z);
OutPolyXY( x ,-y+r );
}
}
EndRecord();
}
// Gedrehten(:-) Kreis ausgeben
void OutCircle(int mx, int my, int r) {
BeginRecord(0x0418/*META_ELLIPSE*/);
OutRect(mx-r,my-r,mx+r,my+r);
EndRecord();
}
// Gedrehtes Achteck ausgeben (für Pads), type=0: normal, type=1: lang, type=2:exzentrisch
void OutOctagon(int mx, int my, real d, real angle, int type) {
d/=2;
int i;
real x[],y[];
real j=tan(22.5*PI/180);
x[0]=d*j; y[0]=d; // Achteck im Uhrzeigersinn bauen
x[1]=d; y[1]=d*j;
x[2]=d; y[2]=-d*j;
x[3]=d*j; y[3]=-d;
x[4]=-d*j; y[4]=-d;
x[5]=-d; y[5]=-d*j;
x[6]=-d; y[6]=d*j;
x[7]=-d*j; y[7]=d;
if (type) for (i=0; i<4; i++) x[i]+=2*d; // lang machen (für Typ 1 und 2)
if (type==1) for (i=0; i<8; i++) x[i]-=d; // zentrieren (für Typ 1)
BeginRecord(0x0324/*META_POLYGON*/);
OutWord(8);
SetPolyTr(mx,my,angle);
for (i=0; i<8; i++) OutPolyXY(x[i],y[i]);
EndRecord();
}
/* Abarbeitung von Eagle-Zeichenprimitiven */
int DontProcess(int ElemLayer) {
if (PaletteType || board) return ElemLayer!=CurLayer; // Farbmodus
else return !col[ElemLayer]; // Schwarzweiß-Modus
}
// wie SetBrush(), jedoch mit _einem_ Füllstil-Attribut
void SetBrush2(int Fill) {
SetBrush(Fill&0x0F,Fill>>4,LayerColor);
}
// Zum angegebenen Layer passenden Stift (minimale Strichbreite) erzeugen
void SetLayerPen() {
SetPen(0/*PS_SOLID*/,0,LayerColor);
}
// Zum angegebenen Layer passenden Pinsel erzeugen
void SetLayerBrush() {
SetBrush2(LayerFill);
}
void HandleCircle(UL_CIRCLE O) {
if (DontProcess(O.layer)) return;
SetPen(0/*PS_SOLID*/,O.width,LayerColor);
if (O.width) SetBrush(1/*BS_NULL*/,0,0);
else SetLayerBrush();
OutCircle(O.x,O.y,O.radius);
}
void HandleJunction(UL_JUNCTION O) {
if (DontProcess(LAYER_NETS)) return;
SetLayerPen();
SetLayerBrush();
OutCircle(O.x,O.y,O.diameter/2);
}
// Längliches Achteck anstelle Linie ausgeben (schraffierte Linien)
// Keine Unterstützung für gestrichelte Linien!
void OutOctLine(int x1, int y1, int x2, int y2, int d) {
BeginRecord(0x0324/*META_POLYGON*/);
d/=2;
int i;
real x[],y[];
real j=tan(22.5*PI/180); // Achteck-Ecken
real c=x2-x1; // (c,s) = Richtungsvektor und
real s=y2-y1; // zu real casten (Überlauf beim Quadrieren vermeiden)
real l=sqrt(c*c+s*s); // Länge (Pythagoras)
x[0]=l+d*j; y[0]=d; // Achteck nach rechts bauen
x[1]=l+d; y[1]=d*j;
x[2]=l+d; y[2]=-d*j;
x[3]=l+d*j; y[3]=-d;
x[4]=-d*j; y[4]=-d;
x[5]=-d; y[5]=-d*j;
x[6]=-d; y[6]=d*j;
x[7]=-d*j; y[7]=d;
OutWord(8);
SetPolyTr(x1,y1,0);
poly_c=c/l; // Hier: Mit möglicher Skalierung ohne Winkelangabe
poly_s=s/l;
for (i=0; i<8; i++) OutPolyXY(x[i],y[i]);
EndRecord();
}
void HandlePolygon(UL_POLYGON O) {
// Polygone mit Kurven-Begrenzung können mit WMF nicht (oder höchstens
// näherungsweise) unterstützt werden.
if (DontProcess(O.layer)) return;
int ExtraBorder=LayerFill && LineReplace>=2 && u2mic(O.width)>=MinBreite;
if (ExtraBorder) SetLayerPen(); // dünner Rand
else SetPen(0/*PS_SOLID*/,O.width,LayerColor);
if (O.pour==POLYGON_POUR_HATCH) SetBrush(2,4/*HS_CROSS*/,LayerColor);
else{
int Fill=0;
if (board) Fill=LayerFill;
SetBrush2(Fill);
}
int np=0,nk[],n=0,x0,y0;
nk[0]=0;
O.contours(W) {
// Hoppla! "contours" macht aus Bögen Geradenstücke, auch im Schaltplan!
// Das erspart hier eine Menge Arbeit.
if (!nk[np]) x0=W.x1, y0=W.y1; // Linienzug-Anfang
nk[np]++; // Kanten abzählen (per Polygon)
n++; // Kanten abzählen (Gesamtanzahl)
if (x0==W.x2 && y0==W.y2) nk[++np]=0; // Linienzug-Ende am Anfang
}
// if (!first) return; // Ende != Anfang (sollte in Eagle nicht vorkommen)
// if (nk[0]<3) return; // Entartetes Polygon (sollte auch nie vorkommen)
if (np==1) {
BeginRecord(0x0324/*META_POLYGON*/);
OutWord(n); // cptl
}else{
BeginRecord(0x0538/*META_POLYPOLYGON*/);
OutWord(np); // Anzahl Einzelpolygone
for (n=0; n<np; n++) OutWord(nk[n]); // Punktzahl der Einzelpolygone
}
SetPolyTr(0,0,0);
O.contours(W) OutPolyXY(W.x1,W.y1); // aptl
EndRecord();
if (ExtraBorder) O.contours(W) {
OutOctLine(W.x1,W.y1,W.x2,W.y2,O.width);
}
}
void HandleRectangle(UL_RECTANGLE O) {
if (DontProcess(O.layer)) return;
SetLayerPen();
SetLayerBrush();
OutRectangle((O.x1+O.x2)/2,(O.y1+O.y2)/2,O.x2-O.x1,O.y2-O.y1,O.angle);
}
void OutLineTo(int x, int y) {
BeginRecord(0x0213/*META_LINETO*/);
OutXY(x,y);
EndRecord();
LastX=x; LastY=y;
}
void OutMoveTo(int x, int y) {
if (LastX!=x || LastY!=y) {
BeginRecord(0x0214/*META_MOVETO*/);
OutXY(x,y);
EndRecord();
}
}
void OutLine(int x1, int y1, int x2, int y2) {
OutMoveTo(x1,y1);
OutLineTo(x2,y2);
}
// Sinus und Kosinus mit Winkelgrad
real Cos(real scale, real angle) {
return scale*cos(angle*PI/180);
}
real Sin(real scale, real angle) {
return scale*sin(angle*PI/180);
}
// Bogen als Polygon-Näherung ausgeben
void OutPolyArc(UL_ARC O) {
real INCREMENT=5; // Grad Schrittweite
int n=ceil((O.angle2-O.angle1-0.1)/INCREMENT); // 1 Bogen ohne Ende
int NumPoints=n+1; // Endpunkt dazu
if (O.cap!=CAP_FLAT) NumPoints+=4; // Achteck-Ende dazu
NumPoints*=2; // beide Seiten
BeginRecord(0x0324/*META_POLYGON*/);
OutWord(NumPoints);
real a,b;
real rad=O.width*0.5412; // Radius für "runde" Enden
int i;
int r=O.radius-O.width/2; // innerer Bogen (entgegen Uhrzeiger)
SetPolyTr(O.xc,O.yc,0);
for (a=O.angle1, i=0; i<n; a+=INCREMENT, i++) {
OutPolyRA(r,a);
}
OutPolyRA(r,O.angle2); // Endpunkt innen
SetPolyTr(O.x2,O.y2,O.angle2);
if (O.cap!=CAP_FLAT) for (b=157.5; b>0; b-=45) {
OutPolyRA(rad,b);
}
r+=O.width; // äußerer Bogen (Uhrzeiger)
SetPolyTr(O.xc,O.yc,0);
OutPolyRA(r,O.angle2); // Endpunkt außen
do{
a-=INCREMENT; // Stützpunkte gegenüberstehend!
OutPolyRA(r,a);
}while(--i);
SetPolyTr(O.x1,O.y1,O.angle1);
if (O.cap!=CAP_FLAT) for (b=-22.5; b>-180; b-=45) {
OutPolyRA(rad,b);
}
EndRecord();
}
void HandleWire(UL_WIRE O) {
if (DontProcess(O.layer)) return;
int st;
switch (O.style) {
case WIRE_STYLE_CONTINUOUS: st=0/*PS_SOLID*/; break;
case WIRE_STYLE_LONGDASH: st=1/*PS_DASH*/; break;
case WIRE_STYLE_SHORTDASH: st=2/*PS_DOT*/; break;
case WIRE_STYLE_DASHDOT: st=3/*PS_DASHDOT*/; break;
}
if (O.arc) {
// Die Ausgabe von Bögen ist bei großen Strichbreiten unbefriedigend,
// weil bei Eagle glatte Kurvenenden gezeichnet werden (PS_ENDCAP_FLAT);
// WMF (Windows 3.1) unterstützt nur PS_ENDCAP_ROUND.
// In diesem Fall ist ein „Tortenstück“ (Pie) die bessere Ausgabe.
// TODO:
// Die Bogenausgabe für gestrichelte Layer erfordert
// Polygonausgabe mit einer großen Anzahl Stützstellen.
// Das ENDCAP-Problem ist hierbei keins.
int rad;
if (IsPie(O.arc)) {
SetLayerPen();
SetLayerBrush();
BeginRecord(0x081A/*META_PIE*/);
rad=O.arc.radius+O.width/2;
}else if (u2mic(O.width)>=MinBreite && (
O.arc.cap==CAP_FLAT && O.arc.width>=FlatArcReplace*O.arc.radius || LayerFill && LineReplace)) {
SetLayerPen();
SetLayerBrush();
if (PiecedReplace && O.style!=WIRE_STYLE_CONTINUOUS)
O.pieces(W) OutPolyArc(W.arc);
else OutPolyArc(O.arc);
return;
}else{
SetPen(st,O.width,LayerColor);
BeginRecord(0x0817/*META_ARC*/);
rad=O.arc.radius;
}
if (odd(Flipped&7)) {
OutXY(O.arc.x1,O.arc.y1);
OutXY(O.arc.x2,O.arc.y2);
}else{
OutXY(O.arc.x2,O.arc.y2);
OutXY(O.arc.x1,O.arc.y1);
}
OutRect(O.arc.xc-rad,O.arc.yc-rad,
O.arc.xc+rad,O.arc.yc+rad);
EndRecord();
}else{
if (LayerFill && LineReplace && u2mic(O.width)>=MinBreite) {
SetLayerPen();
SetLayerBrush();
if (PiecedReplace && O.style!=WIRE_STYLE_CONTINUOUS)
O.pieces(W) OutOctLine(W.x1,W.y1,W.x2,W.y2,W.width);
else OutOctLine(O.x1,O.y1,O.x2,O.y2,O.width);
}else if (PiecedReplace>=2 && O.style!=WIRE_STYLE_CONTINUOUS
&& u2mic(O.width)>=MinBreite) {
SetPen(0,O.width,LayerColor);
O.pieces(W) OutLine(W.x1,W.y1,W.x2,W.y2);
}else{
SetPen(st,O.width,LayerColor);
OutLine(O.x1,O.y1,O.x2,O.y2);
}
}
}
// Textfarbe dunkler machen (auffälliger bei transparenter Ausgabe)
int DarkerColor(int col) {
int rgb[];
rgb[0]=col&0xFF;
rgb[1]=(col>>8)&0xFF;
rgb[2]=(col>>16)&0xFF;
rgb[0]>>=2; // vierteln
rgb[1]>>=2;
rgb[2]>>=2;
return (rgb[2]<<16)|(rgb[1]<<8)|rgb[0];
}
string CharWidth[]={
"8<>OOlY0<<AR8<88OOOOOOOOOO88RRROvYY]]YTb]8KYOg]bYb]YT]YuXXT888FO<OOKOO8OO34L3hOOOO<K8OK]KII<6<R`O`3O<uOO<vY<u`T``33<<>Ou9uK<p`IY8<OOOO6O<_?OR<_OBO<<<QN8<<?OgggTYYYYYYu]YYYY8888]]bbbbbRb]]]]YYTOOOOOOlKOOOO8888OOOOOOOOTOOOOKOK",
"",
"5;CJJfa.<<JO5;57JJJJJJJJJJ77OOOFm]XX]SO]]<B\Rk]]O]XOS]]o]]S<6<GJ<EJEJE=IJ78I7_JJJJ<A7JI\JHEH/HMaJa<JFtJK<tO<kaSaa<<FE=Jt<rA<]aE]5;JJJJ/J>`7HO;`JAN99;PF599;H___E]]]]]]iXSSSS<<<<]]]]]]]O]]]]]]OJEEEEEEXEEEEE7777JJJJJJJNJJJJJJJJ"};
int GetVectorLineWidth(UL_TEXT O) {
O.wires(W) return W.width;
}
// Entfernt Ausrufezeichen sowie Escape-Rückwärtsstriche,
// malt die Überstreichungs-Striche möglichst genau über die (proportionalen,
// True-Type-) Buchstaben in der Linienbreite der alternativen Vektorschrift
string HandleNegation(UL_TEXT O, real angle, int textalign) {
string s=O.value;
int i=DupStrFind(O);
int j=DupStrCount[i]; // gezählte Vorkommen (sollte hier !=0 sein)
if (j>=0) { // Sollte hier nie Null sein, falls doch, dann String unverändert ausgeben
if (j>=2 && DupStrReplace) sprintf(s,"%d×%s",j,s);
DupStrCount[i]=-j; // Negativ: String erledigt
}else return ""; // nicht mehrfach ausspucken
if (!DoNegate) return s;
int n; // n gerade wenn kein Strich
int sel=FontSel[O.font]; // muss !=0 sein!
real mult=O.size*1.6/96;
real x,xx[],y; // laufende Pixel, Pixel für Linie, Höhe
char c;
// Modifizieren des Strings, Finden der Überstreichpositionen
for (i=j=x=n=0; c=s[i]; i++) {
switch (c) {
case '\\': {
c=s[++i]; // Folgezeichen buchstäblich nehmen
if (!c) {
--i;
continue; // Backslash herausnehmen
}
}break;
case '!': if (!(n&1)) {
switch (s[i+1]) { // Folgezeichen untersuchen
case 0:
case ' ':
case '!':
case '\'':
case '"':
case ')':
case ']':
case '}': break;
default: n++;
}
if (n&1) {
xx[n-1]=x; // Startposition vermerken
continue; // Ausrufezeichen herausnehmen
}
}else{
xx[n++]=x; // Endposition vermerken
continue; // Ausrufezeichen herausnehmen
}break;
case ',': if (n&1) {
xx[n++]=x; // Endposition vermerken
}break;
}
s[j++]=c;
if (sel==2) x+=53*mult; // Zeichenbreite für 96 Punkt "Courier New"
else if (c<32) x+=40*mult; // falls Sonderzeichen auftauchen (Eagle6: falsches UTF-8)
else x+=(CharWidth[sel-1][c-32]-32)*mult; // Stringbreite in Pixel mitschneiden
}
if (n&1) xx[n++]=x;
s[j]=0; // Ziel (nie länger als Quelle) abhacken
if (n) {
// Striche malen
angle*=PI/180;
real c=cos(angle); // Drehmatrix-Elemente | +cos +sin |
real s=sin(angle); // | -sin +cos |
real x0=O.x;
real y0=O.y;
if (textalign&2/*TA_RIGHT*/) {
x0-=x*c;
y0-=x*s;
}
if (textalign&24/*TA_BASELINE*/) y=O.size*1.25; // empirischer Abstand
else y=O.size*0.25;
x0-=y*s;
y0+=y*c;
SetPen(0/*PS_SOLID*/,GetVectorLineWidth(O),LayerColor);
for (i=0; i<n; i+=2) {
int x1=x0+xx[i]*c;
int y1=y0+xx[i]*s;
int x2=x0+xx[i+1]*c;
int y2=y0+xx[i+1]*s;
OutLine(x1,y1,x2,y2);
}
}
return s;
}
void HandleText(UL_TEXT O) {
// Sehr schade: Win16 kann keinen gespiegelten Text (für Board) ausgeben!
// Auf dem Schaltplan hat O.mirror eine andere Bedeutung als auf dem Board.
if (DontProcess(O.layer)) return;
if (O.value==" ") return; // unterdrückte Values bei Bauelementen
if (!FontSel[O.font] // Ersetzung durch Vektor gewünscht
|| O.font==FONT_VECTOR && O.layer<=LAYER_BOTTOM // Vektorfont auf Kupfer
|| board && odd(Flipped&7) && !O.mirror // gespiegelten Text stets als Vektoren
|| board && !odd(Flipped&7) && O.mirror) { // auch gespiegelt
// Hier sollte eigentlich Eagle 5+ die Überstreichungen generieren, ist das so?
O.wires(W) HandleWire(W); // Text als Striche ausgeben
return;
}
// Hier ggf. auf Symbol-Zeichensatz umschalten und mit TA_UPDATECP arbeiten!
if (ProblemText(O)) dlgMessageBox(
"Problematischer Text: »"+O.value+"«<p>Zeichen werden falsch wiedergegeben.");
real w=O.angle;
int a=24/*TA_BASELINE*/;
// Nur für Schaltplan:
if (schematic && O.mirror) {
if (w==90 || w==270) a^=24;
else a^=2/*TA_RIGHT*/;
}
if (!O.spin && w>90 && w<=270) { // Änderung von Ausrichtung bei spin=0?
w-=180; if (w<0) w+=360; // (für Ausführung im Board)
a^=24^2; // Ausrichtung genau anders herum
}
if (Flipped&2) w+=180; // huh? Reicht das?
if (Flipped&4) w+=90;
string s=HandleNegation(O,w,a);
int l=strlen(s);
if (!l) return;
int col=LayerColor;
if (Trans && PaletteType==3 && CurLayer>=21) col=DarkerColor(col);
if (Debug) OutCircle(O.x,O.y,508/u2mic(1)); // 20 mil (0,5 mm) Radius
int mx=O.x;
int my=O.y;
if (!(a&8)) { // TA_TOP? Etwas nach oben korrigieren
mx-=O.size*Sin(0.28,w);
my+=O.size*Cos(0.28,w);
}
if (O.font==FONT_VECTOR && HasSymbolChars(s)) {
a|=1/*TA_UPDATECP*/;
OutMoveTo(mx,my);
string s1;
int pos=0, sublen;
int font=O.font;
if (a&2) s=strrev(s); // TA_RIGHT
for(;;) {
s1=GetCharRun(s,pos,font&4);
sublen=strlen(s1);
if (pos && !sublen) break; // abbrechen bei Ende
if (sublen) {
if (a&2) s1=strrev(s1); // TA_RIGHT
SetFont(font,O.size,w*10,col,a);
BeginRecord(0x0521/*META_TEXTOUT*/);
OutWord(sublen);
OutString(s1,0);
OutDword(0); // Position hier nicht ausgewertet!
// Ohne diese Null hat der F3-Viewer von Total Commander ein ernstes Problem!
// Ansonsten kann man diese auch weglassen.
EndRecord();
}
font^=4; // umschalten Symbol<->ANSI-Schriftart
pos+=sublen;
}
LastX=INT_MAX; // Position ungültig
return;
}
SetFont(O.font,O.size,w*10,col,a);
BeginRecord(0x0521/*META_TEXTOUT*/);
OutWord(l);
OutString(s,0);
OutXY(mx,my);
EndRecord();
}
/* Nur für Schaltplan */
string DirNames[]={"Nc","In","Out","Io","Oc","Pwr","Pas","Hiz","Sup"};
void HandlePin(UL_PIN O) {
if (DontProcess(LAYER_PINS)) return;
SetLayerPen();
SetBrush(1/*BS_NULL*/,0,0);
OutCircle(O.x,O.y,1016/u2mic(1));
int w=O.angle;
int a=8|2/*TA_BOTTOM|TA_RIGHT*/;
if (w>=180) {
w-=180;
a^=2/*TA_RIGHT*/;
}
SetFont(1/*Proportional*/,950/u2mic(1),w*10,LayerColor,a);
string s;
BeginRecord(0x0521/*META_TEXTOUT*/);
OutWord(sprintf(s,"%s %d",DirNames[O.direction],O.swaplevel));
OutString(s,0);
OutXY(O.x,O.y);
EndRecord();
}
/* Nur für Boards: */
void OutDrillSymbol(int x, int y, int type) {
int D=4*254/u2mic(1);
switch (type) {
case 0: OutLine(x-D,y-D,x+D,y+D); OutCircle(x,y,D); break; // Ø
case 1: OutLine(x-D,y,x+D,y); OutLine(x,y-D,x,y+D); break; // +
case 2: OutLine(x-D,y-D,x+D,y+D); OutLine(x-D,y+D,x+D,y-D); break; // x
case 3: OutLine(x,y,x,y+D); OutRectangle(x,y,D*2,D*2,0); break;
case 4: OutLine(x,y,x,y+D); OutLineTo(x-D,y); OutLineTo(x,y-D); OutLineTo(x+D,y); OutLineTo(x,y+D); break;
case 5: OutLine(x-D,y-D,x+D,y+D); OutLineTo(x-D,y+D); OutLineTo(x+D,y-D); OutLineTo(x-D,y-D); break;
case 6: OutLine(x-D,y+D,x+D,y-D); OutLineTo(x+D,y+D); OutLineTo(x-D,y-D); OutLineTo(x-D,y+D); break;
case 7: OutLine(x-D,y,x+D,y); OutLineTo(x,y+D); OutLineTo(x,y-D); OutLineTo(x-D,y); break;
case 8: OutLine(x-D,y,x+D,y); OutLineTo(x,y-D); OutLineTo(x,y+D); OutLineTo(x-D,y); break;
case 9: OutLine(x-D,y+D,x+D,y-D); OutLine(x,y-D,x,y+D); OutLine(x+D,y+D,x-D,y-D); break;
case 10: OutLine(x-D,y,x+D,y); OutLine(x+D,y+D,x-D,y-D); OutLine(x-D,y+D,x+D,y-D); break;
case 11: OutLine(x,y-D,x,y); OutLineTo(x+D,y+D); OutLineTo(x-D,y+D); OutLineTo(x,y); break;
case 12: OutLine(x,y+D,x,y); OutLineTo(x+D,y-D); OutLineTo(x-D,y-D); OutLineTo(x,y); break;
case 13: OutLine(x-D,y,x,y); OutLineTo(x+D,y+D); OutLineTo(x+D,y-D); OutLineTo(x,y); break;
case 14: OutLine(x+D,y,x,y); OutLineTo(x-D,y-D); OutLineTo(x-D,y+D); OutLineTo(x,y); break;
case 16: OutCircle(x,y,D/2); /*nobreak;*/
case 15: OutLine(x-D,y,x+D,y); OutLine(x,y+D,x,y-D); OutCircle(x,y,D); break;
case 18: OutRectangle(x,y,D,D,0); /*nobreak;*/
case 17: OutLine(x-D,y,x+D,y); OutLine(x,y+D,x,y-D); OutRectangle(x,y,D*2,D*2,0); break;
}
}
// Die Wärmefallen im Supply-Layer werden wesentlich von den Entwurfsregeln bestimmt.
// Da ist aus ULP-Sicht IMHO kein Zugriff möglich.
// Daher sind hier die Wärmefallen nur Demo und nicht produktionstauglich.
void OutThermal(int x, int y, int r) {
OutOctagon(x-r,y,r>>2,90,1);
OutOctagon(x+r,y,r>>2,90,1);
OutOctagon(x,y-r,r>>2,0,1);
OutOctagon(x,y+r,r>>2,0,1);
}
void DrawHole(int x, int y, int d) {
if (DrawHoles) {
SetPen(5/*PS_NULL*/,0,0);
SetBrush(0/*BS_SOLID*/,0,BackgroundColor);
OutCircle(x,y,d/2);
}
}
int SupplyLayer() {
return 2<=CurLayer && CurLayer<=15 && nam[CurLayer][0]=='$';
}
// ermittelt Bezug zum zu verwendenden Layer für PADs
// Liefert 0 für "don't process"
int GetPadLayer(int flags) {
if (SupplyLayer()) return CurLayer;
switch (CurLayer) {
case LAYER_TSTOP:
case LAYER_BSTOP: if (flags&PAD_FLAG_STOP) return CurLayer; break;
case LAYER_PADS: return LAYER_TOP;
}
return 0;
}
void HandlePad(UL_PAD O) {
if (!O) return;
if (CurLayer==LAYER_DRILLS) {
SetPen(0/*PS_SOLID*/,3*254,LayerColor);
SetBrush(1/*BS_NULL*/,0,0);
OutDrillSymbol(O.x,O.y,O.drillsymbol);
return;
}
int d=0, PadLayer=GetPadLayer(O.flags);
if (!PadLayer) return;
d=O.diameter[PadLayer];
if (!d) return; // raus wenn null geblieben
if (!DrawHoles) {
SetLayerPen();
SetLayerBrush();
switch (O.shape[PadLayer]) {
// im LAYER_PADS ist O.shape immer PAD_SHAPE_ROUND, dann von LAYER_TOP nehmen
case PAD_SHAPE_SQUARE: OutRectangle(O.x,O.y,d,d,O.angle); break;
// hier künftig ARC ausgeben! (aber nur, wenn Layer SOLID)
case PAD_SHAPE_ROUND: OutCircle(O.x,O.y,d/2); break;
case PAD_SHAPE_OCTAGON: OutOctagon(O.x,O.y,d,O.angle,0); break;
case PAD_SHAPE_LONG: OutOctagon(O.x,O.y,d,O.angle,1); break;
case PAD_SHAPE_OFFSET: OutOctagon(O.x,O.y,d,O.angle,2); break;
case PAD_SHAPE_ANNULUS: OutCircle(O.x,O.y,d/2); break;
case PAD_SHAPE_THERMAL: OutThermal(O.x,O.y,d/2); break;
default: dlgMessageBox("??");
}
}
if (PadLayer==LAYER_TOP) DrawHole(O.x,O.y,O.drill);
}
// ermittelt Bezug zum zu verwendenden Layer für PADs
// Liefert 0 für "don't process"
int GetViaLayer(int flags) {
if (SupplyLayer()) return CurLayer;
switch (CurLayer) {
case LAYER_TSTOP:
case LAYER_BSTOP: if (flags&VIA_FLAG_STOP) return CurLayer; break;
case LAYER_VIAS: return LAYER_TOP;
}
return 0;
}
void HandleVia(UL_VIA O) {
if (!O) return;
if (CurLayer==LAYER_DRILLS) {
SetPen(0/*PS_SOLID*/,3*254,LayerColor);
SetBrush(1/*BS_NULL*/,0,0);
OutDrillSymbol(O.x,O.y,O.drillsymbol);
return;
}
int d=0, ViaLayer=GetViaLayer(O.flags);
if (!ViaLayer) return;
d=O.diameter[ViaLayer];
if (!d) return; // raus wenn null geblieben
if (!DrawHoles) {
SetLayerPen();
SetLayerBrush();
switch (O.shape[ViaLayer]) {
case VIA_SHAPE_SQUARE: OutRectangle(O.x,O.y,d,d,0); break;
// hier künftig ARC ausgeben! (aber nur, wenn Layer SOLID)
case VIA_SHAPE_ROUND: OutCircle(O.x,O.y,d/2); break;
case VIA_SHAPE_OCTAGON: OutOctagon(O.x,O.y,d,0,0); break;
case VIA_SHAPE_ANNULUS: OutCircle(O.x,O.y,d/2); break;
case VIA_SHAPE_THERMAL: OutThermal(O.x,O.y,d/2); break;
default: dlgMessageBox("???");
}
}
if (ViaLayer==LAYER_TOP) DrawHole(O.x,O.y,O.drill);
}
int GetSmdLayer(int SmdLayer, int flags) {
switch (SmdLayer) {
case LAYER_BOTTOM: switch (CurLayer) { // SMD-Pad unten
case LAYER_BSTOP: if (flags&SMD_FLAG_STOP) return LAYER_BSTOP; break;
case LAYER_BCREAM: if (flags&SMD_FLAG_CREAM) return LAYER_BCREAM; break;
case LAYER_BOTTOM: return LAYER_BOTTOM;
}break;
case LAYER_TOP: switch (CurLayer) { // SMD-Pad oben
case LAYER_TSTOP: if (flags&SMD_FLAG_STOP) return LAYER_TSTOP; break;
case LAYER_TCREAM: if (flags&SMD_FLAG_CREAM) return LAYER_TCREAM; break;
case LAYER_TOP: return LAYER_TOP;
}break;
}
return 0;
}
void HandleSmd(UL_SMD O) {
if (!O) return;
int SmdLayer=GetSmdLayer(O.layer,O.flags);
if (!SmdLayer) return;
int dx=O.dx[SmdLayer], dy=O.dy[SmdLayer];
if (!dx) return; // raus wenn null geblieben
SetLayerPen();
SetLayerBrush();
if (O.roundness) { // Angabe in Prozent der kürzeren Seite
if (O.roundness==100 && dx==dy) OutCircle(O.x,O.y,dx/2);
else{
int r=dx<dy?dx:dy; // kürzere der beiden Seiten
r=r*O.roundness/200; // Radius der Abrundung
OutRoundRect(O.x,O.y,dx,dy,r,O.angle);
}
}else OutRectangle(O.x,O.y,dx,dy,O.angle);
}
void HandleHole(UL_HOLE O) {
switch (CurLayer) {
case LAYER_DRILLS:
case LAYER_HOLES: {
SetPen(0/*PS_SOLID*/,3*254,LayerColor);
SetBrush(1/*BS_NULL*/,0,0);
OutDrillSymbol(O.x,O.y,O.drillsymbol);
}break;
case LAYER_DIMENSION: {
SetLayerPen();
SetBrush(1/*BS_NULL*/,0,0);
OutCircle(O.x,O.y,O.drill/2);
}break;
default: if (SupplyLayer()) {
int d=O.diameter[LAYER_TSTOP]; // Kein Zugriff auf Spacing von den Entwurfsregeln
if (!d) d=O.diameter[LAYER_BSTOP];
if (!d) d=O.drill;
SetLayerPen();
SetLayerBrush();
OutCircle(O.x,O.y,d/2);
}
}
}
/* Auswahlprüfung für 4 Objekt-Anker-Typen */
int PtInSel(int x, int y) { // alles mit Mittelpunkten
if (!GroupOnly) return 1;
if (GroupRect[0]<=x && x<=GroupRect[2]
&& GroupRect[1]<=y && y<=GroupRect[3]) return 1;
return 0;
}
int WiInSel(UL_WIRE O) { // zwei Enden
return PtInSel(O.x1,O.y1)||PtInSel(O.x2,O.y2);
}
int RcInSel(UL_RECTANGLE O) { // vier Ecken
if (!GroupOnly) return 1;
if (O.x1==GroupRect[0]
&& O.y1==GroupRect[1]
&& O.x2==GroupRect[2]
&& O.y2==GroupRect[3]
&& !O.angle) return 0; // Auswahlrechteck selbst nie ausgeben
int mx=(O.x1+O.x2)/2; // Rechteck drehen (lästig!)
int my=(O.y1+O.y2)/2;
int x=(O.x2-O.x1)/2;
int y=(O.y2-O.y1)/2;
int xx[],yy[];
real angle=O.angle*PI/180;
real c=cos(angle); // Drehmatrix-Elemente | +cos +sin |
real s=sin(angle); // | -sin +cos |
xx[0]=mx+c*x-s*y; yy[0]=my+s*x+c*y;
xx[1]=mx-c*x-s*y; yy[1]=my-s*x+c*y;
xx[2]=mx-c*x+s*y; yy[2]=my-s*x-c*y;
xx[3]=mx+c*x+s*y; yy[3]=my+s*x-c*y;
return PtInSel(xx[0],yy[0])||PtInSel(xx[1],yy[1])
||PtInSel(xx[2],yy[2])||PtInSel(xx[3],yy[3]);
}
int PoInSel(UL_POLYGON O) { // alle Ecken
O.contours(W) if (PtInSel(W.x1,W.y1)) return 1;
return 0;
}
/* Abarbeitung eines Layers (schwarzweiß: alle) */
void SchWalkLayer(UL_SHEET O) {
string e;
sprintf(e,"Seite %d Layer %d (%s)",O.number,CurLayer,nam[CurLayer]);
status(e);
O.busses(O) {
O.segments(O) {
O.texts(Q) if (PtInSel(Q.x,Q.y)) HandleText(Q);
O.wires(Q) if (WiInSel(Q)) HandleWire(Q);
}
}
O.circles(Q) if (PtInSel(Q.x,Q.y)) HandleCircle(Q);
O.nets(O) {
O.segments(O) {
O.junctions(Q) if (PtInSel(Q.x,Q.y)) HandleJunction(Q);
O.texts(Q) if (PtInSel(Q.x,Q.y)) HandleText(Q);
O.wires(Q) if (WiInSel(Q)) HandleWire(Q);
}
}
O.parts(O) {
O.instances(O) if (PtInSel(O.x,O.y)) {
O.gate.symbol.circles(O) HandleCircle(O);
O.gate.symbol.pins(O) {
HandlePin(O);
O.circles(O) HandleCircle(O);
O.texts(O) HandleText(O);
O.wires(O) HandleWire(O);
}
O.gate.symbol.polygons(O) HandlePolygon(O);
O.gate.symbol.rectangles(O) HandleRectangle(O);
O.gate.symbol.texts(O) HandleText(O); // Unsmashed
O.gate.symbol.wires(O) HandleWire(O);
O.texts(O) HandleText(O); // Smashed
}
}
O.polygons(Q) if (PoInSel(Q)) HandlePolygon(Q);
O.rectangles(Q) if (RcInSel(Q)) HandleRectangle(Q);
O.texts(Q) if (PtInSel(Q.x,Q.y)) HandleText(Q);
O.wires(Q) if (WiInSel(Q)) HandleWire(Q);
}
void BrdWalkLayer(UL_BOARD O) {
string e;
sprintf(e,"Layer %d (%s)",CurLayer,nam[CurLayer]);
status(e);
O.circles(Q) if (PtInSel(Q.x,Q.y)) HandleCircle(Q);
O.elements(Q) if (PtInSel(Q.x,Q.y)) {
Q.package.circles(O) HandleCircle(O);
Q.package.contacts(O) {
HandlePad(O.pad);
HandleSmd(O.smd);
}
Q.package.holes(O) HandleHole(O);
Q.package.polygons(O) HandlePolygon(O);
Q.package.rectangles(O) HandleRectangle(O);
Q.package.texts(O) HandleText(O); // unsmashed
Q.package.wires(O) HandleWire(O);
Q.texts(O) HandleText(O); // smashed
}
O.holes(Q) if (PtInSel(Q.x,Q.y)) HandleHole(Q);
O.polygons(Q) if (PoInSel(Q)) HandlePolygon(Q);
O.rectangles(Q) if (RcInSel(Q)) HandleRectangle(Q);
O.signals(O) {
O.polygons(Q) if (IsRatsnestPoly(Q) && PoInSel(Q)) HandlePolygon(Q);
// Eventuell hier wie in Eagle5 ein gestricheltes, ungefülltes Polygon zeichnen?
O.vias(Q) if (PtInSel(Q.x,Q.y)) HandleVia(Q);
O.wires(Q) if (WiInSel(Q)) HandleWire(Q);
}
O.texts(Q) if (PtInSel(Q.x,Q.y)) HandleText(Q);
O.wires(Q) if (WiInSel(Q)) HandleWire(Q);
}
/* Abarbeitung layer-weise von hinten nach vorn, wenn bunt (sonst unexakte Darstellung) */
void SchWalkLayers(UL_SHEET O) {
if (PaletteType) for (CurLayer=255; CurLayer>=0; CurLayer--) {
if (SetLayer(CurLayer)) SchWalkLayer(O);
}else SchWalkLayer(O); // Schwarzweiß NICHT im Sandwich-Betrieb abarbeiten (schneller)
}
void BrdWalkLayer2(UL_BOARD O, int DoLast) {
if (!SetLayer(CurLayer)) return;
if (!DoLast && strchr(LastLayers,CurLayer)>=0) return; // überspringen
BrdWalkLayer(O);
// Zweiter Durchlauf für die Löcher (In einem einzelnen Durchlauf würde,
// wegen der vielen Kontextumschaltungen, die .WMF-Datei viel größer werden.)
if (CurLayer==LAYER_VIAS || CurLayer==LAYER_PADS) {
DrawHoles=1;
OutRop(13/*R2_COPYPEN*/); // Löcher zum Hintergrund "durchstanzen"
BrdWalkLayer(O);
DrawHoles=0;
}
}
/* Abarbeitung layer-weise, sogar wenn schwarz-weiß */
void BrdWalkLayers(UL_BOARD O) {
// Eagle gibt die Kupferlagen von unten nach oben aus
if (Trans) {
// Löcher ausstanzen lassen, bevor Kupferlinien hineingezogen werden (wie Eagle)
for (CurLayer=17; CurLayer<21; CurLayer++) BrdWalkLayer2(O,0);
for (CurLayer=16; CurLayer>=1; CurLayer--) BrdWalkLayer2(O,0);
}else{
for (CurLayer=16; CurLayer>=1; CurLayer--) BrdWalkLayer2(O,0);
for (CurLayer=17; CurLayer<21; CurLayer++) BrdWalkLayer2(O,0);
}
for (CurLayer=29; CurLayer<51; CurLayer++) BrdWalkLayer2(O,0);
for (CurLayer=53; CurLayer<256; CurLayer++) BrdWalkLayer2(O,0);
//Typische Text-Layer zuletzt
for (CurLayer=51; CurLayer<53; CurLayer++) BrdWalkLayer2(O,0);
for (CurLayer=21; CurLayer<29; CurLayer++) BrdWalkLayer2(O,0);
Trans=0; // Transparenz für LastLayers ausschalten
for (int i=0; CurLayer=LastLayers[i]; i++) BrdWalkLayer2(O,1);
}
int Area[]; // Bereich (X1,Y1,X2,Y2; sortiert), in WMF-Einheiten
void FatalExit(string s) {
dlgMessageBox(s);
exit(EXIT_FAILURE);
}
void FatalExit1(string format, string info) {
string s;
sprintf(s,format,info);
FatalExit(s);
}
void OutStart(UL_AREA O) {
if (!GroupOnly) {
GroupRect[0]=O.x1;
GroupRect[1]=O.y1;
GroupRect[2]=O.x2;
GroupRect[3]=O.y2;
}
for (int i=0; i<4; i++) Area[i]=Mass(GroupRect[i]);
if (Flipped&4) {int t=Area[0]; Area[0]=Area[1]; Area[1]=t; t=Area[2]; Area[2]=Area[3]; Area[3]=t;} //X<->Y tauschen (zuerst!)
if (!(Flipped&2)) {int t=Area[1]; Area[1]=-Area[3]; Area[3]=-t;} // Y tauschen und Vorzeichenwechsel (WMF zählt von unten)
if (Flipped&1) {int t=Area[0]; Area[0]=-Area[2]; Area[2]=-t;} // X tauschen und Vorzeichenwechsel
if (max(Area[2]-Area[0],Area[3]-Area[1])>32767) // Notbremse! Dateileiche bleibt liegen.
FatalExit("Teiler zu klein; resultierende Abmessungen übersteigen 16 bit!");
nWords=0; // Kopf beginnen
nMaxRecSize=0;
ClearObjects();
// Normalen Windows-Metafile-Kopf ausgeben
OutWord(2); // FileType = Disk
OutWord(9); // HeaderSize
OutWord(0x0100); // Version
nWords=8; // noch unbekannte WORDs überspringen
OutWord(0); // NumOfParams
// Ohne die 3 folgenden Records haben Total Commander sowie Internet Explorer ein Problem!
BeginRecord(0x0103/*META_SETMAPMODE*/);
OutWord(8/*MM_ANISOTROPIC*/);
EndRecord();
BeginRecord(0x020B/*META_SETWINDOWORG*/);
OutWord(Area[1]);
OutWord(Area[0]);
EndRecord();
BeginRecord(0x020C/*META_SETWINDOWEXT*/);
OutWord(Area[3]-Area[1]+1);
OutWord(Area[2]-Area[0]+1);
EndRecord();
BeginRecord(0x0102/*META_SETBKMODE*/);
OutWord(1/*TRANSPARENT*/); // für Schrift und normale Schraffur
EndRecord();
if (HatchType==2) {
BeginRecord(0x0201/*META_SETBKCOLOR*/);
int col=BackgroundColor;
if (Trans && PaletteType==3) col^=0xFFFFFF;
OutDword(col); // für Pattern-Schraffur (ist immer deckend - leider!)
EndRecord();
}
if (OutBkgnd) {
SetPen(5/*PS_NULL*/,0,0); // Hintergrundrechteck mit leichtem Übermaß zeichnen
SetBrush(0/*BS_SOLID*/,0,BackgroundColor);
BeginRecord(0x041B/*META_RECTANGLE*/);
int o=254/u2mic(1); // oversize: 10 mil = 0,25 mm
OutRect(GroupRect[0]-o,GroupRect[1]-o,GroupRect[2]+o,GroupRect[3]+o);
EndRecord();
}
}
void OutWrite() {
int PHeader[]; // Vorhaltung zur Berechnung der Prüfsumme
int i;
BeginRecord(0/*META_EOF*/);
EndRecord();
status("Datei-Ausgabe ("+FName+")");
// Aldus-Placeable-Metafile-Kopf ausgeben
PHeader[0]=0xCDD7; // DWORD Key
PHeader[1]=0x9AC6;
/*0*/ // WORD Handle
PHeader[3]=Area[0]; // SHORT Left
PHeader[4]=Area[1]; // SHORT Top
PHeader[5]=Area[2]+1; // SHORT Right
PHeader[6]=Area[3]+1; // SHORT Bottom
// Manche Programme interpretieren diese Grenzen als inklusive,
// manche andere als exklusive unteren rechten Rand.
// Das +1 vermeidet Abschneideprobleme.
PHeader[7]=round(25400/Teiler/Scale); // ergibt 1:1-Import in Word
// Beim Standard-Teiler von 25,4 µm/Pixel ergibt das 1000 dpi
// Eagle <6 hat 254000 dpi, also 256 dot per mil und 10 dot per mm
// Eagle 6 hat 8128000 dpi, das 32-fache von Eagle <6
// Die Angaben im SDK scheinen allesamt irreführend zu sein!
// Richtig (und auch logisch) scheint: <inch> WMF-Einheiten ergeben 1 Zoll.
// Es ist also eine dpi-Angabe, betrachtet man eine WMF-Einheit als Pixel.
/*0,0*/ // DWORD Reserved
/*0*/ // WORD Checksum
for (i=0; i<10; i++) PHeader[10]^=PHeader[i]; // Prüfsumme ermitteln
for (i=0; i<11; i++) PrintWord(PHeader[i]);
// Windows-Metafile-Kopfdaten ergänzen
FileData[3]=nWords&0xFFFF; // FileSize
FileData[4]=nWords>>16;
FileData[5]=nHandles; // NumOfObjects
FileData[6]=nMaxRecSize&0xFFFF;// MaxRecordSize
FileData[7]=nMaxRecSize>>16;
for (i=0; i<nWords; i++) PrintWord(FileData[i]); // Daten ausspucken
}
/* Ausgabe einer Metadatei */
void SchPrintMF(UL_SHEET O) {
OutStart(O.area);
SchWalkLayers(O);
OutWrite();
}
void BrdPrintMF(UL_BOARD O) {
OutStart(O.area);
BrdWalkLayers(O);
OutWrite();
}
// Einen Schriftart-Parameter parsen
// Dieser muss den regexp-Aufbau haben: (Vector|Arial|Courier|Times)(,b?i?)?
// oder [VACT][,bi]*
void ParseFont(int idx, string val) {
int p=strchr(val,',');
if (strlen(val)<=3) p=1; // in der Kurzform ohne Komma zulässig
string suffix;
if (p>=0) {
suffix=strsub(val,p);
val=strsub(val,0,p);
}
if (val=="") ; // ohne Schriftname Voreinstellung lassen
else if (val=="Vector" || val=="V") FontSel[idx]=0;
else if (val=="Arial" || val=="A") FontSel[idx]=1;
else if (val=="Courier" || val=="C") FontSel[idx]=2;
else if (val=="Times" || val=="T") FontSel[idx]=3;
else FatalExit1("Unbekannte Schriftartbezeichnung %s!",val);
FontBold[idx]=0;
FontItalic[idx]=0;
for (p=0; p<strlen(suffix); p++) switch (suffix[p]) {
case 'b': FontBold[idx]=1; break;
case 'i': FontItalic[idx]=1; break;
case ',': break;
default:
FatalExit1("Unbekannter Schriftstil %s! (Nur »b« [fett] oder »i« [kursiv])",""+suffix[p]);
}
}
int gridunit; // GRID_UNIT_xxx
void ParseGroupRect(string val) {
string s[];
if (strsplit(s,val,',')!=4) FatalExit1(
"Ungültige Rechteckdefinition '%s'! (Vier kommaseparierte Zahlen)",val);
int fak=1;
switch (gridunit) {
case GRID_UNIT_MIC: fak=u2mic(1); break; // 10 (Werte für Eagle4)
case GRID_UNIT_MM: fak=u2mm(1); break; // 10000
case GRID_UNIT_MIL: fak=u2mil(1); break; // 254
case GRID_UNIT_INCH: fak=u2inch(1);break; // 254000, typisch Schaltplan
}
for (int i=0; i<4; i++) GroupRect[i]=round(strtod(s[i])*fak);
if (GroupRect[0]>=GroupRect[2] || GroupRect[1]>=GroupRect[3]) FatalExit1(
"Falsche Reihenfolge der Zahlen bei Rechteckdefinition '%s'!",val);
GroupOnly=1;
}
int CheckRipupPoly=0; // Prüfung auf nicht geRATSNESTe Signal-Polygone
string AutomaticFileName(void) {
string e;
if (board) {
int i=strrchr(SName,'.');
if (i<0) i=strlen(SName);
if (isalpha(SName[i-1])) e="-"; // Bindestrich um es vom Dateinamen abzugrenzen
e+=odd(Flipped&7)?"b":"t"; // "b" für "bottom" und "t" für "top", später "d" für "Drill"
}
e+=".wmf";
return filesetext(SName,e);
}
// Die Kommandozeile ist an AWK-Syntax angelegt:
// Entweder <dateiname> oder <variable>=<wert>.
// Nur ein <dateiname> ist erlaubt, der kein '=' enthalten darf.
// Alle im Dialog einstellbaren Variablennamen sind erlaubt.
// Der Dateiname "*" spezifiziert den Standard-Dateinamen.
void ParseCommandLine(void) {
int i,j,p;
string arg,key,val;
for (i=1; i<argc; i++) {
arg=argv[i];
p=strchr(arg,'=');
if (p>=0) { // normales Argument
key=strsub(arg,0,p);
val=strsub(arg,p+1);
if (key=="Debug") Debug=strtol(val);
else if (key=="WMF-Unit") Teiler=strtod(val);
else if (key=="MinWidth") MinBreite=strtod(val);
else if (key=="PaletteType" || key=="p") {
PaletteType=strtol(val);
OnSetPaletteType();
}else if (key=="HatchType" || key=="h") HatchType=strtol(val);
else if ((key=="AllPages" || key=="a") && schematic) AllPages=strtol(val);
else if ((key=="Flipped" || key=="f") && board) Flipped=strtol(val);
else if (key=="Trans" || key=="t") Trans=strtol(val);
else if (key=="OutBkgnd" || key=="b") OutBkgnd=strtol(val);
else if (key=="DoNegate" || key=="n") DoNegate=strtol(val);
else if (key=="LineReplace" || key=="l") LineReplace=strtol(val);
else if (key=="FlatArcReplace" || key=="c") FlatArcReplace=strtod(val);
else if (key=="PiecedReplace") PiecedReplace=strtol(val);
else if (key=="DupStrReplace") DupStrReplace=strtol(val);
else if (key=="Dialog" || key=="d") Dialog=strtol(val);
else if ((key=="LastLayers" || key=="y") && board) LastLayers=val;
else if (key=="Vector" || key=="V") ParseFont(0,val);
else if (key=="Proportional" || key=="P") ParseFont(1,val);
else if (key=="Fixed" || key=="F") ParseFont(2,val);
else if ((key=="CheckRipupPoly" || key=="r") && board) CheckRipupPoly=strtol(val);
else if (key=="Group" || key=="g") ParseGroupRect(val);
else if (key=="Scale" || key=="s") Scale=strtod(val);
else FatalExit1("Unbekannte Variable %s!",key);
}else{ // Dateiname
if (FName) FatalExit1("Mehrere Dateinamen (%s) sind nicht möglich!",arg);
if (arg=="*") FName=AutomaticFileName();
else{
FName=arg;
if (FName[0]!='\\' && FName[0]!='/' && FName[1]!=':')
// Relative Pfade sollen sich aufs Schaltplan- bzw. Board-Verzeichnis beziehen.
// Ohne diese Aktion wäre es relativ zum Eagle/bin-Verzeichnis.
FName=filedir(SName)+FName;
}
}
}
if (!Dialog && !CalcLastLayers()) exit(EXIT_FAILURE);
}
/* Hauptprogramm */
if (EAGLE_VERSION==5 && EAGLE_RELEASE==0) {
FatalExit("Dieses ULP läuft wegen eines Fehlers in Eagle 5.00 nicht!\n"+
"Bitte mindestens 5.01 oder altes Eagle verwenden.");
}
if (!schematic && !board) {
FatalExit("Muss aus Schaltplan oder Board gestartet werden!");
}
// Informationen über Schaltplan und Layer einsammeln
if (schematic) {
schematic(O) {
gridunit=O.grid.unit;
O.layers(O) if (O.visible) {
col[O.number]=O.color; // Layer vermerken
fil[O.number]=O.fill; // Füll-Stile (1 = komplett gefüllt)
nam[O.number]=O.name; // Name (für Status-Anzeige)
if (O.fill!=1) NumHatched++;
}
SName=O.name;
SchCountFonts(O);
}
HatchType=0;
Scale=1.3888888;
// ausprobiert für gute 1:1-Bildschirmdarstellung, bspw. im IE6
// Beim Standard-Teiler von 25,4 µm/Pixel ergibt das 720 dpi
}else board(O) {
gridunit=O.grid.unit;
O.layers(O) if (O.visible) {
col[O.number]=O.color; // Layer vermerken
fil[O.number]=O.fill; // Füll-Stile (1 = komplett gefüllt)
nam[O.number]=O.name; // Name (für Status-Anzeige und Supply-Layer)
if (O.fill!=1) NumHatched++;
if (O.fill==2 || O.fill==4 || O.fill==5 || O.fill>=9) NumExact++;
}
SName=O.name;
BrdCountFonts(O);
CheckRipupPoly=1;
// diese Layer sind wesentliches Kriterium zur Voreinstellung
Flipped=col[LAYER_BPLACE] && !col[LAYER_TPLACE];
}
if (GroupOnly>1) GroupOnly=0; // mehrere Rechtecke gefunden: Keine Selektion möglich
if (NumExact) HatchType=2; // Vorgabe wenn solche Schraffuren sichtbar (nur Board)
OnSetPaletteType();
// Kommandozeile auswerten
ParseCommandLine();
if (CheckRipupPoly) board(O) if (BrdAreRipupPolys(O)) switch (dlgMessageBox(
"!Es sind Signal-Polygone vorhanden, die nur als Außenkontur "+
"(nicht freigerechnet) erscheinen.\n"+
"Für korrekte Polygondarstellung vorher RATSNEST ausführen!",
"&RATSNEST starten","Polygone &ignorieren", "&Abbrechen")) {
case 0: exit("RATSNEST;run "+strjoin(argv,' ')); break;
case 2: exit(0); break;
}
// Nutzer-Dialog anzeigen
if (board) {
Rotated=Flipped>>2&1;
TopDown=Flipped>>1&1;
Flipped&=1;
}
if (Dialog && DialogBox()!=1) exit(EXIT_FAILURE);
if (board) {
if (TopDown) Flipped|=2;
if (Rotated) Flipped^=5; // Rotation nach links (nach Spiegelung)
}else switch (Flipped) {
case 1: Flipped=5; break; // 90° links
case 2: Flipped=3; break; // 180° = 2x Flip
case 3: Flipped=6; break; // 90° rechts
}
// Hintergrundfarbe (für Löcher!) einstellen
if (!PaletteType || PaletteType==3 && Trans) BackgroundColor=0xFFFFFF;
else BackgroundColor=rgb2bgr(palette(0,PaletteType-1));
// Dateiname erfragen; Vorgabe für Boards ist Suffix "t" (TOP) oder "b" (BOTTOM)
if (!FName) {
FName=AutomaticFileName();
FName=dlgFileSave("Speichern Windows-Metadatei",FName,"*.wmf");
}
if (!FName) exit(EXIT_FAILURE);
if (AllPages) {
schematic(O) O.sheets(O) {
string e;
sprintf(e,"-%d%s",O.number,fileext(FName));
output(filesetext(FName,e),"wb") SchPrintMF(O);
}
}else{
if (board) board(O) {
output(FName,"wb") BrdPrintMF(O);
}else sheet(O) {
output(FName,"wb") SchPrintMF(O);
}
}
Detected encoding: ANSI (CP1252) | 4
|
|