ПРАВИЛА
:
1. Вариантная часть записи начинается оператором CASE и следует за общей частью; после ее окончания в записи не могут появляться никакие другие поля, поэтому оператор CASE не закрывается служебным словом END.
Обычно некоторое поле общей части указывает вариант. В примере
-это поле МЖ, называемое полем признака или дискриминантом.
2. Все варианты описываются внутри оператора CASE. Каждый вариант характеризуется задаваемым в скобках списком описаний присущих ему компонентов. Перед списком стоит одна или несколько меток. Тип этих меток указывается в заголовке вариантной части.
3.В явном виде указывать тип в заголовке не разрешается.
Допущения: часть при сокращении записи поле признаков включается в заголовок вариантной части.
Пример: case мж:пол of ч1.фам:='Иванов '; ч1.годрож:=1970; ч1.мж:=муж; ч1.воен:=true; ч1.спец:=программист;
PDF created with pdfFactory Pro trial version www.pdffactory.com
11
Семинар
.
Работа
с
типом
данных
«
Записи
».
ЗАДАЧА :
Написать программу, которая вводит информацию о 5 студентах, а именно: ФИО, год рождения, год поступления и оценки сессии.
Результатом работы является вывод списка отличников. program zapwith; type студент=record фио: array[1..10] of char; годрож:1965..1975; годпост:1985..1995; оценки: (алг, ист, эвм):1..5; end; var группа:array[1..5] of студент; s1:студент; i,j:integer; begin with s1 do begin for i:=1 to 5 do begin for j:=1 to 10 do read(фио[j]); readln(годрож,годпост); with оценки do readln (алг, ист, эвм); группа[i]:=s1; end; end; writeln('список отличников'); for i:=1 to 5 do with группа[i],оценки do if (алг=5) and (ист=5) and (эвм=5) then writeln(фио,годрож,годпост); end.
ЗАДАЧА
:
Написать программу, которая для заданного количества ЭВМ n=3 определяет отношение производительности к стоимости.
Производительность задается в миллионах операций в секунду, отдельно для скалярных и векторных операций. Стоимость в тысячах долларов.
Программа должна выбрать ЭВМ с минимальным отношением для векторных операций и скалярных операций. Распечатать соответствующие характеристики.
PDF created with pdfFactory Pro trial version www.pdffactory.com
12 program ЭВМвыбор; const n=3; m=12; type маш=record фирма: array[1..m] of char; годвып:1980..1992; произв=record скал,вект:integer; end; стоим:integer; сткпр:array[1..2] of real; end; var masbm:array[1..n] of маш; i,j,j1,j2:integer; r1,r2:real; begin for i:=1 to n do with masbm[i],произ do begin writeln('введите название фирмы из m букв'); for i:=1 to m do read(фирма[i]); writeln('введите название ЭВМ из n букв'); for j:=1 to n do read(назв[i]); writeln('введите характеристики ЭВМ'); readln(годвып, скал, вект, стоимость); сткпр[1]:=стоимость/скал; сткпр[2]:=стоимость/вект; end; r1:=masbm[1].сткпр[1];j1:=1; r2:=masbm[1].сткпр[2];j2:=1; for i:=2 to n do with masbm[i] do begin if сткпр[1]PDF created with pdfFactory Pro trial version www.pdffactory.com
13
ЗАДАЧА
:
В библиотеке храниться не более 20000 книг, каждая из которых имеет: автора, название, издательство и год издания. Проверить есть ли в библиотеке книга Иенсен Вирт "Паскаль". Если данной книги нет, то включит ее в состав библиотеки. Считать, что алфавитный каталог составлен по фамилиям авторов. program поиск книги; label 1,2; const колкниг=20000; type книга=record автор: array[1..18] of char; название: array[1..35] of char; издательство: array[1..20] of char; годизд:1900.1992; end; библиотека=array[1..20000] of книга; var исккнига,буферкнига:книга; (*исккнига - искомая книга*) каталог:библиотека; i,n;1..колкниг; k:integer; begin for i:=1 to колкниг do begin for k:=1 to 18 do read(буферкнига.автор[k]); for k:=1 to 35 do read(буферкнига.назв[k]); for k:=1 to 10 do read(буферкнига.издат[k]); read(буферкнига.годизд[k]); каталог[i]:=буферкнига; end; (*чтение сведений о книгах with исккнига do begin и запись их в каталоге*) автор:='Иенсен,Вирт'; назван:='Паскаль'; издат:='Финансы и статистика'; годиздан:=1982; end; for i:=1 to колкниг do (*сведения об искомой книге*) if каталог[i].автор>=исккнига.автор then begin if каталог[i].автор=исккнига.автор then begin writeln(i,'номер книги в каталоге'); goto 1; end else begin n:=i; goto 2; end; end; (*поиск книги в каталоге,печать ее номера,если
PDF created with pdfFactory Pro trial version www.pdffactory.com
14 книга найдена или поиск места,куда надо включить книгу*)
2: for i:=колкниг downto n+1 do каталог[i]:=каталог[i-1];
(*сдвиг содержимого каталога*) каталог[n]:=исккнига; (*включение в каталог книги*) for i:=1 to колкниг do begin буферкнига:=каталог[i]; with буферкнига do begin writeln('автор); writeln('название'); writeln('издательство'); writeln('год издания'); end; end; (*печать нового каталога*)
1: end; end.
PDF created with pdfFactory Pro trial version www.pdffactory.com
15
ЛЕКЦИЯ
15.
МНОЖЕСТВЕННЫЙ
ТИП
ДАННЫХ
.
Множественный тип s строится на основе некоторого базового типа bs, в качестве которого используется любой ограниченный или скалярный тип, кроме неограниченного целого и вещественного типа. bs-определяет конечное множество значений множественного типа s type s=set of bs
Любые подмножества базового множества могут являться значениями переменных множественного типа s . Они образуются перечислением через запятую элементов базового множества или выражении базового типа и заключаются в [ ] скобки.
Множество не содержащее элементов называется. пустым.
Переменные
множественных
типов
данных
.
type города=(Москва, Киев, Ялта); md=set of города; var p1,pp1:md; p2:set of 'a'..'f'; p3,p4:set of 0..9;
Если элементы множества являются последовательными значениями базового множества, то можно указывать только первый и последний.
Элементы множества могут задаваться и выражениями соответствующего базового скалярного типа. k:=7; p3:=[1,k+1]; p4:=[k-1,8]
Операции
над
множествами
.
+ объединение
* пересечение
- разность
1.Объединением 2-х множеств называется множество тех элементов, которые принадлежат этим множествам.
['b','f']+['b'..'d']=_['b','c','d','f']
PDF created with pdfFactory Pro trial version www.pdffactory.com
16 2.Пересечением 2-х множеств называется множество тех элементов, которые принадлежат одновременно двум множествам.
[Киев,Ялта]*[Москва, Киев]=[Киев]
3. Разностью 2-х множеств называется множество,
содержащее те элементы множества, которые не являются элементами второго множества.
[1,5,9]-[2,4,8,9]=[1,5]
Для сравнения множеств: =,<>,<=,>=
4.Выражение a=b истинно, когда сравниваемые множества содержат одни и те же элементы.
5.Выражение a< >b истинно, когда одно из сравниваемых множеств содержит хотя бы один элемент не входящий в другое множество.
6.Выражение a<=b истинно, когда все элементы множества «а» одновременно являются элементами множества b.
7.Выражение a>=b истинно, когда все элементы множества b одновременно являются элементами множества а.
8.Результат логического типа вырабатывает специальная операция принадлежности элемента некоторому множеству; обозначается
in. Слева пишется выражение соответствующего базового скалярного типа, а справа выражение множественного типа.
Пример: 5 in [3..7] ответ: true
9.Приоритет операций:
1)*
2)+,-
3)in,=,<>,<=,>=.
Последовательность выполнения операций одного приоритета определяется порядком их появления в выражении. Для изменения порядка используются круглые скобки
PDF created with pdfFactory Pro trial version www.pdffactory.com
17
Семинар
.
Работа
с
множественным
типом
данных
.
ЗАДАЧА
:Написать программу, которая формирует множество lb , в которое входят только латинские буквы, множество pr со знаками препинания из входной строки. program lat; var c,I, j : char; lb:set of 'A'..'z'; pr:set of '!'..'.'; begin lb:=[ ];pr:=[ ]; repeat read(c); if c in [ 'A'..'z'] then lb:=lb+[c] else if c in [':', ';', '.', ‘,’, '!'] then pr:=pr+[c]; until eoln; write('латинские буквы'); for i:='A' to 'z' do if i in lb then write(i); write('знаки препинания'); for j:='!' to '.' do if j in pr then write (j); end.
ЗАДАЧА
Определить из какого числа разных цифр состоит целое положительное число.
В этом примере для выделения каждой цифры, составляющей число
N целесообразно воспользоваться алгоритмом последовательного определения остатка от деления N на 10
Пример: 172:10=17(2-остаток)
17:10=1 (7-остаток) цифры остатков 1,7,2 -это различные цифры составляющие число
172. program Число цифр; var цифра:0..9; n,чрц:integer; (чрц - число различных цифр)
PDF created with pdfFactory Pro trial version www.pdffactory.com
18 нц:set of 0..9; (нц - набор цифр) begin read(n); нц:=[ ]; чрц:=0; repeat цифра:=n mod 10; if not (цифра in нц) then begin чрц:=чрц+1; нц:=нц+[цифра]; end; n:=n div 10; until n=0{ n>= 0 and n<=9}; writeln('чрц=',чрц) end.
ЗАДАЧА
Написать программу, которая формирует множество LB, в которое входят только латинские буквы, встретившиеся во входной строке, и множество знаков препинания PR из входной строки.
Program lat; uses crt;
Var c,i,j:char; lb:set of 'a'..'z'; pr:set of '!'..'?';
Begin clrscr; writeln('Введите строку символов'); write('=>'); lb:=[]; pr:=[]; repeat read(c); if c in ['a'..'z'] then lb:=lb+[c] else if c in [':',';','.',',','!','?'] then pr:=pr+[c] until eoln; writeln('Латинские буквы'); for i:='a' to 'z' do if i in lb then write(i:2); writeln;
PDF created with pdfFactory Pro trial version www.pdffactory.com
19 writeln('Знаки препинания'); for j:='!' to '?' do if j in pr then write(j:2);
End.
ЗАДАЧА
Вычислить следующие выражения: а) ['A','B','C']=['C','B','A'] --> true б) [3,4,6]<=[2..8] --> true в) [ ]>=[1,2] --> false г) trunc(8.3) in [1..9] --> true д) succ('C') in ['B'..'D'] --> true е) [2] < >[2,2,2] --> false ж) 16 in [16] --> true з) [1..14]*[5,12..60]+[4..7]-[2..16]*[6] -->[4,5,7,12,13,14]
ЗАДАЧА
Упростить данные выражения множественного типа: а) [11,17]*[2]+[7,17..40]*[2..17]-[2..8] -->[17 ]-[2..8] б) (А-В)*А+(А-В)*В -->А-В в) (А+В)*(А-В)*(В-А) -->[] г) А+В - (А-В) - (В-А) -->А*В д) А-(А-В) -->А*В
ЗАДАЧА
Известен набор товаров: хлеб, масло, сыр, молоко, имеющихся в ассортименте магазинов. В 3 магазина доставлены отдельные виды этих продуктов. Построить множества A, B, C, которые содержат: A - продукты, имеющиеся во всех магазинах одновременно; B - продукты, имеющиеся, по крайней мере, в одном магазине; C - продукты, которых нет ни в одном магазине. program amaf; const n=3; type продукты=(хлеб, масло, молоко, сыр); ассорт=set of продукты; магазин=array[1..n] of ассорт;
PDF created with pdfFactory Pro trial version www.pdffactory.com
20 var m1:магазин; x:продукты;
A, B, C, xm1:ассорт; i,j,iw:integer; begin for i:=1 to n do begin xm1:=[]; writeln('введите номера продуктов i-ого магазина'); repeat read(iw); case iw of
1: x:=хлеб;
2: x:=масло;
3: x:=молоко;
4: x:=сыр; end; xm1:=xm1+[x]; until eoln; m1[i]:=xm1; end;
A:=m1[1];
B:=[];
C:=[хлеб..сыр]; for i:=1 to n do begin
B:=B+m1[i]; { vo vsex (*по крайней мере в одном*)}
A:=A*m1[i]; { perechen produktov ваще (*во всех*)}
C:=C-B; { (*нет ни в одном*)} end; for i:=1 to n do begin case i of
2: writeln('ассортимент продуктов по крайней мере в одном магазине');
1: writeln('продукты имеются одновременно во всех магазинах');
3: writeln('продукты, которых нет ни в одном магазине'); end; for x:= хлеб to сыр do if x in A then case x of хлеб: writeln('хлеб'); масло: writeln('масло'); молоко: writeln('молоко'); сыр: writeln('сыр'); end; if i:=1 then A:=B else A:=C; end; end.
PDF created with pdfFactory Pro trial version www.pdffactory.com
21
( По крайней мере в 2-х магазинах :
A:=[]; for x:= хлеб to сыр do begin m:=0; for i:=1 to n do begin if x in m1[i] then m:=m+1; if ((m>1) and(m<3)) then a:=a+[x]; end;end; )
В программе AMAF в разделе типов задается список объектов
(продуктов), определяющий базовый тип (Продукты), на котором определен множественный тип (АССОРТ). Информация о наличии продуктов во всех магазинах задается как массив множеств.
В начале программы вложенные циклы обеспечивают ввод входной информации. При этом во внутреннем цикле формируется множество
ХМ1, характеризующее наличие товара в одном магазине. По выходе из внутреннего цикла эта информация заносится в массив М1. Затем строятся множества А,В,С с использованием операций над множествами
(пересечение, объединение, вычитание). В конце программы вложенные циклы по i и x обеспечивают распечатку полученных множеств. При этом используется оператор CASE.
PDF created with pdfFactory Pro trial version www.pdffactory.com
22
ЛЕКЦИЯ
16.
ФАЙЛЫ
Под файлом понимается либо именованная область внешней памяти
ПК (жесткого диска, гибкой дискеты, электронного "виртуального" диска), либо логическое устройство - потенциальный источник или приемник информации.
Любой файл имеет три характерные особенности.
Во-первых, у него есть имя, что дает возможность программе работать одновреенно с несколькими файлами.
Во-вторых, он содержит комроненты одного типа. Типом компонентов может быть любой тип Турбо Паскаля, кроме файлов.
Иными словами, нельзя создать "файл файлов".
В-третьих, длина вновь создоваемого файла никак не оговаривается при его объявлении и ограничивается только емкостью устройств внешней памяти.
Файловый тип или переменную файлового типа можно задать одним из трех способов:
<имя>=FILE OF<тип>;
{типизированный}
<имя>=TEXT;
{текстовый}
<имя>=FILE;
{нетипизированный}
Здесь <имя>-има файлового типа (правильный идентификатор);
FILE,OF- зарезервированные слова (файл,из); TEXT- имя стандартного типа текстовых файлов; <тип> - любой тип Турбо
Паскаля,кроме файлов.
Например: type produсt = record name : string; code : word; cost : comp end; text80=file of string[80]; var f1: file of char; f2: text; f3: file; f4: text80; f5: file of produсt;
PDF created with pdfFactory Pro trial version www.pdffactory.com
23
В зависимости от способа объявления можно выделить три вида файлов:
* типизированные файлы (задаются предложением FILE,OF...);
* текстовые файлы (определяются типом TEXT);
* нетипизированные файлы (определяются типом FILE).
В наших примерах F1, F4 и F5- типизированные файлы, F2 текстовый файл, F3- нетипизированный файл. Вид файла, вообще говоря, определяет способ хранения информации в файле. Однако в Турбо
Паскале нет средств контроля вида ранее созданных файлов.
При объявлении уже существующих файлов программист должен сам следить за соответствием вида объявления характеру файла.
Доступ
к
файлам
.
Любой программе доступны два предварительно объявленных файла со стандартными файловыми переменными: INPUT-для чтения данных с клавиатуры и OUTRUT-для вывода на экран.Стандартный Паскаль требует обязательного упоминания этих файлов в заголовке программы, например, так:
PROGRAM NameOfProgram(input , output);
В Турбо Паскале это необязательно, вот почему заголовок программы можно опускать.
Любые другие файлы , а также логические устройства становятся доступны программе только после выполнения особой процедуры открытия файла (логического устройства). Эта процедура заключается в связывании ранее объявленной файловой переменной с именем существующего или вновь создаваемого файла, а также в указании направления обмена информацией: чтение из файла или запись в него.
Файловая переменная связывается с именем файла в результате обращения к стандартной процедуре ASSIGN:
ASSIGN(<ф.п.>,<имя файла или л.у.>);
Здесь <ф.п.>- файловая переменная (правильный идентификатор, объявленный в программе как переменная файлового типа);
<имя файла или л.у.>-текстовое выражение,содержащее имя файла или логическое устройство. Если имя файла задается в виде пустой строки, например, ASSIGN(f,"), то в зависимости от направления обмена
PDF created with pdfFactory Pro trial version www.pdffactory.com
24 данными файловая переменная связывается со стандартным файлом
INPUT или OUTPUT.
Именафайлов. Имя файла -это любое выражение строкового типа, которое строится по правилам определения имен в MS DOS (операционной системе ПК):
* имя содержит до восьми разрешенных символов; разрешенные символы- это прописные и строчные латинские буквы, цифры и символы:
! @ # $ % ^ & ( ) ` - _
* имя начинается с любого разрешенного символа;
* за именем может следовать расширение -последовательность до трех разрешенных символов; расширение, если оно есть, отделяется от имени точкой.
Перед именем может ставится так называемый путь к файлу: имя диска и/или имя текущего каталога и имена каталогов вышестоящих уровней.
Имя диска- это один из символов A...Z, после которого ставится двоеточие. Имена А: и В: относятся к дисковым накопителям на гибких дискетах, имена C:, D: и т.д.- к жестким дискам. Эти имена могут относится также к одному или нескольким виртуальным дискам, созданным в оперативной памяти ПК специальной командой VDISK в ходе выполнения файла автоустановки параметров дисковой операционной системы (ДОС) CONFIG.SYS.
Если имя диска не указано подразумевается устройство по умолчанию - то, которое было установлено в операционной системе перед началом работы программы.
За именем диска может указываться имя каталога, содержащего файл. Если имени каталога предшествует обратная косая черта, то путь к
файлу начинается из корневого каталога, если черты нет - из текущего каталога, установленного в системе по умолчанию. За именем каталога может следовать одно или несколько имен каталогов нижнего уровня.
Каждому из них должна предшествовать обратная косая черта. Весь путь к файлу отделяется от имени файла обратной косой чертой. Максимальная длина имени вместе с путем79 символов, например: var finp : text; fout : file of string; const name='c:\dir\subdir\out.txt';
PDF created with pdfFactory Pro trial version www.pdffactory.com
25 assign(finp,'123.dat'); assign(fout,name);
Логические
устройства
.
Стандартные аппаратные средства ПК такие, как клавиатура, экран дисплея, печатающее устройство (принтер) и коммуникационные каналы ввода-вывода, определяются в Турбо Паскале специальными именами, которые называются логическими устройствами. Все они в Турбо Паскале рассматриваются как потенциальные источники или приемники текстовой информации.
CON- логическое имя, которое определяет консоль -клавиатура или экран дисплея. Турбо Паскаль устанавливает различие между этими физическими устройствами по направлению передачи данных: чтение данных возможно только с клавиатуры, а запись- только на зкран. Таким образом, с помощью логического устройства CON нельзя, например, прочитать данные с экрана ПК, хотя такая аппаратная возможность существует.
Ввод с клавиатуры буферируется: символы по мере нажатия на клавиши помещаются в специальный строковый буфер, который передается программе только после нажатия на клави "Ввод". Буферизация ввода обеспечивает возможность редактирования вводимой строки стандартными средствами ДОС. При вводе символов осуществляется их эхо-повтор на экране ПК. В Турбо Паскале можно прочитать любой символ клавиатуры, в том числе и символ CR, вырабатываемый клавишей "Ввод", сразу после нажатия на соответствующую клавишу без эхо-повтора.
PRN- логическое имя принтера. Если к ПК подключено несколько принтеров, доступ к ним осуществлается по логическим именам LPT1,
LPT2 и LPT3. Имена PRN и LPT1 первоначально-синонимы.
Средствами ДОС можно присвоить имя PRN любому другому логическому устройству, способному принимать информацию.
Стандартный библиотечный модуль PRINTER, входящий в библиотеку TURBO.TRL, объявляет имя файловой переменной LST и связывет его с логическим устройством LPT1. Это дает возвможность использовать простое обращение к принтеру. Например, программа
Uses Printer;
Begin writeln(LST, 'Привет, мир!')
PDF created with pdfFactory Pro trial version www.pdffactory.com
26
End. выведет на принтер фразу "Привет, мир!", а все необходимые операции по открытию логического устройства выполнит библиотечный блок
PRINTER.
AUX-логическое имя коммуникационного канала, который обычно используется для связи ПК с другими машимами.
Коммуникационный канал может осуществить и прием, и передачу данных, однако в программе в каждый момент времени ему можно назначить только одну из этих функций. Как правило, в составе ПК имеются два коммуникационных канала, которым даются имена логических устройств COM1 и COM2.
Первоночально имена AUX и COM1- синонимы.
NUL- логическое имя "пустого" устройства. Это устройство чаще всего используется в отладочном режиме и трактуется как устройство-премник информации неограниченной емкости.
При обращении к NIL как источнику информации выдается признак конца файла EOF.
Связывание логического устройства с файловой переменной осуществляется процедурой ASSIGN, например: var f1,f0 :text; assign(f1, 'AUX'); assign(f0,'LRT2');
Турбо Паскаль никогда не связывает имена логических устройств с дисковыми файлами, в этом смысле эти имена можно считать зарезервированными. Иными словами нельзя, например, обратится к дисковому файлу с именем PRN, так как Турбо Паскаль всегда интерпритирует такой запрос как обращение к принтеру.
ИнициализацияфайлаИнициализировать файл означает указать для этого файла направление передачи данных. В Турбо Паскале можно открыть файл для чтения, для записи информации, а также для чтения и записи
PDF created with pdfFactory Pro trial version www.pdffactory.com
27 одновременно. Для чтения файл инициируется с помощью стандартной процедуры RESET:
RESET (<ф.п.>);
<ф.п.>-файловая переменная, связанная ранее процедурой ASSIGN с уже существующим файлом или логическим устройством-приемником информации.
При выполнении этой процедуры дисковой файл или логическое устройство подготавливается к чтению информации. В результате специальная переменная-указатель, связанная с этим файлом, будет указывать на начало файла, т.е. на компонент с порядковым номером 0.
Если дается попытка инициировать чтение из несуществующего файла или из логического устройства PRN,возникает ошибка периода исполнения,которая может быть сообщена программе ненулевым значением встроенной функции IORESULT типа WORD. Например,
следующий фрагмент программы позволяет установить, существует ли требуемый файл на диске: var f:file of char; assign(f, 'myfile.dat');
{$I-} { Отключить контроль ошибок ввода-вывода } reset(t);
{$I+} { Включить конроль ошибок ввода-вывода } if IoResult <> 0 then
.... { Файл не существует } else
.... { Файл существует }
В этом фрагменте с помощью директивы компилятора {$I-} отключается автоматический контроль ошибок ввода-вывода. Если этого не сделать, то отсутствие файла приведет к аварийному завершению программы.
В Турбо Паскале разрешается обращатся к типизированным файлам, открытым процедурой RESET (т.е. для чтения информации), с помощью процедуры WRITE (т.е. для записи информации). Такая возможность позволяет легко обновлять ранее созданные типизированные файлы и при необходимости расширять их. Для текстовых файлов, открытых процедурой RESET, нельзя использовать процедуру WRITE или WRITELN.
PDF created with pdfFactory Pro trial version www.pdffactory.com
28
Стандартная процедура REWRITE(<ф.п.>) инициирует запись информации в файл или в логическое устройство, связанное ранее с файловой переменной <ф.п.>.Процедурой REWRITE нельзя инициировать запись информации в ранее существующий дисковой файл: при выполнении этой процедуры старый файл уничтижается и никаких сообщений об этом в программу не передается. Новый файл подготовляется к приему информации и его указатель принимает значение
0.
Стандартная процедура APPEND(<ф.п.>) инициирует запись в ранее существовавшой текстовый файл для его расширения, при этом указатель файла устанавливается в конец. Процедура APPEND применима только к текстовым файлам, т.е. их файловая переменная должна иметь тип
TEXT (см. выше).Процедурой APPEND нельзя инициировать запись в типизипованный или нетипизированный файл. Если текстовый файл ранее уже был открыт с помощью RESET или REWRUTE, использование процедуры APPEND проведет к закрытию этого файла и открытию вновь, но уже для добавления записей.
PDF created with pdfFactory Pro trial version www.pdffactory.com