mirror of
https://github.com/clockworkpi/DevTerm.git
synced 2025-12-12 10:18:49 +01:00
add devterm_thermal_printer_cups
This commit is contained in:
parent
09be831555
commit
e12dac0af2
20
Code/devterm_thermal_printer_cups/Makefile
Normal file
20
Code/devterm_thermal_printer_cups/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
CC = gcc
|
||||||
|
BIN = rastertocpi
|
||||||
|
|
||||||
|
all:
|
||||||
|
${CC} rastertocpi.c -o ${BIN} -I /usr/include `cups-config --cflags` `cups-config --image --libs`
|
||||||
|
|
||||||
|
debug:
|
||||||
|
${CC} rastertocpi.c -o ${BIN} -DSAFEDEBUG -I /usr/include `cups-config --cflags` `cups-config --image --libs`
|
||||||
|
|
||||||
|
#sudo make -B install
|
||||||
|
install:
|
||||||
|
/etc/init.d/cups stop
|
||||||
|
cp rastertocpi /usr/lib/cups/filter/
|
||||||
|
mkdir -p /usr/share/cups/model/clockworkpi
|
||||||
|
cp cpi58.ppd /usr/share/cups/model/clockworkpi/
|
||||||
|
cd /usr/lib/cups/filter
|
||||||
|
chmod 755 rastertocpi
|
||||||
|
chown root:root rastertocpi
|
||||||
|
cd -
|
||||||
|
/etc/init.d/cups start
|
||||||
14
Code/devterm_thermal_printer_cups/README.md
Normal file
14
Code/devterm_thermal_printer_cups/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
STM32duino / wiringPi gpio thermal printer filter for cups
|
||||||
|
|
||||||
|
Clockworkpi CPI-58 (grayscale)
|
||||||
|
|
||||||
|
serial:/dev/ttyACM0?baud=115200+bits=8+parity=none+flow=none
|
||||||
|
|
||||||
|
serial:/tmp/DEVTERM\_PRINTER\_IN?baud=115200+bits=8+parity=none+flow=none
|
||||||
|
|
||||||
|
## build
|
||||||
|
sudo apt -y install libcups2-dev
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
|
||||||
95
Code/devterm_thermal_printer_cups/cpi.drv
Normal file
95
Code/devterm_thermal_printer_cups/cpi.drv
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// CUPS PPD Compiler CUPS v2.1.3
|
||||||
|
// (Don't edit .ppd directly, edit this file instead, then use
|
||||||
|
// ppdc cpi.drv to generate cpi58.ppd and others)
|
||||||
|
|
||||||
|
// common for all thermal printers
|
||||||
|
Version "1.2"
|
||||||
|
DriverType custom
|
||||||
|
ManualCopies Yes
|
||||||
|
Throughput 1
|
||||||
|
Attribute "PSVersion" "" "(3010.000) 550"
|
||||||
|
Attribute "LanguageLevel" "" "3"
|
||||||
|
Attribute "DefaultColorSpace" "" "Gray"
|
||||||
|
Attribute "FileSystem" "" "False"
|
||||||
|
Attribute "LandscapeOrientation" "" "Plus90"
|
||||||
|
Attribute "TTRasterizer" "" "Type42"
|
||||||
|
Attribute "VariablePaperSize" "" "True"
|
||||||
|
Filter "application/vnd.cups-raster 100 rastertocpi"
|
||||||
|
ColorDevice False
|
||||||
|
VariablePaperSize Yes
|
||||||
|
|
||||||
|
// cutter
|
||||||
|
Option "CutMedia/Cut Media" PickOne AnySetup 10
|
||||||
|
*Choice "None/No cutting" "<</CutMedia 0>>setpagedevice"
|
||||||
|
Choice "EndOfPage/Cut at every page" "<</CutMedia 4>>setpagedevice"
|
||||||
|
Choice "EndOfJob/Cut at every job" "<</CutMedia 2>>setpagedevice"
|
||||||
|
|
||||||
|
Option "Resolution/Resolution" PickOne AnySetup 10
|
||||||
|
*Choice "203x203dpi/203 DPI Grayscale" "<</HWResolution[203 203]/cupsColorSpace 3>>setpagedevice"
|
||||||
|
|
||||||
|
HWMargins 14 0 14 0
|
||||||
|
|
||||||
|
// feeding, spacing
|
||||||
|
Group "BlankGroup/Blank Options"
|
||||||
|
Option "FeedDist/Feed distance" PickOne AnySetup 10
|
||||||
|
Choice "0feed3mm/3mm" "<</AdvanceDistance 24>>setpagedevice"
|
||||||
|
Choice "1feed6mm/6mm" "<</AdvanceDistance 48>>setpagedevice"
|
||||||
|
*Choice "2feed9mm/9mm" "<</AdvanceDistance 72>>setpagedevice"
|
||||||
|
Choice "3feed12mm/12mm" "<</AdvanceDistance 96>>setpagedevice"
|
||||||
|
Choice "4feed15mm/15mm" "<</AdvanceDistance 120>>setpagedevice"
|
||||||
|
Choice "5feed18mm/18mm" "<</AdvanceDistance 144>>setpagedevice"
|
||||||
|
Choice "6feed21mm/21mm" "<</AdvanceDistance 168>>setpagedevice"
|
||||||
|
Choice "7feed24mm/24mm" "<</AdvanceDistance 192>>setpagedevice"
|
||||||
|
Choice "8feed27mm/27mm" "<</AdvanceDistance 216>>setpagedevice"
|
||||||
|
Choice "9feed30mm/30mm" "<</AdvanceDistance 240>>setpagedevice"
|
||||||
|
Choice "10feed33mm/33mm" "<</AdvanceDistance 264>>setpagedevice"
|
||||||
|
Choice "11feed36mm/36mm" "<</AdvanceDistance 288>>setpagedevice"
|
||||||
|
Choice "12feed39mm/39mm" "<</AdvanceDistance 312>>setpagedevice"
|
||||||
|
Choice "13feed42mm/42mm" "<</AdvanceDistance 336>>setpagedevice"
|
||||||
|
Choice "14feed45mm/45mm" "<</AdvanceDistance 360>>setpagedevice"
|
||||||
|
|
||||||
|
Option "FeedWhere/When to feed" PickOne AnySetup 10
|
||||||
|
Choice "None/Never" ""
|
||||||
|
Choice "AfterPage/After each page" "<</AdvanceMedia 4>>setpagedevice"
|
||||||
|
*Choice "AfterJob/After whole printing" "<</AdvanceMedia 2>>setpagedevice"
|
||||||
|
|
||||||
|
Option "BlankSpace/Blank space at page's end" Boolean AnySetup 10
|
||||||
|
Choice "True/Print" "<</cupsInteger6 1>>setpagedevice"
|
||||||
|
*Choice "False/None" "<</cupsInteger6 0>>setpagedevice"
|
||||||
|
|
||||||
|
Installable "OptionCutter/Cutter"
|
||||||
|
UIConstraints "*CutMedia *OptionCutter False"
|
||||||
|
|
||||||
|
// Attribute cupsUIConstraints CutterOff "*OptionCutter False *CutMedia"
|
||||||
|
// Attribute cupsUIResolver CutterOff "*CutMedia None"
|
||||||
|
|
||||||
|
// 58mm printers
|
||||||
|
{
|
||||||
|
#define POINTS 164
|
||||||
|
#define PIXELS 384
|
||||||
|
|
||||||
|
// model num is used by filter to determine raster width. 384 for 48mm, 560 for 70mm
|
||||||
|
ModelNumber $PIXELS
|
||||||
|
MinSize $POINTS 56
|
||||||
|
MaxSize $POINTS 9286
|
||||||
|
|
||||||
|
*CustomMedia "X48MMY65MM/58mm x 65mm" $POINTS 182 14 0 14 0 "<</PageSize[$POINTS 182]>>setpagedevice" "<</PageSize[$POINTS 182]>>setpagedevice"
|
||||||
|
CustomMedia "X48MMY105MM/58mm x 105mm" $POINTS 298 14 0 14 0 "<</PageSize[$POINTS 298]>>setpagedevice" "<</PageSize[$POINTS 298]>>setpagedevice"
|
||||||
|
CustomMedia "X48MMY210MM/58mm x 210mm" $POINTS 595 14 0 14 0 "<</PageSize[$POINTS 595]>>setpagedevice" "<</PageSize[$POINTS 595]>>setpagedevice"
|
||||||
|
CustomMedia "X48MMY297MM/58mm x 297mm" $POINTS 842 14 0 14 0 "<</PageSize[$POINTS 842]>>setpagedevice" "<</PageSize[$POINTS 842]>>setpagedevice"
|
||||||
|
CustomMedia "X48MMY3276MM/58mm x 3276mm" $POINTS 9286 14 0 14 0 "<</PageSize[$POINTS 9286]>>setpagedevice" "<</PageSize[$POINTS 9286]>>setpagedevice"
|
||||||
|
|
||||||
|
// Clockworkpi CPI-58
|
||||||
|
{
|
||||||
|
Manufacturer "Clockworkpi"
|
||||||
|
ModelName "CPI-58"
|
||||||
|
PCFileName "cpi58.ppd"
|
||||||
|
Attribute "NickName" "" "Clockworkpi CPI-58"
|
||||||
|
Attribute "ShortNickName" "" "CPI-58"
|
||||||
|
Attribute "Product" "" "(cpi-58)"
|
||||||
|
Attribute "1284DeviceID" "" "MFG:Clockworkpi;CMD:Clockworkpi;MDL:CPI-58;CLS:PRINTER;"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
126
Code/devterm_thermal_printer_cups/cpi58.ppd
Normal file
126
Code/devterm_thermal_printer_cups/cpi58.ppd
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
*PPD-Adobe: "4.3"
|
||||||
|
*%%%% PPD file for CPI-58 with CUPS.
|
||||||
|
*%%%% Created by the CUPS PPD Compiler CUPS v2.2.12.
|
||||||
|
*FormatVersion: "4.3"
|
||||||
|
*FileVersion: "1.2"
|
||||||
|
*LanguageVersion: English
|
||||||
|
*LanguageEncoding: ISOLatin1
|
||||||
|
*PCFileName: "cpi58.ppd"
|
||||||
|
*Product: "(cpi-58)"
|
||||||
|
*Manufacturer: "Clockworkpi"
|
||||||
|
*ModelName: "Clockworkpi CPI-58"
|
||||||
|
*ShortNickName: "CPI-58"
|
||||||
|
*NickName: "Clockworkpi CPI-58"
|
||||||
|
*PSVersion: "(3010.000) 550"
|
||||||
|
*LanguageLevel: "3"
|
||||||
|
*ColorDevice: False
|
||||||
|
*DefaultColorSpace: Gray
|
||||||
|
*FileSystem: False
|
||||||
|
*Throughput: "1"
|
||||||
|
*LandscapeOrientation: Plus90
|
||||||
|
*TTRasterizer: Type42
|
||||||
|
*% Driver-defined attributes...
|
||||||
|
*VariablePaperSize: True
|
||||||
|
*1284DeviceID: "MFG:Clockworkpi;CMD:Clockworkpi;MDL:CPI-58;CLS:PRINTER;"
|
||||||
|
*cupsVersion: 2.2
|
||||||
|
*cupsModelNumber: 384
|
||||||
|
*cupsManualCopies: True
|
||||||
|
*cupsFilter: "application/vnd.cups-raster 100 rastertocpi"
|
||||||
|
*cupsLanguages: "en"
|
||||||
|
*UIConstraints: *CutMedia *OptionCutter False
|
||||||
|
*UIConstraints: *OptionCutter False *CutMedia
|
||||||
|
*OpenUI *PageSize/Media Size: PickOne
|
||||||
|
*OrderDependency: 10 AnySetup *PageSize
|
||||||
|
*DefaultPageSize: X48MMY65MM
|
||||||
|
*PageSize X48MMY65MM/58mm x 65mm: "<</PageSize[164 182]>>setpagedevice"
|
||||||
|
*PageSize X48MMY105MM/58mm x 105mm: "<</PageSize[164 298]>>setpagedevice"
|
||||||
|
*PageSize X48MMY210MM/58mm x 210mm: "<</PageSize[164 595]>>setpagedevice"
|
||||||
|
*PageSize X48MMY297MM/58mm x 297mm: "<</PageSize[164 842]>>setpagedevice"
|
||||||
|
*PageSize X48MMY3276MM/58mm x 3276mm: "<</PageSize[164 9286]>>setpagedevice"
|
||||||
|
*CloseUI: *PageSize
|
||||||
|
*OpenUI *PageRegion/Media Size: PickOne
|
||||||
|
*OrderDependency: 10 AnySetup *PageRegion
|
||||||
|
*DefaultPageRegion: X48MMY65MM
|
||||||
|
*PageRegion X48MMY65MM/58mm x 65mm: "<</PageSize[164 182]>>setpagedevice"
|
||||||
|
*PageRegion X48MMY105MM/58mm x 105mm: "<</PageSize[164 298]>>setpagedevice"
|
||||||
|
*PageRegion X48MMY210MM/58mm x 210mm: "<</PageSize[164 595]>>setpagedevice"
|
||||||
|
*PageRegion X48MMY297MM/58mm x 297mm: "<</PageSize[164 842]>>setpagedevice"
|
||||||
|
*PageRegion X48MMY3276MM/58mm x 3276mm: "<</PageSize[164 9286]>>setpagedevice"
|
||||||
|
*CloseUI: *PageRegion
|
||||||
|
*DefaultImageableArea: X48MMY65MM
|
||||||
|
*ImageableArea X48MMY65MM/58mm x 65mm: "14 0 150 182"
|
||||||
|
*ImageableArea X48MMY105MM/58mm x 105mm: "14 0 150 298"
|
||||||
|
*ImageableArea X48MMY210MM/58mm x 210mm: "14 0 150 595"
|
||||||
|
*ImageableArea X48MMY297MM/58mm x 297mm: "14 0 150 842"
|
||||||
|
*ImageableArea X48MMY3276MM/58mm x 3276mm: "14 0 150 9286"
|
||||||
|
*DefaultPaperDimension: X48MMY65MM
|
||||||
|
*PaperDimension X48MMY65MM/58mm x 65mm: "164 182"
|
||||||
|
*PaperDimension X48MMY105MM/58mm x 105mm: "164 298"
|
||||||
|
*PaperDimension X48MMY210MM/58mm x 210mm: "164 595"
|
||||||
|
*PaperDimension X48MMY297MM/58mm x 297mm: "164 842"
|
||||||
|
*PaperDimension X48MMY3276MM/58mm x 3276mm: "164 9286"
|
||||||
|
*MaxMediaWidth: "164"
|
||||||
|
*MaxMediaHeight: "9286"
|
||||||
|
*HWMargins: 14 0 14 0
|
||||||
|
*CustomPageSize True: "pop pop pop <</PageSize[5 -2 roll]/ImagingBBox null>>setpagedevice"
|
||||||
|
*ParamCustomPageSize Width: 1 points 164 164
|
||||||
|
*ParamCustomPageSize Height: 2 points 56 9286
|
||||||
|
*ParamCustomPageSize WidthOffset: 3 points 0 0
|
||||||
|
*ParamCustomPageSize HeightOffset: 4 points 0 0
|
||||||
|
*ParamCustomPageSize Orientation: 5 int 0 0
|
||||||
|
*OpenUI *CutMedia/Cut Media: PickOne
|
||||||
|
*OrderDependency: 10 AnySetup *CutMedia
|
||||||
|
*DefaultCutMedia: None
|
||||||
|
*CutMedia None/No cutting: "<</CutMedia 0>>setpagedevice"
|
||||||
|
*CutMedia EndOfPage/Cut at every page: "<</CutMedia 4>>setpagedevice"
|
||||||
|
*CutMedia EndOfJob/Cut at every job: "<</CutMedia 2>>setpagedevice"
|
||||||
|
*CloseUI: *CutMedia
|
||||||
|
*OpenUI *Resolution/Resolution: PickOne
|
||||||
|
*OrderDependency: 10 AnySetup *Resolution
|
||||||
|
*DefaultResolution: 203x203dpi
|
||||||
|
*Resolution 203x203dpi/203 DPI Grayscale: "<</HWResolution[203 203]/cupsColorSpace 3>>setpagedevice"
|
||||||
|
*CloseUI: *Resolution
|
||||||
|
*OpenGroup: InstallableOptions/Installable Options
|
||||||
|
*OpenUI *OptionCutter/Cutter: Boolean
|
||||||
|
*OrderDependency: 10 AnySetup *OptionCutter
|
||||||
|
*DefaultOptionCutter: False
|
||||||
|
*OptionCutter False/Not Installed: ""
|
||||||
|
*OptionCutter True/Installed: ""
|
||||||
|
*CloseUI: *OptionCutter
|
||||||
|
*CloseGroup: InstallableOptions
|
||||||
|
*OpenGroup: BlankGroup/Blank Options
|
||||||
|
*OpenUI *FeedDist/Feed distance: PickOne
|
||||||
|
*OrderDependency: 10 AnySetup *FeedDist
|
||||||
|
*DefaultFeedDist: 2feed9mm
|
||||||
|
*FeedDist 0feed3mm/3mm: "<</AdvanceDistance 24>>setpagedevice"
|
||||||
|
*FeedDist 1feed6mm/6mm: "<</AdvanceDistance 48>>setpagedevice"
|
||||||
|
*FeedDist 2feed9mm/9mm: "<</AdvanceDistance 72>>setpagedevice"
|
||||||
|
*FeedDist 3feed12mm/12mm: "<</AdvanceDistance 96>>setpagedevice"
|
||||||
|
*FeedDist 4feed15mm/15mm: "<</AdvanceDistance 120>>setpagedevice"
|
||||||
|
*FeedDist 5feed18mm/18mm: "<</AdvanceDistance 144>>setpagedevice"
|
||||||
|
*FeedDist 6feed21mm/21mm: "<</AdvanceDistance 168>>setpagedevice"
|
||||||
|
*FeedDist 7feed24mm/24mm: "<</AdvanceDistance 192>>setpagedevice"
|
||||||
|
*FeedDist 8feed27mm/27mm: "<</AdvanceDistance 216>>setpagedevice"
|
||||||
|
*FeedDist 9feed30mm/30mm: "<</AdvanceDistance 240>>setpagedevice"
|
||||||
|
*FeedDist 10feed33mm/33mm: "<</AdvanceDistance 264>>setpagedevice"
|
||||||
|
*FeedDist 11feed36mm/36mm: "<</AdvanceDistance 288>>setpagedevice"
|
||||||
|
*FeedDist 12feed39mm/39mm: "<</AdvanceDistance 312>>setpagedevice"
|
||||||
|
*FeedDist 13feed42mm/42mm: "<</AdvanceDistance 336>>setpagedevice"
|
||||||
|
*FeedDist 14feed45mm/45mm: "<</AdvanceDistance 360>>setpagedevice"
|
||||||
|
*CloseUI: *FeedDist
|
||||||
|
*OpenUI *FeedWhere/When to feed: PickOne
|
||||||
|
*OrderDependency: 10 AnySetup *FeedWhere
|
||||||
|
*DefaultFeedWhere: AfterJob
|
||||||
|
*FeedWhere None/Never: ""
|
||||||
|
*FeedWhere AfterPage/After each page: "<</AdvanceMedia 4>>setpagedevice"
|
||||||
|
*FeedWhere AfterJob/After whole printing: "<</AdvanceMedia 2>>setpagedevice"
|
||||||
|
*CloseUI: *FeedWhere
|
||||||
|
*OpenUI *BlankSpace/Blank space at page's end: Boolean
|
||||||
|
*OrderDependency: 10 AnySetup *BlankSpace
|
||||||
|
*DefaultBlankSpace: False
|
||||||
|
*BlankSpace True/Print: "<</cupsInteger6 1>>setpagedevice"
|
||||||
|
*BlankSpace False/None: "<</cupsInteger6 0>>setpagedevice"
|
||||||
|
*CloseUI: *BlankSpace
|
||||||
|
*CloseGroup: BlankGroup
|
||||||
|
*DefaultFont: Courier
|
||||||
|
*% End of cpi58.ppd, 05572 bytes.
|
||||||
511
Code/devterm_thermal_printer_cups/rastertocpi.c
Normal file
511
Code/devterm_thermal_printer_cups/rastertocpi.c
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
// To get required headers, run
|
||||||
|
// sudo apt-get install libcups2-dev libcupsimage2-dev
|
||||||
|
#include <cups/cups.h>
|
||||||
|
#include <cups/ppd.h>
|
||||||
|
#include <cups/raster.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifndef DEBUGFILE
|
||||||
|
#define DEBUGFILE "/tmp/debugraster.txt"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline int min(int a, int b) {
|
||||||
|
if (a > b)
|
||||||
|
return b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// settings and their stuff
|
||||||
|
struct settings_ {
|
||||||
|
int modelNum; // the only setting we get from PPD.
|
||||||
|
cups_bool_t InsertSheet;
|
||||||
|
cups_adv_t AdvanceMedia;
|
||||||
|
cups_cut_t CutMedia;
|
||||||
|
unsigned int AdvanceDistance;
|
||||||
|
};
|
||||||
|
struct settings_ settings;
|
||||||
|
|
||||||
|
static void initializeSettings(char *commandLineOptionSettings, struct settings_ *pSettings) {
|
||||||
|
ppd_file_t *pPpd = ppdOpenFile(getenv("PPD"));
|
||||||
|
// char* sDestination = getenv("DestinationPrinterID");
|
||||||
|
memset(pSettings, 0, sizeof(struct settings_));
|
||||||
|
pSettings->modelNum = pPpd->model_number;
|
||||||
|
ppdClose(pPpd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_settings_from_job (cups_page_header2_t * pHeader)
|
||||||
|
{
|
||||||
|
if (!pHeader)
|
||||||
|
return;
|
||||||
|
|
||||||
|
settings.InsertSheet = pHeader->cupsInteger[6];
|
||||||
|
settings.AdvanceMedia = pHeader->AdvanceMedia;
|
||||||
|
settings.CutMedia = pHeader->CutMedia;
|
||||||
|
settings.AdvanceDistance = pHeader->AdvanceDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DEBUGP
|
||||||
|
static inline void mputchar(char c) { putchar(c); }
|
||||||
|
#define DEBUGSTARTPRINT()
|
||||||
|
#define DEBUGFINISHPRINT()
|
||||||
|
#define DEBUGPRINT(...)
|
||||||
|
#else
|
||||||
|
FILE *lfd = 0;
|
||||||
|
// putchar with debug wrappers
|
||||||
|
static inline void mputchar(char c) {
|
||||||
|
unsigned char m = c;
|
||||||
|
if (lfd)
|
||||||
|
fprintf(lfd, "%02x ", m);
|
||||||
|
putchar(m);
|
||||||
|
}
|
||||||
|
// on macos cups filters works in a sandbox and cant write
|
||||||
|
// filtes everywhere. We'll use $TMPDIR/debugraster.txt for them
|
||||||
|
#ifdef SAFEDEBUG
|
||||||
|
static inline void DEBUGSTARTPRINT() {
|
||||||
|
char * tmpfolder = getenv("TMPDIR");
|
||||||
|
const char *filename = "/debugraster.txt";
|
||||||
|
char *dbgpath = (char *)malloc(strlen(tmpfolder) + strlen(filename) + 1);
|
||||||
|
strcpy(dbgpath,tmpfolder);
|
||||||
|
strcat(dbgpath,filename);
|
||||||
|
lfd = fopen(dbgpath,"w");
|
||||||
|
free(dbgpath);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define DEBUGSTARTPRINT() lfd = fopen(DEBUGFILE, "w")
|
||||||
|
#endif
|
||||||
|
#define DEBUGFINISHPRINT() \
|
||||||
|
if (lfd) \
|
||||||
|
fclose(lfd)
|
||||||
|
#define DEBUGPRINT(...) if (lfd) fprintf(lfd, "\n" __VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// procedure to output an array
|
||||||
|
static inline void outputarray(const char *array, int length) {
|
||||||
|
int i = 0;
|
||||||
|
for (; i < length; ++i)
|
||||||
|
mputchar(array[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// output a command. -1 is because we determine them as string literals,
|
||||||
|
// so \0 implicitly added at the end, but we don't need it at all
|
||||||
|
#define SendCommand(COMMAND) outputarray((COMMAND),sizeof((COMMAND))-1)
|
||||||
|
|
||||||
|
static inline void mputnum(unsigned int val) {
|
||||||
|
mputchar(val&0xFFU);
|
||||||
|
mputchar((val>>8)&0xFFU);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cpi-58 uses kind of epson ESC/POS dialect code. Here is subset of commands
|
||||||
|
*
|
||||||
|
* initialize - esc @
|
||||||
|
* cash drawer 1 - esc p 0 @ P
|
||||||
|
* cash drawer 2 - esc p 1 @ P // @ =0x40 and P=0x50 <N> and <M>
|
||||||
|
* where <N>*2ms is pulse on time, <M>*2ms is pulse off.
|
||||||
|
* start raster - GS v 0 // 0 is full-density, may be also 1, 2, 4
|
||||||
|
* skip lines - esc J // then N [0..255], each value 1/44 inches (0.176mm)
|
||||||
|
* // another commands out-of-spec:
|
||||||
|
* esc 'i' - cutter; xprinter android example shows as GS V \1 (1D 56 01)
|
||||||
|
* esc '8' - wait{4, cutter also (char[4]){29,'V','A',20}}; GS V 'A' 20
|
||||||
|
*/
|
||||||
|
|
||||||
|
// define printer initialize command
|
||||||
|
static const char escInit[] = "\x1b@";
|
||||||
|
|
||||||
|
// define cashDrawerEjector command
|
||||||
|
static const char escCashDrawerEject[] = "\x1bp";
|
||||||
|
|
||||||
|
// define raster mode start command
|
||||||
|
static const char escRasterMode[] = "\x1dv0\0";
|
||||||
|
|
||||||
|
// define flush command
|
||||||
|
static const char escFlush[] = "\x1bJ";
|
||||||
|
|
||||||
|
// define cut command
|
||||||
|
//static const char escCut[] = "\x1bi";
|
||||||
|
static const char escCut[] = "\x1dV\1";
|
||||||
|
|
||||||
|
// enter raster mode and set up x and y dimensions
|
||||||
|
static inline void sendRasterHeader(int xsize, int ysize) {
|
||||||
|
// outputCommand(rasterModeStartCommand);
|
||||||
|
SendCommand(escRasterMode);
|
||||||
|
|
||||||
|
mputnum(xsize);
|
||||||
|
mputnum(ysize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void flushLines(unsigned short lines)
|
||||||
|
{
|
||||||
|
SendCommand(escFlush);
|
||||||
|
mputchar (lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print all unprinted (i.e. flush the buffer)
|
||||||
|
static inline void flushBuffer() {
|
||||||
|
flushLines(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush, then feed 24 lines
|
||||||
|
static inline void flushManyLines(int iLines)
|
||||||
|
{
|
||||||
|
DEBUGPRINT("Skip %d empty lines: ", iLines);
|
||||||
|
while ( iLines )
|
||||||
|
{
|
||||||
|
int iStride = min (iLines, 24);
|
||||||
|
flushLines ( iStride );
|
||||||
|
iLines -= iStride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void cutMedia()
|
||||||
|
{
|
||||||
|
SendCommand(escCut);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent on the beginning of print job
|
||||||
|
void setupJob() {
|
||||||
|
SendCommand(escInit);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent at the very end of print job
|
||||||
|
void finalizeJob() {
|
||||||
|
|
||||||
|
// SendCommand(escInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent at the end of every page
|
||||||
|
#ifndef __sighandler_t
|
||||||
|
typedef void (*__sighandler_t)(int);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__sighandler_t old_signal;
|
||||||
|
void finishPage() {
|
||||||
|
signal(SIGTERM, old_signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent on job canceling
|
||||||
|
void cancelJob() {
|
||||||
|
int i = 0;
|
||||||
|
for (; i < 0x258; ++i)
|
||||||
|
mputchar(0);
|
||||||
|
finishPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoked before starting to print a page
|
||||||
|
void startPage() { old_signal = signal(SIGTERM, cancelJob); }
|
||||||
|
|
||||||
|
void DebugPrintHeader (cups_page_header2_t* pHeader)
|
||||||
|
{
|
||||||
|
DEBUGPRINT(
|
||||||
|
"MediaClass '%s'\n"
|
||||||
|
"MediaColor '%s'\n"
|
||||||
|
"MediaType '%s'\n"
|
||||||
|
"OutputType '%s'\n"
|
||||||
|
"AdvanceDistance %d\n"
|
||||||
|
"AdvanceMedia %d\n"
|
||||||
|
"Collate %d\n"
|
||||||
|
"CutMedia %d\n"
|
||||||
|
"Duplex %d\n"
|
||||||
|
"HWResolution %d %d\n"
|
||||||
|
"ImagingBoundingBox %d %d %d %d\n"
|
||||||
|
"InsertSheet %d\n"
|
||||||
|
"Jog %d\n"
|
||||||
|
"LeadingEdge %d\n"
|
||||||
|
"Margins %d %d\n"
|
||||||
|
"ManualFeed %d\n"
|
||||||
|
"MediaPosition %d\n"
|
||||||
|
"MediaWeight %d\n"
|
||||||
|
"MirrorPrint %d\n"
|
||||||
|
"NegativePrint %d\n"
|
||||||
|
"NumCopies %d\n"
|
||||||
|
"Orientation %d\n"
|
||||||
|
"OutputFaceUp %d\n"
|
||||||
|
"PageSize %d %d\n"
|
||||||
|
"Separations %d\n"
|
||||||
|
"TraySwitch %d\n"
|
||||||
|
"Tumble %d\n"
|
||||||
|
"cupsWidth %d\n"
|
||||||
|
"cupsHeight %d\n"
|
||||||
|
"cupsMediaType %d\n"
|
||||||
|
"cupsBitsPerColor %d\n"
|
||||||
|
"cupsBitsPerPixel %d\n"
|
||||||
|
"cupsBytesPerLine %d\n"
|
||||||
|
"cupsColorOrder %d\n"
|
||||||
|
"cupsColorSpace %d\n"
|
||||||
|
"cupsCompression %d\n"
|
||||||
|
"cupsRowCount %d\n"
|
||||||
|
"cupsRowFeed %d\n"
|
||||||
|
"cupsRowStep %d\n"
|
||||||
|
"cupsNumColors %d\n"
|
||||||
|
"cupsBorderlessScalingFactor %f\n"
|
||||||
|
"cupsPageSize %f %f\n"
|
||||||
|
"cupsImagingBBox %f %f %f %f\n"
|
||||||
|
"cupsInteger %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n"
|
||||||
|
"cupsReal %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n"
|
||||||
|
"cupsString '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s'\n"
|
||||||
|
"cupsMarkerType '%s'\n"
|
||||||
|
"cupsRenderingIntent '%s'\n"
|
||||||
|
"cupsPageSizeName '%s'\n",
|
||||||
|
pHeader->MediaClass, pHeader->MediaColor, pHeader->MediaType,
|
||||||
|
pHeader->OutputType, pHeader->AdvanceDistance, pHeader->AdvanceMedia,
|
||||||
|
pHeader->Collate, pHeader->CutMedia, pHeader->Duplex,
|
||||||
|
pHeader->HWResolution[0], pHeader->HWResolution[1],
|
||||||
|
pHeader->ImagingBoundingBox[0], pHeader->ImagingBoundingBox[1],
|
||||||
|
pHeader->ImagingBoundingBox[2], pHeader->ImagingBoundingBox[3],
|
||||||
|
pHeader->InsertSheet, pHeader->Jog, pHeader->LeadingEdge, pHeader->Margins[0],
|
||||||
|
pHeader->Margins[1], pHeader->ManualFeed, pHeader->MediaPosition,
|
||||||
|
pHeader->MediaWeight, pHeader->MirrorPrint, pHeader->NegativePrint,
|
||||||
|
pHeader->NumCopies, pHeader->Orientation, pHeader->OutputFaceUp,
|
||||||
|
pHeader->PageSize[0], pHeader->PageSize[1], pHeader->Separations,
|
||||||
|
pHeader->TraySwitch, pHeader->Tumble, pHeader->cupsWidth, pHeader->cupsHeight,
|
||||||
|
pHeader->cupsMediaType, pHeader->cupsBitsPerColor, pHeader->cupsBitsPerPixel,
|
||||||
|
pHeader->cupsBytesPerLine, pHeader->cupsColorOrder, pHeader->cupsColorSpace,
|
||||||
|
pHeader->cupsCompression, pHeader->cupsRowCount, pHeader->cupsRowFeed,
|
||||||
|
pHeader->cupsRowStep, pHeader->cupsNumColors,
|
||||||
|
pHeader->cupsBorderlessScalingFactor, pHeader->cupsPageSize[0],
|
||||||
|
pHeader->cupsPageSize[1], pHeader->cupsImagingBBox[0],
|
||||||
|
pHeader->cupsImagingBBox[1], pHeader->cupsImagingBBox[2],
|
||||||
|
pHeader->cupsImagingBBox[3], pHeader->cupsInteger[0],
|
||||||
|
pHeader->cupsInteger[1], pHeader->cupsInteger[2], pHeader->cupsInteger[3],
|
||||||
|
pHeader->cupsInteger[4], pHeader->cupsInteger[5], pHeader->cupsInteger[6],
|
||||||
|
pHeader->cupsInteger[7], pHeader->cupsInteger[8], pHeader->cupsInteger[9],
|
||||||
|
pHeader->cupsInteger[10], pHeader->cupsInteger[11], pHeader->cupsInteger[12],
|
||||||
|
pHeader->cupsInteger[13], pHeader->cupsInteger[14], pHeader->cupsInteger[15],
|
||||||
|
pHeader->cupsReal[0], pHeader->cupsReal[1], pHeader->cupsReal[2],
|
||||||
|
pHeader->cupsReal[3], pHeader->cupsReal[4], pHeader->cupsReal[5],
|
||||||
|
pHeader->cupsReal[6], pHeader->cupsReal[7], pHeader->cupsReal[8],
|
||||||
|
pHeader->cupsReal[9], pHeader->cupsReal[10], pHeader->cupsReal[11],
|
||||||
|
pHeader->cupsReal[12], pHeader->cupsReal[13], pHeader->cupsReal[14],
|
||||||
|
pHeader->cupsReal[15], pHeader->cupsString[0], pHeader->cupsString[1],
|
||||||
|
pHeader->cupsString[2], pHeader->cupsString[3], pHeader->cupsString[4],
|
||||||
|
pHeader->cupsString[5], pHeader->cupsString[6], pHeader->cupsString[7],
|
||||||
|
pHeader->cupsString[8], pHeader->cupsString[9], pHeader->cupsString[10],
|
||||||
|
pHeader->cupsString[11], pHeader->cupsString[12], pHeader->cupsString[13],
|
||||||
|
pHeader->cupsString[14], pHeader->cupsString[15], pHeader->cupsMarkerType,
|
||||||
|
pHeader->cupsRenderingIntent, pHeader->cupsPageSizeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rearrange (compress) rows in pBuf, discarding tails of them
|
||||||
|
static inline unsigned compress_buffer(unsigned char *pBuf, unsigned iSize,
|
||||||
|
unsigned int iWideStride, unsigned int iStride) {
|
||||||
|
const unsigned char *pEnd = pBuf + iSize;
|
||||||
|
unsigned char *pTarget = pBuf;
|
||||||
|
while (pBuf < pEnd) {
|
||||||
|
int iBytes = min(pEnd - pBuf, iStride);
|
||||||
|
memmove(pTarget, pBuf, iBytes);
|
||||||
|
pTarget += iBytes;
|
||||||
|
pBuf += iWideStride;
|
||||||
|
}
|
||||||
|
return min(iSize, pTarget - pBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns -1 if whole line iz filled by zeros. Otherwise 0.
|
||||||
|
static inline int line_is_empty(const unsigned char *pBuf, unsigned iSize) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < iSize; ++i)
|
||||||
|
if (pBuf[i])
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void send_raster(const unsigned char *pBuf, int width8,
|
||||||
|
int height) {
|
||||||
|
if (!height)
|
||||||
|
return;
|
||||||
|
DEBUGPRINT("Raster block %dx%d pixels\n", width8 * 8, height);
|
||||||
|
|
||||||
|
sendRasterHeader(width8, height);
|
||||||
|
|
||||||
|
outputarray((char *)pBuf, width8 * height);
|
||||||
|
flushBuffer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXITPRINT(CODE) \
|
||||||
|
{ \
|
||||||
|
if (pRasterSrc) \
|
||||||
|
cupsRasterClose(pRasterSrc); \
|
||||||
|
if (pRasterBuf) \
|
||||||
|
free(pRasterBuf); \
|
||||||
|
if (fd) \
|
||||||
|
close(fd); \
|
||||||
|
return (CODE); \
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
if (argc < 6 || argc > 7) {
|
||||||
|
fputs("ERROR: rastertocpi job-id user title copies options [file]\n",
|
||||||
|
stderr);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = STDIN_FILENO; // File descriptor providing CUPS raster data
|
||||||
|
if (argc == 7) {
|
||||||
|
if ((fd = open(argv[6], O_RDONLY)) == -1) {
|
||||||
|
perror("ERROR: Unable to open raster file - ");
|
||||||
|
sleep(1);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUGSTARTPRINT();
|
||||||
|
int iCurrentPage = 0;
|
||||||
|
// CUPS Page tHeader
|
||||||
|
cups_page_header2_t tHeader;
|
||||||
|
unsigned char *pRasterBuf = NULL; // Pointer to raster data from CUPS
|
||||||
|
// Raster stream for printing
|
||||||
|
cups_raster_t *pRasterSrc = cupsRasterOpen(fd, CUPS_RASTER_READ);
|
||||||
|
initializeSettings(argv[5],&settings);
|
||||||
|
|
||||||
|
DEBUGPRINT("ModelNumber from PPD '%d'\n", settings.modelNum);
|
||||||
|
|
||||||
|
// set max num of pixels per line depended from model number (from ppd)
|
||||||
|
int iMaxWidth = settings.modelNum;
|
||||||
|
if (!iMaxWidth)
|
||||||
|
iMaxWidth = 0x180;
|
||||||
|
|
||||||
|
// postpone start of the job until we parse first header and get necessary values from there.
|
||||||
|
int iJobStarted = 0;
|
||||||
|
|
||||||
|
// loop over the whole raster, page by page
|
||||||
|
while (cupsRasterReadHeader2(pRasterSrc, &tHeader)) {
|
||||||
|
if ((!tHeader.cupsHeight) || (!tHeader.cupsBytesPerLine))
|
||||||
|
break;
|
||||||
|
|
||||||
|
update_settings_from_job( &tHeader );
|
||||||
|
|
||||||
|
if (!iJobStarted)
|
||||||
|
{
|
||||||
|
setupJob();
|
||||||
|
iJobStarted = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugPrintHeader ( &tHeader );
|
||||||
|
|
||||||
|
if (!pRasterBuf) {
|
||||||
|
pRasterBuf = malloc(tHeader.cupsBytesPerLine * 24);
|
||||||
|
if (!pRasterBuf) // hope it never goes here...
|
||||||
|
EXITPRINT(EXIT_FAILURE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "PAGE: %d %d\n", ++iCurrentPage, tHeader.NumCopies);
|
||||||
|
|
||||||
|
startPage();
|
||||||
|
|
||||||
|
// calculate num of bytes to print given width having 1 bit per pixel.
|
||||||
|
int foo = min(tHeader.cupsWidth, iMaxWidth); // 0x180 for 58mm (48mm printable)
|
||||||
|
foo = (foo + 7) & 0xFFFFFFF8;
|
||||||
|
int width_bytes = foo >> 3; // in bytes, [0..0x30]
|
||||||
|
|
||||||
|
DEBUGPRINT("cupsWidth=%d, cupsBytesPerLine=%d; foo=%d, width_bytes=%d",
|
||||||
|
tHeader.cupsWidth, tHeader.cupsBytesPerLine, foo, width_bytes );
|
||||||
|
|
||||||
|
int iRowsToPrint = tHeader.cupsHeight;
|
||||||
|
int zeroy = 0;
|
||||||
|
|
||||||
|
// loop over one page, top to bottom by blocks of most 24 scan lines
|
||||||
|
while (iRowsToPrint) {
|
||||||
|
fprintf(stderr, "INFO: Printing iCurrentPage %d, %d%% complete...\n",
|
||||||
|
iCurrentPage,
|
||||||
|
(100 * (tHeader.cupsHeight - iRowsToPrint) / tHeader.cupsHeight));
|
||||||
|
|
||||||
|
int iBlockHeight = min(iRowsToPrint, 24);
|
||||||
|
|
||||||
|
DEBUGPRINT("--------Processing block of %d, starting from %d lines",
|
||||||
|
iBlockHeight, tHeader.cupsHeight - iRowsToPrint);
|
||||||
|
|
||||||
|
iRowsToPrint -= iBlockHeight;
|
||||||
|
unsigned iBytesChunk = 0;
|
||||||
|
|
||||||
|
// first, fetch whole block from the image
|
||||||
|
if (iBlockHeight)
|
||||||
|
iBytesChunk = cupsRasterReadPixels(
|
||||||
|
pRasterSrc, pRasterBuf, tHeader.cupsBytesPerLine * iBlockHeight);
|
||||||
|
|
||||||
|
DEBUGPRINT("--------Got %d from %d requested bytes",
|
||||||
|
iBytesChunk,
|
||||||
|
tHeader.cupsBytesPerLine *
|
||||||
|
iBlockHeight);
|
||||||
|
|
||||||
|
// if original image is wider - rearrange buffer so that our calculated
|
||||||
|
// lines come one-by-one without extra gaps
|
||||||
|
if (width_bytes < tHeader.cupsBytesPerLine) {
|
||||||
|
DEBUGPRINT("--------Compress line from %d to %d bytes", tHeader.cupsBytesPerLine, width_bytes);
|
||||||
|
iBytesChunk = compress_buffer(pRasterBuf, iBytesChunk,
|
||||||
|
tHeader.cupsBytesPerLine, width_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// runaround for sometimes truncated output of cupsRasterReadPixels
|
||||||
|
if (iBytesChunk < width_bytes * iBlockHeight) {
|
||||||
|
DEBUGPRINT("--------Restore truncated gap of %d bytes",
|
||||||
|
width_bytes * iBlockHeight - iBytesChunk);
|
||||||
|
memset(pRasterBuf + iBytesChunk, 0,
|
||||||
|
width_bytes * iBlockHeight - iBytesChunk);
|
||||||
|
iBytesChunk = width_bytes * iBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lazy output of current raster. First check current line if it is zero.
|
||||||
|
// if there were many zeroes and met non-zero - flush zeros by 'feed' cmd
|
||||||
|
// if opposite - send non-zero chunk as raster.
|
||||||
|
unsigned char *pBuf = pRasterBuf;
|
||||||
|
unsigned char *pChunk = pBuf;
|
||||||
|
const unsigned char *pEnd = pBuf + iBytesChunk;
|
||||||
|
int nonzerolines = 0;
|
||||||
|
while ( pBuf<pEnd ) {
|
||||||
|
if (line_is_empty(pBuf, width_bytes)) {
|
||||||
|
if (nonzerolines) { // met zero, need to flush collected raster
|
||||||
|
send_raster(pChunk, width_bytes, nonzerolines);
|
||||||
|
nonzerolines = 0;
|
||||||
|
}
|
||||||
|
++zeroy;
|
||||||
|
} else {
|
||||||
|
if (zeroy) { // met non-zero, need to feed calculated num of zero lines
|
||||||
|
flushManyLines(zeroy);
|
||||||
|
zeroy=0;
|
||||||
|
}
|
||||||
|
if (!nonzerolines)
|
||||||
|
pChunk = pBuf;
|
||||||
|
++nonzerolines;
|
||||||
|
}
|
||||||
|
pBuf += width_bytes;
|
||||||
|
}
|
||||||
|
send_raster(pChunk, width_bytes, nonzerolines);
|
||||||
|
//flushBuffer();
|
||||||
|
} // loop over page
|
||||||
|
|
||||||
|
// page is finished.
|
||||||
|
// m.b. we have to print empty tail at the end
|
||||||
|
if (settings.InsertSheet)
|
||||||
|
flushManyLines (zeroy);
|
||||||
|
|
||||||
|
if (settings.AdvanceMedia == CUPS_ADVANCE_PAGE)
|
||||||
|
flushManyLines(settings.AdvanceDistance);
|
||||||
|
|
||||||
|
if (settings.CutMedia == CUPS_CUT_PAGE)
|
||||||
|
cutMedia();
|
||||||
|
|
||||||
|
finishPage();
|
||||||
|
} // loop over all pages pages
|
||||||
|
|
||||||
|
if (settings.AdvanceMedia==CUPS_ADVANCE_JOB)
|
||||||
|
flushManyLines(settings.AdvanceDistance);
|
||||||
|
|
||||||
|
if (settings.CutMedia == CUPS_CUT_JOB)
|
||||||
|
cutMedia();
|
||||||
|
|
||||||
|
finalizeJob();
|
||||||
|
fputs(iCurrentPage ? "INFO: Ready to print.\n" : "ERROR: No pages found!\n",
|
||||||
|
stderr);
|
||||||
|
DEBUGFINISHPRINT();
|
||||||
|
EXITPRINT(iCurrentPage ? EXIT_SUCCESS : EXIT_FAILURE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of rastertocpi.c
|
||||||
Loading…
x
Reference in New Issue
Block a user