Пишем программу для создания книг FB2 (СИ) - Карпов Юрий
Умрут о людях и преданья;
Всё, чем могуч и горд наш ум,
В иные не войдет созданья.
Оледенелою звездой
Или потухнувшим волканом
Помчится, как корабль пустой,
Земля небесным океаном.
И, странствуя между миров,
Воссядет дух мимолетящий
На остов наших городов,
Как на гранит
неговорящий
…
Так разум в тайнах бытия
Читает нам… Но сердце бьется,
Надежду робкую тая -
Авось он, гордый, ошибется!
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 есть еще кое-что, но я пока это игнорировал. Предоставляю Вам такую возможность. Код Вам в руки…
Итак Title-info
Поле Project - само заполнится при открытии текстового файла. При желании, Вы можете изменить, имя сохраняемого fb2 файла.
Поле book-title действительно обязательно надо заполнить
Теперь Genre - Жанр.
Ага, тут немного интереснее, есть о чем погуторить.
Нажимаем кнопку с тремя точками.
И открывается окошко Жанры.
Наша цель добавить один или несколько жанров в левый ListBox.
Выберите подходящий жанр в правом ListBoxсике и нажмите кнопку Add
В навигации по жанрам поможет верхний ComboBox
О коде в этом unit мне говорить лень, ничего особенного, рутина.
Интереснее, вот, что, информация для загрузки в эти Боксики находится в unit dm
Посмотрите на нее, и поругайте мою лень. Дело в том, что я не уверен, что этот список жанров правилен. Второе, этот список, очевидно, не окончателен. А значит он не должен быть жестко зафиксирован в программе.
Значит, так. Вам задание - переписать прогу, чтобы эти списки грузились или из текстового файла или из INI файла.
Вернемся к заполнению заголовка
Нам надо ввести данные об авторе / авторах и переводчике / переводчиках
Так же нажимаем на соответствующую кнопочку с троеточием и работаем в открывшемся окне.
Вы уже наверно заметили, что мне прискучило очень уж подробно расписывать код. Но в данном unit тоже ничего особенного, единственно, пришлось ввести структуру TPerson, я думаю Вы легко разберетесь зачем она мне нужна.
Мне интереснее, совершенствование программы. Представьте ситуацию, Вы делаете 10 книг (или 100) одного автора и каждый раз делая новую книгу, заполняете опять и опять данные об этом человеке. Мне было бы лень. Ваши предложения?…