tei = $tei;
$this->createDirs();
$this->outFileName = $tei->getFileName( anthologize_get_session() ) . '.epub';
$this->proc = new XSLTProcessor();
$anthEpubDir = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'anthologize' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'epub' . DIRECTORY_SEPARATOR;
$this->ncxXSL = $anthEpubDir . 'tei2ncx.xsl';
$this->opfXSL = $anthEpubDir . 'tei2opf.xsl';
//dig up the selected cover image
$cover_item = $this->tei->xpath->query("//anth:param[@name = 'cover']")->item(0);
if ( $cover_item && isset( $cover_item->nodeValue ) ) {
$this->coverImage = $cover_item->nodeValue;
} else {
$this->coverImage = 'none';
}
$this->localizeLinks();
if (is_string($data)) {
if (file_exists($data)) {
//$data is the path to an xslt
$this->htmlXSL = $data;
$this->html = $this->doProc($data, $this->tei->dom);
} else {
// html needs to be a DOMDocument so we can xpath over it to fetch the images
$this->html = new DOMDocument();
$this->html->loadXML($data);
}
} elseif (get_class($data) == 'DOMDocument') {
$this->html = $data;
}
$this->fetchImages();
$this->saveContainer();
$this->saveNCX();
$this->saveOPF();
$this->saveHTML();
//do any final processing, especially things like rewriting the ToC etc that need to happen
//after everything is done
$this->finish();
}
public function fetchImages() {
//TODO: switch to HTML based image work so arbitrary HTML can be passed in.
//will require adjusting the XSL so that it does not remove the full URL
$xpath = $this->tei->xpath;
$srcNodes = $xpath->query("//img/@src");
foreach ($srcNodes as $srcNode) // Iterate through images
{
// Get image url & open file
$image_url = $srcNode->nodeValue;
$image_filename = preg_replace('/^.*\//', '', $image_url); // Erase all but filename from URL (no directories)
$new_filename = $this->saveImage($image_url, $image_filename);
$srcNode->nodeValue = $new_filename;
//TODO: sort out the danger of duplicate file names
}
}
public function saveImage($image_url, $image_filename) {
// TODO: check mimetype of image and assign generated name to file rather than derive from URL as above
//sort out the danger of duplicate file names
$count = 0;
while(file_exists($image_filename)) {
$index = strpos($image_filename, '-');
$countPrefix = (int) substr($image_filename, $index);
$image_filename = substr_replace($image_filename, $count, 0, $index);
}
$exploded = explode('?', $image_filename);
$image_filename = $exploded[0];
$ch = curl_init($image_url);
$fp = fopen($this->oebpsDir . DIRECTORY_SEPARATOR . $image_filename, "w");
// Fetch image from url & put into file
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
return $image_filename;
}
public function createDirs() {
$upload_dir = wp_upload_dir();
$tempDir = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'anthologize-temp';
if(! is_dir($tempDir)) {
mkdir($tempDir);
}
$this->tempDir = $tempDir .
DIRECTORY_SEPARATOR .
sha1(microtime()) . //make sure that if two users export different project from same site, they don't clobber each other
DIRECTORY_SEPARATOR;
$this->epubDir = $this->tempDir . 'epub' ;
$this->oebpsDir = $this->epubDir . DIRECTORY_SEPARATOR . 'OEBPS' . DIRECTORY_SEPARATOR;
$this->metaInfDir = $this->epubDir . DIRECTORY_SEPARATOR . 'META-INF' . DIRECTORY_SEPARATOR;
mkdir($this->tempDir, 0777, true);
mkdir($this->epubDir, 0777, true);
mkdir($this->oebpsDir, 0777, true);
mkdir($this->metaInfDir, 0777, true);
}
public function saveContainer() {
$container_file_contents = '';
$container_file_contents .= '';
$container_file_contents .= '';
$container_file_contents .= '';
$container_file_contents .= '';
$container_file_contents .= '';
file_put_contents($this->metaInfDir . 'container.xml', $container_file_contents);
}
public function saveNCX() {
$ncx = $this->doProc($this->ncxXSL, $this->tei->dom);
$this->rewriteTOC($ncx);
$ncx->save($this->oebpsDir . 'toc.ncx' );
}
public function saveOPF() {
$opf = $this->doProc($this->opfXSL, $this->tei->dom);
// overwrite the bad metadata
//TODO address this in the core xsl
$xpath = new DOMXPath($opf);
$xpath->registerNamespace('dc', 'http://purl.org/dc/elements/1.1/');
$titleNode = $xpath->query("//dc:title")->item(0);
$teiTitleNL = $this->tei->xpath->query("//tei:front/tei:head/tei:bibl/tei:title[@type='main']");
$teiTitle = $teiTitleNL->item(0);
$titleNode->nodeValue = trim($teiTitle->nodeValue);
$teiCreatorNode = $this->tei->xpath->query("//tei:front/tei:head/tei:bibl/tei:author[@role='projectCreator']")->item(0);
/*
* this is being hard-coded in the opf xsl
$creatorNode = $xpath->query("//dc:creator")->item(0);
$creatorNode->nodeValue = trim($teiCreatorNode->nodeValue);
*/
//add a cover image, if it is set
if($this->coverImage != 'none') {
//add the meta element
$metadataNode = $opf->getElementsByTagName('metadata')->item(0);
$coverNode = $opf->createElement('meta');
$coverNode->setAttribute('name', 'cover');
$coverNode->setAttribute('content', 'cover');
$metadataNode->appendChild($coverNode);
//add to the manifest
$manifestNode = $opf->getElementsByTagName('manifest')->item(0);
$coverItemNode = $opf->createElement('item');
$coverItemNode->setAttribute('href', 'cover.jpg');
$coverItemNode->setAttribute('id', 'cover');
$coverItemNode->setAttribute('media-type', 'image/jpeg');
$manifestNode->appendChild($coverItemNode);
//copy the image over to the epub tmp dir
$coverImgPath = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'anthologize' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'epub' . DIRECTORY_SEPARATOR . 'covers' . DIRECTORY_SEPARATOR . $this->coverImage;
copy($coverImgPath, $this->oebpsDir . 'cover.jpg');
}
$opf->save($this->oebpsDir . 'book.opf');
}
public function saveHTML() {
$this->html->save($this->oebpsDir . 'main_content.html');
}
public function doProc($xsl, $dom) {
$xslDOM = new DOMDocument();
$xslDOM->load($xsl);
$do_colophon = isset( $this->tei->outputParams['colophon'] ) && 'on' == $this->tei->outputParams['colophon'] ? 'on' : false;
$this->proc->setParameter( '', 'doColophon', $do_colophon );
$this->proc->importStylesheet($xslDOM);
return $this->proc->transformToDoc($dom);
}
public function output() {
$source = $this->epubDir;
$destination = $this->tempDir . "book.epub";
if (is_readable($source) === true) {
// ZIP extension code
if (extension_loaded('zip') === true) {
// make the archive first
// EPUB wants the first file to be mimetype, and not compressed. PHP can't do this
// This fancy trick came from http://stackoverflow.com/questions/3142810/adding-a-file-to-a-zip-uncompressed-with-php
file_put_contents($destination, base64_decode("UEsDBAoAAAAAAOmRAT1vYassFAAAABQAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi9lcHViK3ppcFBLAQIUAAoAAAAAAOmRAT1vYassFAAAABQAAAAIAAAAAAAAAAAAIAAAAAAAAABtaW1ldHlwZVBLBQYAAAAAAQABADYAAAA6AAAAAAA="));
$zip = new ZipArchive();
// open archive
if ($zip->open($destination)) {
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
// Iterate through files & directories and add to archive object
foreach ($files as $file) {
$exploded = explode(DIRECTORY_SEPARATOR, $file);
if($exploded[count($exploded) - 1] == "." || $exploded[count($exploded) - 1] == "..") {
continue;
}
if (is_dir($file) === true) { // Create directories as they are found
$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
}
else if (is_file($file) === true) { // Add files as they are found
$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
}
}
}
else {
echo "Couldn't create zip file
";
}
$zip->close();
}
// ZLib extension code
//@TODO: figure out how to use the same trick as above for the mimetype file
elseif (extension_loaded('zlib') === true) {
$original_dir = getcwd(); // Remember CWD for later reset
chdir($source); // Set CWD to temp area
// ZIP up files
File_Archive::extract(
File_Archive::read('.'),
File_Archive::toArchive(
$destination,
File_Archive::toFiles(),
'zip'
)
);
chdir($original_dir); // Reset CWD
}
// No ZIP compression available
else {
die("ePub requires a ZIP compression library");
}
}
else {
echo "Source content does not exist or is not readable
";
}
header("Content-type: application/epub+zip");
header("Content-Disposition: attachment; filename=" . $this->outFileName);
header("Pragma: no-cache");
header("Expires: 0");
readfile($destination);
$this->cleanup();
}
public function cleanup($dir = false) {
if ( ! $dir ) {
$dir = $this->tempDir;
} else {
$dir = $dir . DIRECTORY_SEPARATOR;
}
$files = scandir($dir);
array_shift($files); // remove '.' from array
array_shift($files); // remove '..' from array
foreach ($files as $file)
{
$file = $dir . $file;
if (is_dir($file))
{
$this->cleanup($file);
}
else
{
unlink($file);
}
}
rmdir($dir);
}
protected function finish() {
}
//TODO: move this into the XSLT
protected function rewriteTOC($tocDOM) {
//$tocDOM = new DOMDocument();
//$tocDOM->load($this->oebpsDir . 'toc.ncx');
//remove
//change depth?
$navMap = $tocDOM->getElementsByTagName('navMap')->item(0);
while($navMap->childNodes->length != 0 ) {
$navMap->removeChild($navMap->firstChild);
}
//$parts = $htmlXPath->query("//div[@id='body']/div[@class='part']");
$parts = $this->tei->xpath->query("//tei:body/tei:div[@type='part']");
// for jdh, bring in the tei api so I can dig up the authors
require_once(ANTHOLOGIZE_TEIDOMAPI_PATH);
$api = new TeiApi($this->tei);
$playOrder = 0;
for($partN = 0; $partN < $parts->length; $partN++) {
$part = $parts->item($partN);
$title = $part->firstChild->firstChild->textContent; //shitty practice, I know
$partNavPoint = $this->newNavPoint("body-$partN", $title, $tocDOM);
$partNavPoint = $navMap->appendChild($partNavPoint);
$partNavPoint->setAttribute('playOrder', $playOrder);
$playOrder++;
//set playorder on $partNavPoint
$navMap->appendChild($partNavPoint);
$items = $this->tei->xpath->query("tei:div[@type='libraryItem']", $part);
for($itemN = 0; $itemN < $items->length; $itemN++) {
$item = $items->item($itemN);
$author = $api->getSectionPartItemAssertedAuthor('body', $partN, $itemN);
$itemTitle = $item->firstChild->firstChild->textContent; //shitty practice, I know
$itemNavPoint = $this->newNavPoint("body-$partN-$itemN", $itemTitle . " by $author", $tocDOM);
//set playOrder
//append where it goes
//lets try this
$itemNavPoint->setAttribute('playOrder', $playOrder);
$playOrder++;
$partNavPoint->appendChild($itemNavPoint);
}
}
}
protected function newNavPoint($id, $label, $tocDOM) {
$label = htmlspecialchars($label);
$navPoint = $tocDOM->createElement('navPoint');
$navPoint->setAttribute('id', $id);
$navLabelNode = $tocDOM->createElement('navLabel');
$text = $tocDOM->createElement('text', $label);
$navLabelNode->appendChild($text);
$navPoint->appendChild($navLabelNode);
$content = $tocDOM->createElement('content');
$content->setAttribute('src', "main_content.html#$id");
$navPoint->appendChild($content);
return $navPoint;
}
private function localizeLinks() {
//TODO: this will likely play hell with links directly to anchor!
$test = substr(get_bloginfo('url'),7);
$links = $this->tei->xpath->query("//a[contains(@href, '$test' )]");
foreach($links as $link) {
$guid = $link->getAttribute('href');
$targetGuidNL = $this->tei->xpath->query("//tei:ident[@type = 'permalink'][ . = '$guid']");
if($targetGuidNL->length == 0 ) {
//I hate the problem of links and trailing slashes
//if length is zero, see if including the slash produces matches
$targetGuidNL = $this->tei->xpath->query("//tei:ident[@type = 'permalink'][ . = '$guid/']");
}
if($targetGuidNL->length != 0) {
$item = $this->tei->getParentItem($targetGuidNL->item(0));
$link->setAttribute('href', '#' . $this->tei->getId($item));
}
}
}
}