#usage "en: Output of a Schematic (or Board) as " "Enhanced Windows Meta File " "for convenient embedding into office documents and searchable strings in resulting PDFs

\n" "Only visible layers will be processed.

" "The resulting EMF file requires a 32-bit Windows for processing. " "Use SCH2WMF if you need Win3x/9x/Me compatibility.

" "Command-line arguments are: [arguments] [filename]
\n" "Possible arguments see SCH2EMF.TXT.\n" "


" "Author: Henrik Haftmann,
" "latest version: TU Chemnitz
", "de: Ausgabe eines Schaltplanes (oder Boards) als " "Erweiterte Windows-Meta-Datei (File) " "für bequemen Dokument-Import sowie String-Suchmöglichkeit in PDFs

\n" "Es werden nur die gerade sichtbaren Layer ausgegeben.

" "Die entstehende EMF-Datei ist nur unter 32-bit-Windows verarbeitbar. " "Für Kompabilität zu Win3x/9x/Me nehmen Sie bitte SCH2WMF.

" "Kommandozeilenargumente: [Argumente] [Dateiname]
\n" "Argumente siehe SCH2EMF.TXT.\n" "


" "Autor: Henrik Haftmann,
" "neueste Version: TU Chemnitz
" #require 5.01 // Für EAGLE 4.x nehmen Sie bitte SCH2WMF. // For EAGLE 4.x, use SCH2WMF instead. // Hinweis: EMF-Dateien bieten gegenüber WMF folgende Vorteile: // Unter Win9x/Me: // * Linien mit Pinselfüllung [bei SCH2WMF per Polygonausgabe simulierbar] // * Strichellinien auch bei Darstellstrichbreiten > 1 Pixel [... Einzelausgabe] // * Bögen mit flachen Enden [... Polygon- oder Tortenstück-Ausgabe] // * Unicode-Zeichenersetzung [... Linienausgabe Vektorfont] // Zusätzlich unter WinNT/2k/XP/Vista: // * Gespiegelten Text [... Linienausgabe Vektorfont] // * 32-bit-Koordinatensystem [... Teiler erforderlich] // * Pfade mit Bögen als Begrenzung [... Polygonausgabe] - hier nicht benutzt! // Angeblich sind EMF-Dateien in StarOffice/OpenOffice auch unter Linux // verarbeitbar; vermutlich werden diese Programme an diesen EMFs elendig // verrecken (SCH2EMF zieht alle Register, um kleine EMFs zu produzieren!). // Nachteile: // * Wegen 32-bit-Ausrichtung etwa doppelt so groß wie gleiche .WMF-Dateien // * Unklare Skalierungsverhältnisse, bspw. bei Total Commander F3-Viewer, // ACDSee oder IrfanView // * Überraschungen bei der PostScript/PDF-Ausgabe breiter Linien und Bögen // EMF-Dateien unterstützen kein Alpha-Blending! Das kann erst EMF+. // Ist aber undokumentiert, und mir ist kein geeignetes Importprogramm bekannt. // Deshalb erfolgt hier die Pseudotransparenz, wie gehabt, mittels ROP-Kodes. // 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) string SName; // Schaltplan- oder Board-Name (ohne 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 Filled=1; // Gestrichelt definierte Layer gefüllt (!Polygone) int Flipped=0; // gespiegelt (an X-Achse, 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 Dialog=1; // =2: Erweiterter Dialog (nur für Füchse) string LastLayers; // Zuletzt auszugebende Layer (stets opak, nur Board) string FName; // Dateiname (ggf. Kommandozeilenargument) 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"}; string FontLabel[]; // Normalerweise ist für EMF-Dateien kein Teiler notwendig. // Allerdings kommt Windows 9x/Me nicht mit 32-bit-Koordinaten zurecht. // 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!
%s\n%*s^---hier!",
    LastLayers,i,"");
  dlgMessageBox(s);
  return 0;
 }
 LastLayers=A;
 return 1;
}

/***********************
 *** Dialog-Routinen ***
 ***********************/
// globale Variablen: Farben und Füllstile per Layer
int col[],fil[];

// 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],
   "%s:",
   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 layout-treue Ausgabedateien.

"+ "Allerdings ist die WMF-Datei wesentlich größer "+ "und nicht durchsuchbar nach Text.

"+ "Die Einstellungen Fett und Kursiv 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; } } int DialogBox(void) { string DeckLabel=Trans?"(transparent)":"(deckend)"; status("Dialog"); return dlgDialog("EMF-Export-Optionen") { dlgLabel("Dateiname: "+SName+""); 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("Hinweis: Vektorschrift auf Kupfer wird niemals ersetzt."); } } if (NumHatched) { string s; sprintf(s,"Füll&muster (%d schraffierte%s Layer vorhanden)", NumHatched,NumHatched==1?"r":""); dlgGroup(s) { dlgRadioButton("Ausgefüllt",HatchType); 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); } } } if (board) { if (AnyBottomLayers()) { dlgCheckBox("&Gespiegelt (an Y-Achse, zur Seite)",Flipped); } 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.

" "Hier werden Komplementärfarben ausgegeben.

"+ "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("&Negationsstriche statt »!« (wie Eagle 5)",DoNegate); dlgCheckBox("&Hintergrund ausmalen (Vektorgrafik nicht transparent)",OutBkgnd); if (NumSheet>1) { dlgSpacing(4); dlgCheckBox("&Alle Seiten (mehrere Dateien)",AllPages); } dlgSpacing(4); dlgHBoxLayout { dlgPushButton(FName?"+O&K":"+&Weiter, zum Speichern ...") { if (Dialog<2 || CalcLastLayers()) dlgAccept(1); } dlgPushButton("Abbrechen") dlgReject(); } }; } void CountFont(UL_TEXT O) { if (!col[O.layer]) return; // unsichtbare Schrift nicht mitzählen NumFont[O.font]++; if (!DoNegate && strchr(O.value,'!')!=-1) DoNegate++; } /* Suche nach verwendeten Text-Fonts, zur Vorbereitung des Dialogs */ void SchCountFonts(UL_SCHEMATIC SC) { SC.sheets(S){ string e; sprintf(e,"Seite %d",S.number); status(e); NumSheet++; S.busses(B) { B.segments(SE) { SE.texts(T) CountFont(T); } } S.frames(FR) { // ab Eagle 5 FR.texts(T) CountFont(T); } S.nets(N) { N.segments(SE) { SE.texts(T) CountFont(T); } } S.parts(P) { P.instances(I) { I.gate.symbol.pins(P) { P.texts(T) CountFont(T); } I.gate.symbol.texts(T) CountFont(T); I.texts(T) CountFont(T); } } S.texts(T) CountFont(T); } } // das gleiche für's Board void BrdCountFonts(UL_BOARD B) { B.elements(E) { E.texts(T) CountFont(T); E.package.texts(T) CountFont(T); } B.texts(T) { if (T.layer>LAYER_BOTTOM || T.font!=FONT_VECTOR) CountFont(T); else if (col[T.layer]) NumCopperVector++; } } // Vektor-Schrift im Kupfer wird NICHT gezählt (bleibt stets Vektor) int IsRatsnestPoly(UL_POLYGON P) { P.fillings(F) return 1; return 0; } // Prüft auf Vorhandensein sichtbarer, nicht geRATSNESTer Polygone (nur Kupfer) int BrdAreRipupPolys(UL_BOARD B) { B.signals(S) S.polygons(P) if (P.layer<=LAYER_BOTTOM && col[P.layer] && !IsRatsnestPoly(P)) return 1; return 0; } // **** globale Variablen **** /* für den EMF-Kopf */ int nDwords; // Länge EMF-Datei int nRecords; // (Künftige) Anzahl Metadatei-Records int nHandles; // (Künftige) Anzahl GDI-Handles /* EMF-Daten (Ansammlung im RAM, DWORD-Array ("unsigned" gibt's nicht) */ 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 4 Byte in Intel-Notation (Little Endian) */ void PrintDword(int d) { printf("%c%c%c%c",d&0xFF,(d>>8)&0xFF,(d>>16)&0xFF,(d>>24)&0xFF); } /* Umwandeln in binäre Repräsentation des FLOAT(=float,Single)-Datentyps */ int CastFloat(real f) { int ret=0; if (f<0) { ret=0x80000000; f=-f; } if (f) { int expo=log(f)/log(2)+127; ret|=int(f*pow(2,150-expo))&0x7FFFFF|expo<<23; } return ret; } /* Ausgabe 4 Byte in Datenpuffer */ void OutDword(int d) { FileData[nDwords++]=d; } void OutFloat(real f) { OutDword(CastFloat(f)); } int VectorSubst[]={ '?', '▓', '?', '?', '?', '?', '?', '?', /*80*/ '?', '?', '?', '?', '?', '┌', '?', '≥', '▀', '?', '?', '?', '?', '?', '?', '?', /*90*/ '?', '?', '?', '?', '?', '╔', '?', '?', '_', '¡', '╜', '£', '╧', '╛', '▐', '⌡', /*A0*/ '▄', '╕', 'ª', '«', '¬', '≡', '⌐', '∈', '°', '±', '²', 'ⁿ', '∩', 'µ', '⌠', '·', /*B0*/ '≈', '√', 'º', '»', '¼', '½', '≤', '¿', '╖', '╡', '╢', '╟', 'Ä', 'Å', 'Æ', 'Ç', /*C0*/ '╘', 'É', '╥', '╙', '▐', '╓', '╫', '╪', '╤', 'Ñ', 'π', 'α', 'Γ', 'σ', 'Ö', '₧', /*D0*/ 'Ø', 'δ', 'θ', 'Ω', 'Ü', 'ø', 'Φ', 'ß', 'à', 'á', 'â', '╞', 'ä', 'å', 'æ', 'ç', /*E0*/ 'è', 'é', 'ê', ' ', 'ì', 'í', 'î', 'ï', '╨', 'ñ', 'ò', 'ó', 'ô', 'Σ', 'ö', '÷', /*F0*/ 'ø', 'ù', 'ú', 'û', 'ü', '∞', 'τ', ' '}; int ToUnicode(char c, int font) { if (font!=FONT_VECTOR) return c; if (c<128) return c; return VectorSubst[c-128]; } /* Ausgabe String in Unicode, ggf. mit Konvertierung bei FONT_VECTOR */ void OutString(string s, int font) { int i, len=strlen(s); for (i=0; i>16)&0xFF); } // Füllstil-Code für Layer berechnen int CalcFill(int n) { int ret; switch (HatchType) { case 0: ret=0; break; case 1: { ret=FillStyles[fil[n]]; if (Flipped) 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 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=1; j=nHandles) return ret; switch (OD_type[idx]) { case ObjPen: { string stil="unbekannt"; switch (OD_param1[idx]) { case 0: stil="durchgezogen"; break; case 1: stil="Striche"; break; case 2: stil="Punkte"; break; case 3: stil="Strich-Punkt"; break; case 5: stil="leer"; break; } sprintf(ret,"Stift, Stil=%s(%d), Breite=%d, Farbe=%06X", stil,OD_param1[idx],OD_param2[idx],OD_color[idx]); }break; case ObjBrush: { string stil="unbekannt"; string hatch="unbekannt"; switch (OD_param1[idx]) { case 0: stil="voll"; break; case 1: stil="leer"; break; case 2: stil="gestreift"; break; } switch (OD_param2[idx]) { case 0: hatch="keine"; break; case 4: hatch="+++"; break; } sprintf(ret,"Pinsel, Stil=%s(%d), Streifen=%s(%d), Farbe=%06X", stil,OD_param1[idx],hatch,OD_param2[idx],OD_color[idx]); }break; case ObjFont: { sprintf(ret,"Schrift Art=%s(%d), Größe=%d", FontName[OD_param1[idx]&3],OD_param1[idx]&3,OD_param2[idx]); }break; } return ret; } void ShowHandleTable(void) { string msg=""; for (int i=1; i\n",i,ObjDescription(i)); msg+=t; } dlgMessageBox(msg); } void ClearObjects(void) { // Handletabelle löschen, sonstige GDI-Attribute invalidieren nHandles=1; TextColor=-1; TextAlign=-1; LastX=INT_MAX; LastY=INT_MAX; LayerRop=13/*R2_COPYPEN*/; } real WorldRotate; real WorldC=1, WorldS; // Weltkoordinatenrotation setzen für 0 <= angle < 360; // zusätzlich Spiegelung (für Text) setzen für -360 <= angle < 0 void SetRotate(real angle) { if (WorldRotate!=angle) { WorldRotate=angle; BeginRecord(35/*EMR_SETWOLRDTRANSFORM*/); angle*=PI/180; WorldC=cos(angle); // Drehmatrix-Elemente | +cos +sin | WorldS=sin(angle); // | -sin +cos | OutFloat(angle<0?-WorldC:WorldC); OutFloat(angle<0?WorldS:-WorldS); OutFloat(WorldS); OutFloat(WorldC); OutFloat(0); OutFloat(0); EndRecord(); } } // Mittelpunkt bzgl. rotiertem Koordinatensystem berechnen. // Statt Rückgabewerte setzt diese Funktion die globalen // Hilfsvariablen tx und ty int tx,ty; void CalcCenter(int mx, int my) { tx=WorldC*mx+WorldS*my; ty=-WorldS*mx+WorldC*my; if (WorldRotate<0) tx=-tx; } void OutWindow(void/*int w, int h*/) { if (0) { int w,h; BeginRecord(10/*EMR_SETWINDOWORG*/); OutDword(0); OutDword(0); EndRecord(); BeginRecord(9/*EMR_SETWINDOWEXT*/); OutMassX(w); OutMassY(h); EndRecord(); } BeginRecord(18/*EMR_SETBKMODE*/); OutDword(1/*TRANSPARENT*/); EndRecord(); if (HatchType==2) { BeginRecord(25/*EMR_SETBKCOLOR*/); int col=BackgroundColor; if (Trans && PaletteType==3) col^=0xFFFFFF; OutDword(col); // für Pattern-Schraffur (ist immer deckend - leider!) EndRecord(); } } // Liefert Index in Handle-Tabelle (für schnelle EMF-Darstellung), // liefert 0, 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; // Aufrufer muss Create() und SelectObject() rufen nHandles++; return j; } // gefunden: ggf. als aktuelles Objekt aktivieren if (ObjInUse[objtype]!=j) { OutSelectObject(j); ObjInUse[objtype]=j; } return 0; } // BoundingBox-Dummy (4 Nullen) ausgeben void OutBBox(void) { for (int i=0; i<4; i++) OutDword(0); } void SetTextColor(int color) { if (TextColor!=color) { BeginRecord(24/*EMR_SETTEXTCOLOR*/); OutDword(color); EndRecord(); TextColor=color; } } // wird für SetPen() und SetBrush() gebraucht! void OutBitmapData(int hatch) { OutDword(40); // sizeof(header) OutDword(8); // Breite OutDword(8); // Höhe OutDword(0x10001); // Planes und Bit pro Pixel OutDword(0); OutDword(32); // Länge der Daten in Bytes int i; OutBBox(); // sonstige Header-Angaben for (i=0; i<8; i++) OutDword(~EaglePattern[hatch*8+(Flipped?7-i:i)]&0xFF); } void SetPen(int style, int width, int color) { if (style==5) width=color=0; // PS_NULL: keine Breite, keine Farbe int idx=GetNewHandleIndex(ObjPen,style,width,color); if (idx<0) return; if (Debug>1) { string msg; sprintf(msg,"SetPen(%d,%d,%06X) idx=%d nHandles=%d",style,width,color,idx,nHandles); dlgMessageBox(msg); } if (!idx) return; // bereits selektiert if (!width){ BeginRecord(38/*EMR_CREATEPEN*/); OutDword(idx); OutDword(style); OutDword(width); OutDword(width); OutDword(color); }else{ style|=0x10000/*PS_GEOMETRIC*/; int brushstyle=LayerFill&0x0F; if (brushstyle==3) SetTextColor(color); int brushhatch=LayerFill>>4; BeginRecord(95/*EMR_EXTCREATEPEN*/); OutDword(idx); // ihPen if (brushstyle<3) { OutBBox(); OutDword(style); // elpPenStyle; OutDword(width); // elpWidth; OutDword(brushstyle);// elpBrushStyle; OutDword(color); // elpColor; OutDword(brushhatch);// elpHatch; OutDword(0); // elpNumEntries; OutDword(0); // elpStyleEntry[1]; }else{ OutDword(56); // offBmi (BitMapInfo) OutDword(40); // cbBmi OutDword(96); // offBits (BitMapBits) OutDword(32); // cbBits OutDword(style); // elpPenStyle; OutDword(width); // elpWidth; OutDword(brushstyle);// elpBrushStyle; OutDword(2); // elpColor; OutDword(0); // elpHatch; OutDword(0); // elpNumEntries; OutDword(0);// elpStyleEntry[1]; OutBitmapData(brushhatch); } } EndRecord(); OutSelectObject(idx); } 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 (Debug>1) { string msg; sprintf(msg,"SetBrush(%d,%d,%06X) idx=%d nHandles=%d",style,hatch,color,idx,nHandles); dlgMessageBox(msg); } if (!idx) return; if (style<3) { // BS_SOLID,BS_NULL,BS_HATCHED BeginRecord(39/*EMR_CREATEBRUSHINDIRECT*/); OutDword(idx); OutDword(style); OutDword(color); OutDword(hatch); EndRecord(); }else{ // BS_PATTERN BeginRecord(93/*EMR_CREATEMONOBRUSH*/); OutDword(idx); OutDword(2); // iUsage OutDword(36); // offBmi; OutDword(40); // cbBmi; OutDword(76); // offBits; OutDword(32); // cbBits; OutDword(0x20000000); // weiß keiner! OutBitmapData(hatch); EndRecord(); SetTextColor(color); } OutSelectObject(idx); } void SetFont(int font, int size, int color,int align) { int sel=FontSel[font]; if (!sel) return; // VEKTOR - das geht hier nicht! size*=1.6; int idx=GetNewHandleIndex(ObjFont,font,size,0); if (Debug>1) { string msg; sprintf(msg,"SetFont(nr=%d,ali=%d,size=%d,%06X) idx=%d nHandles=%d", font,align,size,color,idx,nHandles); dlgMessageBox(msg); } if (idx) { BeginRecord(82/*EMR_EXTCREATEFONTINDIRECTW*/); OutDword(idx); OutDword(size); //nHeight OutDword(0); //nWidth OutDword(0); //nEscapement (Schriftrichtung per SetWorldTransform) OutDword(0); //nOrientation OutDword(FontBold[font]?700:400); //fnWeight OutDword(FontItalic[font]); //fItalic,fUnderline,fStrikeout,fCharset OutDword(0); // fOutPrecision,fClipPrecision,fQuality,fPitchAndFamily OutString(FontName[font],-1); EndRecord(); OutSelectObject(idx); } SetTextColor(color); if (TextAlign!=align) { BeginRecord(22/*EMR_SETTEXTALIGN*/); OutDword(align); EndRecord(); TextAlign=align; } } // Koordinaten sortieren und zwei Punktkordinaten (eines Boxrechtecks) ausgeben void OutRect(int left, int top, int right, int bottom) { if (!Flipped && left>right || Flipped && left>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); } void HandlePolygon(UL_POLYGON O) { // Obwohl Polygone mit Kurven-Begrenzung exakt mittels Path-Funktionen // gezeichnet werden können, wird hier nur ein näherungsweises Polygon // gezeichnet. Erstens läuft es so mit freigerechneten Signalpolygonen, // und zweitens bleibt das EMF auch unter Windows 9x/Me benutzbar. // Vorausgesetzt, die Koordinaten bleiben 16bittrig. // Und es wird kein SetWorldTransform() für gespiegelten Text verwendet. if (DontProcess(O.layer)) return; 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! 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 } SetRotate(0); if (np==1) { BeginRecord(3/*EMR_POLYGON*/); OutBBox(); OutDword(n); // cptl }else{ BeginRecord(8/*EMR_POLYPOLYGON*/); OutBBox(); OutDword(np); // Anzahl Einzelpolygone OutDword(n); // Anzahl aller Punkte for (n=0; n>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<Ou9uK`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) { if (!DoNegate) return O.value; string s=O.value; int i,j,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; } if (c<32) { } s[j++]=c; if (sel==2) x+=53*mult; // Zeichenbreite für 96 Punkt "Courier New" 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 x0-=y*s; y0+=y*c; } SetPen(0/*PS_SOLID*/,GetVectorLineWidth(O),LayerColor); for (i=0; i90 && 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 (board && Flipped && !O.mirror // gespiegelter Text? || board && !Flipped && O.mirror) { w=-w; if (!w) w=-360; } if (Debug) OutCircle(O.x,O.y,10000); SetRotate(w); CalcCenter(O.x,O.y); 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); SetFont(O.font,O.size,col,a); // Eine Zusammenfassung mehrerer Texte zu PolyTextOut ist naheliegend... // ExtTextOutA wäre interessant, weil's besser packt und evtl. chinesisch läuft BeginRecord(84/*EMR_EXTTEXTOUTW*/); OutBBox(); // rclBounds (unbenutzt) OutDword(2); // iGraphicsMode=GM_ADVANCED OutDword(0); // exScale (unbenutzt bei GM_ADVANCED) OutDword(0); // eyScale OutMassX(tx); // ptlReference OutMassY(ty); OutDword(l); // nChars OutDword(76); // offString OutDword(0); // fOptions OutBBox(); // rcl OutDword(0); // offDx: Welch ein Glück: Windows wertet das "korrekt" aus! OutString(s,O.font); EndRecord(); } /* Nur für Boards: */ void OutDrillSymbol(int x, int y, int type) { int D=40*254; 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; } } 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); } } // ermittelt Bezug zum zu verwendenden Layer für PADs // Liefert 0 für "don't process" int GetPadLayer(int flags) { 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; 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) { 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; 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=0; CurLayer--) { if (SetLayer(CurLayer)) SchWalkLayer(S); }else SchWalkLayer(S); // Schwarzweiß NICHT im Sandwich-Betrieb abarbeiten (schneller) } void BrdWalkLayer2(UL_BOARD B, int DoLast) { if (!SetLayer(CurLayer)) return; if (!DoLast && strchr(LastLayers,CurLayer)>=0) return; // überspringen BrdWalkLayer(B); // 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(B); DrawHoles=0; } } /* Abarbeitung layer-weise, sogar wenn schwarz-weiß */ void BrdWalkLayers(UL_BOARD B) { // 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(B,0); for (CurLayer=16; CurLayer>=1; CurLayer--) BrdWalkLayer2(B,0); }else{ for (CurLayer=16; CurLayer>=1; CurLayer--) BrdWalkLayer2(B,0); for (CurLayer=17; CurLayer<21; CurLayer++) BrdWalkLayer2(B,0); } for (CurLayer=29; CurLayer<51; CurLayer++) BrdWalkLayer2(B,0); for (CurLayer=53; CurLayer<256; CurLayer++) BrdWalkLayer2(B,0); //Typische Text-Layer zuletzt for (CurLayer=51; CurLayer<53; CurLayer++) BrdWalkLayer2(B,0); for (CurLayer=21; CurLayer<29; CurLayer++) BrdWalkLayer2(B,0); Trans=0; // Transparenz für LastLayers ausschalten for (int i=0; CurLayer=LastLayers[i]; i++) BrdWalkLayer2(B,1); } void OutStart() { nDwords=0; // Kopf beginnen nRecords=0; ClearObjects(); // Normalen EMF-Kopf ausgeben BeginRecord(1/*EMR_HEADER*/); nDwords+=8; // rclBounds, rclFrame übergehen (wird später eingesetzt) OutDword(0x464D4520); // dSignature ENHMETA_SIGNATURE OutDword(0x00010000); // nVersion nDwords+=3; // nBytes, nRecords, nHandles übergehen OutDword(0); // nDescription OutDword(0); // offDescription (Unicode!) OutDword(0); // nPalEntries nDwords+=4; // szlDevice, szlMillimeters übergehen EndRecord(); OutWindow(); } void OutWrite(int left, int top, int right, int bottom) { SetRotate(0); // falls es Programme gibt, die kein SaveDC aufrufen? BeginRecord(14/*EMR_EOF*/); OutDword(0); // nPalEntries OutDword(0); // offPalEntries OutDword(0); // nSizeLast EndRecord(); status("Datei-Ausgabe ("+FName+")"); // Windows-Metafile-Kopfdaten ergänzen FileData[2]=left/254; // rclBounds FileData[3]=top/254; FileData[4]=right/254; FileData[5]=bottom/254; // Ohne Teiler verreckt ACDSee; ansonsten scheint der Teiler ohne Belang. // Eine 1:1-Anzeige mit dem Total Commander F3-Viewer ist nicht möglich, // ggf. "F" drücken. Möglicherweise ein Bug/Un-Feature im Total Commander. FileData[6]=left/100; // rclFrame [HIMETRIC = 10µm] FileData[7]=top/100; FileData[8]=right/100; FileData[9]=bottom/100; FileData[12]=nDwords*4;// nBytes FileData[13]=nRecords; // nRecords FileData[14]=nHandles; // nHandles + sReserved int w=right-left; int h=bottom-top; FileData[18]=w; // szlDevice [pel], diese Angabe muss stimmen FileData[19]=h; FileData[20]=u2mm(w); // szlMillimeters [mm], muss mit rclFrame korrelieren FileData[21]=u2mm(h); for (int i=0; i=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{ dlgMessageBox("Unbekannte Schriftartbezeichnung "+val+"!"); exit(EXIT_FAILURE); } FontBold[idx]=0; FontItalic[idx]=0; for (p=0; p oder =. // Nur ein ist erlaubt, der kein '=' enthalten darf. // Alle im Dialog einstellbaren Variablennamen sind erlaubt. void ParseCommandLine(void) { int i,j,p; string arg,key,val; for (i=1; i=0) { // normales Argument key=strsub(arg,0,p); val=strsub(arg,p+1); if (key=="Debug") Debug=strtol(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=="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{ dlgMessageBox("Unbekannte Variable "+key+"!"); exit(EXIT_FAILURE); } }else{ // Dateiname if (FName) { dlgMessageBox("Mehrere Dateinamen ("+arg+") sind nicht möglich!"); exit(EXIT_FAILURE); } if (arg=="*") FName=AutomaticFileName(); else FName=arg; } } if (!Dialog && !CalcLastLayers()) exit(EXIT_FAILURE); } /* Hauptprogramm */ if (!schematic && !board) { dlgMessageBox("Muss aus Schaltplan oder Board gestartet werden!"); exit(EXIT_FAILURE); } // Informationen über Schaltplan und Layer einsammeln if (schematic) schematic(S) { S.layers(L) if (L.visible) { col[L.number]=L.color; // Layer vermerken fil[L.number]=L.fill; // Füll-Stile (1 = komplett gefüllt) if (L.fill!=1) NumHatched++; } SName=filename(S.name); SchCountFonts(S); }else board(B) { B.layers(L) if (L.visible) { col[L.number]=L.color; // Layer vermerken fil[L.number]=L.fill; // Füll-Stile (1 = komplett gefüllt) if (L.fill!=1) NumHatched++; if (L.fill==2 || L.fill==4 || L.fill==5 || L.fill>=9) NumExact++; } SName=filename(B.name); BrdCountFonts(B); CheckRipupPoly=1; // diese Layer sind wesentliches Kriterium zur Voreinstellung Flipped=col[LAYER_BPLACE] && !col[LAYER_TPLACE]; } if (schematic) HatchType=0; if (NumExact) HatchType=2; // Vorgabe wenn solche Schraffuren sichtbar (nur Board) OnSetPaletteType(); // Kommandozeile auswerten ParseCommandLine(); if (CheckRipupPoly) board(B) if (BrdAreRipupPolys(B)) 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 (Dialog && DialogBox()!=1) exit(EXIT_FAILURE); // 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=filesetext(GetWorkFileName(),schematic?".emf":Flipped?"b.emf":"t.emf"); FName=dlgFileSave("Speichern Erweiterte Metadatei",FName,"*.emf"); }else if (FName==filename(FName)) FName=filedir(GetWorkFileName())+FName; // Pfad ergänzen! Sonst landet's im BIN-Verzeichnis! if (!FName) exit(EXIT_FAILURE); if (AllPages) { schematic(S) S.sheets(SH) { string e; sprintf(e,"-%d%s",SH.number,fileext(FName)); output(filesetext(FName,e),"wb") SchPrintMF(SH); } }else{ if (board) board(B) { output(FName,"wb") BrdPrintMF(B); }else sheet(S) { output(FName,"wb") SchPrintMF(S); } }