Source file: /~heha/hs/sch2cgm/sch2cgm.zip/sch2cgm.ulp

#usage	"Ausgabe eines <b>Sch</b>altplanes als "
	"<b>C</b>omputer <b>G</b>raphics <b>M</b>etafile "
	"für bequemen Dokument-Import sowie String-Suchmöglichkeit in "
	"daraus generierten PDFs.<p>\n"
	"Ein entsprechendes Standard-Importfilter muss in Ihrer "
	"Textverarbeitung (bspw. MS Office) installiert sein!<p>"
	"Einzige bleibende Unzulänglichkeit sind Polygone mit Bogen-Begrenzung."
	"<p><b>Kommandozeilenargument:</b> Dateiname (optional)<p><hr>"
	"<author>Autor: <A href=mailto:henrik.haftmann@e-technik.tu-chemnitz.de>"
	"henrik.haftmann@e-technik.tu-chemnitz.de</A>,<br>\n"
	"URL: <A href='http://www.tu-chemnitz.de/~heha/hs_freeware/sch2cgm/'>"
	"http://www.tu-chemnitz.de/~heha/hs_freeware/sch2cgm/</A>,<br>\n"
	"Chemnitz, 7.10.05</author>"
	
// Mehr Features:
// Die Ausgabe erfolgt (im Bunt-Fall) lagenweise von hinten nach vorn.
// Deshalb ist es hiermit möglich, Schaltungsteile farblich zu hinterlegen
// (was in Eagle auf dem Bildschirm, nicht jedoch beim Drucken funktioniert)
// um eine bessere Schaltungsdokumentation zu erreichen.
// Zur Hinterlegung benutze man sehr helle Farben,
// für Schwarzweiß-Ausgabe eine Schraffur.
// Farb-Auswahl:
// Die im Dialog angegebene Hintergrundfarbe wird nicht im .CGM ausgegeben;
// das .CGM ist stets transparent (und ohne Hintergrundfarbe)
// Ist ein schwarzer Hintergrund gewünscht, muss die Grafik über
// eine schwarze Fläche positioniert werden.
// Könnte mal später noch ein Schalter werden... (bei Bedarf)
// Schrift-Ersetzung:
// "Arial fett" macht m. E. den besten Eindruck, für alle drei Eagle-Schriften.
// (Wer verwendet sie ernsthaft alle?)
// Schaltpläne im WWW:
// Ein brauchbares Vektorformat fürs WWW ist mir noch nicht über den
// Weg gelaufen (SWF?), hier muss man bei Pixelgrafik bleiben.
// Bewährt hat sich ein Export bei (nur) 100 dpi ins Clipboard,
// anschließend Reduktion auf 16 Farben und Speichern als .GIF oder .PNG.
// Getestet mit:
// Eagle 4.11 für Windows, Microsoft Office 2000 deutsch

int NumSheet;		// Anzahl Seiten des Schaltplans
int NumHatched;		// Anzahl Layer mit Füll-Stil<>1 (üblich: keine)
int NumFont[];		// Anzahl Vorkommen von Vektor/Prop/Fixed-Fonts
string SName;		// Schaltplan-Name (ohne Pfad)
int col[];		// Farb-Indizes der (bis zu 256) Layer, 0 = unsichtbar
int fil[];		// Füll-Stile der (bis zu 256) Layer, 1 = gefüllt

// **** Dialog ****
	// Voreinstellungen
int PaletteType=2;	// Farbmodell, 2. Parameter für palette()
			// PaletteType=0 bedeutet Schwarzweiß-Ausgabe (1 Layer)
int Filled=1;		// Gestrichelt definierte Layer gefüllt (!Polygone)
int MakePolyline=0;	// Zusammenfassung von Linienstücken zu PolyLine
// Rätselhafterweise ergibt MakePolyline auf dem Bildschirm fehlpositionierte
// Linien, sieht erschreckend hässlich aus!
// Deshalb nur für (kleine) PDFs und zum Ausdrucken verwenden!
int AllPages=0;		// Automatisch alle Seiten generieren

int FontSel[]={1,1,0};	// ... ein Fall für eine Art INI-Datei
int FontBold[]={1,1,1};
int FontItalic[]={0,0,0};
string FontSrc[]={"Vektor","Proportional","Fixed"};
string FontSys[]={"Courier","Arial","Times"};
string FontLabel[];

