ACCESSING PRINTER_DAT ===================== by Dilwyn Jones This article sets out to explain the file structure of the PRINTER_DAT printer driver used by Quill, Archive and Abacus. A program is presented which prints to the screen all of the codes used in the driver and in so doing, shows how you can access the driver for use in your own programs, for example, or to write an alternative printer driver editor. The program listed works with the Xchange versions of these printer drivers too, since the file format appears to be the same. The program listed uses no toolkit commands, it should work on all versions of the QL. If you have Toolkit 2 or other basic extensions toolkit, it should be possible to rewrite the program to use some extensions such as string and byte fetching extensions to greatly simplify the program. First, I shall explain the file format used. It was quite tricky to work out the file format and I hope I got it right since I do not have formal documentation on Quill. 4 bytes "prt1" characters used to identify printer driver 1 byte total of lengths of code strings below, but if length bytes are 255, this value is not added to this byte 10 bytes driver name, which is always 10 characters long, filled with extra spaces to make it 10 characters 1 byte port type if port type = 1 or 2: ser1 or ser2 respectively 1 byte parity code (0=none,1=space,2=mark,3=odd,4=even 1 word baudrate, stored as 2 byte integer, MSB first 15 bytes spaces to pad to file position 34 if port type = 0 : parallel or other non-serial port 1 byte length of name of printer port (e.g. 3 for PAR) x bytes spaces to pad out to file position 34, where x is 17-(length of name of printer port) 1 byte number of lines per page 1 byte number of characters per line 1 byte forms type, 1 for continuous, 0 for cut sheets 4 bytes appear to be spare, filled with spaces or nulls. The following data all consist of strings containing codes for various printer functions. The format is unusual in that the string consists of one byte for the length of the string rather than 2 bytes normally used on the QL. If the length byte is 0 or 255 (hex FF), there are no other codes. 0 appears to represent NO CODES, or NONE as it appears in INSTALL_BAS. 255 appears to represent an inbuilt default value. string end of line codes string preamble codes string postamble codes string bold on codes string bold off codes string underline on codes string underline off codes string subscript on codes string subscript off codes string superscript on codes string superscript off codes Next comes the translate character strings. The format of the strings is the same (byte for length, followed by that number of codes). The first character after the length byte is the character code translated from, and the remainder of the characters indicate what that first character is translated to. 10 strings translate 1 to 10 1 byte usually a linefeed code. Note that the strings are in an unusual format. One byte indicates how many characters to follow in that string. If that length byte is 0 it means 'no codes for this function'. If the length byte is 255 it means use an inbuilt default value, used for only a few cases. This does not get added to the 'total of lengths of code string' byte. That lot was a bit of a mouthful. Now for the easy bit, the program to decode it all. I cannot guarantee this will work on every printer_dat and xchange_dat file, but it seems to work for the ones I have tried with it. It asks for the filename of the printer driver file (if you just press ENTER it uses the default of 'printer_dat' on FLP1_). It reads it and then prints it to the screen. Since there is a long list to print, it might scroll off the screen. To pause it to view the firt half, press CTRL F5 to freeze the screen momentarily. You can use similar techniques to read printer_dat files for your own programs. The data ends up in strings in this program and by reading the codes into strings in your programs, you can simply send the codes to the printer with a PRINT statement when required. For example, "PRINT #printer_channel,bold_on$;" to turn bold printing on. HINT: The Psion programs allow you to specify a parallel printer port in place of the usual SER1 or SER2. This program will print that out, of course. But some versions of the Psion programs seem to have difficulty with the name PAR - they require this to be entered as 2PAR for some reason, I don't know why the number TWO is required before the name. Of course, if you use serial ports, you will not care (and not know) about this. Within Quill itself, if the driver is configured for, say, SER1 or SER2, you can still print to another device such as PAR or network by preceding the device name with an underscore '_' character, e.g. F3 Print, Current, Whole, To _PAR. 100 REMark PRINTER_DAT reader by Dilwyn Jones 1994 110 CLS : CLS #0 120 INPUT#0,'Enter name of printer driver (default PRINTER_DAT):';driver$ 130 IF driver$ = '' : driver$ = 'FLP1_PRINTER_DAT' 140 OPEN_IN #3,driver$ 150 ident$ = ' ' : REMark 4 spaces 160 FOR a = 1 TO 4 : ident$(a) = INKEY$(#3) 170 IF ident$ <> 'prt1' : PRINT #0,'Unsuitable file' : STOP 180 code_strings_length = CODE(INKEY$(#3)) 190 driver_name$ = FILL$(' ',10) 200 FOR a = 1 TO 10 : driver_name$(a) = INKEY$(#3) 210 PRINT'DRIVER NAME : ';driver_name$ 220 : 230 REMark get port details 240 port_type = CODE(INKEY$(#3)) 250 IF port_type = 1 OR port_type = 2 THEN 260 REMark serial port 270 PRINT 'PRINTER PORT : SER';port_type 280 parity = CODE(INKEY$(#3)) : PRINT 'PARITY : '; 290 SELect ON parity 300 =0:PRINT'NONE' 310 =1:PRINT'SPACE' 320 =2:PRINT'MARK' 330 =3:PRINT'ODD' 340 =4:PRINT'EVEN' 350 END SELect 360 baudrate = 256*CODE(INKEY$(#3))+CODE(INKEY$(#3)) 370 PRINT 'BAUDRATE : ';baudrate 380 FOR a = 1 TO 15 : temp$ = INKEY$(#3) : REMark skip the spaces 390 ELSE 400 REMark parallel or other type of port 410 PRINT'PRINTER PORT : '; 420 portlen=CODE(INKEY$(#3)) : REMark name of port 430 FOR a = 1 TO portlen : PRINT INKEY$(#3); 440 PRINT 450 FOR a = 1 TO 17-portlen : temp$ = INKEY$(#3) : REMark skip spaces 460 END IF 470 lines_per_page = CODE(INKEY$(#3)) 480 PRINT'LINES PER PAGE : ';lines_per_page 490 chars_per_line = CODE(INKEY$(#3)) 500 PRINT'CHARACTERS PER LINE : ';chars_per_line 510 forms_type = CODE(INKEY$(#3)) 520 PRINT'FORMS TYPE : '; 530 IF forms_type = 1 THEN 540 PRINT'Continuous' 550 ELSE 560 PRINT'Cut' 570 END IF 580 FOR a = 1 TO 4 : temp$ = INKEY$(#3) : REMark skip spaces 590 : 600 REMark read code strings for end of line, pre/postamble, bold etc 610 PRINT'END OF LINE CODE : '; 620 eol$ = FETCH_CODE_STRING$ : CODE_STRING eol$ 630 PRINT'PREAMBLE CODES : '; 640 preamble$ = FETCH_CODE_STRING$ : CODE_STRING preamble$ 650 PRINT'POSTAMBLE CODES : '; 660 postamble$ = FETCH_CODE_STRING$ : CODE_STRING postamble$ 670 PRINT'BOLD ON CODES : '; 680 bold_on$ = FETCH_CODE_STRING$ : CODE_STRING bold_on$ 690 PRINT'BOLD OFF CODES : '; 700 bold_off$ = FETCH_CODE_STRING$ : CODE_STRING bold_off$ 710 PRINT'UNDERLINE ON CODES : '; 720 under_on$ = FETCH_CODE_STRING$ : CODE_STRING under_on$ 730 PRINT'UNDERLINE OFF CODES : '; 740 under_off$ = FETCH_CODE_STRING$ : CODE_STRING under_off$ 750 PRINT'SUBSCRIPT ON CODES : '; 760 sub_on$ = FETCH_CODE_STRING$ : CODE_STRING sub_on$ 770 PRINT'SUBSCRIPT OFF CODES : '; 780 sub_off$ = FETCH_CODE_STRING$ : CODE_STRING sub_off$ 790 PRINT'SUPERSCRIPT ON CODES : '; 800 sup_on$ = FETCH_CODE_STRING$ : CODE_STRING sup_on$ 810 PRINT'SUPERSCRIPT OFF CODES : '; 820 sup_off$ = FETCH_CODE_STRING$ : CODE_STRING sup_off$ 830 : 840 REMark translates 1 - 10 850 FOR tr = 1 TO 10 860 PRINT'TRANSLATE ';tr;': '; 870 t$ = FETCH_CODE_STRING$ 880 IF LEN(t$)=1 THEN 890 CODE_STRING t$ 900 ELSE 910 PRINT'FROM ';CODE(t$);' (';t$(1);') TO '; 920 CODE_STRING t$(2 TO LEN(t$)) 930 END IF 940 END FOR tr 950 CLOSE #3 960 : 970 DEFine PROCedure CODE_STRING (str) 980 LOCal a 990 IF str = CHR$(255) THEN PRINT'DEFAULT' : RETurn 1000 IF str = CHR$(0) THEN PRINT'NONE' : RETurn 1010 FOR a = 1 TO LEN(str) 1020 PRINT CODE(str(a)); 1030 IF a < LEN(str) THEN PRINT','; : ELSE PRINT : END IF 1040 END FOR a 1050 END DEFine CODE_STRING 1060 : 1070 DEFine FuNction FETCH_CODE_STRING$ 1080 LOCal a,cd_length,temp$ 1090 cd_length = CODE(INKEY$(#3)) 1100 IF cd_length = 0 THEN RETurn CHR$(0) 1110 IF cd_length = 255 THEN RETurn CHR$(255) 1120 temp$ = FILL$(' ',cd_length) 1130 FOR a = 1 TO cd_length : temp$(a) = INKEY$(#3) 1140 RETurn temp$ 1150 END DEFine FETCH_CODE_STRING$