Skip to content

Commit

Permalink
Add: Generating Epub samples with adherence to the Epub 3 checking pr…
Browse files Browse the repository at this point in the history
…ocedures
  • Loading branch information
Sambit003 committed Feb 1, 2025
1 parent 5063a36 commit ae7e4fd
Show file tree
Hide file tree
Showing 11 changed files with 350 additions and 38 deletions.
2 changes: 1 addition & 1 deletion samples/Sample_Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}

// Set writers
$writers = ['Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf'];
$writers = ['Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf', 'EPub3' => 'epub'];

// Set PDF renderer
if (null === Settings::getPdfRendererPath()) {
Expand Down
11 changes: 11 additions & 0 deletions src/PhpWord/Shared/ZipArchive.php
Original file line number Diff line number Diff line change
Expand Up @@ -423,4 +423,15 @@ public function pclzipLocateName($filename)

return ($listIndex > -1) ? $listIndex : false;
}

/**
* Add an empty directory to the zip archive (emulate \ZipArchive).
*
* @param string $dirname Directory name to add to the zip archive
*/
public function addEmptyDir(string $dirname): bool
{
// Create a directory entry by adding an empty file with trailing slash
return $this->addFromString(rtrim($dirname, '/') . '/', '');
}
}
26 changes: 13 additions & 13 deletions src/PhpWord/Writer/EPub3.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

namespace PhpOffice\PhpWord\Writer;

use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Writer\EPub3\Part\AbstractPart;

Expand All @@ -42,14 +41,16 @@ public function __construct(?PhpWord $phpWord = null)
'Toc' => 'toc.ncx',
'Styles' => 'styles.css',
'Manifest' => 'META-INF/container.xml',
'Nav' => 'nav.xhtml',
'ContentXhtml' => 'content.xhtml',
];
foreach (array_keys($this->parts) as $partName) {
$partClass = static::class . '\\Part\\' . $partName;
if (class_exists($partClass)) {
/** @var AbstractPart $partObject Type hint */
$partObject = new $partClass();
$partObject->setParentWriter($this);
$this->writerParts[strtolower($partName)] = $partObject;
/** @var WriterPartInterface $part */
$part = new $partClass($partName === 'Content' || $partName === 'ContentXhtml' ? $phpWord : null);
$part->setParentWriter($this);
$this->writerParts[strtolower($partName)] = $part;
}
}

Expand All @@ -65,13 +66,11 @@ public function save(string $filename): void
$filename = $this->getTempFile($filename);
$zip = $this->getZipArchive($filename);

// Add section media files
$sectionMedia = Media::getElements('section');
if (!empty($sectionMedia)) {
$this->addFilesToPackage($zip, $sectionMedia);
}
// Add mimetype first without compression
$zip->addFromString('mimetype', 'application/epub+zip');
$zip->addEmptyDir('META-INF');

// Write parts
// Add other files
foreach ($this->parts as $partName => $fileName) {
if ($fileName === '') {
continue;
Expand All @@ -80,12 +79,13 @@ public function save(string $filename): void
if (!$part instanceof AbstractPart) {
continue;
}

$zip->addFromString($fileName, $part->write());
}

// Close zip archive and cleanup temp file
// Close zip archive
$zip->close();

// Cleanup temp file
$this->cleanupTempFile();
}
}
7 changes: 3 additions & 4 deletions src/PhpWord/Writer/EPub3/Part/AbstractPart.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
namespace PhpOffice\PhpWord\Writer\EPub3\Part;

use PhpOffice\PhpWord\Writer\AbstractWriter;
use PhpOffice\PhpWord\Writer\WriterPartInterface;