void SetFontLabels(void) {
 for (int idx=0; idx<3; idx++) {
  string s=FontSrc[idx];
  if (FontItalic[idx])	s="<i>"+s+"</i>";
  if (FontBold[idx])	s="<b>"+s+"</b>";
  sprintf(FontLabel[idx],"<font face=%s>%s</font>:",FontSys[FontSel[idx]],s);
 }
}

void FontSubstLine(int idx) {
 dlgCell(idx,0) {
  dlgLabel(FontLabel[idx],1);
  dlgSpacing(10);
 }
 dlgCell(idx,1) {
  dlgComboBox(FontSys,FontSel[idx]) SetFontLabels();
  dlgSpacing(10);
 }
 dlgCell(idx,2) dlgCheckBox("Fett",FontBold[idx]) SetFontLabels();
 dlgCell(idx,3) dlgCheckBox("Kursiv",FontItalic[idx]) SetFontLabels();
}

int Dialog(void) {
 status("Dialog");
 return dlgDialog("CGM-Export-Optionen") {
  dlgLabel("Dateiname: <b>"+SName+"</b>");
  dlgGroup("Ausgabe-&Farben   (siehe Eagle-Palette)") {
   dlgRadioButton("&Schwarz auf Weiß (Schwarzweiß)",PaletteType);
   dlgRadioButton("Bunt auf Schwarz",PaletteType);
   dlgRadioButton("&Bunt auf Weiß (Standard)",PaletteType);
   dlgRadioButton("Bunt auf farbigem Hintergrund",PaletteType);
  }
  dlgGroup("Schrift-&Ersetzung   (verwendete Schriften gelistet)") dlgGridLayout{
   SetFontLabels();
   for (int i=0; i<3; i++) if (NumFont[i]) FontSubstLine(i);
  }
  if (NumHatched) dlgCheckBox("A&usgefüllt (schraffierte Layer)",Filled);
  dlgCheckBox("P&olylinien generieren (statt einzelne Linien)",MakePolyline)
    if (MakePolyline) dlgMessageBox(
    "Die Bildschirmdarstellung der .CGM-Grafik wird fehlpositionierte "
    "Linien anzeigen.\nBeim Drucken oder PDF-Export ist jedoch alles OK, "
    "und die Ausgabedaten sind etwas kleiner.");
  if (NumSheet>1) {
   dlgSpacing(4);
   dlgCheckBox("&Alle Seiten (mehrere Dateien »-nr.cgm«)",AllPages);
  }
  dlgSpacing(4);
  dlgHBoxLayout {
   dlgPushButton("+OK") dlgAccept(1);
   dlgPushButton("Abbrechen") dlgReject();
   dlgPushButton("Über...") dlgMessageBox(";"+usage);
  }
 };
}

/* Suche nach verwendeten Text-Fonts, für Dialog */
void CountFonts(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) if (col[T.layer]) NumFont[T.font]++;
   }
  }
  S.nets(N) {
   N.segments(SE) {
    SE.texts(T) if (col[T.layer]) NumFont[T.font]++;
   }
  }
  S.parts(P) {
   P.instances(I) {
    I.gate.symbol.pins(P) {
     P.texts(T) if (col[T.layer]) NumFont[T.font]++;
    }
    I.gate.symbol.texts(T) if (col[T.layer]) NumFont[T.font]++;
    I.texts(T) if (col[T.layer]) NumFont[T.font]++;
   }
  }
  S.texts(T) NumFont[T.font]++;
 }
}

// **** globale Variablen ****
int MinColor=64,MaxColor;	// zur Einschränkung der Farbpaletteneinträge
int CurLayer;
int InLine;		// zur Verkettung von Line zu PolyLine
int LastX,LastY;	// (kleinere Datei, schnelleres Rendern)

