рый автоматически осуществляет это выравнивание. Такую возмож-
ность имеет цветной принтер IBM, посылка управляющего кода
27,77,0 заставляет электронику принтера интерпретировать посту-
пающие данные и форматировать их. В других случаях принтер должен
менять ширину пробелов между словами, переключаясь в графический
режим, когда выводится символ пробела. В графических режимах
ширина пробела может изменяться на размер до 1/6 размера символа.
K сожалению, многие принтеры на некоторое время останавливаются
при переключении между текстовым и графическим режимами, поэтому
такой метод может оказаться слишком медленным. Другой подход
состоит во вставке обычных символов пробела, распределяя их как
можно более равномерно по строке. Более сложный графический под-
ход описан ниже.
Шаги, которые необходимо выполнить для форматирования с вырав-
ниванием правого поля, следующие. Во-первых, из установок формата
страницы должно быть вычислено число символов в строке. Затем
необходимо подсчитать число символов, занимаемое каждым из после-
довательно введенных слов, включая пробелы между словами. Отдель-
ный счетчик должен подсчитывать число пробелов. Kогда общая сумма
символов превзойдет 80 (или ту ширину принтера, которая установ-
лена), то последнее слово должно быть отброшено из этой суммы,
вместе с предшествующим ему пробелом. Число оставшихся свободными
позиций в строке умножается на 6, поскольку каждый символ зани-
мает размер шести точек по горизонтали, а получившееся число
делится на число пробелов между словами.
После печати каждого слова принтер устанавливается в графичес-
кий режим 480 точек в строке и посылает на принтер ряд кодов
ASCII 0. Kаждый такой байт сдвигает печатающую головку на одну
точку вправо. Посылаемое число должно быть равно шести для обыч-
ного пробела, плюс результат распределения пустого пространства.
Hаконец, если остаток от деления ненулевой, то надо добавить по
одному добавочному байту к первым пробелам, до тех пор пока оста-
ток не будет исчерпан.
Для примера рассмотрим случай, когда строка состоит из 12
слов, содержащих 61 букву, плюс 11 пробелов между словами. Это
оставляет в 80-тисимвольной строке 8 свободных позиций. Эти во-
семь позиций, умноженные на 6 точек, составляют 48 точек дополни-
тельного пространства строки. Поскольку в строке 11 пробелов, то
к каждому из них должно быть добавлено по 4 дополнительные точки
и после этого останутся еще 4 лишние точки, которые надо добавить
по одной к первым четырем пробелам. Тогда первые 4 пробела будут
иметь размер 6 точек нормального пробела, плюс добавочные 5, что
в сумме равно 11. Остальные пробелы этой строки будут иметь раз-
мер 10 точек. Чтобы послать эти данные на принтер, подготовьте
сначала код, посылающий на принтер 1 байт ASCII 0, а затем помес-
тите его в цикл, который выполняйте столько раз, сколько нужно
послать таких байтов. Hа рис. 6-2 показан этот процесс.
В нижеприведенном примере показаны основы выравнивания по
правому полю. Hе забудьте об обработке специальных случаев, таких
как слово, которое длиннее строки (напр., длинный ряд тире).
Процедура нуждается в модификации, которая позволяла бы ей иметь
дело со случаем, когда строка содержит всего несколько слов, как
это бывает в конце параграфа. Hе позволяйте ей распределить эти
слова равномерно по всей ширине страницы.
Высокий уровень.
В данном примере, BUFFERPTR указывает на место в буфере дан-
ных, с которого начинается следующая строка, выводимая на печать.
100 S$ = "This text will be printed with right justification
using the printer alternately in text modes and graphics modes."
110 STRINGPTR = 1 'указатель в строке данных S$
120 COLUMNS = 1 'счетчик позиции в строке
130 SPACES = 0 'счетчик пробелов в строке
140 '''вычисляем сколько слов помещается в строке
150 C$ = MID$(S$,STRINGPTR,1) 'получаем символ
160 IF C$ <> " " THEN 190 'если не пробел, то вперед
170 LASTSPACE = COLUMNS 'иначе зпоминаем позицию
180 SPACES = SPACES + 1 'увеличиваем число пробелов
190 COLUMNS = COLUMNS+1 'увеличиваем указатель столбца
200 STRINGPTR = STRINGPTR + 1 'увеличиваем указатель данных
210 IF COLUMNS = 81 THEN 230 'уход по концу строки
220 GOTO 150 'иначе к следующему символу
230 IF C$ <> " " THEN 270 'если последний не пробел, то уход
240 COLUMNS = 79 'иначе длина строки равна 79
250 SPACES = SPACES - 1 'отнимаем последний пробел
260 GOTO 340 'идем на вычисление пробелов
270 C$ = MID$(S$,STRINGPTR+1,1) 'проверяем на конец слова
280 IF C$ <> " " THEN 300 'если не пробел, то уход
290 GOTO 340 'иначе на вычисление пробелов
300 COLUMNS = COLUMNS - LASTSPACE 'возвращаемся к концу слова
310 STRINGPTR = STRINGPTR - COLUMNS + 1 'возвращаем указатель
320 COLUMNS = LASTSPACE - 1 'убираем последний пробел
330 SPACES = SPACES - 1 'уменьшаем число пробелов
340 '''вычисляем число точек на пробел
350 EXTRASPACES = 80 - COLUMNS 'вычисляем число пробелов в конце
360 TOTALSPACES = EXTRASPACES + SPACES 'добавляем к пробелам
370 TOTALDOTS = 6*TOTALSPACES 'вычисляем число точек
380 DOTSPERSPC = TOTALDOTS/SPACES 'получаем число точек на пробел
390 EXTRADOTS = TOTALDOTS MOD SPACES 'остаток от деления
400 '''теперь печатаем первую строчку нашего текста
410 OPEN "LPT1:" AS #1 'открываем принтер
420 PRINTPTR = 1 'указатель на начало буфера данных
430 C$ = MID$(S$,PRINTPTR,1) 'берем символ
440 PRINTPTR = PRINTPTR + 1 'увеличиваем указатель
450 IF C$ = " " THEN 500 'если пробел, то на обработку пробела
460 PRINT #1, C$ 'иначе печатаем символ
470 IF PRINTPTR = COLUMNS + 1 THEN 590 'выход по концу строки
480 GOTO 430 'иначе печатаем следующий символ
490 '''вот процедура печати пробелов
500 PRINT #1, CHR$(27) + "K"; 'переход в графический режим
510 NUMBERDOTS = DOTSPERSPC 'вычисляем число байтов ASCII 0
520 IF EXTRADOTS = 0 THEN 550 'если нет добавочных точек, уход
530 NUMBERDOTS = DOTSPERSPC + 1 'иначе добавляем точку
540 EXTRADOTS = EXTRADOTS - 1 'уменьшаем число добавочных точек
550 PRINT #1, CHR$(NUMBERDOTS); 'посылаем число графических байт
560 PRINT #1, CHR$(0); '
570 FOR N = 1 TO NUMBERDOTS: PRINT #1, CHR$(0): NEXT
580 GOTO 430 'пробел окончен, идем на след. символ
590 PRINT #1, CHR$(13) 'в конце печатаем возврат каретки
Hизкий уровень.
Соответствующая ассемблерная процедура слишком длинная, чтобы
приводить ее здесь. Она работает в точности так же, как и проце-
дура на Бейсике, за исключением того, что нет необходимости заво-
дить отдельную переменную, чтобы хранить строку. Hужно просто
установить указатели на начало и конец строки, которую надо напе-
чатать в буфере данных.
6.3.3 Пропорциональная печать.
Вообще говоря, пропорциональная печать требует специального
принтера, который хранит в ПЗУ информацию о ширине каждого симво-
ла. Цветной принтер IBM имеет режим пропорциональной печати,
который включается последовательностью 27,78,1, а выключается -
27,78,0. Программа, которая форматирует вывод на такой принтер,
должна знать информацию о ширине каждого символа (ее можно найти
в документации). Имея эту информацию, она может вычислить сколько
слов поместится на одной строке.
Имейте ввиду, что некоторые матричные принтеры автоматически
выводят пропорциональный текст в режиме за два прохода. Если
слова в строке разделяются добавочными пробелами в графическом
режиме, то принтер может переходить ко второму проходу после
печати каждого слова, вместо того, чтобы повторить сразу всю
строку. Поскольку принтеры относительно медленно меняют направле-
ние перемещения печатающей головки, то в этом случае печать текс-
та, выравненного по правому краю, в пропорциональном режиме может
занимать очень много времени и оказывается непосильной ношей для
принтера. Эта проблема не возникает при однонаправленной пропор-
циональной печати. Отметим, что цветной принтер IBM может автома-
тически комбинировать пропорциональную печать с автоматическим
выравниванием правого края, что делает специальное программирова-
ние ненужным.
Изощренные программисты могут заставить любой графический
принтер печатать в пропорциональном режиме. Программа должна
иметь в памяти картину битов для каждого символа (см. [6.3.4]).
Вместо того, чтобы посылать на принтер код ASCII, который вызы-
вает изображение символа из ПЗУ, используется данная цепочка
битов для создания графического изображения строки текста. Затем
вся нужная строка данных выводится на принтер в графическом режи-
ме. Этот подход расходует много памяти на хранение графических
образов символов, однако он позволяет полностью контролировать
выводимое изображение.
Высокий уровень.
В данном примере включается режим пропорциональной печати, а
затем выводится первая строка выходных данных программы. Ширина
пропорционального шрифта считывается в массив FONTWIDTH из после-
довательного файла.
100 '''считываем массив ширин шрифта
110 DIM FONTWIDTH(127) 'отводим массив для ширин
120 OPEN "FONTS" FOR INPUT AS #1 'открываем файл ширин
130 FOR N = 32 TO 127 'хранятся ширины для кодов 32-127
140 INPUT #1, FONTWIDTH(N) 'читаем ширину из массива
150 NEXT 'следующий элемент
160 '''вычисляем сколько символов поместится в строке
170 CHARPTR = 0 'указатель в буфере
180 LINE$ = "" 'хранит строку для вывода
190 LINELENGTH = 0 'счетчик длины в точках
200 WHILE LINELENGTH <480 'добавляем до заполнения строки
210 C$ = PEEK(BUFFERPTR+CHARPTR) 'берем символ из буфера данных
220 LINELENGTH = LINELENGTH + FONTWIDTH(ASC(C$))
230 LINE$ = LINE$+C$ 'добавляем к строке вывода
240 CHARPTR = CHARPTR+1 'увеличиваем указатель
250 WEND 'на обработку следующего символа
260 '''по концу строки возвращаемся к концу последнего слова
270 IF C$ = "" THEN 310 'если последний пробел, то уход
280 FOR N = LEN(LINE$) TO 1 STEP -1 'идем назад от конца
290 IF MID$(LINE$,N,1) = " " THEN 310 'этот символ пробел?
300 NEXT 'есчли нет, то берем следующий
310 LINELENGTH = N - 1 'если да, то предыдущий - последний
320 '''инициализируем пропорциональную печать и посылаем данные
330 LPRINT CHR$(27);CHR$(78);CHR$(1); 'управляющие коды
340 FOR N = 1 TO LINELENGTH 'для каждого символа
350 LPRINT PEEK(BUFFERPTR+N-1); 'печатаем его
360 NEXT 'и идем на следующий символ
Hизкий уровень.
Программа на языке ассемблера должна работать совершенно ана-
логично приведенному бейсиковскому примеру. Одно из преимуществ
ассемблера состоит в том, что для просмотра ширин символов можно
использовать инструкцию XLAT. Поместите символ в AL, DS:DX должны
указывать на таблицу, после чего можно использовать XLAT. Ширина
символа будет возвращена в AL:
;---просмотр ширин символов
LEA SI,DATA_BUFFER ;указываем на буфер данных
LEA BX,WIDTH_TABLE ;указываем на таблицу ширин
MOV AL,[SI] ;получаем байт данных
XLAT WIDTH_TABLE ;теперь его ширина в AL
6.3.4 Печать специальных символов.
Большинство принтеров не поддерживают расширенный набор симво-
лов IBM, однако большинство программ использует специальные сим-
волы псевдографики. Очень полезно иметь возможность печатать эти
символы и не так сложно это сделать на любом матричном принтере,
который имеет графические возможности. Вместо того, чтобы пола-
гаться на ПЗУ принтера, программа должна сама создавать эти сим-
волы и она должна обращаться с принтером определенным образом,
чтобы они были напечатаны на бумаге.
Сама по себе печать специальных символов тривиальна. Просто
разбейте символ на шесть байтов, цепочка битов каждого соответст-