/**
* Abstract class for EPub3 parts.
*/
abstract class AbstractPart
abstract class AbstractPart implements WriterPartInterface
{
/**
* Parent writer.
Expand All @@ -35,11 +36,9 @@ abstract class AbstractPart
/**
* Set parent writer.
*/
public function setParentWriter(AbstractWriter $writer): self
public function setParentWriter(AbstractWriter $writer): void
{
$this->parentWriter = $writer;

return $this;
}

/**
Expand Down
113 changes: 98 additions & 15 deletions src/PhpWord/Writer/EPub3/Part/Content.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,113 @@

namespace PhpOffice\PhpWord\Writer\EPub3\Part;

use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
use XMLWriter;

/**
* Class for EPub3 content part.
*/
class Content extends AbstractPart
{
/**
* PHPWord object.
*
* @var ?PhpWord
*/
private $phpWord;

/**
* Constructor.
*/
public function __construct(?PhpWord $phpWord = null)
{
$this->phpWord = $phpWord;
}

/**
* Get XML Writer.
*
* @return XMLWriter
*/
protected function getXmlWriter()
{
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->startDocument('1.0', 'UTF-8');

return $xmlWriter;
}

/**
* Write part content.
*/
public function write(): string
{
$content = '<?xml version="1.0" encoding="UTF-8"?>';
$content .= '<package xmlns="http://www.idpf.org/2007/opf" version="3.0">';
$content .= '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/">';
$content .= '<dc:title>Sample EPub3 Document</dc:title>';
$content .= '<dc:language>en</dc:language>';
$content .= '</metadata>';
$content .= '<manifest>';
$content .= '<item id="content" href="content.xhtml" media-type="application/xhtml+xml"/>';
$content .= '</manifest>';
$content .= '<spine>';
$content .= '<itemref idref="content"/>';
$content .= '</spine>';
$content .= '</package>';

return $content;
if ($this->phpWord === null) {
throw new Exception('No PhpWord assigned.');
}

$xmlWriter = $this->getXmlWriter();
$docInfo = $this->phpWord->getDocInfo();

// Write package
$xmlWriter->startElement('package');
$xmlWriter->writeAttribute('xmlns', 'http://www.idpf.org/2007/opf');
$xmlWriter->writeAttribute('version', '3.0');
$xmlWriter->writeAttribute('unique-identifier', 'book-id');
$xmlWriter->writeAttribute('xml:lang', 'en');

// Write metadata
$xmlWriter->startElement('metadata');
$xmlWriter->writeAttribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/');
$xmlWriter->writeAttribute('xmlns:opf', 'http://www.idpf.org/2007/opf');

// Required elements
$xmlWriter->startElement('dc:identifier');
$xmlWriter->writeAttribute('id', 'book-id');
$xmlWriter->text('book-id-' . uniqid());
$xmlWriter->endElement();
$xmlWriter->writeElement('dc:title', $docInfo->getTitle() ?: 'Untitled');
$xmlWriter->writeElement('dc:language', 'en');

// Required modified timestamp
$xmlWriter->startElement('meta');
$xmlWriter->writeAttribute('property', 'dcterms:modified');
$xmlWriter->text(date('Y-m-d\TH:i:s\Z'));
$xmlWriter->endElement();

$xmlWriter->endElement(); // metadata

// Write manifest
$xmlWriter->startElement('manifest');

// Add nav document (required)
$xmlWriter->startElement('item');
$xmlWriter->writeAttribute('id', 'nav');
$xmlWriter->writeAttribute('href', 'nav.xhtml');
$xmlWriter->writeAttribute('media-type', 'application/xhtml+xml');
$xmlWriter->writeAttribute('properties', 'nav');
$xmlWriter->endElement();

// Add content document
$xmlWriter->startElement('item');
$xmlWriter->writeAttribute('id', 'content');
$xmlWriter->writeAttribute('href', 'content.xhtml');
$xmlWriter->writeAttribute('media-type', 'application/xhtml+xml');
$xmlWriter->endElement();

$xmlWriter->endElement(); // manifest

// Write spine
$xmlWriter->startElement('spine');
$xmlWriter->startElement('itemref');
$xmlWriter->writeAttribute('idref', 'content');
$xmlWriter->endElement();
$xmlWriter->endElement(); // spine

$xmlWriter->endElement(); // package

return $xmlWriter->outputMemory(true);
}
}
105 changes: 105 additions & 0 deletions src/PhpWord/Writer/EPub3/Part/ContentXhtml.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

namespace PhpOffice\PhpWord\Writer\EPub3\Part;

use PhpOffice\PhpWord\PhpWord;
use XMLWriter;

/**
* Class for EPub3 content.xhtml part.
*/
class ContentXhtml extends AbstractPart
{
/**
* PHPWord object.
*
* @var ?PhpWord
*/
private $phpWord;

/**
* Constructor.
*/
public function __construct(?PhpWord $phpWord = null)
{
$this->phpWord = $phpWord;
}

/**
* Get XML Writer.
*
* @return XMLWriter
*/
protected function getXmlWriter()
{
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();

return $xmlWriter;
}

/**
* Write part content.
*/
public function write(): string
{
if ($this->phpWord === null) {
throw new \PhpOffice\PhpWord\Exception\Exception('No PhpWord assigned.');
}

$xmlWriter = $this->getXmlWriter();

$xmlWriter->startDocument('1.0', 'UTF-8');
$xmlWriter->startElement('html');
$xmlWriter->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
$xmlWriter->writeAttribute('xmlns:epub', 'http://www.idpf.org/2007/ops');
$xmlWriter->startElement('head');
$xmlWriter->writeElement('title', $this->phpWord->getDocInfo()->getTitle() ?: 'Untitled');
$xmlWriter->endElement(); // head
$xmlWriter->startElement('body');

// Write sections content
foreach ($this->phpWord->getSections() as $section) {
$xmlWriter->startElement('div');
$xmlWriter->writeAttribute('class', 'section');

foreach ($section->getElements() as $element) {
if ($element instanceof \PhpOffice\PhpWord\Element\TextRun) {
$xmlWriter->startElement('p');
foreach ($element->getElements() as $textElement) {
if ($textElement instanceof \PhpOffice\PhpWord\Element\Text) {
$xmlWriter->text($textElement->getText());
} elseif (method_exists($textElement, 'getText')) {
$xmlWriter->text($textElement->getText());
}
}
$xmlWriter->endElement(); // p
} elseif (method_exists($element, 'getText')) {
$textValue = $element->getText();
if ($textValue instanceof \PhpOffice\PhpWord\Element\TextRun) {
$xmlWriter->startElement('p');
foreach ($textValue->getElements() as $childElement) {
if ($childElement instanceof \PhpOffice\PhpWord\Element\Text) {
$xmlWriter->text($childElement->getText());
} elseif (method_exists($childElement, 'getText')) {
$xmlWriter->text($childElement->getText());
}
}
$xmlWriter->endElement(); // p
} else {
$xmlWriter->startElement('p');
$xmlWriter->text($textValue);
$xmlWriter->endElement(); // p
}
}
}

$xmlWriter->endElement(); // div
}

$xmlWriter->endElement(); // body
$xmlWriter->endElement(); // html

return $xmlWriter->outputMemory(true);
}
}
Loading

0 comments on commit ae7e4fd

Please sign in to comment.