Структура

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

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

В FB2 структура задается тэгами

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

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

Полученный код 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('
'); // стили равны, ничего особенного делать не надо

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

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

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

begin

OutList.Add('
');

while CurStyle <>LastStyle do

begin

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

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

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

end;

end;

end;// case

end;

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

OutList.Add('');</code></pre></p>
   <p><pre><code>end;</code></pre></p>
   <p><pre><code>OutList.Add('<p>'+s+'</p>'); // записываем заголовок секции</code></pre></p>
   <p><pre><code>end; // StyleStucture;</code></pre></p>
   <p><pre><code>// конец кода</code></pre></p>
   <p>Пожалуй, это самый тяжелый код в данном манускрипте, но он вроде работает, хотя я вижу в нем по крайней мере две неувязки, но что это, не скажу…</p>
   <p>Ну вот с обработкой книги почти закончили, мелкие подробности увидите в исходнике.</p>
   <p>Нажимаем пункт меню File – Save as FB2.</p>
   <p>И – ничего не получается. Запланированная шутка. Вылезла надпись «Заполнить поля» и фокус перенаправлен на начальную закладку.</p>
   <p>Напоминаю FB2 – это не только легкоусвояемый (легкоусваиваемый) текст, но и очень нужный и полезный заголовок книги.</p>
   <p>Давайте посмотрим, все таки, что происходит при выборе пункта Save as FB2</p>
   <p><pre><code>// начало кода</code></pre></p>
   <p><pre><code>procedure TForm1.SaveasFB21Click(Sender: TObject);</code></pre></p>
   <p><pre><code>begin</code></pre></p>
   <p><pre><code>if not BookHaveName then // проверяем, все ли в порядке в заголовке</code></pre></p>
   <p><pre><code>begin // если нет, то происходит все то что Вы видели</code></pre></p>
   <p><pre><code>PageControl1.ActivePageIndex:= 0;</code></pre></p>
   <p><pre><code>ShowMessage('Fill the form.');</code></pre></p>
   <p><pre><code>exit;</code></pre></p>
   <p><pre><code>end;</code></pre></p>
   <p><pre><code>SaveDialog1.FileName:= form1.FB2_file.Text;</code></pre></p>
   <p><pre><code>if SaveDialog1.Execute then</code></pre></p>
   <p><pre><code>Make_fb2(SaveDialog1.FileName);</code></pre></p>
   <p><pre><code>end;</code></pre></p>
   <p><pre><code>// конец кода</code></pre></p>
   <p><pre><code>Посмотрим на процедуру BookHaveName</code></pre></p>
   <p><pre><code>// начало кода</code></pre></p>
   <p><pre><code>function BookHaveName: boolean;</code></pre></p>
   <p><pre><code>begin</code></pre></p>
   <p><pre><code>with Form1 do</code></pre></p>
   <p><pre><code>result:= (book_title.Text <> '') and</code></pre></p>
   <p><pre><code>(FB2_file.Text <> '') and</code></pre></p>
   <p><pre><code>(GenresBox.Count > 0);</code></pre></p>
   <p><pre><code>end;</code></pre></p>
   <p><pre><code>// конец кода</code></pre></p>
   <p>Ничего особенного в этой функции нет. Единственно из-за чего я ее вытащил, это сказать, что Вы можете и скорее даже будете вынуждены, как-то изменить ее, чтобы контроль заполнения заголовка книги был более разумным.</p>
   <p>А я пока вернусь к заполнению заголовка.</p>
   <p>В программе Вы видите три закладки Title-info, Document-info и Publish-info. В формате FB2 есть еще кое-что, но я пока это игнорировал. Предоставляю Вам такую возможность. Код Вам в руки…</p>
   <p>Итак Title-info</p>
   <p>Поле Project ― само заполнится при открытии текстового файла. При желании, Вы можете изменить, имя сохраняемого fb2 файла.</p>
   <p>Поле book-title действительно обязательно надо заполнить</p>
   <p>Теперь Genre ― Жанр.</p>
   <p>Ага, тут немного интереснее, есть о чем погуторить.</p>
   <p>Нажимаем кнопку с тремя точками.</p>
   <p>И открывается окошко Жанры.</p>
   <p>Наша цель добавить один или несколько жанров в левый ListBox.</p>
   <p>Выберите подходящий жанр в правом ListBoxсике и нажмите кнопку Add</p>
   <p>В навигации по жанрам поможет верхний ComboBox</p>
   <p>О коде в этом unit мне говорить лень, ничего особенного, рутина.</p>
   <p>Интереснее, вот, что, информация для загрузки в эти Боксики находится в unit dm</p>
   <p>Посмотрите на нее, и поругайте мою лень. Дело в том, что я не уверен, что этот список жанров правилен. Второе, этот список, очевидно, не окончателен. А значит он не должен быть жестко зафиксирован в программе.</p>
   <p>Значит, так. Вам задание ― переписать прогу, чтобы эти списки грузились или из текстового файла или из INI файла.</p>
   <p>Вернемся к заполнению заголовка</p>
   <p>Нам надо ввести данные об авторе / авторах и переводчике / переводчиках</p>
   <p>Так же нажимаем на соответствующую кнопочку с троеточием и работаем в открывшемся окне.</p>
   <p>Вы уже наверно заметили, что мне прискучило очень уж подробно расписывать код. Но в данном unit тоже ничего особенного, единственно, пришлось ввести структуру <pre><code>TPerson</code></pre>, я думаю Вы легко разберетесь зачем она мне нужна.</p>
   <p>Мне интереснее, совершенствование программы. Представьте ситуацию, Вы делаете 10 книг (или 100) одного автора и каждый раз делая новую книгу, заполняете опять и опять данные об этом человеке. Мне было бы лень. Ваши предложения?…</p>
   <p>Ну хорошо мы заполнили и Title-info и Document-info и Publish-info.</p>
   <p>Давайте-ка глянем, что там в коде записи файла FB2.</p>
   <p><pre><code>// начало кода</code></pre></p>
   <p><pre><code>Procedure Make_fb2(S: string);</code></pre></p>
   <p><pre><code>begin //</code></pre></p>
   <p><pre><code>if Form1.ListBox1.Items.Count = 0 then exit;</code></pre></p>
   <p><pre><code>SytleStack.Clear; // подготовка стека стилей</code></pre></p>
   <p><pre><code>OutList.Clear; // подготовка выходного списка</code></pre></p>
   <p><pre><code>SaveDescription;</code></pre></p>
   <p><pre><code>SaveBodyFB2; // это мы уже в общем рассмотрели</code></pre></p>
   <p><pre><code>SaveEndnotes;</code></pre></p>
   <p><pre><code>OutList.Add('</FictionBook>'); // закрываем книгу</code></pre></p>
   <p><pre><code>OutList.SaveToFile(S); // Запись в файл</code></pre></p>
   <p><pre><code>showMessage('Done.'); // Сообщаем об удачном завершении</code></pre></p>
   <p><pre><code>end;</code></pre></p>
   <p><pre><code>// конец кода</code></pre></p>
   <p>Как видите мы еще не рассмотрели две процедуры.</p>
   <p><pre><code>// начало кода</code></pre></p>
   <p><pre><code>procedure SaveDescription;</code></pre></p>
   <p><pre><code>const</code></pre></p>
   <p><pre><code>max = 5; // может я захочу изменить число строк в массиве, тогда я изменю только одну цифру</code></pre></p>
   <p><pre><code>mas: array[1.. max] of string =</code></pre></p>
   <p><pre><code>(// массив для заголовочной части FB2 файла</code></pre></p>
   <p><pre><code>'<?xml version=«1.0» encoding=«windows-1251» ?>', // как видите я делаю файл в кодировке Win</code></pre></p>
   <p><pre><code>// я не вижу смысла в применении юникода, но если речь идет не о русском языке,</code></pre></p>
   <p><pre><code>// то сделайте здесь изменение.</code></pre></p>
   <p><pre><code>'<FictionBook xmlns=«http://www.gribuser.ru/xml/fictionbook/2.0» ',</code></pre></p>
   <p><pre><code>' xmlns: l=«http://www.w3.org/1999/xlink» >',</code></pre></p>
   <p><pre><code>' <description>',</code></pre></p>
   <p><pre><code>' <title-info>'</code></pre></p>
   <p><pre><code>);</code></pre></p>
   <p><pre><code>var i: byte;</code></pre></p>
   <p><pre><code>begin</code></pre></p>
   <p><pre><code>// Выводим в выходной файл начало FB2 файла</code></pre></p>
   <p><pre><code>for i:= 1 to max do</code></pre></p>
   <p><pre><code>OutList.Add(Mas[i]);</code></pre></p>
   <p><pre><code>// конец кода</code></pre></p>
   <p>Дальше просматриваем списки Жанров, Автором и Переводчиков и выводим оттуда информацию (если она там есть).</p>
   <p>Т.е. проверяем все заполненные поля форм описывающих книгу и выводим информацию в соответствующие секции заголовка книги.</p>
   <p>Будем считать, что с Description – покончили.</p>
   <p>Осталось только</p>
   <p><pre><code>// начало кода</code></pre></p>
   <p><pre><code>procedure SaveEndnotes;</code></pre></p>
   <p><pre><code>var</code></pre></p>
   <p><pre><code>S: string;</code></pre></p>
   <p><pre><code>i: integer;</code></pre></p>
   <p><pre><code>begin</code></pre></p>
   <p><pre><code>if Form1.EndNotesList.Items.Count = 0 then exit;</code></pre></p>
   <p><pre><code>OutList.Add('<body name=«notes» ><title><p>Примечания</p>');

for i:= 0 to Form1.EndNotesList.Items.Count – 1 do

begin

S:= Form1.EndNotesList.Items[i];

OutList.Add('
<p>'+IntToStr(i+1)+'</p>');</code></pre></p> <p><pre><code>OutList.Add('

'+S+'

');

OutList.Add('');

end;

OutList.Add('');

end;

// конец кода

Согласитесь, что здесь все просто, просматриваем список сносок и соблюдаем формат FB2. Но остается один маленький вопрос, а что если нам нужно будет вывести многострочную (точнее много абзачную) сноску. Да, возникает вопрос, приходят и варианты решения…

Ладно, кое-что в коде я пропустил. Но основные недостатки программы, я кажется описал. Правда, наверняка есть ляпы, которые я не заметил…

Наконец файл книги в формате FB2 создан.

На этом все? Ну нет, сейчас все авторы заканчивают книги словами «Продолжение следует» . И я замыслил по крайней мере одно продолжение. Мне кажется оно просто необходимо.

Программа должна уметь не только создавать, но и читать файл FB2. Тогда можно растягивать удовольствие изготовления книги на несколько дней, и не потребуется каждый раз заново: читать текстовый файл, расставлять стили, форматировать строки и т. д. и все это с одной и той же книгой ― ужас.

Но, как известно из математики, обратная задача всегда сложнее прямой. И т. к. я считаю, что на сегодня уже достаточно утомил Вас. Давайте рассказ о второй версии программы отложим.

Связаться со мной Вы сможете по адресу w__cat@mail.ru (обратите внимание, 2 подчеркивания, т. к. w_cat@mail.ru оказался уже занят). Предупреждаю сразу, я ленив, почту смотрю не каждый день, да и отвечать всем может и не смогу (я же не знаю сколько найдется желающих мне написать). Второе, эту почту я специально завел для этой программки, и если Вы, друзья мои завалите ее спамом или матом, я просто забуду туда дорогу.

Лицензионные условия таковы, пользуйтесь на здоровье, копируйте, переделывайте, если сможете заработать на этом деньги, буду только рад.

Карпов Юрий.

Кохтла-Ярве.

2010.

Итак.

Продолжение следует…

Загрузка...