Юрий Карпов - Пишем программу для создания книг FB2.. Страница 2

begin

S:= Items[i]; // считываем строку

Ss:= GetStyle(S, CurStyle); // получаем чистую строку и стиль

s:= ''; // подготавливаемся к преобразованию строки

if ss <> '' then

for j:= 1 to length(Ss) do

begin // просматриваем строку посимвольно

case ss[j] of

'~': begin // если это концевая сноска

S:= S + '<a l: href="#n_'+IntToStr(EndNotes_count)+'" type="note">'

+IntToStr(EndNotes_count)+'</a>';

inc(EndNotes_count); // увеличиваем счетчик сносок

end;

'^': S:= S + '&#769;'; // ставим ударение

else S:= S + ss[j]; // иначе записываем символ в итоговую строку

end; // case

end;

// тут я пока немножко пропущу

// анализ стилей

case CurStyle of // в зависимости от стиля абзаца

Norm,Epig,Citat: OutList.Add('

'+S+'

');

H1..H5: StyleStucture; // Heading

Sub: OutList.Add('<subtitle>'+s+'</subtitle>'); // Subtitle

// конец кода

Давайте рассмотрим все по порядку:

Начнем со стихов. В стандарте FB2 используется три тега для работы со стихами, я использую только один стиль "P".

Для разделения стихов на строфы я предлагаю использовать пустые строки помеченные стилем "P".

// начало кода

if (CurStyle <> oldStyle) then // если предыдущий стиль отличен от текущего

begin // а нынешний стиль есть в данном списке, то значит надо начинать нужный блок.

case CurStyle of // начало блока

Poem: OutList.Add('<poem><stanza>');

Epig: OutList.Add('<epigraph>');

Citat: OutList.Add('<cite>');

end; // case начало блока

end;

// конец кода

А для обработки стиля используется следующие строки

// начало кода

case CurStyle of // в зависимости от стиля абзаца

Norm,Epig,Citat: OutList.Add('

'+S+'

');

Poem: begin

if S = ''

then OutList.Add('</stanza><stanza>')

else OutList.Add('<v>'+S+'</v>');

end;

// конец кода

В случае Нормальное стиля, Эпиграфа и Цитаты, просто добавляются абзацы, а для стихов еще отслеживается пустая строка…

Как видите блоки не завершены. Эту функцию выполняет следующий код.

// начало кода

if (CurStyle <> oldStyle) and (CurStyle <> Auth) then

begin

case oldStyle of // завершение предыдущего блока

Poem: OutList.Add('</stanza></poem>');

Epig: OutList.Add('</epigraph>');

Citat: OutList.Add('</cite>');

end; // case завершение предыдущего блока

end;

// конец кода

Но как Вы увидите в исходнике последний программный кусок находится выше предыдущего (и вообще все немного не так), но в данном тексте, мне пришлось расположить их так для последовательного, логичного объяснения, а в программе: сначала проверяется завершенность предыдущих блоков, затем при необходимости начинается другой, а затем обрабатываем текущий стиль.

В данном сочинении, я часто буду пользоваться таким приемом, отступлением от порядка следования текста в исходнике, что делать, человеческая логика и машинная не совсем совпадают.

Если Вы внимательно следите за процессом, то заметили " and (CurStyle <> Auth) " в предыдущем кусочке о начале блока, я это дело опустил, что бы не затуманивать описание.

Это достаточно забавный код призван выполнить требования формата:

// начало цитаты

Внутри тэгов <poem>, <cite> и <epigraph> возможно указать автора соответственно стихотворения, цитаты или эпиграфа. Для этого служит тэг <text-author>. Этот тэг должен стоять в самом конце родительского тэга, то есть непосредственно перед его закрытием.

// конец цитаты

А теперь как это я сделал.

// начало кода

Auth: begin

OutList.Add('<text-author>'+S+'</text-author>');

if oldStyle in [Poem, Epig, Citat]

then CurStyle:= oldStyle;

// т. е. корректно отработается закрытие родительских блоков

end;

// конец кода

Т.к. естественно я сделаю эту брошюрку с помощью своей программки. Опробуем вышеизложенные методы форматирования на следующем оптимистичном стихотворении.

ДОПОТОПНАЯ КОСТЬ[1] Аполлон Майков

Я с содроганием смотрелНа эту кость иного века…И нас такой же ждет удел:Пройдет и время человека…

Умолкнет славы нашей шум;Умрут о людях и преданья;Всё, чем могуч и горд наш ум,В иные не войдет созданья.

Оледенелою звездойИли потухнувшим волканомПомчится, как корабль пустой,Земля небесным океаном.

И, странствуя между миров,Воссядет дух мимолетящийНа остов наших городов,Как на гранит неговорящий

Так разум в тайнах бытияЧитает нам… Но сердце бьется,Надежду робкую тая -Авось он, гордый, ошибется!

1857

Структура

Теперь, после лирического отступления, самое интересное: структурирование книги.

Книга может иметь разделение на части, главы, тома и книги, ну мало ли чего придумает автор…

В FB2 структура задается тэгами <section> разной степени вложенности. Но в любом случае эта структура - дерево. В корне(в первой строчке), я предлагаю писать название книги, а дальше части, главы или что там есть.

Программе для обработки структуры понадобится стек (напомню, стек - это список с правилом "последний пришел - первый вышел")

Полученный код FB2, как эталоном, я проверяю программой "FictionBook Editor". Так вот, экзаменатору не нравится такая структура:

// начало примера

H1 | Кальман Миксат. ОСАДА БЕСТЕРЦЕ

S| (История одного чудака)

H2 | ВВЕДЕНИЕ

// конец примера

Т.е. между секциями не должно быть ничего лишнего…

А вот так будет все нормально:

// начало примера

H1 | Кальман Миксат. ОСАДА БЕСТЕРЦЕ

H1 | (История одного чудака)

H2 | ВВЕДЕНИЕ

// конец примера

Итак, когда при обработке списка ListBox1 встречается строка с типом от H1 до H5 вызывается процедура StyleStucture;

// начало кода

procedure StyleStucture;

begin

if CurStyle <> oldStyle then

begin // пока предположим, что предыдущий стиль был не заголовок

if SytleStack.Count = 0 then // если стек пуст

begin // записываем стиль в стек

SytleStack.Add(TObject(CurStyle))

end

else // если в стеке что-то есть

begin // значит надо проверить последний из заголовков

LastStyle:= TmyStyle(SytleStack.Last); // считываем последний стиль

case SubStyle(CurStyle, LastStyle) of // вычисляем разность текущий стиль минус последний

0: OutList.Add('</section>'); // стили равны, ничего особенного делать не надо

1: SytleStack.Add(TObject(CurStyle)); // новый стиль больше, добавляем его в стек

// предыдущая секция не закончилась, т. к. новая будет в ее входить как матрешка

else // иначе, считаем что разность меньше нуля

begin

OutList.Add('</section>');

while CurStyle <>LastStyle do

begin

SytleStack.Delete(SytleStack.Count-1); // уменьшаем стек

OutList.Add('</section>'); // завершаем секции до тех пор пока

LastStyle:= TmyStyle(SytleStack.Last); // текущий стиль и стиль в стеке не сравняются.

end;

end;

end;// case

end;

OutList.Add('<section>'); // начинаем новую секцию

OutList.Add('<title>');

end;

OutList.Add('

'+s+'

'); // записываем заголовок секции

end; // StyleStucture;

// конец кода

Пожалуй, это самый тяжелый код в данном манускрипте, но он вроде работает, хотя я вижу в нем по крайней мере две неувязки, но что это, не скажу…

Ну вот с обработкой книги почти закончили, мелкие подробности увидите в исходнике.

Нажимаем пункт меню File - Save as FB2.

И - ничего не получается. Запланированная шутка. Вылезла надпись "Заполнить поля" и фокус перенаправлен на начальную закладку.

Напоминаю FB2 - это не только легкоусвояемый (легкоусваиваемый) текст, но и очень нужный и полезный заголовок книги.

Давайте посмотрим, все таки, что происходит при выборе пункта Save as FB2

// начало кода

procedure TForm1.SaveasFB21Click(Sender: TObject);

begin

if not BookHaveName then // проверяем, все ли в порядке в заголовке

begin // если нет, то происходит все то что Вы видели

PageControl1.ActivePageIndex:= 0;

ShowMessage('Fill the form.');

exit;

end;

SaveDialog1.FileName:= form1.FB2_file.Text;

if SaveDialog1.Execute then

Make_fb2(SaveDialog1.FileName);

end;

// конец кода

Посмотрим на процедуру BookHaveName

// начало кода

function BookHaveName: boolean;

begin

with Form1 do

result:= (book_title.Text <> '') and

(FB2_file.Text <> '') and

(GenresBox.Count > 0);

end;

// конец кода

Ничего особенного в этой функции нет. Единственно из-за чего я ее вытащил, это сказать, что Вы можете и скорее даже будете вынуждены, как-то изменить ее, чтобы контроль заполнения заголовка книги был более разумным.

А я пока вернусь к заполнению заголовка.

В программе Вы видите три закладки Title-info, Document-info и Publish-info. В формате FB2 есть еще кое-что, но я пока это игнорировал. Предоставляю Вам такую возможность. Код Вам в руки…