// Hier: zur sparsamen Ausgabe von Gerätekontextinformationen verwendet
int LineWidth;
int IntStyle;		// 0=leer (Kreise!), 1=gefüllt (alles andere), 2=Hatch
int HatchIndex;		// Füllmuster: 1 == 2 || 3 // 4 \\ 5 ++ 6 xx
int Color;
int TextAlign;
int EdgeVis;		// 0 = AUS, 1 = EIN
int CharOri;		// 0 = horizontal, 1 = vertikal
int LineStyle;		// 1 = durchgehend, 2 = ---, 3 = ···, 4 = -·-·-
int CharHeight;
int Font;
/* Behandlung globaler Variablen */
void ResetDC(void) {	// für jede neue Datei aufrufen
 LineWidth=0;
 IntStyle=-1;
 HatchIndex=0;
 Color=0;
 TextAlign=-1;
 EdgeVis=-1;
 CharOri=0;
 LineStyle=0;
 CharHeight=-1;
 Font=0;
}
int Hatch(int layer) {
 return Filled?1:fil[layer];
}
void SetLineWidth(int n) {
 if (LineWidth!=n) {
  printf("linewidth %d;\n",n);
  printf("edgewidth %d;\n",n);
  LineWidth=n;
 }
}
void SetLineStyle(int n) {
 if (LineStyle!=n) {
  printf("linetype %d;\n",n);
  LineStyle=n;
 }
}
void SetHatchIndex(int n) {
 if (HatchIndex!=n) {
  printf("hatchindex %d;\n",n);
  HatchIndex=n;
 }
}
void SetIntStyle(int n) {
 string st[]={"EMPTY","SOLID","HATCH"};
 int ha[]={0,0,1,3,3,4,4,5,6,6,6,6,5,5,5,5};	// etwa wie Eagle
 int j=n; if (j>2) j=2;
 if (IntStyle!=j) {
  printf("intstyle %s;\n",st[j]);
  IntStyle=j;
 }
 if (j==2) SetHatchIndex(ha[n]);
}
void SetEdgeVis(int n) {
 if (EdgeVis!=n) {
  printf("edgevis %s;\n",n?"ON":"OFF");
  EdgeVis=n;
 }
}
void SetTextAlign(int n) {
 if (TextAlign!=n) {
  string s1[]={"LEFT","RIGHT"};
  string s2[]={"BASE","CAP"};
  printf("textalign %s,%s;\n",s1[n&1],s2[n>>1]);
  TextAlign=n;
 }
}
void SetCharHeight(int n) {
 if (CharHeight!=n) {
  printf("charheight %d;\n",n);
  CharHeight=n;
 }
}
void SetCharOri(int n) {
 if (CharOri!=n) {
  string s[]={"(0,1200) (1200,0)","(-1200,0) (0,1200)"};
  printf("charori %s;\n",s[n]);
  CharOri=n;
 }
}
void SetFont(int n) {
 if (Font!=n) {
  printf("textfontindex %d;\n",n);
  Font=n;
 }
}
int SetLayer(int n) {
 int c=col[n];	// c=0 wenn Layer nicht sichtbar
 if (c) {
  if (Color!=c) {
   printf("linecolr %d;\n",c);
   printf("edgecolr %d;\n",c);
   printf("fillcolr %d;\n",c);
   printf("textcolr %d;\n",c);
  }
  Color=c;
 }
 return c;
}
string EscapeStr(string s) {
 int j;
 j=strchr(s,'\'');
 if (j==-1) return s;	// unverändert
 string t;
 int i=0;
 do{
  j++;
  t+=strsub(s,i,j-i)+"'";
  i=j;
  j=strchr(s,'\'',i);
 }while (j!=-1);
 return t;
}
void EndLine(void) {
 if (InLine) printf(";\n");
 InLine=0;
}
int DontDraw(int layer) {
 return PaletteType && layer!=CurLayer	// Bunt: lagenweise
 || !PaletteType && !col[layer];	// Schwarzweiß: sichtbares
}

