Вернуться к разделу "OpenOCR".
Описание библиотеки CED - контейнера для работы с ed файлами
Библиотека выполнена в виде dll файла(ced.dll), заголовочные файлы ced_struc.h
или ced.h. ced_struc.h следует включать в программы на C++, он содержит описатели
классов, рассматриваемых ниже. ced.h - несколько урезанная версия
для включения в C-код, не содержит описателей классов, но содержит С-функции,
работающие с Handl'ами, позволяющие работать с деревом(впрочем эти функции
есть и в ced_struct). Не следует включать эти два хедера одновременно.
Для работы требует наличия и предварительной инициализации модуля CFIO.
Библиотека позволяет работать с ed не напрямую, что неприемлемо по соображениям
совместимости с будующими версиями, а посредством заполнения древовидной
структуры, которая в дальнейшем может быть записана в файл, либо прочитана
из него. Впрочем, желающие читать и писать данные напрямик в ed тоже не
останутся без внимания - для них существует интерфейс низкого уровня, который,
однако, позволяет добиться требуемой независимости программного кода
он версии ed формата. Повторюсь: вся запись в ed должна осуществляться исключительно
через библиотеку. В противном случае никто не гарантирует, что ваш код будет
работать с будущими версиями ed-файлов.
Все желающие могут ознакомиться с бинарным ed-форматом, он описан в файле
ed-diskr.txt. По всем вопросам обращаться dukei@com2com.ru
7.12.98 Артем Боженов
Интерфейс высокого уровня.
Для того, чтобы записать/читать файл, используется довольно хитрая
(но очевидная, на мой взгляд) иерархическая структура. Она несет в
себе всю информацию по форматированию текста, которая может быть записана
в рамках стандартного набора команд ed. Также в ней заложена возможность
читать и сохранять прочую информацию, которую пользователь библиотеки
сочтет нужной. Структура в своих узлах имеет представителей следующих С-классов:
CEDPage - описатель страницы, CEDSection - описатель раздела(секции),
CEDParagraph - описатель параграфа, CEDLine - строки, CEDChar - символа.
В настоящий момент в ed-файле может находиться на более 1й страницы, так
что и класс CEDPage присутствует здесь в единственном экземпляре.
Под разделом понимается то же сомое, что и при рассотрении rtf формата,
т.е. горизонтальный сегмент текста, имеющий собственное количество
колонок и свои отступы от краев бумаги.
Взаимоотношения между представителями классов изображены на диаграмме:
CEDPage
¦
-------T----+--------T-------------T------¬
v v v v v
CEDSection0-CEDSection1-CEDSection2-CEDSection3-CEDSection4
¦ ¦ ¦
¦ L--------------¬ ¦
v v v
CEDParagraph0-CEDParagraph1-CEDParagraph2-CEDParagraph3-CEDParagraph4
¦ ¦
----------- -------
v v
CEDLine0-CEDLine1-CEDLine2-CEDLine3-CEDLine4-CEDLine5
¦ ¦ ¦
¦ L-----------------¬ L----¬
v v v
CEDChar0-CEDChar1-CEDChar2-CEDChar3-CEDChar4-CEDChar5-CEDChar6-CEDChar7
Преобразованый к привычному формату, данные из диаграмы выглядят так:
------------Начало страницы-------------------
(раздел0)
(абзац0) (пустой)
(абзац1)
(строка0)(символы0,1,2)
(строка1)(символы3,4,5)
(строка2)(пустая)
(строка3)(пустая)
(строка4)(пустая)
----------------разрыв раздела----------------
(раздел1)
(абзац2) (пустой)
(абзац3) (пустой)
----------------разрыв раздела----------------
(раздел2) (пустой)
----------------разрыв раздела----------------
(раздел3) (пустой)
----------------разрыв раздела----------------
(раздел4)
(абзац4)
(строка5)(символы6,7)
По этой диаграмме можно путешествовать как сверху вниз, так и по горизонтали.
Для путешествий по горизонтали в классе CEDPage есть ф-ции
CEDSection * GetSection(int _num);
CEDParagraph * GetParagraph(int _num);
CEDLine * GetLine(int _num);
CEDChar * GetChar(int _num);
которые возвращают указатель на соответствующий элемент. _num - сквозной номер
элемента(та цифра, которая нарисована на картинке вверху).
В каждом узле хранится т.н. текущий элемент более низкого уровня.
Все операции в структуре происходят относительно него.
Каждый из вышеперечисленных классов(за исключением CEDChar) имеет ф-ции работы с
текущим эл-том.
Номер или адрес текущего
элемента можно получить с помощью функций GetNumOfCur... и GetCur..., где под ... подразумевается элемент
нижестоящего класса.
Всё это сделано для
того, чтобы можно было хранить один глобальный указатель на CEDPage, а
для доступа к содержимому дерева из различных функций использовать
конструкции вида
myPage->GetCurSection()->GetCurParagraph()->userNumber=0;
При создании объекта текущий эл-т устанавливается равным 0, после добавления
объекта более низкого уровня - начинает указывать на него.
Текущий элемент можно изменить
непосредственным присаиванием, либо заданием его порядкового номера
с помощью функций SetCur...
В качестве порядкового номера здесь выступает не сквозной номер, а номер
в текущем узле. На картинке:абзац0 можно получить так:
section0->SetCurParagraph(0);
абзац2 так:
section1->SetCurParagraph(0);
абзац3:
section1->SetCurParagraph(1);
и т.п.
Также существуют ф-ции Next...(Bool32 _goThroughSect) и
Prev...(Bool32 _goThroughSect), которые
возвращают соответственно
следующий или предыдущий элементы относительно текущего. Аргмент указывает,
что возвращать, если следующий элемент более низкого уровня имеет другого
родителя, чем текущий. Если аргумент - FALSE, то возвращается 0, иначе возвращается
элемент. Напр. (картинка) если в разделе1 текущим является абзац3, то
section1->NextParagraph(FALSE)==0;
section1->NextParagraph(TRUE)==paragraph4.
Существует еще один способ изменить текущий элемент - это ф-ции
Bool32 GoToNextSection();
Bool32 GoToNextParagraph();
Bool32 GoToNextLine();
Bool32 GoToNextChar();
определенные в классе CEDPage.
Рассмотрим, к примеру GoToNextLine. Она берет текущую секцию, там - текущий
абзац, там - текущую строку и делает текущей строку, идущую следом. Если
эта новая строка находится в следующем абзаце, то сменяется и текущий абзац
и т.д. Фактически, конструкция
myPage->GetCurSection()->GetCurParagraph()->GetCurLine() начинает указывать
на следующую строку. Ф-ции возвращают TRUE, если текущий элемент был
успешно изменен, и FALSE в противном случае. FALSE означает, что текущий
эл-т является последним на данном уровне дерева либо что на одном из
уровней дерева текущий эл-т не имеет детей. Напр, на рисунке, это будет
в случае GoToNextParagraph(), если текущими являются секция4 и абзац4. Или
GoToNextLine(), если текущие секция1 и абзац2 (нет детей)
Добавить элемент в дерево можно при помощи ф-ций Insert... Вставится он
сразу после текущего. Ф-ции Insert... возвращают
указатель на вновь вставленный эл-т, и делают этот новый эл-т текущим.
Напр. если в CEDSection три абзаца и текущим
является 2й, то комада mySection->InsertParagraph() вставит новый абзац
на 3е место, старый абзац номер3 станет четвёртым, текущим станет абзац3,
указатель на который будет возвращен.
Ф-ции из CEDPage
int GetNumberOfSections();
int GetNumberOfParagraphs();
int GetNumberOfLines();
int GetNumberOfChars();
возвращают количество соответствующих элементов в дереве
Каждый из CED классов имеет поля extData и extDataLen(длина в байтах).
Туда помещается та информация, которая была или должна быть записана
в ed файле сразу вслед за информацией, взятой из класса. В качестве этой
информации должны выступать исключительно структуры расширенного ed
(edExtension). Информация по ситаксису таких структур лежит в ed-discr.txt.
Иерархическая структура
На самом верху мы имеем CEDPage. Этот класс хранит данные о всей странице
в целом. Это:
EDSIZE sizeOfImage //Размеры исходной картинки в пикселях
EDSIZE dpi; //Разрешение сканера для этой картинки
int turn; //Тангенс угла поворота картинки относительно вертикали*2048
char* imageName; //Имя файла изображения. Если путь не указан, ищется в одном
//каталоге с ed файлом
int pageNumber; //Номер страницы(=0 не в пакетном режиме)
EDSIZE pageSizeInTwips; //Ширина страницы в твипах(1дюйм=1440твипов) для текстового редактора
Далее следуют классы CEDSection. Каждый из них может сожержать
несколько абзатцев. Информация по форматированию (где начинать колонки,
где - таблицы и картини) хранится в заголовках соответствующих абзатцев.
Класс содержит:
int numberOfColumns; //Количество колонок в секции(n штук)
EDRECT borders; //отступы от края бумаги
поле borders аналогично полю в rtf. В нем сожержится информация по
левому и правому отступах от края бумаги, относительно которых будут
выравниваться абзацы(см. серая зона на Линейке Word'a). Верхний и нижний
отступы учитываются лишь в том случае, если абзац первый(соответственно
последний) на странице.
На следующем уровне мы встречаем CEDParagraph. Этот класс описывает абзацы.
int type; //Тип абзаца
int alligment; //Выравнивание абзатца
EDRECT ident; //Отступы: left=левый, right=ширина,top=красн.строка(в твипах)
int userNumber; //Номер, данный пользователем на этапе фрагментации
int border; //Рамка вокруг абзатца
EDSIZE interval; //cx-верхний отступ, cy- нижний
edBox layout; //Расположение абзаца на странице
void * descriptor; //Указатель на расширенный описатель специальных структур
Самые интересные поля здесь - type и descriptor. Каждому типу абзаца
соответстует свой описатель, либо не соответствует никакой(=0). Стили
могут комбинироваться оператором '|'.
Рассмотрим различные варианты форматирования:
колонки - для того, чтобы описать колонки, следует в заголовке секции указать
их число, каждую колонку начинать фиктивным абзатцем с типом
COLUMN_BEGIN и descriptor=EDCOLDISCR,где width=ширине колонки, spacing=расстоянию
до следующей,next - указатель на начало следующей колонки(если колонка
последняя- то 0).В конце последней
колонки присвоить фиктивному абзацу тип LAST_IN_COLUMN,
descriptor=0
Для всего этого можно использовать ф-цию из CEDSection
CEDParagraph * CreateColumn();
которая создает обрамляющие абзацы, увеличивает на 1 счетчик колонок,
текущим делает первый фиктивный абзац. Возвращается указатель, который
можно передавать ф-ции из CEDSection
CEDParagraph * CreateParagraph(CEDParagraph * hObject,int align, EDRECT indent, int UserNum,int FlagBorder,
EDSIZE interval, edBox layout, int color, int shading, int spaceBetweenLines,
char spcBtwLnsMult, char keep);
(см. ниже)
фрейм(рамка) - Все фреймы должны лежать после колонок. Каждый фрейм начинать
фиктивным абзатцем с типом
FRAME_BEGIN и descriptor=EDFRAMEDISCR, указывающий на структуру, содержащую
координаты в твипах фрейма относительно страницы и ее размер,где
rec-размер,next - указатель на начало следующго фрейма(если фрейм
последний- то 0).В конце последнго
фрейма присвоить фиктивному абзацу тип FRAME_END,
descriptor=0
Для всего этого можно использовать ф-цию из CEDSection
CEDParagraph * CreateFrame(CEDParagraph* hObject, edBox rect, char position=-1, DWORD borderSpace=-1,
DWORD dxfrtextx=-1, DWORD dxfrtexty=-1)
которая создает обрамляющие абзацы,
текущим делает первый фиктивный абзац. Возвращается указатель, который
можно передавать ф-ции из CEDSection
CEDParagraph * CreateParagraph(CEDParagraph * hObject,int align, EDRECT indent, int UserNum,int FlagBorder,
EDSIZE interval, edBox layout, int color, int shading, int spaceBetweenLines,
char spcBtwLnsMult, char keep);
(см. ниже)
картинка - тип PICTURE,
descriptor = ctp_pic_hdr*. Описание структуры- см. ed-discr.txt
таблица - таблица начинается фиктивным абзацем, который имеет тип TAB_BEGIN,
descriptor= указатель на структуру edTabDescr. Далее идет фиктивный абзац,
описывающий строку таблицы с типом TAB_ROW_BEGIN, descriptor=указатель на структуру edRowDescr
далее идет фиктивный абзац, описывающий ячейку с типом TAB_CELL_BEGIN и соотв. дискриптором,
ячейка, еще один и.т.д., потом снова TAB_ROW_BEGIN и в самом конце - TAB_END, descriptor=0.
Других типов абзацев на данный момент не существует.
Следующий уровень - строки - CEDLine.
Класс не обладает никакой содержательной информацией, служит лишь
для разбиения сиволов внутри абзацев
И, наконец, CEDChar
edRect layout; //Размещение символа на исходном изображении(в пикселях)
int fontHeight,fontAttribs; //параметры шрифта
letter * alternatives; //Массив альтернатив
Чтение ed-файлов
Для чтения файлов в подобную структуру, библиотека имеет экспортируемую функцию
CEDPage * CED_FormattedLoad (char * file,Bool32 readFromFile, Word32 bufLen);
Если readFromFile=TRUE, то file должен содержать имя файла, bufLen - игнорируется
Если readFromFile=FALSE, то file - указатель на область в памяти, где лежит ed,
BufLen - размер этого буфера
, возвращающую указатель на CEDPage. Полученный указатель
можно, после необходимых вам преобразований в структуре передать в функию
CED_FormattedWrite. По окончанию работы со стуктурой, следует вызвать для
нее
void CED_DeleteTree(CEDPage * pg). Все нижестоящие
классы удаляются автоматически.