Create single-page HTML output, prepares the terrain for PDF/ePub output

This commit is contained in:
Stéphane Goetz
2015-11-06 22:44:34 +01:00
parent 975f7fdee3
commit 295aee5f77
24 changed files with 2478 additions and 93 deletions

View File

@ -0,0 +1,142 @@
<?php namespace Todaymade\Daux\Format\HTMLFile;
use RuntimeException;
use Todaymade\Daux\Tree\Content;
use Todaymade\Daux\Tree\Directory;
class Book
{
protected $cover;
protected $tree;
protected $pages = [];
public function __construct(Directory $tree, $cover)
{
$this->tree = $tree;
$this->cover = $cover;
}
protected function getStyles()
{
// TODO :: un-hardcode that
return '<style>' . file_get_contents('themes/daux_singlepage/css/main.min.css') . '</style>';
}
protected function getSectionId(Content $node)
{
foreach ($this->pages as $id => $page) {
if ($page['page'] == $node) {
return $id;
}
}
throw new RuntimeException("Could not find the content page");
}
protected function buildNavigation(Directory $tree)
{
$nav = [];
foreach ($tree->getEntries() as $node) {
if ($node instanceof Content) {
if ($node->isIndex()) {
continue;
}
$nav[] = [
'title' => $node->getTitle(),
'href' => '#section_' . $this->getSectionId($node),
];
} elseif ($node instanceof Directory) {
if (!$node->hasContent()) {
continue;
}
$page_index = ($index = $node->getIndexPage())? $index : $node->getFirstPage();
$nav[] = [
'title' => $node->getTitle(),
'href' => "#section_" . $this->getSectionId($page_index),
'children' => $this->buildNavigation($node)
];
}
}
return $nav;
}
private function renderNavigation($entries)
{
$nav = "";
foreach ($entries as $entry) {
if (array_key_exists('children', $entry)) {
if (array_key_exists('href', $entry)) {
$link = '<a href="' . $entry['href'] . '" class="folder">' . $entry['title'] . '</a>';
} else {
$link = '<a href="#" class="aj-nav folder">' . $entry['title'] . '</a>';
}
$link .= $this->renderNavigation($entry['children']);
} else {
$link = '<a href="' . $entry['href'] . '">' . $entry['title'] . '</a>';
}
$nav .= "<li>$link</li>";
}
return "<ul>$nav</ul>";
}
protected function generateTOC()
{
return "<h1>Table of Contents</h1>" .
$this->renderNavigation($this->buildNavigation($this->tree)) .
"</div><div class=\"page-break\">&nbsp;</div>";
}
protected function generateCover()
{
return "<div style='margin:4em 30% 4em 0;'>" .
"<h1 style='font-size:40pt; margin-bottom:0;'>{$this->cover['title']}</h1>" .
"<p><strong>{$this->cover['subject']}</strong> by {$this->cover['author']}</p>" .
"</div><div class=\"page-break\">&nbsp;</div>";
}
protected function generatePages()
{
$content = '';
foreach ($this->pages as $section => $page) {
$content .= '<a id="section_' . $section . '"></a>';
$content .= '<h1>' . $page['page']->getTitle() . '</h1>';
$content .= '<section class="content">' . $page['content'] . '</section>';
$content .= '<div class="page-break">&nbsp;</div>';
}
return $content;
}
public function addPage($page, $content)
{
$this->pages[] = ['page' => $page, 'content' => $content];
}
public function generateHead()
{
$head = [
"<title>{$this->cover['title']}</title>",
"<meta name='description' content='{$this->cover['subject']}' />",
"<meta name='author' content='{$this->cover['author']}'>",
"<meta charset='UTF-8'>",
$this->getStyles(),
];
return '<head>' . implode('', $head) . '</head>';
}
public function generateBody()
{
return '<body>' . $this->generateCover() . $this->generateTOC() . $this->generatePages() . '</body>';
}
public function generate()
{
return "<!DOCTYPE html><html>" . $this->generateHead() . $this->generateBody() . "</html>";
}
}

View File

@ -0,0 +1,34 @@
<?php namespace Todaymade\Daux\Format\HTMLFile;
use Todaymade\Daux\Format\Base\EmbedImages;
use Todaymade\Daux\Tree\Raw;
class ContentPage extends \Todaymade\Daux\Format\Base\ContentPage
{
public $attachments = [];
protected function generatePage()
{
$content = parent::generatePage();
//Embed images
// We do it after generation so we can catch the images that were in html already
$content = (new EmbedImages($this->params['tree']))
->embed(
$content,
$this->file,
function ($src, array $attributes, Raw $file) {
$content = base64_encode(file_get_contents($file->getPath()));
$attr = '';
foreach ($attributes as $name => $value) {
$attr .= ' ' .$name . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
}
return "<img $attr src=\"data:image/png;base64,$content\"/>";
}
);
return $content;
}
}

View File

@ -0,0 +1,103 @@
<?php namespace Todaymade\Daux\Format\HTMLFile;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Todaymade\Daux\Console\RunAction;
use Todaymade\Daux\ContentTypes\Markdown\ContentType;
use Todaymade\Daux\Daux;
class Generator implements \Todaymade\Daux\Format\Base\Generator
{
use RunAction;
/** @var Daux */
protected $daux;
/**
* @param Daux $daux
*/
public function __construct(Daux $daux)
{
$this->daux = $daux;
}
/**
* @return array
*/
public function getContentTypes()
{
return [
'markdown' => new ContentType($this->daux->getParams())
];
}
protected function initPDF()
{
// create new PDF document
$pdf = new Book(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$params = $this->daux->getParams();
// set document information
$pdf->SetCreator(PDF_CREATOR);
// set default header data
$pdf->SetHeaderData('', 0, $params['title'], $params['tagline']);
// set header and footer fonts
$pdf->setHeaderFont(array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// set auto page breaks
$pdf->SetAutoPageBreak(true, PDF_MARGIN_BOTTOM);
// set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// set font
$pdf->SetFont('helvetica', '', 10);
return $pdf;
}
/**
* {@inheritdoc}
*/
public function generateAll(InputInterface $input, OutputInterface $output, $width)
{
$params = $this->daux->getParams();
$data = ['author' => $params['author'], 'title' => $params['title'], 'subject' => $params['tagline']];
$book = new Book($this->daux->tree, $data);
$current = $this->daux->tree->getIndexPage();
while ($current) {
$this->runAction(
"Generating " . $current->getTitle(),
$output,
$width,
function () use ($book, $current, $params) {
$contentType = $this->daux->getContentTypeHandler()->getType($current);
$content = ContentPage::fromFile($current, $params, $contentType)->getContent();
$book->addPage($current, $content);
}
);
$current = $current->getNext();
}
$content = $book->generate();
file_put_contents($input->getOption('destination') . '/file.html', $content);
}
}