The following information is provided as is, and the authors take no responsibility for the correctness.
Sagem GDI language is used in at least two MFU models by Ricoh: Aficio SP1000s and Aficio SP1100s. These devices include GDI 600dpi printers that are bundled with Windows driver only.
An easy way to investigate this format is to find a Windows box and print some documents “to file”.
The structure of the print job data:
Document header
Page1: Page header datablock1 datablock2 datablock3 ... Page footer
Page2: Page header datablock1 datablock2 datablock3 ... Page footer
...
PageN: Page header datablock1 datablock2 datablock3 ... Page footer
Document footer
As one can see the data is structured rather naturally and consists of pages divided into datablocks.
A static data of 83 bytes for any document: string ”) SAG-GDI RL;0;0;Comment Copyright Sagem Communication 2005. Version 1.0.0.0” followed by ”\r\n” followed by 0x1000 0200 0000 0000. To create it in python one would use
pack( '>76sbbHHI', ') SAG-GDI RL;0;0;Comment Copyright Sagem Communication 2005. Version 1.0.0.0', 0x0D,0x0A, 0x1000,0x0200, 0 )
Page header carries information about page format and also number of copies to be made, paper type, paper position (paper tray selection) and toner economy control. It begins with “start page” command: 0x1100 0F00, then there comes the paper tray index (0x0000 0000 for “auto”, but its value doesn't seem to play any role at all for the mentioned Ricoh devices), then there follows some value 0x0404 0000 of an unknown purpose (using this static seems ok in every case), then page format width and height (double-bytes) measured in printer pixels (those “dots” that are in “dpi”), and then there come 5 bytes: index of the format (see formats table below), paper type (doesn't seem to have any effect in those Ricohs), number of copies, a zero, and 1 or 0 to switch toner economy on or off.
pack( '<II4H2B3B', 0x000F0011, mediaposition, 0x0404,0,paper_width,paper_height, format_index,mediatype, numcopies,0,toner_economy )
MediaType 0 Auto MediaType 3 "Heavyweight"
InputSlot 0 Auto InputSlot 1 "Automatic tray" InputSlot 3 "Manual tray"
Index: 0x0000 Width: 0x129A Height: 0x1A7A Linefill: 0x9A4A
Index: 0x0004 Width: 0x0CE2 Height: 0x1276 Linefill: 0xA233
Index: 0x000E Width: 0x08E9 Height: 0x0CBE Linefill: 0xA923
Index: 0x0001 Width: 0x1324 Height: 0x18DC Linefill: 0xA44C
Index: 0x0002 Width: 0x1324 Height: 0x1FE4 Linefill: 0xA44C
Index: 0x0005 Width: 0x1006 Height: 0x16CC Linefill: 0x8640
Index: 0x000D Width: 0x0B14 Height: 0x0FE2 Linefill: 0x942C
Index: 0x0008 Width: 0x0850 Height: 0x10A8 Linefill: 0x9021
Width and Height are given in dots (those “dots” that are in “dpi”). For example, for A4 one has: 0x129A / 600 = 7.94” = 20.16cm - the width of A4 with narrow borders (hardware borders for the Ricohs mentioned above is ~3-4mm)
Linefill is explained below in Line format section.
Each datablock has a small header, that includes “start block” command 0x1200, the size of the upcoming block data in bytes (without the length of this header which is 6 bytes, only data length that follows) and a zero, for example, the following command is for the datablock of total length 0x70 = header length 0x06 + data length 0x6A
0x1200 6A00 0000
If offset of this “start block” command this 0x6B then the next “start block” command of the datablock that follows is expected at offset 0x6B + 6 + 0x6A = 0xDB
“Start block” in python:
pack('<3H',0x0012,block_data_size,0)
There is no any detected rule on how to divide data into datablocks, which involves also that the length of a datablock can be selected as one wishes. Successful tests (with those Ricohs) include datablock lengths up to 0xFF.
Print data is represented by lines. There is no special explicit command for starting and ending the line. Line end is given implicitly. Line is considered finished when the size of the data exceeds the Width of the page (see formats listing above). An importaint and not obvious behaviour here is that the last command is NOT splitted between lines independently of how many pixels it overflows the page Width, the last command is simply cropped at the right border of the page. If for example one wants to print out a page fully filled with black, he cannot do it with one long line - there must be given as many lines as Height of page for the given format (see formats listing above). If last pixel of the line is not black, one must fill the rest of the line with white pixels. According to previous note, this filling white pixels block can be of ANY width long enough to overflow the page Width and it also must be a single command - so it is ok (for simplicity) to use as many as Width white pixels for any line of data for any format.
There is no restriction found on how lines should be spead over datablocks. This means datablocks are more “blocks of commans” rather than “blocks of lines”. So one line can occupy more than one block, and the biggest block of length 0xFF can contain as many as 0xFF/2=127 lines (2 bytes is the shortest possible command for a line - a line filled with white/black). It is the complexity of the data that governs here.
Every line is considered as a sequence of segments with alternating colors white-black-white-black-white… etc. To describe this line one starts from the left side of the page and writes commands that correspond to these segments, so that the number of commands = number of segments. Each command (segment) is a 1- or 2-bytes sequence depending on the length of the segment. If color segment is less that 64 pixels long a single byte is used, if more that 64 the one has to use 2 bytes.
In the following command format consider binary representation of bytes: 0b00000000 (bits from 7 to 0 left to right). Introduce L1=segment_length%64, L2=segment_length/64, note L2=0 for segment_length<64.
first byte:
bit7: 0 if segment length < 64 (this means 1-byte command), 1 if segment length >= 64 (this means 2-byte command) bit6: 0 for white, 1 for black segments bits5-0: L1, maximum length of segment with 1-byte command is ''0b00111111 = 63''
second byte:
bits7-0: L2, maximum length of segment with 2-byte command is ''0b00111111 + 64 * 0b11111111 = 16383px''
which is approx. 27" at 600dpi
examples:
0x5A = 0b01011010'' for black segment of length ''0b00011010 = 26px
Linefill is the command for a completely white line, which is a frequently used command especially for text documents and documents with a lof of white space on pages. Take a Linefill from the formats table above for A4 and make sure it is really a white line of length equal to page Width.
''0x9A4A = 0b10011010 0b01001010''.
First bit = 1 ⇒ this is a 2-byte command, second bit is 0 ⇒ this is a white segment, its length is length = 0b00011010 + 64 * 0b01001010 = 0x129A = 4762px compare to A4 page Width value. All linefills in the above table are pre-calculated by this formula:
Linefill = (0x80 + Width % 64) << 8 + Width / 64
0x0CE2 = 3298. Half of it is 1649:1649 = 64 * 25 + 49 = 64 * 0x19 + 0x31 = 64 * 0b00011001 + 0b00110001
Thus this will be a 2-byte command for both segments. First segment is black, second is white, one gets 4 bytes for the line:
0b11110001 0b00011001 0b10110001 0b00011001 = 0xF119 B119.
Note that the last command will be anyway cropped at the right end of the page so we could fearlessly use 0xA233 (a Linefill for A5, see table above) as the second command or any other which corresponds to a segment longer than a half of a page.
A 6-bytes “end page” command: 0x1300 0000 0000. Python code:
pack('>3H',0x1300,0,0)
A 6-bytes “end document” command: 0x1400 0000 0000. In python we write
pack('>3H',0x1400,0,0)
This is not the absolutely full description of format, some static value might still be parameters, but they play secondary role as linux driver for Ricoh SP1000s and SP1100s works well based on this information.