/* Abarbeitung von Eagle-Zeichenprimitiven */
void HandleCircle(UL_CIRCLE O) {
 if (DontDraw(O.layer)) return;
 EndLine();
 SetLineStyle(1);
 SetIntStyle(0);
 SetEdgeVis(1);
 SetLineWidth(O.width);
 printf("circle (%d,%d) %d;\n",O.x,O.y,O.radius);
}
void HandleJunction(UL_JUNCTION O) {
 if (DontDraw(LAYER_NETS)) return;
 EndLine();
 SetLineStyle(1);
 SetIntStyle(1);
 SetEdgeVis(1);
 SetLineWidth(1524);
 printf("circle (%d,%d) %d;\n",O.x,O.y,O.diameter/2);
}
void HandleText(UL_TEXT O) {
 if (DontDraw(O.layer)) return;
 EndLine();
 SetFont(O.font+1);
 SetCharHeight(O.size);
 int i=O.angle/90+O.mirror*4;	// Bit2=Mirror, Bit1:0=Drehung
 int k[]={0,0,3,3,1,2,2,1};	// Zuordnungstabelle TextAlign
 SetCharOri(i&1);
 SetTextAlign(k[i]);
// printf("circle (%d,%d) %d;\n",T.x,T.y,10000);
 printf("text (%d,%d) final '%s';\n",O.x,O.y,EscapeStr(O.value));
}
void HandlePolygon(UL_POLYGON O) {
 if (DontDraw(O.layer)) return;
 EndLine();
 SetLineStyle(1);
 SetIntStyle(O.pour==POLYGON_POUR_HATCH ? 7 : (Filled?1:fil[O.layer]));
 SetEdgeVis(1);
 SetLineWidth(O.width);
 printf("polygon");
 O.wires(W) {
  printf(" (%d,%d)",W.x1,W.y1);
 }
 printf(";\n");
}
void HandleRectangle(UL_RECTANGLE O) {
 if (DontDraw(O.layer)) return;
 EndLine();
 SetIntStyle(Filled?1:fil[O.layer]);
 if (IntStyle==1) SetEdgeVis(0);
 else{
  SetEdgeVis(1);
  SetLineWidth(0);
 }
 int l=O.x1,t=O.y1,r=O.x2,b=O.y2;
 int i=O.angle/90;
 if (i&1) {			// 90° oder 270°
  int x=(l+r)/2,y=(t+b)/2;	// Dreh-Mitte
  l=x+y-O.y1;			// neue Ecken berechnen
  t=y-x+O.x1;
  r=x+y-O.y2;
  b=y-x+O.x2;
 }
 printf("rect (%d,%d) (%d,%d);\n",l,t,r,b);
}
void HandleWire(UL_WIRE O) {
 if (DontDraw(O.layer)) return;
 int st;
 switch (O.style) {
  case WIRE_STYLE_CONTINUOUS:	st=1; break;
  case WIRE_STYLE_LONGDASH:	st=2; break;
  case WIRE_STYLE_SHORTDASH:	st=3; break;
  case WIRE_STYLE_DASHDOT:	st=4; break;
 }
 if (st!=LineStyle || O.width!=LineWidth || O.arc) EndLine();
 SetLineStyle(st);
 SetLineWidth(O.width);
 if (O.arc) {
  printf("arcctr (%d,%d),(%d,%d),(%d,%d),%d;\n",
   O.arc.xc,O.arc.yc,
   O.arc.x1-O.arc.xc,O.arc.y1-O.arc.yc,
   O.arc.x2-O.arc.xc,O.arc.y2-O.arc.yc,
   O.arc.radius);
 }else{
  if (!InLine || O.x1!=LastX || O.y1!=LastY) {
   EndLine();
   printf("line (%d,%d)",O.x1,O.y1);
   InLine=1;
  }
  printf(" (%d,%d)",O.x2,O.y2);
  if (!MakePolyline) EndLine();
  LastX=O.x2; LastY=O.y2;
 }
}

/* Abarbeitung eines Layers (schwarzweiß: alle) */
/* Warum auch immer erscheinen die Elemente des Layers 93 (Pins) nicht */
void WalkLayer(UL_SHEET S) {
 string e;
 sprintf(e,"Seite %d Layer %d",S.number,CurLayer);
 status(e);
 S.busses(B) {
  B.segments(SE) {
   SE.texts(T) HandleText(T);
   SE.wires(W) HandleWire(W);
  }
 }
 S.circles(C) HandleCircle(C);
 S.nets(N) {
  N.segments(SE) {
   SE.junctions(J) HandleJunction(J);
   SE.texts(T) HandleText(T);
   SE.wires(W) HandleWire(W);
  }
 }
 S.parts(P) {
  P.instances(I) {
   I.gate.symbol.circles(C) HandleCircle(C);
   I.gate.symbol.pins(P) {
    P.circles(C) HandleCircle(C);
    P.texts(T) HandleText(T);
    P.wires(W) HandleWire(W);
   }
   I.gate.symbol.polygons(P) HandlePolygon(P);
   I.gate.symbol.rectangles(R) HandleRectangle(R);
   I.gate.symbol.texts(T) HandleText(T);	// Unsmashed
   I.gate.symbol.wires(W) HandleWire(W);
   I.texts(T) HandleText(T);	// Smashed
  }
 }
 S.polygons(P) HandlePolygon(P);
 S.rectangles(R) HandleRectangle(R);
 S.texts(T) HandleText(T);
 S.wires(W) HandleWire(W);
 EndLine();
}

