#include "escp.h"
#include <shlwapi.h>
#include <windowsx.h>
#include <malloc.h>
#define elemof(x) (sizeof(x)/sizeof(*(x))) // Elemente eines Arrays
BYTE ESCP::ReadChar() const{
BYTE c;
DWORD br;
if (!ReadFile(f,&c,1,&br,NULL) || !br) longjmp(env,1);
if (msb&1) c=c&127|msb&128; // Zwangsbeeinflussung des MSB
if (msb&2 && (128<=c && c<160 || c==255 || msb&4)) c&=127; // mögliches Steuerzeichen oder während ESC-Verarbeitung?
return c;
}
void ESCP::ReadChar(BYTE*p, int len) const{
do *p++=ReadChar(); while(--len);
}
static const WCHAR CharSubst[]=
L"#$@[\\]^`{|}~\0" // 0 USA
L"#$à°ç§^`éùè¨\0" // 1 Frankreich
L"#$§ÄÖÜ^`äöüß\0" // 2 Deutschland
L"£$@[\\]^`{|}~\0" // 3 Großbritannien
L"#$@ÆØÅ^`æøå~\0" // 4 Dänemark
L"#¤ÉÄÖÅÜéäöåü\0" // 5 Schweden
L"#$@°\\é^ùàòèì\0" // 6 Italien
L"\x20A7$@¡Ñ¿^`¨ñ}~\0" // 7 Spanien
L"#$@[¥]^`{|}~\0" // 8 Japan
L"#¤ÉÆØÅÜéæøåü\0" // 9 Norwegen
L"#$ÉÆØÅÜéæøåü\0" // 10 Dänemark II
L"#$á¡Ñ¿é`íñóú\0" // 11 Spanien II
L"#$á¡Ñ¿éüíñóú\0" // 12 Lateinamerika
L"#$@[\x20A9]^`{|}~\0" // 13 Korea
L"#$§°’”¶`©®†™"; // 64 US-Gesetztext
// Angesammelte Zeichen ausgeben
void ESCP::Print() {
if (!ci) return;
if (!(pagectl&1)) {
StartPage(dc);
pagectl|=1;
SetMapMode(dc,MM_ANISOTROPIC);
SetWindowOrgEx(dc,-Offset.x,-Offset.y,NULL);
SetWindowExtEx(dc,2160,2160,NULL);
SetViewportOrgEx(dc,-GetDeviceCaps(dc,PHYSICALOFFSETX),-GetDeviceCaps(dc,PHYSICALOFFSETY),NULL);
SetViewportExtEx(dc,GetDeviceCaps(dc,LOGPIXELSX),GetDeviceCaps(dc,LOGPIXELSY),NULL);
SetBkMode(dc,TRANSPARENT);
}
// Font auswählen
int weight=400;
if (font&8) weight+=300;
if (font&16) weight+=200; // Doppeldruck-Wiedergabe mit SEMIBOLD (eigentlich müsste man Graustufen verwenden :-)
int j=GetCw();
HFONT f=CreateFontA(font&2?720:ss?270:360,j,0,0,
weight,
(font>>6)&1, // kursiv
(font>>7)&1, // unterstrichen
(lq>>3)&1, // durchgestrichen
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
lq&3?PROOF_QUALITY:DRAFT_QUALITY,
lq&2?VARIABLE_PITCH:FIXED_PITCH,
lq&2?"Proportional":lq&3?"Courier":font&1?"Elite":"Pica");
HFONT of=SelectFont(dc,f);
// Zeichen konvertieren
WCHAR s[256];
UINT CodePage=cp[cpi];
if (CodePage<2) CodePage=437;
int l=MultiByteToWideChar(CodePage,0,(char*)cb,ci,s,256);
int i=charset;
if (i==64) i=14;
if (i<15) for (PWSTR ps=s; ps=wcspbrk(ps,CharSubst); ps++) {
*ps=wcschr(CharSubst,*ps)[i*13];
}
// Zusätzlichen Leerraum ausgeben (entfällt wegen Distanz-Array)
// i=lq&3?espace:0;
// if (charextra!=i) {
// charextra=i;
// SetTextCharacterExtra(dc,i);
// }
// Zeichenkette ausgeben
int cy=pos.y;
if (font&2) cy-=ly; // Doppelte Höhe? (Ausrichtung auf Basislinie ??)
else if (ss&2) cy+=90; // Tiefstellung (Hochstellung ohne Anpassung)
#ifdef _DEBUG
s[l]=0; // terminieren für erleichtertes Debuggen
#endif
// Ohne das Distanz-Array ist die PostScript-Ausgabe nicht zufrieden stellend!!
// Deshalb ExtTextOut() statt TextOut(). Clipping wird nicht benötigt.
int *d=(int*)_alloca(l*sizeof(int));
if (lq&3) j+=espace;
for (i=0; i<l; i++) d[i]=j;
ExtTextOutW(dc,margin.left+cx,margin.top+cy,0,NULL,s,l,d);
SelectFont(dc,of);
DeleteFont(f);
ci=0;
}
void ESCP::FormFeed() {
Print();
if (!(pagectl&1)) StartPage(dc); // leere Seite produzieren
EndPage(dc);
pagectl&=~1; // neue Seite ist leer
lq&=~32;
pos.y=0;
}
void ESCP::SetPosX(int i) {
if ((unsigned)i>(unsigned)margin.Width()) return;
Print();
pos.x=i;
}
void ESCP::SetPosY(int i) {
Print();
if (i<0) return;
lq&=~32;
pos.y=i;
if (i>margin.Height()) FormFeed();
}
void ESCP::LineFeed() {SetPosY(pos.y+ly);}
void ESCP::Printable(BYTE c) {
if (crlf&4 && pos.x+GetCx()>margin.Width()) { // Zeichen passt nicht?
LineFeed(); // neue Zeile beginnen (nur wenn AUTOMATIC LINE FEED = TRUE!!)
pos.x=0;
}
if (!ci) cx=pos.x; // Startposition für Print() merken
if (ci<elemof(cb)) cb[ci++]=c;
pos.x+=GetCx();
}
// Berechne momentane Zeichenbreite in 1/240 Zoll
int ESCP::GetCw() const{
int cx=216; // 10 Zeichen pro Zoll ("Pica", NLQ)
BYTE f=font|lq&32;
if (!(lq&2)) { // bei Proportionalschrift stets 10 cpi
if (f&1) cx=180; // "Elite", 12 Zeichen pro Zoll
if (!(f&2)) { // keine Kompression bei doppelter Höhe
if (f&4) cx=126; // Condensed Pica, ca. 17,14 Zeichen pro Zoll
if ((f&5)==5) cx=108;// Condensed Elite, 20 Zeichen pro Zoll
}
if (lq&4) cx=144; // 15 Zeichen pro Zoll (keine komprimierte Version; hat Vorrang)
}
if (f&32) cx<<=1; // doppelte Breite (für alle Schriften)
return cx;
}
// Berechne momentann Zeichenabstand in 1/240 Zoll
int ESCP::GetCx() const{
int cx=GetCw();
if (lq&3) cx+=espace;
return cx;
}
// Unterfunktion: ESC '('
// Danach folgt immer eine 2-Byte-Datenlänge 0..32767 und ein entsprechend langer Datenblock
bool ESCP::HandleEscK() {
char c=ReadChar();
WORD dlen;
ReadChar((BYTE*)&dlen,2);
BYTE*p=(BYTE*)_alloca(dlen);
ReadChar(p,dlen);
int i;
switch (c) {
case '-': {
if (dlen!=3) return false;
switch (p[1]) {
case 1: SetFontBit(7,p[2]?1:0); break; // unterstreichen
case 2: lq=lq&~8|(p[2]?8:0); break; // durchstreichen (überstreichen gibts bei Windows erst mal nicht)
}
}break;
case 'B': break; // Strichkode
case 'C': {
if (dlen!=2) return false;
i=scale(*(short*)p,6);
if (!i) return false;
// if (i>22*2160) return false; // nicht mehr als 22" (warum eigentlich nicht??)
paper.cy=i;
}break; // Papierlänge
case 'G': /*GRAFIK*/ break; // Grafikmodus
case 'U': {
if (dlen!=1) return false;
switch (*p) {
case 5: case 10: case 20: case 30: case 40: case 50: case 60: unit=*p/5;
default: return false;
}
}break; // Maßeinheit
case 'V': if (dlen!=2) return false; SetPosY(margin.top+scale(SH(p),6)); break; // vertikale Druckposition in 1/360"
case '^': {
if (i=dlen) do Printable(*p++); while (--i);
}break; // Steuerzeichen als druckbare Zeichen ausgeben
case 'c': {
}break; // Seitenformat-Auswahl
case 'i': break;
case 't': {
if (dlen!=3) return false;
p[0]=AB(p[0]);
if (p[0]>=4) return false;
WORD *cpp=cp+p[0];
static const BYTE cp1[]={132,50,51,53,55,60,63,65,52,57,62,64,66,69};
if (2<=p[1] && p[1]<16) *cpp=800+cp1[p[1]-2]; // Liste nicht vollständig! Geht bis Index 42.
else if (p[1]==127 && p[2]<16) *cpp=28590+p[2]; // ISO-Latin1..ISO-Latin15
else *cpp=437;
}break; // Zeichentabellen-Zuordnung
case 'v': if (dlen!=2) return false; SetPosY(pos.y+scale(SH(p),6)); break; // relative vertikale Druckposition in 1/360"
default: return false; // unbekanntes Escape
}
return true;
}
int ESCP::scale(int i, int def) const{
if (unit) return i*unit;
return i*def;
}
void ESCP::SetFontBit(BYTE bitno,BYTE state) {
if (state==255) state=AB(ReadChar());
if (state>1) return; // bei falschen Zahlen nichts tun!
Print();
BYTE mask=1<<bitno;
font=font&~mask|state<<bitno;
}
// Perforationssprung (also oberen und unteren Seitenrand; hier: gemittelt) setzen
void ESCP::SetFormSkip(int skip) {
margin.top=skip>>1; margin.bottom=paper.cy-skip+margin.top;
}
// Setzt Tabs entsprechend ReadChar() (muss aufsteigende Zahlenreihe liefern)
void ESCP::SetTabs(int t[], int tlen, int factor) {
char c,cb;
for (cb=0; (c=ReadChar())>cb; tlen--,cb=c)
if (tlen>0) *t++=c*factor;
if (tlen>0) *t=0;
}
// Setzt gleichmäßige (horizontale oder vertikale) Tabs
void ESCP::SetEqualTabs(int t[], int tlen, int delta) {
int a=0;
do{
a+=delta;
*t++=a;
}while(--tlen);
}
// Nächsten Tabulator zur gegebenen Position suchen (rechter/unterer Rand muss danach geprüft werden)
int ESCP::FindTab(const int t[], int tlen, int pos) {
do{
if (pos<*t) return *t;
t++;
}while(--tlen);
return 32767;
}
void ESCP::HandleESCe() {
switch (AB(ReadChar())) {
case 0: SetTabs(vfu[vtc],16,ReadChar()*ly); break;
case 1: SetTabs(ht,32,ReadChar()*GetCx()); break;
}
}
void ESCP::HandleESCf() {
BYTE hv=AB(ReadChar());
for (int i=ReadChar(); i; i--) switch (hv) {
case 0: Printable(' '); break;
case 1: LineFeed(); break;
}
}
bool ESCP::HandleEsc() {
int i;
BYTE b[2], c=ReadChar();
switch (c) {
case 14: Print(); lq|=32; break; // SO = doppelte Breite für eine Zeile
case 15: SetFontBit(2,1); break; // SI = komprimierte Zeichen
case 25: ReadChar(); break; // EM = Einzelblatteinzug einschalten (EM 4) / ausschalten (EM 0)
case ' ': espace=ReadChar(); break; // SP = Extra-Platz nach jedem Zeichen
case '!': Print(); c=ReadChar(); font=font&2|c&~2; lq=lq&~2|c&2; break; // Schriftart (Bit 1 = proportional: umsetzen!)
case '#': msb&=~129; break; // MSB-Steuerung AUS
case '$': ReadChar(b,2); SetPosX(margin.left+scale(SH(b),36)); break; // horizontale Druckposition setzen
case '%': Print(); /*TODO*/ break; // Benutzerdefinierter Zeichensatz
case '&': /*TODO*/ break; // Benutzerdefinierte Zeichen
case '(': return HandleEscK(); // Verschiedenes
case '*': /*GRAFIK*/ break; // Bitmap-Grafikdruck
case '+': ly=ReadChar()*6; break; // Zeilenabstand n/360"
case '-': SetFontBit(7,255); break; // Unterstreichung ein/aus
case '.': /*GRAFIK*/ break;
case '/': c=ReadChar(); if (c<8) vtc=c; break; // Vertikal-Tabulatoren-Kanal auswählen
case '0': ly=270; break; // Zeilenabstand 1/8"
case '1': ly=210; break; // Zeilenabstand 7/72"
case '2': ly=360; break; // Zeilenabstand 1/6"
case '3': ly=ReadChar()*12; break; // Zeilenabstand n/180" (9-Nadler: n/216", ergibt Faktor 10)
case '4': SetFontBit(6,1); break; // Kursiv EIN
case '5': SetFontBit(6,0); break; // Kursiv AUS
case '6': msb&=~2; break; // Steuerzeichen mit Bit 7 drucken
case '7': msb|=2; break; // Steuerzeichen mit Bit 7 interpretieren (also Bit 7 löschen)
case '8': break; // Papierendesensor ausschalten
case '9': break; // Papierendesensor einschalten
case ':': /*TODO*/ break; // ROM in RAM kopieren
case '<': break; // Unidirektionaler Druck der aktuellen Zeile
case '=': msb|=1; msb&=~128; break; // Bit 7 der Daten löschen
case '>': msb|=129; break; // Bit 7 der Daten setzen
case '?': /*GRAFIK*/ break;
case '@': msb=0; break; // Drucker initialisieren
case 'A': ly=ReadChar()*36; break; // Zeilenabstand n/60" (9-Nadler: n/72", ergibt Faktor 30)
case 'B': SetTabs(vfu[vtc],16,ly); break; // Vertikale Tabs löschen/setzen
case 'C': c=ReadChar(); paper.cy=c?c*ly:ReadChar()*2160; break; // Seitenlänge setzen (in Zeilen oder Zoll)
case 'D': SetTabs(ht,32,GetCx()); break; // Horizontale Tabs setzen/löschen
case 'E': SetFontBit(3,1); break; // Fett EIN
case 'F': SetFontBit(3,0); break; // Fett AUS
case 'G': SetFontBit(4,1); break; // Durchgestrichen EIN
case 'H': SetFontBit(4,0); break; // Durchgestrichen AUS
case 'I': if (ReadChar()&1) msb=msb&~24|32; else msb=msb&~32|24; break; // Steuerkodes drucken EIN/AUS
case 'J': SetPosY(pos.y+ReadChar()*10); break; // Vertikal relativ positionieren
case 'K': /*GRAFIK*/ break;
case 'L': /*GRAFIK*/ break;
case 'M': SetFontBit(0,1); break; // Elite-Schrift
case 'N': SetFormSkip(ReadChar()*ly); break; // Perforationssprung in Zeilen (hier: mittelt oberen und unteren Rand)
case 'O': SetFormSkip(0); break; // Perforationssprung AUS
case 'P': SetFontBit(0,0); break; // Pica-Schrift (10 cpi)
case 'Q': i=ReadChar()*GetCx(); if (margin.left<i && i<paper.cx) margin.right=i; break;
case 'R': Print(); charset=ReadChar(); break; // Sprachspezifische Zeichenersetzung
case 'S': Print(); ss=(ReadChar()&1)+1; break; // Hoch- oder Tiefstellung EIN
case 'T': Print(); ss=0; break; // Hoch- oder Tiefstellung AUS
case 'U': ReadChar(); break; // Unidirektional ein/aus
case 'W': SetFontBit(5,255); break; // doppelte Breite ein/aus
case 'X': /*TODO*/ break;
case 'Y': /*GRAFIK*/ break;
case 'Z': /*GRAFIK*/ break;
case '\\': ReadChar(b,2); SetPosX(pos.x+scale(SH(b),lq&3?12:18)); break; // Relative horizontale Druckposition, NLQ: 1/180", sonst 1/120"
case '^': /*GRAFIK*/ break;
case 'a': ReadChar(); break; // Ausrichtung
case 'b': c=ReadChar(); if (c<8) SetTabs(vfu[c],16,ly); break; // Tabulatoren eines VTAB-Kanals setzen
case 'c': break; // horizontal motion index ??
case 'e': HandleESCe(); break; // Tabs mit konstantem Abstand setzen
case 'f': HandleESCf(); break; // Horizontaler/vertikaler Sprung
case 'g': lq=lq&~4|AB(ReadChar())<<2&4; break; // 15 cpi
case 'i': ReadChar(); break; // Sofortausdruck (Schreibmaschinen-Ersatz)
case 'j': SetPosY(pos.y-ReadChar()*10); break; // Walze zurückdrehen
case 'k': /*TODO*/ ReadChar(); break; // Typeface ??
case 'l': Print(); i=ReadChar()*GetCx(); if ((unsigned)i<(unsigned)margin.right) margin.left=i; break;
case 'm': c=AB(ReadChar()); if (!c) msb&=~32; else if (c==4) msb|=32; else return false; break; // noch eine MSB-Steuerung
case 'p': Print(); lq=lq&~2|AB(ReadChar())<<1&2; break; // Proportionalschrift
case 'q': /*TODO*/ ReadChar(); break; // Zeichenstil
case 'r': color=ReadChar(); break; // Farbe (Farbband-Auswahl)
case 's': ReadChar(); break; // Halbe Geschwindigkeit ein/aus (ignorieren:-)
case 't': c=AB(ReadChar()); if (c>=4) return false; Print(); cpi=c; break; // italic/Epson ??
case 'w': SetFontBit(1,255); break; // Ausdruck in doppelter Höhe
case 'x': Print(); lq=lq&~1|AB(ReadChar())&1; break; // Answahl (N)LQ oder Draft
default: return false;
}
return true;
}
void ESCP::Save(DEFAULTS*def) const{
memcpy(def,&font,8);
def->skip=short(paper.cy-margin.bottom+margin.top);
def->ly=short(ly);
def->paper.x=short(paper.cx);
def->paper.y=short(paper.cy);
}
void ESCP::Default(DEFAULTS*def) {
static const DEFAULTS d={
0,0,0,0,0,0,0,4, // PICA, US-englisch, Autofeed
2160, // 1/2" oberer und unterer Rand
360, // Zeilenabstand 1/6 Zoll
{8*2160,12*2160} // Papiergröße 8 x 12 Zoll
};
*def=d;
TCHAR s[8];
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ICOUNTRY,s,elemof(s)); // ermittelt Telefonvorwahl
static const char someCountries[]={33,49,44,45,46,39,34,81,47,0}; // fr,de,uk,dk,se,it,es,jp,no
const char *p=strchr(someCountries,StrToInt(s));
if (p) def->charset=(BYTE)(p-someCountries+1); // diese Vorgabe ist systemspezifisch
}
void ESCP::Init(DEFAULTS*def) {
ZeroMemory(this,sizeof(*this));
paper.cx=margin.right=def->paper.x;
paper.cy=def->paper.y;
SetFormSkip(def->skip);
ly=def->ly;
cp[1]=437;
cp[2]=1;
cp[3]=437;
memcpy(&font,def,8);
SetEqualTabs(ht,32,1728); // Standard-Tabstopps aller 8/10 Zoll
}
bool ESCP::Filter() {
if (!setjmp(env)) for (;;) {
BYTE c=ReadChar();
switch (c) {
case 7: Beep(440,100); break; // BEL = Glocke
case 8: SetPosX(pos.x-GetCx()); break; // BS = Rückschritt
case 9: SetPosX(FindTab(ht,32,pos.x)); break; // HT = Horizontaler Tabulator
case 10: LineFeed(); if (crlf&1) pos.x=0; break; // LF = Zeilenvorschub (manchmal mit Wagenrücklauf!)
case 11: SetPosY(FindTab(vfu[vtc],16,pos.y)); break; // VT = Vertikaler Tabulator
case 12: FormFeed(); break; // FF = Formularvorschub (neue Seite)
case 13: if (crlf&2) LineFeed(); else Print(); pos.x=0; break; // CR = Wagenrücklauf
case 14: Print(); lq|=32; break; // SO = Doppelte Breite für eine Zeile
case 15: SetFontBit(2,1); break; // SI = Komprimiert (17 cpi)
case 17: break; // DC1 = Drucker auswählen
case 18: SetFontBit(2,0); break; // DC2 = Komprimiert (17 cpi) EIN
case 19: break; // DC3 = Drucker nicht auswählen
case 20: Print(); lq&=~32; break; // DC4 = Doppelte Breite in aktueller Zeile AUS
case 24: ci=0; pos.x=0; break; // CAN = Zeichenpuffer löschen
case 27: msb|=4; HandleEsc(); msb&=~4; break;// ESC = Präfix für komplexe Steuerkodes
case 127: if (ci) ci--; break; // DEL = 1 Zeichen in Zeichenpuffer löschen
default: if (!(msb&8) && c<' ') break; // 0..31 verwerfen
if (msb&16 && 128<=c && c<160) break; // 128..159 verwerfen
Printable(c); // druckbares Zeichen ausgeben
}
}else{
Print();
if (pagectl&1) {
EndPage(dc);
pagectl&=~1;
}
}
return true;
}
Detected encoding: UTF-8 | 0
|