changes
This commit is contained in:
+345
-127
@@ -46,7 +46,15 @@ if (!$book) {
|
||||
$_SESSION['error'] = "Книга не найдена";
|
||||
redirect('books.php');
|
||||
}
|
||||
// Получаем информацию об авторе
|
||||
$author_info = null;
|
||||
if ($book) {
|
||||
$stmt = $pdo->prepare("SELECT display_name, username FROM users WHERE id = ?");
|
||||
$stmt->execute([$book['user_id']]);
|
||||
$author_info = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$author_name = $author_info['display_name'] ?? $author_info['username'] ?? 'Неизвестный автор';
|
||||
// Функция для очистки имени файла
|
||||
// function cleanFilename($filename) {
|
||||
// return preg_replace('/[^a-zA-Z0-9_\-]/', '_', $filename);
|
||||
@@ -57,7 +65,104 @@ function markdownToPlainText($markdown) {
|
||||
// Обрабатываем диалоги (заменяем - на —)
|
||||
$markdown = preg_replace('/^- (.+)$/m', "— $1", $markdown);
|
||||
|
||||
// Убираем Markdown разметку
|
||||
// Убираем Markdown разметку, но сохраняем переносы строк
|
||||
$text = $markdown;
|
||||
|
||||
// Убираем заголовки
|
||||
$text = preg_replace('/^#+\s+/m', '', $text);
|
||||
|
||||
// Убираем жирный и курсив
|
||||
$text = preg_replace('/\*\*(.*?)\*\*/', '$1', $text);
|
||||
$text = preg_replace('/\*(.*?)\*/', '$1', $text);
|
||||
$text = preg_replace('/__(.*?)__/', '$1', $text);
|
||||
$text = preg_replace('/_(.*?)_/', '$1', $text);
|
||||
|
||||
// Убираем зачеркивание
|
||||
$text = preg_replace('/~~(.*?)~~/', '$1', $text);
|
||||
|
||||
// Убираем код (встроенный)
|
||||
$text = preg_replace('/`(.*?)`/', '$1', $text);
|
||||
|
||||
// Убираем блоки кода (сохраняем содержимое)
|
||||
$text = preg_replace('/```.*?\n(.*?)```/s', '$1', $text);
|
||||
|
||||
// Убираем ссылки
|
||||
$text = preg_replace('/\[(.*?)\]\(.*?\)/', '$1', $text);
|
||||
|
||||
// Обрабатываем списки - заменяем маркеры на *
|
||||
$text = preg_replace('/^[\*\-+]\s+/m', '* ', $text);
|
||||
$text = preg_replace('/^\d+\.\s+/m', '* ', $text);
|
||||
|
||||
// Обрабатываем цитаты
|
||||
$text = preg_replace('/^>\s+/m', '', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
// Улучшенная функция для разбивки Markdown на абзацы с сохранением структуры
|
||||
function markdownToParagraphs($markdown) {
|
||||
// Нормализуем переносы строк
|
||||
$text = str_replace(["\r\n", "\r"], "\n", $markdown);
|
||||
|
||||
// Обрабатываем диалоги (заменяем - на —)
|
||||
$text = preg_replace('/^- (.+)$/m', "— $1", $text);
|
||||
|
||||
// Разбиваем на строки
|
||||
$lines = explode("\n", $text);
|
||||
$paragraphs = [];
|
||||
$currentParagraph = '';
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$trimmedLine = trim($line);
|
||||
|
||||
// Пустая строка - конец абзаца
|
||||
if (empty($trimmedLine)) {
|
||||
if (!empty($currentParagraph)) {
|
||||
$paragraphs[] = $currentParagraph;
|
||||
$currentParagraph = '';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Диалог (начинается с —) всегда начинает новый абзац
|
||||
if (str_starts_with($trimmedLine, '—')) {
|
||||
if (!empty($currentParagraph)) {
|
||||
$paragraphs[] = $currentParagraph;
|
||||
}
|
||||
$currentParagraph = $trimmedLine;
|
||||
$paragraphs[] = $currentParagraph;
|
||||
$currentParagraph = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Заголовки (начинаются с #) всегда начинают новый абзац
|
||||
if (str_starts_with($trimmedLine, '#')) {
|
||||
if (!empty($currentParagraph)) {
|
||||
$paragraphs[] = $currentParagraph;
|
||||
}
|
||||
$currentParagraph = preg_replace('/^#+\s+/', '', $trimmedLine);
|
||||
$paragraphs[] = $currentParagraph;
|
||||
$currentParagraph = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
// Обычный текст - добавляем к текущему абзацу
|
||||
if (!empty($currentParagraph)) {
|
||||
$currentParagraph .= ' ' . $trimmedLine;
|
||||
} else {
|
||||
$currentParagraph = $trimmedLine;
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем последний абзац
|
||||
if (!empty($currentParagraph)) {
|
||||
$paragraphs[] = $currentParagraph;
|
||||
}
|
||||
|
||||
return $paragraphs;
|
||||
}
|
||||
|
||||
// Функция для очистки Markdown разметки
|
||||
function cleanMarkdown($markdown) {
|
||||
$text = $markdown;
|
||||
|
||||
// Убираем заголовки
|
||||
@@ -79,7 +184,7 @@ function markdownToPlainText($markdown) {
|
||||
// Убираем ссылки
|
||||
$text = preg_replace('/\[(.*?)\]\(.*?\)/', '$1', $text);
|
||||
|
||||
// Обрабатываем списки
|
||||
// Обрабатываем списки - убираем маркеры
|
||||
$text = preg_replace('/^[\*\-+]\s+/m', '', $text);
|
||||
$text = preg_replace('/^\d+\.\s+/m', '', $text);
|
||||
|
||||
@@ -129,38 +234,38 @@ function formatPlainText($text) {
|
||||
// Обработка экспорта
|
||||
switch ($format) {
|
||||
case 'pdf':
|
||||
exportPDF($book, $chapters, $is_public);
|
||||
exportPDF($book, $chapters, $is_public, $author_name);
|
||||
break;
|
||||
case 'docx':
|
||||
exportDOCX($book, $chapters, $is_public);
|
||||
exportDOCX($book, $chapters, $is_public, $author_name);
|
||||
break;
|
||||
case 'odt':
|
||||
exportODT($book, $chapters, $is_public);
|
||||
exportODT($book, $chapters, $is_public, $author_name);
|
||||
break;
|
||||
case 'html':
|
||||
exportHTML($book, $chapters, $is_public);
|
||||
exportHTML($book, $chapters, $is_public, $author_name);
|
||||
break;
|
||||
case 'txt':
|
||||
exportTXT($book, $chapters, $is_public);
|
||||
exportTXT($book, $chapters, $is_public, $author_name);
|
||||
break;
|
||||
default:
|
||||
$_SESSION['error'] = "Неверный формат экспорта";
|
||||
redirect($share_token ? "view_book.php?share_token=$share_token" : "book_edit.php?id=$book_id");
|
||||
}
|
||||
|
||||
function exportPDF($book, $chapters, $is_public) {
|
||||
function exportPDF($book, $chapters, $is_public, $author_name) {
|
||||
global $Parsedown;
|
||||
|
||||
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
|
||||
|
||||
// Устанавливаем метаданные документа
|
||||
$pdf->SetCreator(APP_NAME);
|
||||
$pdf->SetAuthor($is_public ? 'Автор' : ($_SESSION['display_name'] ?? 'Автор'));
|
||||
$pdf->SetAuthor($author_name);
|
||||
$pdf->SetTitle($book['title']);
|
||||
$pdf->SetSubject($book['genre'] ?? '');
|
||||
|
||||
// Устанавливаем margins
|
||||
$pdf->SetMargins(15, 20, 15);
|
||||
$pdf->SetMargins(15, 25, 15);
|
||||
$pdf->SetHeaderMargin(10);
|
||||
$pdf->SetFooterMargin(10);
|
||||
|
||||
@@ -174,10 +279,30 @@ function exportPDF($book, $chapters, $is_public) {
|
||||
$pdf->SetFont('dejavusans', '', 12);
|
||||
|
||||
// Заголовок книги
|
||||
$pdf->SetFont('dejavusans', 'B', 16);
|
||||
$pdf->SetFont('dejavusans', 'B', 18);
|
||||
$pdf->Cell(0, 10, $book['title'], 0, 1, 'C');
|
||||
$pdf->Ln(2);
|
||||
|
||||
// Автор
|
||||
$pdf->SetFont('dejavusans', 'I', 14);
|
||||
$pdf->Cell(0, 10, $author_name, 0, 1, 'C');
|
||||
$pdf->Ln(5);
|
||||
|
||||
// Обложка книги
|
||||
if (!empty($book['cover_image'])) {
|
||||
$cover_path = COVERS_PATH . $book['cover_image'];
|
||||
if (file_exists($cover_path)) {
|
||||
list($width, $height) = getimagesize($cover_path);
|
||||
$max_width = 80;
|
||||
$ratio = $width / $height;
|
||||
$new_height = $max_width / $ratio;
|
||||
|
||||
$x = (210 - $max_width) / 2;
|
||||
$pdf->Image($cover_path, $x, $pdf->GetY(), $max_width, $new_height, '', '', 'N', false, 300, '', false, false, 0, false, false, false);
|
||||
$pdf->Ln($new_height + 5);
|
||||
}
|
||||
}
|
||||
|
||||
// Жанр
|
||||
if (!empty($book['genre'])) {
|
||||
$pdf->SetFont('dejavusans', 'I', 12);
|
||||
@@ -187,8 +312,27 @@ function exportPDF($book, $chapters, $is_public) {
|
||||
|
||||
// Описание
|
||||
if (!empty($book['description'])) {
|
||||
$pdf->SetFont('dejavusans', '', 12);
|
||||
$pdf->MultiCell(0, 8, $book['description'], 0, 'J');
|
||||
$pdf->SetFont('dejavusans', '', 11);
|
||||
$pdf->MultiCell(0, 6, $book['description'], 0, 'J');
|
||||
$pdf->Ln(10);
|
||||
}
|
||||
|
||||
// Интерактивное оглавление - СОЗДАЕМ ССЫЛКИ
|
||||
$chapterLinks = [];
|
||||
if (!empty($chapters)) {
|
||||
$pdf->SetFont('dejavusans', 'B', 14);
|
||||
$pdf->Cell(0, 10, 'Оглавление', 0, 1, 'C');
|
||||
$pdf->Ln(5);
|
||||
|
||||
$toc_page = $pdf->getPage();
|
||||
|
||||
$pdf->SetFont('dejavusans', '', 11);
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$chapter_number = $index + 1;
|
||||
$link = $pdf->AddLink();
|
||||
$chapterLinks[$chapter['id']] = $link; // Сохраняем ссылку для этой главы
|
||||
$pdf->Cell(0, 6, "{$chapter_number}. {$chapter['title']}", 0, 1, 'L', false, $link);
|
||||
}
|
||||
$pdf->Ln(10);
|
||||
}
|
||||
|
||||
@@ -196,34 +340,37 @@ function exportPDF($book, $chapters, $is_public) {
|
||||
$pdf->Line(15, $pdf->GetY(), 195, $pdf->GetY());
|
||||
$pdf->Ln(10);
|
||||
|
||||
// Главы
|
||||
// Главы с закладками и правильными ссылками
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$pdf->SetFont('dejavusans', '', 12);
|
||||
// Добавляем новую страницу для каждой главы
|
||||
$pdf->AddPage();
|
||||
|
||||
// УСТАНАВЛИВАЕМ ЯКОРЬ ДЛЯ ССЫЛКИ
|
||||
if (isset($chapterLinks[$chapter['id']])) {
|
||||
$pdf->SetLink($chapterLinks[$chapter['id']]);
|
||||
}
|
||||
|
||||
// Устанавливаем закладку для этой главы
|
||||
$pdf->Bookmark($chapter['title'], 0, 0, '', 'B', array(0, 0, 0));
|
||||
|
||||
// Название главы
|
||||
$pdf->SetFont('dejavusans', 'B', 14);
|
||||
$pdf->Cell(0, 8, $chapter['title'], 0, 1);
|
||||
$pdf->Ln(2);
|
||||
|
||||
// Контент главы (форматированный HTML)
|
||||
// Контент главы
|
||||
$pdf->SetFont('dejavusans', '', 11);
|
||||
$htmlContent = $Parsedown->text($chapter['content']);
|
||||
$pdf->writeHTML($htmlContent, true, false, true, false, '');
|
||||
|
||||
$pdf->Ln(8);
|
||||
|
||||
// Разделитель между главами (кроме последней)
|
||||
if ($index < count($chapters) - 1) {
|
||||
$pdf->Line(15, $pdf->GetY(), 195, $pdf->GetY());
|
||||
$pdf->Ln(8);
|
||||
}
|
||||
}
|
||||
|
||||
// Футер с информацией
|
||||
$pdf->SetY(-25);
|
||||
$pdf->SetFont('dejavusans', 'I', 8);
|
||||
$pdf->Cell(0, 6, 'Экспортировано из ' . APP_NAME . ' - ' . date('d.m.Y H:i'), 0, 1, 'C');
|
||||
$pdf->Cell(0, 6, 'Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')), 0, 1, 'C');
|
||||
$pdf->Cell(0, 6, 'Автор: ' . $author_name . ' | Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')), 0, 1, 'C');
|
||||
|
||||
// Отправляем файл
|
||||
$filename = cleanFilename($book['title']) . '.pdf';
|
||||
@@ -231,7 +378,7 @@ function exportPDF($book, $chapters, $is_public) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function exportDOCX($book, $chapters, $is_public) {
|
||||
function exportDOCX($book, $chapters, $is_public, $author_name) {
|
||||
global $Parsedown;
|
||||
|
||||
$phpWord = new PhpWord();
|
||||
@@ -245,8 +392,25 @@ function exportDOCX($book, $chapters, $is_public) {
|
||||
|
||||
// Заголовок книги
|
||||
$section->addText($book['title'], ['bold' => true, 'size' => 16], ['alignment' => 'center']);
|
||||
$section->addTextBreak(1);
|
||||
|
||||
// Автор
|
||||
$section->addText($author_name, ['italic' => true, 'size' => 14], ['alignment' => 'center']);
|
||||
$section->addTextBreak(2);
|
||||
|
||||
// Обложка книги
|
||||
if (!empty($book['cover_image'])) {
|
||||
$cover_path = COVERS_PATH . $book['cover_image'];
|
||||
if (file_exists($cover_path)) {
|
||||
$section->addImage($cover_path, [
|
||||
'width' => 150,
|
||||
'height' => 225,
|
||||
'alignment' => 'center'
|
||||
]);
|
||||
$section->addTextBreak(2);
|
||||
}
|
||||
}
|
||||
|
||||
// Жанр
|
||||
if (!empty($book['genre'])) {
|
||||
$section->addText('Жанр: ' . $book['genre'], ['italic' => true], ['alignment' => 'center']);
|
||||
@@ -255,33 +419,52 @@ function exportDOCX($book, $chapters, $is_public) {
|
||||
|
||||
// Описание
|
||||
if (!empty($book['description'])) {
|
||||
$section->addText($book['description']);
|
||||
$descriptionParagraphs = markdownToParagraphs($book['description']);
|
||||
foreach ($descriptionParagraphs as $paragraph) {
|
||||
$section->addText($paragraph);
|
||||
}
|
||||
$section->addTextBreak(2);
|
||||
}
|
||||
|
||||
// Интерактивное оглавление
|
||||
if (!empty($chapters)) {
|
||||
$section->addText('Оглавление', ['bold' => true, 'size' => 14], ['alignment' => 'center']);
|
||||
$section->addTextBreak(1);
|
||||
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$chapter_number = $index + 1;
|
||||
// Создаем гиперссылку на заголовок главы - ИСПРАВЛЕННЫЙ СИНТАКСИС
|
||||
$section->addLink("chapter_{$chapter['id']}", "{$chapter_number}. {$chapter['title']}", null, null, true);
|
||||
$section->addTextBreak(1);
|
||||
}
|
||||
$section->addTextBreak(2);
|
||||
}
|
||||
|
||||
// Разделитель
|
||||
$section->addText('СОДЕРЖАНИЕ', ['bold' => true, 'size' => 14], ['alignment' => 'center']);
|
||||
$section->addTextBreak(2);
|
||||
$section->addPageBreak();
|
||||
|
||||
// Главы
|
||||
// Главы с закладками - ДОБАВЛЯЕМ ПРАВИЛЬНЫЕ ЗАКЛАДКИ
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
// Добавляем закладку для главы ПЕРЕД заголовком
|
||||
$section->addBookmark("chapter_{$chapter['id']}");
|
||||
|
||||
// Заголовок главы
|
||||
$section->addText($chapter['title'], ['bold' => true, 'size' => 14]);
|
||||
$section->addTextBreak(1);
|
||||
|
||||
// Контент главы (форматированный HTML)
|
||||
$htmlContent = $Parsedown->text($chapter['content']);
|
||||
|
||||
// Упрощенное добавление HTML контента
|
||||
$plainContent = strip_tags($htmlContent);
|
||||
$paragraphs = explode("\n\n", $plainContent);
|
||||
// Получаем очищенный текст и разбиваем на абзацы
|
||||
$cleanContent = cleanMarkdown($chapter['content']);
|
||||
$paragraphs = markdownToParagraphs($cleanContent);
|
||||
|
||||
// Добавляем каждый абзац
|
||||
foreach ($paragraphs as $paragraph) {
|
||||
if (trim($paragraph)) {
|
||||
if (!empty(trim($paragraph))) {
|
||||
$section->addText($paragraph);
|
||||
$section->addTextBreak(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Разрыв страницы между главами (кроме последней)
|
||||
// Добавляем разрыв страницы между главами (кроме последней)
|
||||
if ($index < count($chapters) - 1) {
|
||||
$section->addPageBreak();
|
||||
}
|
||||
@@ -290,7 +473,7 @@ function exportDOCX($book, $chapters, $is_public) {
|
||||
// Футер
|
||||
$section->addTextBreak(2);
|
||||
$section->addText('Экспортировано из ' . APP_NAME . ' - ' . date('d.m.Y H:i'), ['italic' => true, 'size' => 9]);
|
||||
$section->addText('Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')), ['italic' => true, 'size' => 9]);
|
||||
$section->addText('Автор: ' . $author_name . ' | Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')), ['italic' => true, 'size' => 9]);
|
||||
|
||||
// Сохраняем и отправляем
|
||||
$filename = cleanFilename($book['title']) . '.docx';
|
||||
@@ -302,69 +485,7 @@ function exportDOCX($book, $chapters, $is_public) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function exportODT($book, $chapters, $is_public) {
|
||||
global $Parsedown;
|
||||
|
||||
$phpWord = new PhpWord();
|
||||
|
||||
// Стили документа
|
||||
$phpWord->setDefaultFontName('Liberation Serif');
|
||||
$phpWord->setDefaultFontSize(12);
|
||||
|
||||
// Секция документа
|
||||
$section = $phpWord->addSection();
|
||||
|
||||
// Заголовок книги
|
||||
$section->addText($book['title'], ['bold' => true, 'size' => 16], ['alignment' => 'center']);
|
||||
$section->addTextBreak(2);
|
||||
|
||||
// Жанр
|
||||
if (!empty($book['genre'])) {
|
||||
$section->addText('Жанр: ' . $book['genre'], ['italic' => true], ['alignment' => 'center']);
|
||||
$section->addTextBreak(1);
|
||||
}
|
||||
|
||||
// Описание
|
||||
if (!empty($book['description'])) {
|
||||
$section->addText($book['description']);
|
||||
$section->addTextBreak(2);
|
||||
}
|
||||
|
||||
// Главы
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
// Заголовок главы
|
||||
$section->addText($chapter['title'], ['bold' => true, 'size' => 14]);
|
||||
|
||||
// Контент главы (форматированный HTML)
|
||||
$htmlContent = $Parsedown->text($chapter['content']);
|
||||
$plainContent = strip_tags($htmlContent);
|
||||
$paragraphs = explode("\n\n", $plainContent);
|
||||
|
||||
foreach ($paragraphs as $paragraph) {
|
||||
if (trim($paragraph)) {
|
||||
$section->addText($paragraph);
|
||||
}
|
||||
}
|
||||
|
||||
$section->addTextBreak(2);
|
||||
}
|
||||
|
||||
// Футер
|
||||
$section->addTextBreak(2);
|
||||
$section->addText('Экспортировано из ' . APP_NAME . ' - ' . date('d.m.Y H:i'), ['italic' => true, 'size' => 9]);
|
||||
$section->addText('Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')), ['italic' => true, 'size' => 9]);
|
||||
|
||||
// Сохраняем и отправляем
|
||||
$filename = cleanFilename($book['title']) . '.odt';
|
||||
header('Content-Type: application/vnd.oasis.opendocument.text');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
|
||||
$objWriter = IOFactory::createWriter($phpWord, 'ODText');
|
||||
$objWriter->save('php://output');
|
||||
exit;
|
||||
}
|
||||
|
||||
function exportHTML($book, $chapters, $is_public) {
|
||||
function exportHTML($book, $chapters, $is_public, $author_name) {
|
||||
global $Parsedown;
|
||||
|
||||
$html = '<!DOCTYPE html>
|
||||
@@ -378,7 +499,7 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
font-family: "Times New Roman", serif;
|
||||
line-height: 1.6;
|
||||
margin: 40px;
|
||||
max-width: 800px;
|
||||
max-width: 900px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
color: #333;
|
||||
@@ -387,7 +508,24 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.book-author {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.book-cover {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.book-cover img {
|
||||
max-width: 200px;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
.book-genre {
|
||||
text-align: center;
|
||||
@@ -402,11 +540,41 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
margin: 20px 0;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
.table-of-contents {
|
||||
background: #f8f9fa;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
margin: 20px 0;
|
||||
columns: 2;
|
||||
column-gap: 2rem;
|
||||
}
|
||||
.table-of-contents h3 {
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
column-span: all;
|
||||
}
|
||||
.table-of-contents ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
.table-of-contents li {
|
||||
margin-bottom: 5px;
|
||||
padding: 5px 0;
|
||||
break-inside: avoid;
|
||||
}
|
||||
.table-of-contents a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
.table-of-contents a:hover {
|
||||
color: #007bff;
|
||||
}
|
||||
.chapter-title {
|
||||
border-bottom: 2px solid #007bff;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 30px;
|
||||
font-size: 20px;
|
||||
scroll-margin-top: 2rem;
|
||||
}
|
||||
.chapter-content {
|
||||
margin: 20px 0;
|
||||
@@ -420,13 +588,22 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
/* Улучшаем отображение абзацев */
|
||||
.chapter-content p {
|
||||
margin-bottom: 1em;
|
||||
text-align: justify;
|
||||
}
|
||||
.dialogue {
|
||||
margin-left: 2rem;
|
||||
font-style: italic;
|
||||
color: #2c5aa0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
/* Остальные стили */
|
||||
.chapter-content h1, .chapter-content h2, .chapter-content h3 {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
.chapter-content p {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.chapter-content blockquote {
|
||||
border-left: 4px solid #007bff;
|
||||
padding-left: 15px;
|
||||
@@ -462,29 +639,51 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
.chapter-content th {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.dialogue {
|
||||
margin-left: 2rem;
|
||||
font-style: italic;
|
||||
color: #2c5aa0;
|
||||
@media (max-width: 768px) {
|
||||
.table-of-contents {
|
||||
columns: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="book-title">' . htmlspecialchars($book['title']) . '</div>';
|
||||
<div class="book-title">' . htmlspecialchars($book['title']) . '</div>
|
||||
<div class="book-author">' . htmlspecialchars($author_name) . '</div>';
|
||||
|
||||
if (!empty($book['genre'])) {
|
||||
$html .= '<div class="book-genre">Жанр: ' . htmlspecialchars($book['genre']) . '</div>';
|
||||
}
|
||||
|
||||
// Обложка книги
|
||||
if (!empty($book['cover_image'])) {
|
||||
$cover_url = COVERS_URL . $book['cover_image'];
|
||||
$html .= '<div class="book-cover">';
|
||||
$html .= '<img src="' . $cover_url . '" alt="' . htmlspecialchars($book['title']) . '">';
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
if (!empty($book['description'])) {
|
||||
$html .= '<div class="book-description">' . nl2br(htmlspecialchars($book['description'])) . '</div>';
|
||||
}
|
||||
|
||||
// Интерактивное оглавление
|
||||
if (!empty($chapters)) {
|
||||
$html .= '<div>';
|
||||
$html .= '<h3>Оглавление</h3>';
|
||||
$html .= '<ul>';
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$chapter_number = $index + 1;
|
||||
$html .= '<li><a href="#chapter-' . $chapter['id'] . '">' . $chapter_number . '. ' . htmlspecialchars($chapter['title']) . '</a></li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
$html .= '<hr style="margin: 30px 0;">';
|
||||
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$html .= '<div class="chapter">';
|
||||
$html .= '<div class="chapter-title">' . htmlspecialchars($chapter['title']) . '</div>';
|
||||
$html .= '<div class="chapter-title" id="chapter-' . $chapter['id'] . '" name="chapter-' . $chapter['id'] . '">' . htmlspecialchars($chapter['title']) . '</div>';
|
||||
|
||||
$htmlContent = $Parsedown->text($chapter['content']);
|
||||
$html .= '<div class="chapter-content">' . $htmlContent . '</div>';
|
||||
@@ -497,7 +696,7 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
|
||||
$html .= '<div class="footer">
|
||||
Экспортировано из ' . APP_NAME . ' - ' . date('d.m.Y H:i') . '<br>
|
||||
Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')) . '
|
||||
Автор: ' . htmlspecialchars($author_name) . ' | Всего глав: ' . count($chapters) . ' | Всего слов: ' . array_sum(array_column($chapters, 'word_count')) . '
|
||||
</div>
|
||||
</body>
|
||||
</html>';
|
||||
@@ -509,10 +708,11 @@ function exportHTML($book, $chapters, $is_public) {
|
||||
exit;
|
||||
}
|
||||
|
||||
function exportTXT($book, $chapters, $is_public) {
|
||||
$content = "=" . str_repeat("=", 60) . "=\n";
|
||||
$content .= str_pad($book['title'], 60, " ", STR_PAD_BOTH) . "\n";
|
||||
$content .= "=" . str_repeat("=", 60) . "=\n\n";
|
||||
function exportTXT($book, $chapters, $is_public, $author_name) {
|
||||
$content = "=" . str_repeat("=", 80) . "=\n";
|
||||
$content .= str_pad($book['title'], 80, " ", STR_PAD_BOTH) . "\n";
|
||||
$content .= str_pad($author_name, 80, " ", STR_PAD_BOTH) . "\n";
|
||||
$content .= "=" . str_repeat("=", 80) . "=\n\n";
|
||||
|
||||
if (!empty($book['genre'])) {
|
||||
$content .= "Жанр: " . $book['genre'] . "\n\n";
|
||||
@@ -520,29 +720,47 @@ function exportTXT($book, $chapters, $is_public) {
|
||||
|
||||
if (!empty($book['description'])) {
|
||||
$content .= "ОПИСАНИЕ:\n";
|
||||
$content .= wordwrap($book['description'], 80) . "\n\n";
|
||||
// Увеличиваем ширину до 144 символов (80 * 1.8)
|
||||
$content .= wordwrap($book['description'], 144) . "\n\n";
|
||||
}
|
||||
|
||||
$content .= str_repeat("-", 80) . "\n\n";
|
||||
// Оглавление
|
||||
if (!empty($chapters)) {
|
||||
$content .= "ОГЛАВЛЕНИЕ:\n";
|
||||
$content .= str_repeat("-", 60) . "\n";
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$chapter_number = $index + 1;
|
||||
$content .= "{$chapter_number}. {$chapter['title']}\n";
|
||||
}
|
||||
$content .= "\n";
|
||||
}
|
||||
|
||||
$content .= str_repeat("-", 144) . "\n\n";
|
||||
|
||||
foreach ($chapters as $index => $chapter) {
|
||||
$content .= $chapter['title'] . "\n";
|
||||
$content .= str_repeat("-", 40) . "\n\n";
|
||||
$content .= str_repeat("-", 60) . "\n\n";
|
||||
|
||||
// Форматируем текст для TXT
|
||||
$plainText = markdownToPlainText($chapter['content']);
|
||||
$formattedText = formatPlainText($plainText);
|
||||
$content .= wordwrap($formattedText, 80) . "\n\n";
|
||||
// Получаем очищенный текст и разбиваем на абзацы
|
||||
$cleanContent = cleanMarkdown($chapter['content']);
|
||||
$paragraphs = markdownToParagraphs($cleanContent);
|
||||
|
||||
foreach ($paragraphs as $paragraph) {
|
||||
if (!empty(trim($paragraph))) {
|
||||
// Увеличиваем ширину до 144 символов
|
||||
$content .= wordwrap($paragraph, 144) . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($index < count($chapters) - 1) {
|
||||
$content .= str_repeat("-", 80) . "\n\n";
|
||||
$content .= str_repeat("-", 144) . "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
$content .= "\n" . str_repeat("=", 80) . "\n";
|
||||
$content .= "\n" . str_repeat("=", 144) . "\n";
|
||||
$content .= "Экспортировано из " . APP_NAME . " - " . date('d.m.Y H:i') . "\n";
|
||||
$content .= "Всего глав: " . count($chapters) . " | Всего слов: " . array_sum(array_column($chapters, 'word_count')) . "\n";
|
||||
$content .= str_repeat("=", 80) . "\n";
|
||||
$content .= "Автор: " . $author_name . " | Всего глав: " . count($chapters) . " | Всего слов: " . array_sum(array_column($chapters, 'word_count')) . "\n";
|
||||
$content .= str_repeat("=", 144) . "\n";
|
||||
|
||||
$filename = cleanFilename($book['title']) . '.txt';
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
Reference in New Issue
Block a user