#!/usr/bin/python3 -u
#coding=utf8
# CUPS raster filter for Ricoh Aficio SP1000s
# this printer uses SAG-GDI raster format by Sagem Communication
# licence: GPL
# created by palz <paalzza@gmail.com>
# Feb 2011, Moscow, Russia
from struct import *
import sys
from time import time
# parse command-line parameters
if len(sys.argv)<6:
exit(1)
jobid = sys.argv[1]
username = sys.argv[2]
jobtitle = sys.argv[3]
try:
numcopies = int(sys.argv[4])
except:
numcopies = 1
options = sys.argv[5]
filename='-'
if len(sys.argv)>6:
filename=sys.argv[6]
if filename=='.' or filename=='-':
input=sys.stdin.buffer
else:
input = open(sys.argv[6],'rb')
output = sys.stdout.buffer
def print_stderr(s,newline=True):
sys.stderr.write(str(s)+('\n' if newline else ''))
sys.stderr.flush()
#interpret data from CUPS format version 3 supplied via stdin
cups = input.read() # Read all bytes from stdin
sync_word=cups[0:4] # length of sync-word is 4 bytes: "3SaR"
version=cups[0]-48
cups=cups[4:]
dtype={
'C String': {'b': 's','size': 1},
'Unsigned Integer': {'b': 'I','size': 4},
'IEEE Single Precision': {'b': 'f','size': 4},
}
header_length={ # length of page header
1: 420,
2: 1796,
3: 1796,
}
def make_unpack_format(format,begin,end,split_count=1):
return '<'+(str((end-begin+1)//dtype[format]['size']//split_count)+dtype[format]['b'])*split_count
hdr={
1: {}, # version 1 header
2: {}, # version 2-3 header
}
def unpack_header(cups,version):
# unpack v1 page header
hdr[1]['MediaClass'], = unpack(make_unpack_format('C String',0,63),cups[0:63+1]) # Media class string
hdr[1]['MediaColor'], = unpack(make_unpack_format('C String',64,127),cups[64:127+1]) # Media color string
hdr[1]['MediaType'], = unpack(make_unpack_format('C String',128,191),cups[128:191+1]) # Media type string
hdr[1]['OutputType'], = unpack(make_unpack_format('C String',192,255),cups[192:255+1]) # Output type string
hdr[1]['AdvanceDistance'], = unpack(make_unpack_format('Unsigned Integer',256,259),cups[256:259+1]) # 0 to 2<sup>32</sup> - 1 points
hdr[1]['AdvanceMedia'], = unpack(make_unpack_format('Unsigned Integer',260,263),cups[260:263+1]) # 0 = Never advance roll; 1 = Advance roll after file; 2 = Advance roll after job; 3 = Advance roll after set; 4 = Advance roll after page
hdr[1]['Collate'], = unpack(make_unpack_format('Unsigned Integer',264,267),cups[264:267+1]) # 0 = do not collate copies; 1 = collate copies
hdr[1]['CutMedia'], = unpack(make_unpack_format('Unsigned Integer',268,271),cups[268:271+1]) # 0 = Never cut media; 1 = Cut roll after file; 2 = Cut roll after job; 3 = Cut roll after set; 4 = Cut roll after page
hdr[1]['Duplex'], = unpack(make_unpack_format('Unsigned Integer',272,275),cups[272:275+1]) # 0 = Print single-sided; 1 = Print double-sided
hdr[1]['HWResolution'] = unpack(make_unpack_format('Unsigned Integer',276,283),cups[276:283+1]) # Horizontal and vertical resolution in dots-per-inch.
hdr[1]['ImagingBoundingBox'] = unpack(make_unpack_format('Unsigned Integer',284,299),cups[284:299+1]) # Four integers giving the left, bottom, right, and top positions of the page bounding box in points
hdr[1]['InsertSheet'], = unpack(make_unpack_format('Unsigned Integer',300,303),cups[300:303+1]) # 0 = Do not insert separator sheets; 1 = Insert separator sheets
hdr[1]['Jog'], = unpack(make_unpack_format('Unsigned Integer',304,307),cups[304:307+1]) # 0 = Do no jog pages; 1 = Jog pages after file; 2 = Jog pages after job; 3 = Jog pages after set
hdr[1]['LeadingEdge'], = unpack(make_unpack_format('Unsigned Integer',308,311),cups[308:311+1]) # 0 = Top edge is first; 1 = Right edge is first; 2 = Bottom edge is first; 3 = Left edge is first
hdr[1]['Margins'] = unpack(make_unpack_format('Unsigned Integer',312,319),cups[312:319+1]) # Left and bottom origin of image in points
hdr[1]['ManualFeed'], = unpack(make_unpack_format('Unsigned Integer',320,323),cups[320:323+1]) # 0 = Do not manually feed media; 1 = Manually feed media
hdr[1]['MediaPosition'], = unpack(make_unpack_format('Unsigned Integer',324,327),cups[324:327+1]) # Input slot position from 0 to N
hdr[1]['MediaWeight'], = unpack(make_unpack_format('Unsigned Integer',328,331),cups[328:331+1]) # Media weight in grams per meter squared, 0 = printer default
hdr[1]['MirrorPrint'], = unpack(make_unpack_format('Unsigned Integer',332,335),cups[332:335+1]) # 0 = Do not mirror prints; 1 = Mirror prints
hdr[1]['NegativePrint'], = unpack(make_unpack_format('Unsigned Integer',336,339),cups[336:339+1]) # 0 = Do not invert prints; 1 = Invert prints
hdr[1]['NumCopies'], = unpack(make_unpack_format('Unsigned Integer',340,343),cups[340:343+1]) # 0 to 2<sup>32</sup> - 1, 0 = printer default
hdr[1]['Orientation'], = unpack(make_unpack_format('Unsigned Integer',344,347),cups[344:347+1]) # 0 = Do not rotate page; 1 = Rotate page counter-clockwise; 2 = Turn page upside down; 3 = Rotate page clockwise
hdr[1]['OutputFaceUp'], = unpack(make_unpack_format('Unsigned Integer',348,351),cups[348:351+1]) # 0 = Output face down; 1 = Output face up
hdr[1]['PageSize'] = unpack(make_unpack_format('Unsigned Integer',352,359),cups[352:359+1]) # Width and length in points
hdr[1]['Separations'], = unpack(make_unpack_format('Unsigned Integer',360,363),cups[360:363+1]) # 0 = Print composite image; 1 = Print color separations
hdr[1]['TraySwitch'], = unpack(make_unpack_format('Unsigned Integer',364,367),cups[364:367+1]) # 0 = Do not change trays if selected tray is empty; 1 = Change trays if selected tray is empty
hdr[1]['Tumble'], = unpack(make_unpack_format('Unsigned Integer',368,371),cups[368:371+1]) # 0 = Do not rotate even pages when duplexing; 1 = Rotate even pages when duplexing
hdr[1]['cupsWidth'], = unpack(make_unpack_format('Unsigned Integer',372,375),cups[372:375+1]) # Width of page image in pixels
hdr[1]['cupsHeight'], = unpack(make_unpack_format('Unsigned Integer',376,379),cups[376:379+1]) # Height of page image in pixels
hdr[1]['cupsMediaType'], = unpack(make_unpack_format('Unsigned Integer',380,383),cups[380:383+1]) # Driver-specific 0 to 2<sup>32</sup> - 1
hdr[1]['cupsBitsPerColor'], = unpack(make_unpack_format('Unsigned Integer',384,387),cups[384:387+1]) # 1, 2, 4, 8 bits for version 1 raster files; 1, 2, 4, 8, and 16 bits for version 2 raster files
hdr[1]['cupsBitsPerPixel'], = unpack(make_unpack_format('Unsigned Integer',388,391),cups[388:391+1]) # 1 to 32 bits for version 1 raster files; 1 to 64 bits for version 2 raster files
hdr[1]['cupsBytesPerLine'], = unpack(make_unpack_format('Unsigned Integer',392,395),cups[392:395+1]) # 1 to 2<sup>32</sup> - 1 bytes
hdr[1]['cupsColorOrder'], = unpack(make_unpack_format('Unsigned Integer',396,399),cups[396:399+1]) # 0 = chunky pixels (CMYK CMYK CMYK); 1 = banded pixels (CCC MMM YYY KKK); 2 = planar pixels (CCC... MMM... YYY... KKK...)
hdr[1]['cupsColorSpace'], = unpack(make_unpack_format('Unsigned Integer',400,403),cups[400:403+1]) # 0 = gray (device, typically sRGB-based); 1 = RGB (device, typically sRGB); 2 = RGBA (device, typically sRGB); 3 = black; 4 = CMY; 5 = YMC; 6 = CMYK; 7 = YMCK; 8 = KCMY; 9 = KCMYcm; 10 = GMCK; 11 = GMCS; 12 = WHITE; 13 = GOLD; 14 = SILVER; 15 = CIE XYZ; 16 = CIE Lab; 17 = RGBW (sRGB); 18 = sGray (gray using sRGB gamma/white point); 19 = sRGB; 20 = AdobeRGB; 32 = ICC1 (CIE Lab with hint for 1 color); 33 = ICC2 (CIE Lab with hint for 2 colors); 34 = ICC3 (CIE Lab with hint for 3 colors); 35 = ICC4 (CIE Lab with hint for 4 colors); 36 = ICC5 (CIE Lab with hint for 5 colors); 37 = ICC6 (CIE Lab with hint for 6 colors); 38 = ICC7 (CIE Lab with hint for 7 colors); 39 = ICC8 (CIE Lab with hint for 8 colors); 40 = ICC9 (CIE Lab with hint for 9 colors); 41 = ICCA (CIE Lab with hint for 10 colors); 42 = ICCB (CIE Lab with hint for 11 colors); 43 = ICCC (CIE Lab with hint for 12 colors); 44 = ICCD (CIE Lab with hint for 13 colors); 45 = ICCE (CIE Lab with hint for 14 colors); 46 = ICCF (CIE Lab with hint for 15 colors); 48 = Device1 (DeviceN for 1 color); 48 = Device2 (DeviceN for 2 colors); 48 = Device3 (DeviceN for 3 colors); 48 = Device4 (DeviceN for 4 colors); 48 = Device5 (DeviceN for 5 colors); 48 = Device6 (DeviceN for 6 colors); 48 = Device7 (DeviceN for 7 colors); 48 = Device8 (DeviceN for 8 colors); 48 = Device9 (DeviceN for 9 colors); 48 = DeviceA (DeviceN for 10 colors); 48 = DeviceB (DeviceN for 11 colors); 48 = DeviceC (DeviceN for 12 colors); 48 = DeviceD (DeviceN for 13 colors); 48 = DeviceE (DeviceN for 14 colors); 48 = DeviceF (DeviceN for 15 colors)
hdr[1]['cupsCompression'], = unpack(make_unpack_format('Unsigned Integer',404,407),cups[404:407+1]) # Driver-specific 0 to 2<sup>32</sup> - 1
hdr[1]['cupsRowCount'], = unpack(make_unpack_format('Unsigned Integer',408,411),cups[408:411+1]) # Driver-specific 0 to 2<sup>32</sup> - 1
hdr[1]['cupsRowFeed'], = unpack(make_unpack_format('Unsigned Integer',412,415),cups[412:415+1]) # Driver-specific 0 to 2<sup>32</sup> - 1
hdr[1]['cupsRowStep'], = unpack(make_unpack_format('Unsigned Integer',416,419),cups[416:419+1]) # Driver-specific 0 to 2<sup>32</sup> - 1
# unpack v2 page header
if(version>1):
hdr[2]['cupsNumColors'], = unpack(make_unpack_format('Unsigned Integer',420,423),cups[420:423+1]) # 1 to 6 colors
hdr[2]['cupsBorderlessScalingFactor'], = unpack(make_unpack_format('IEEE Single Precision',424,427),cups[424:427+1]) # 0.0 or 1.0 or greater
hdr[2]['cupsPageSize'] = unpack(make_unpack_format('IEEE Single Precision',428,435),cups[428:435+1]) # Width and length in points
hdr[2]['cupsImagingBBox'] = unpack(make_unpack_format('IEEE Single Precision',436,451),cups[436:451+1]) # Four floating point numbers giving the left, bottom, right, and top positions of the page bounding box in points
hdr[2]['cupsInteger'] = unpack(make_unpack_format('Unsigned Integer',452,515),cups[452:515+1]) # 16 driver-defined integer values
hdr[2]['cupsReal'] = unpack(make_unpack_format('IEEE Single Precision',516,579),cups[516:579+1]) # 16 driver-defined floating point values
hdr[2]['cupsString'] = unpack(make_unpack_format('C String',580,1603,16),cups[580:1603+1]) # 16 driver-defined strings
hdr[2]['cupsMarkerType'], = unpack(make_unpack_format('C String',1604,1667),cups[1604:1667+1]) # Ink/toner type string
hdr[2]['cupsRenderingIntent'], = unpack(make_unpack_format('C String',1668,1731),cups[1668:1731+1]) # Color rendering intent string
hdr[2]['cupsPageSizeName'], = unpack(make_unpack_format('C String',1732,1795),cups[1732:1795+1]) # Page size name/keyword string from PPD
return hdr
page_data=''
def get_cups_page():
global cups,page_data
if len(cups):
hdr=unpack_header(cups[:header_length[version]],version)
print_stderr('cupsWidth x cupsHeight: '+str(hdr[1]['cupsWidth'])+' x '+str(hdr[1]['cupsHeight']))
print_stderr('cupsBytesPerLine: '+str(hdr[1]['cupsBytesPerLine']))
print_stderr('cupsBitsPerPixel: '+str(hdr[1]['cupsBitsPerPixel']))
print_stderr('cupsColorOrder: '+str(hdr[1]['cupsColorOrder']))
print_stderr('page size in bytes = '+str(hdr[1]['cupsBytesPerLine']*hdr[1]['cupsHeight']))
page_data=cups[header_length[version]:header_length[version]+hdr[1]['cupsBytesPerLine']*hdr[1]['cupsHeight']]
cups=cups[header_length[version]+len(page_data):]
return True
else:
return False
def make_bytes():
ret=[]
for byte in range(256):
ret.append([
1 if byte&0b10000000 else 0,
1 if byte&0b01000000 else 0,
1 if byte&0b00100000 else 0,
1 if byte&0b00010000 else 0,
1 if byte&0b00001000 else 0,
1 if byte&0b00000100 else 0,
1 if byte&0b00000010 else 0,
1 if byte&0b00000001 else 0,
])
return ret
bytes = make_bytes()
def get_cups_line(y):
numbytes=hdr[1]['cupsBytesPerLine']
w=hdr[1]['cupsWidth']
line=unpack('<'+str(numbytes)+'B',page_data[y*numbytes:(y+1)*numbytes])
ret=[]
extend=ret.extend
for x in range(0,w//8):
extend(bytes[line[x]])
if w%8:
extend(bytes[line[w//8]][:w%8])
return ret
FORMAT_WIDTH=0
FORMAT_HEIGHT=1
FORMAT_LINEFILL=2
FORMAT_INDEX=3
formats = {
'A4': (0x129A,0x1A7A,0x9A4A,0x0000),
'A5': (0x0CE2,0x1276,0xA233,0x0004),
'A6': (0x08E9,0x0CBE,0xA923,0x000E),
'Letter': (0x1324,0x18DC,0xA44C,0x0001),
'Legal': (0x1324,0x1FE4,0xA44C,0x0002),
'B5': (0x1006,0x16CC,0x8640,0x0005),
'B6': (0x0B14,0x0FE2,0x942C,0x000D),
'Monarch': (0x0850,0x10A8,0x9021,0x0008),
# (width,height,linefill,index,)
# LINEFILL is created by common rule 0x129A = 0x9A + 64 * 0x4A
}
def begin_document():
output.write(pack(
'>76sbbHHI',
b') SAG-GDI RL;0;0;Comment Copyright Sagem Communication 2005. Version 1.0.0.0',
0x0D,0x0A,
0x1000,0x0200,
0
))
def end_document():
output.write(pack('>3H',0x1400,0,0))
format = 'A4'
def begin_page(page_format='A4',numcopies=1,toner_economy=False):
if not (page_format in formats):
page_format='A4'
global linefill,format
format=page_format
linefill=formats[format][FORMAT_LINEFILL]
output.write(pack(
'<II4H2B3B',
0x000F0011,
int(hdr[1]['MediaPosition']),
0x0404,0,formats[format][FORMAT_WIDTH],formats[format][FORMAT_HEIGHT],
formats[format][FORMAT_INDEX],int(hdr[1]['cupsMediaType']),
numcopies,0,1 if toner_economy else 0
))
def end_page():
output.write(pack('>3H',0x1300,0,0))
current_block_data=''
current_line_length=0
def begin_block():
global current_block_data
current_block_data=b''
def get_block_size():
return len(current_block_data)
def end_block():
output.write(pack('<3H',0x0012,get_block_size(),0)+current_block_data)
def write_px_data(col,length):
if length<=0:
return
color = 1 if col else 0
color_bit = color << 6
first_byte = length%64
second_byte = length//64
two_bytes_bit = 0b10000000 if second_byte else 0b00000000
px_data=pack('>B', two_bytes_bit | color_bit | first_byte)
if second_byte:
px_data=px_data+pack('>B',second_byte)
# start new block of commands if previous is full
if len(px_data)+get_block_size()>0xFF:
end_block()
begin_block()
global current_line_length,current_block_data
current_block_data+=px_data
current_line_length+=length
def fill_line():
global current_line_length
write_px_data(0,formats[format][FORMAT_WIDTH]-current_line_length)
current_line_length=0
def write_cups():
w,h=min(hdr[1]['cupsWidth'],formats[format][FORMAT_WIDTH]),min(hdr[1]['cupsHeight'],formats[format][FORMAT_HEIGHT])
t=time()
for y in range(h):
if y%(h//15)==0:
print_stderr('%d%% '%(int(float(y)/h*100),),False)
yline=get_cups_line(y)
lastcol=yline[0]
lastcolidx=0
for x in range(1,w):
currcol=yline[x] # 1 means black pixel
if (currcol!=lastcol):
write_px_data(lastcol,x-lastcolidx)
lastcol=currcol
lastcolidx=x
write_px_data(lastcol,w-lastcolidx)
fill_line() # fills rest of the line and resets current_line_length
if h<formats[format][FORMAT_HEIGHT]:
for y in range(h,formats[format][FORMAT_HEIGHT]):
fill_line()
print_stderr('100%%.\nDone in %.3f seconds.'%(time()-t,))
begin_document()
curr_page_number=0
while get_cups_page():
begin_page(hdr[2]['cupsPageSizeName'][:2],numcopies=numcopies)
begin_block()
curr_page_number+=1
print_stderr('Rendering page %i'%(curr_page_number,))
write_cups()
end_block()
end_page()
end_document()