/* Abarbeitung layer-weise von hinten nach vorn */
void WalkLayers(UL_SHEET S) {
 if (PaletteType) for (CurLayer=255; CurLayer>=0; CurLayer--) {
  if (SetLayer(CurLayer)) WalkLayer(S);
 }else WalkLayer(S);
}

string BuildFontName(int idx) {	// .CGM-Schriftartenname generieren
 int sel=FontSel[idx];
 string r=sel==1?"Helvetica":FontSys[sel];
 if (sel==2 || FontBold[idx] || FontItalic[idx]) r+="-";
 if (sel==2 && !FontBold[idx] && !FontItalic[idx]) r+="Roman";
 if (FontBold[idx]) r+="Bold";
 if (FontItalic[idx]) r+=sel==2?"Italic":"Oblique";
 return r;
}

/* Ausgabe einer Metadatei */
void PrintMF(UL_SHEET S) {
 printf("BEGMF 'Schaltplan %s",filename(SName));
 if (NumSheet>1) printf(" Seite %d/%d",S.number,NumSheet);
 printf("';\n");
 printf("mfversion 1;\n");
 printf("mfelemlist 'DRAWINGPLUS';\n");
 printf("vdctype integer;\n");
 printf("fontlist '%s', '%s', '%s';\n",
   BuildFontName(0),BuildFontName(1),BuildFontName(2));
 printf("BEGMFDEFAULTS;\n");
 printf(" vdcext (%d,%d) (%d,%d);\n",S.area.x1,S.area.y1,S.area.x2,S.area.y2);
 printf(" clip off;\n");
 if (PaletteType) {
  int i,k,rgb;
  printf(" colrmode indexed;\n");
  printf(" colrtable %d\n",MinColor);
  i=MaxColor; k=MinColor; do{
   rgb=palette(k++,PaletteType-1);
   printf("  %d %d %d%s\n",rgb>>16,rgb>>8&0xFF,rgb&0xFF,--i?"":";");
  }while (i);
  rgb=palette(0,PaletteType-1);
  printf(" backcolr %d %d %d;\n",rgb>>16,rgb>>8&0xFF,rgb&0xFF);
 }
 printf(" linewidthmode abs;\n");
 printf(" edgewidthmode abs;\n");
 printf(" textprec stroke;\n");
 printf(" transparency ON;\n");
 printf("ENDMFDEFAULTS;\n");
 printf("BEGPIC '%s';\n",SName);
 printf("BEGPICBODY;\n");
//   printf("linecap 3;\n");	// runde Enden, auch an Strichellinien
//   printf("linejoin 3;\n");	// runde Ecken (keine Wirkung in Word)
 ResetDC();		// globale Variablen in Ausgangsstellung
 WalkLayers(S);
 printf("ENDPIC;\n");
 printf("ENDMF;\n");
}

/* Hauptprogramm */
if (schematic) {
 string FName;
// Informationen über Schaltplan und Layer einsammeln
 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++;
   MinColor=min(MinColor,L.color);
   MaxColor=max(MaxColor,L.color);
  }
  FName=filesetext(S.name,".cgm");
  SName=filename(S.name);
  CountFonts(S);
 }
// Nutzer-Dialog anzeigen (Kommandozeilen-Optionsauswertung fehlt noch)
 if (Dialog()!=1) exit(0);
// Dateiname erfragen
 if (argc < 2) {
  FName=dlgFileSave("Speichern Computer Graphics Metafile",FName,"*.cgm");
 }else{
  FName=argv[1];
 }
 if (!FName) exit(0);
  
 if (AllPages) {
  schematic(S) S.sheets(SH) {
   string e;
   sprintf(e,"-%d%s",SH.number,fileext(FName));
   output(filesetext(FName,e),"wb") PrintMF(SH);
  }
 }else{
  sheet(S) output(FName,"wb") PrintMF(S);
 }
}else{
 dlgMessageBox("Muss aus Schaltplan gestartet werden! Nur für Schaltpläne!", "OK");
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded