Nenhum produto encontrado nessa seleção.
Provavelmente você já deve ter lido algum livro digital no formato EPUB (Electronic Publication) um formato de arquivo digital padrão específico para ebooks mantido pela idpf. Este é um formato muito adotado nos leitores de livros digitais atuais, como Calibre e até mesmo a Google Play Books, do Android (além de outros milhares).
A parte interessante dos arquivos no formato EPUB é que suas páginas são inteiramente criadas com XHTML, o que facilita a vida dos desenvolvedores front-end. Eles incluem até mesmo um arquivo CSS, para estilizar as páginas conforme preferir.
Neste artigo vou explicar como extrair ou criar livros EPUB manualmente e em seguida vou apresentar uma classe que criei para gerar livros EPUB com PHP.
Então vamos lá.
Como extrair livros EPUB manualmente
Quando você lê um livro com a extensão EPUB, na verdade está lendo um pacote compactado (zipado) com a extensão modificada de .zip para .epub. Isso significa que os arquivos que estão dentro desse pacote podem ser extraídos e manipulados.
Para extrair tais arquivos basta modificar a extensão do arquivo de .epub para .zip e, em seguida, descompactar a pasta do mesmo.
Criei um vídeo tutorial para um blog de tecnologia há algum tempo detalhando como fazer todo o processo. Assista abaixo.
Agora vamos ver como criar livros EPUB manualmente.
Como criar livros EPUB manualmente
Não será necessário utilizar nenhum programa adicional para criar seu livro, com exceção de um editor de textos HTML. Minha recomendação é o Notepad++.
Você vai precisar de um livro digital EPUB para servir como base de arquivos para seu projeto. Você pode extrair seu próprio livro e tentar seguir o tutorial utilizando ele, ou pode baixar o exemplo que vou utilizar para este tutorial no endereço abaixo.
Guarde bem este livro, já que ele servirá como base para todos os livros EPUB que desejar criar.
Estrutura dos arquivos EPUB
Antes de qualquer coisa, temos que saber algumas partes importantes para a criação do seu livro digital, tais como arquivos necessários e a estrutura dos mesmos.
Se você baixou nosso arquivo de exemplo (epub.zip), extraia seu conteúdo e você terá uma pasta chamada “Epub“.
Dentro dessa pasta temos a seguinte estrutura de arquivos:
| mimetype | ---META-INF | container.xml | ---OEBPS | content.opf | css.css | pagina1.xhtml | pagina2.xhtml | toc.ncx | ---images imagem1.jpg
Calma que eu já vou explicar.
A pasta EPUB
Nela existem mais duas pastas: META-INF e OEBPS.
O arquivo mimetype
Também existe um arquivo mimetype sem extensão nenhuma. Este arquivo contém apenas uma linha indicando exatamente o mimetype do arquivo EPUB:
application/epub+zip
A pasta META-INF
Dentro da pasta META-INF existe apenas um arquivo chamado container.xml. Este arquivo XML indica onde está o arquivo OPF, o qual lista todos os arquivos presentes no seu EPUB.
Não é necessário alterá-lo, mas veja seu conteúdo:
<?xml version="1.0" encoding="UTF-8" ?> <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/> </rootfiles> </container>
Observação: A pasta META-INF e o arquivo mimetype não precisam ser editados caso você mantenha a estrutura de arquivos da pasta que disponibilizei para download.
A pasta OEBPS
A outra pasta que ainda não falamos (OEBPS) é onde o baile irá ocorrer!
Nela teremos todas as páginas do seu livro, o arquivo CSS, a pasta de imagens, o arquivo OPF e o arquivo NCX, vamos falar deles posteriormente.
A pasta images
Essa pasta é simplesmente o local onde você salva as imagens que vai utilizar em seu livro.
Conteúdo do livro EPUB
Como você já deve ter percebido, deixei os arquivos da pasta OEBPS para serem explicados agora. Isso porque eles são os arquivos que vão criar seu livro. Alguns para a parte de estilização (como o CSS), outros serão as páginas do livro (os arquivos XHTML), outros a configuração do seu livro (content.opf e toc.ncx).
Vamos começar visualizando o arquivo que vai configurar seu livro, a tabela de conteúdo (toc.ncx). Vou postar abaixo seu conteúdo, em seguida vou explicar as partes importantes que devem ser alteradas.
O arquivo toc.ncx
<?xml version="1.0" encoding="UTF-8"?> <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1"> <head> <meta name="dtb:uid" content="teoteste"/> <meta name="dtb:depth" content="1"/> <meta name="dtb:totalPageCount" content="0"/> <meta name="dtb:maxPageNumber" content="0"/> </head> <docTitle> <text>TEO test</text> </docTitle> <navMap> <navPoint id="pagina1" playOrder="1"> <navLabel> <text>Página 1</text> </navLabel> <content src="pagina1.xhtml"/> </navPoint> <navPoint id="pagina2" playOrder="2"> <navLabel> <text>Página 2</text> </navLabel> <content src="pagina2.xhtml"/> </navPoint> </navMap> </ncx>
Muita coisa, não é? Mas vamos quebrar tudo em partes e ver o que realmente importa.
UID
Na parte da <head>, a única linha que deve ser alterada é a linha que identifica seu livro:
<meta name="dtb:uid" content="IDDOLIVRO"/>
Você deve alterar IDDOLIVRO para o ID único do seu livro. E quando digo único, é único mesmo, algo como 2eb60380-b2d7-11e3-a5e2-0800200c9a66 estaria de bom tamanho.
Caso queira gerar um ID único para seu livro, você pode acessar esta ferramenta ou fazer como preferir.
docTitle
<docTitle> <text><strong>Tutsup test</strong></text> </docTitle>
Este é o título do seu livro, basta substituir Tutsup test para o título que deseja.
navMap
Este é o local onde você adiciona as páginas do seu livro. Cada página deverá ter uma sessão parecida com o trecho abaixo:
<navPoint id="pagina1" playOrder="1"> <navLabel> <text>Página 1</text> </navLabel> <content src="pagina1.xhtml"/> </navPoint>
O id, playorder e o content deverão ser diferentes para cada página. Por exemplo, para um livro com três páginas, o código ficaria assim:
<navPoint id="pagina1" playOrder="1"> <navLabel> <text>Título da Página 1</text> </navLabel> <content src="pagina1.xhtml"/> </navPoint> <navPoint id="pagina2" playOrder="2"> <navLabel> <text>Título da Página 2</text> </navLabel> <content src="pagina2.xhtml"/> </navPoint> <navPoint id="pagina3" playOrder="3"> <navLabel> <text>Título da Página 3</text> </navLabel> <content src="pagina3.xhtml"/> </navPoint>
Perceba os números que utilizei para identificar cada página em cada uma das sessões. Você não precisa utilizar números se cada identificador for diferente para cada página, contudo, creio que é a melhor maneira de organização.
Outra observação importante: A tag content indica o local do arquivo XHTML que está sua página, este será explicado posteriormente.
O arquivo content.opf
O arquivo content.opf “diz” ao seu EPUB quais são os arquivos que você vai utilizar no seu livro. Todos eles deverão ser listados nele, todas as páginas, todas as imagens, o arquivo CSS, o arquivo NCX, tudo deve ser listado nele. Além disso, ainda temos mais algumas tags que deverão ser preenchidas.
Vamos ver o arquivo como um todo, em seguida pegamos pequenas partes do mesmo para explicação.
Content.opf
<?xml version="1.0" encoding="UTF-8"??> <package xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookID" version="2.0" > <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf"> <dc:title>Nome do livro</dc:title> <dc:creator opf:role="aut">Criador</dc:creator> <dc:language>pt</dc:language> <dc:rights>Public Domain</dc:rights> <dc:publisher>todoespacoonline.com/w</dc:publisher> <dc:identifier id="BookID" opf:scheme="UUID">IDDOLIVRO</dc:identifier> </metadata> <manifest> <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" /> <item id="style" href="css.css" media-type="text/css" /> <item id="pagina1" href="pagina1.xhtml" media-type="application/xhtml+xml" /> <item id="pagina2" href="pagina2.xhtml" media-type="application/xhtml+xml" /> <item id="imgl" href="images/sample.png" media-type="image/png" /> </manifest> <spine toc="ncx"> <itemref idref="pagina1" /> <itemref idref="pagina2" /> </spine> </package>
As tags da parte superior do documento são autoexplicativas, nome do livro, criador, idioma e demais. Porém, a última tag (identifier) é o mesmo UID que você gerou para seu livro.
<dc:identifier id="BookID" opf:scheme="UUID">IDDOLIVRO</dc:identifier>
Portanto, substitua IDDOLIVRO para o seu UUID.
manifest
A parte das tags <manifest></manifest> deverão ser alteradas, nela você indica todos os arquivos que estão presentes no seu EPUB seguindo o padrão abaixo:
<manifest> <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" /> <item id="style" href="css.css" media-type="text/css" /> <item id="pagina1" href="pagina1.xhtml" media-type="application/xhtml+xml" /> <item id="pagina2" href="pagina2.xhtml" media-type="application/xhtml+xml" /> <item id="imgl" href="images/sample.png" media-type="image/png" /> </manifest>
Se você quiser continuar seguindo nosso exemplo, você só vai precisar adicionar as páginas e as imagens, o que diminui suas alterações para:
<item id="pagina1" href="pagina1.xhtml" media-type="application/xhtml+xml" /> <item id="pagina2" href="pagina2.xhtml" media-type="application/xhtml+xml" /> <item id="imgl" href="images/sample.png" media-type="image/png" />
Cada página e imagem deverá ser listada como descrito acima.
spine
A parte entre as tags <spine></spine> você só precisa listar o ID das páginas, como no exemplo:
<spine toc="ncx"> <itemref idref="pagina1" /> <itemref idref="pagina2" /> </spine>
Para cada página deverá ser criado um idref, como no código acima.
Os arquivos XHTML
Os arquivos XHTML são bastante parecidos com arquivos HTML comum, no entanto, não devem existir tags sem fechamento. Isso quer dizer que só serão permitidas tags como <p></p>, <b></b>, <img />, e demais.
Na verdade, existe uma lista gigante que mostra todas as tags que podem ser utilizadas.
Veja o exemplo da nossa página:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="application/xhtml+xml; charset=utf-8" http-equiv="Content-Type"/> <link href="css.css" type="text/css" rel="stylesheet"/> <title>Página 2</title> </head> <body> <p>Oi, sou a página 2. <a href="pagina1.xhtml">Clique aqui para ir para a página 1</a>.</p> </body> </html>
A parte inicial deverá ser mantida:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="application/xhtml+xml; charset=utf-8" http-equiv="Content-Type"/> <link href="css.css" type="text/css" rel="stylesheet"/> <title>Página 2</title> </head>
No entanto, o título “Página 2“, poderá ser alterado para o título correto da sua página.
A segunda parte:
<body> <p>Oi, sou a página 2. <a href="pagina1.xhtml">Clique aqui para ir para a página 1</a>.</p> </body> </html>
Poderá ser alterada dentro da tag <body></body>. Por exemplo, nosso texto visível para o leitor será apenas:
<p>Oi, sou a página 2. <a href="pagina1.xhtml">Clique aqui para ir para a página 1</a>.</p>
O arquivo css.css
O CSS é simplesmente um CSS comum, veja abaixo o nosso arquivo css.css
body { margin-left: .5em; margin-right: .5em; text-align: left; direction: ltr; font-family: arial; direction: ltr; font-size:12pt; font-weight:400; }
Básico do básico.
Compactando e transformando em EPUB
Agora vem a parte boa, você não precisa de programa nenhum para transformar todos estes arquivos que criamos em um livro EPUB, veja como é simples.
- Clique com o botão direito do mouse sobre a pasta que tem os arquivos do livro;
- Passe o mouse sobre “Enviar para” e clique em “Pasta compactada”. Isso deverá criar um arquivo com o nome da sua pasta, mais a extensão .zip;
- Clique com o botão direito do mouse sobre a pasta e selecione a opção “Renomear”;
- Troque a extensão .zip para .epub e pronto
Classe PHP para gerar livros EPUB
Para facilitar sua vida, criei uma classe PHP chamada TPEpubCreator que gera os livros EPUB para você, então não será necessário passar por todo o processo que descrevi acima para criar seus e-books EPUB.
Antes de mais nada, vamos ver onde você pode conseguir a classe e como contribuir com o projeto.
Download da classe TPEpubCreator
Caso queira fazer o download da classe TPEpubCreator.php, acesse o link abaixo:
Ou você pode acessar pelo Github e contribuir com o projeto:
Você escolhe.
Conteúdo da classe TPEpubCreator
A classe faz tudo por você, ou seja, cria todas as pastas e arquivos. Ela também gera os arquivos XHTML, você só precisa enviar o conteúdo dos mesmos.
Veja a classe completa:
<?php /** * TPEpubCreator - PHP EPUB Creator * * This PHP class creates e-books using the EPUB standard format. An example can * be found at ../index.php. * * @package TPEpubCreator * @author Luiz Otávio Miranda <[email protected]/w> * @version $Revision: 1.0.0 $ * @access public * @see http://www.todoespacoonline.com/w/ */ class TPEpubCreator { /** * This is the abspath for this file * * @access private * @var string * @since 1.0.0 */ private $abspath; /** * This is the cover img path * * @access private * @var string * @since 1.0.0 */ private $cover_img; /** * This is the content.opf file * * @access private * @var array * @since 1.0.0 */ private $opf = array(); /** * This is the toc.ncx file * * @access private * @var array * @since 1.0.0 */ private $ncx = array(); /** * This is the pages array * * @access private * @var array * @since 1.0.0 */ private $pages = array(); /** * This is the images array * * @access private * @var array * @since 1.0.0 */ private $images = array(); /** * This is the new images array * * @access private * @var array * @since 1.0.0 */ private $new_images = array(); /** * This is to check if a cover has been added * * @access private * @var bool * @since 1.0.0 */ private $cover; /** * This is our errors output * * @access public * @var string * @since 1.0.0 */ public $error; /** * This is the book's uuid * * @access public * @var string * @since 1.0.0 */ public $uuid; /** * This is the book's title * * @access public * @var string * @since 1.0.0 */ public $title = 'Untitled'; /** * This is the book's creator * * @access public * @var string * @since 1.0.0 */ public $creator = 'Tutsup.com'; /** * This is the book's language * * @access public * @var string * @since 1.0.0 */ public $language = 'pt'; /** * This is the book's rights * * @access public * @var string * @since 1.0.0 */ public $rights = 'Public Domain'; /** * This is the book's publisher * * @access public * @var string * @since 1.0.0 */ public $publisher = 'http://www.todoespacoonline.com/w/'; /** * This is the book's css * * @access public * @var string * @since 1.0.0 */ public $css; /** * This is the temp folder used to store the book's files before zip it * * @access public * @var string * @since 1.0.0 */ public $temp_folder; /** * This is the path to output the epub file * * @access public * @var string * @since 1.0.0 */ public $epub_file; /** * This is the container.xml file * * @access public * @var string * @since 1.0.0 */ public $container; /** * This is the key for the pages array * * @access public * @var int * @since 1.0.0 */ public $key = 0; /** * This is the key for the images array * * @access public * @var int * @since 1.0.0 */ public $image_key = 0; /** * Constructor. * * This only sets the abspath and uuid * * @since 1.0.0 * @access public * */ public function __construct () { $this->abspath = dirname( __FILE__ ); $this->uuid = md5( microtime() ); } /** * Add Image * * This stores the images in the images array and set the cover. * * @since 1.0.0 * @access public * * @param string $path Image's Path * @param string $type Image's Mime-type * @param bool $cover Whether it will or will not be a cover * * @return bool false if the image does not exists */ public function AddImage( $path = false, $type = false, $cover = 0 ) { $this->image_key++; // Checks if the image exists first /*if ( ! file_exists( $path ) ) { $this->error = 'Cannot find image ' . $path . '.'; return; }*/ $this->images[$this->image_key]['path'] = $path; $this->images[$this->image_key]['type'] = $type; $this->images[$this->image_key]['cover'] = $cover; } /** * Add Page * * This stores the pages in the pages array. It'll store the XHTML content * for the page, but won't check or parse it. * * @since 1.0.0 * @access public * * @param string $content Page content (XHTML) * @param string $file A file that has the page content (XHTML) * @param string $title Page's title * @param bool $download_images Whether to download images from the HTML or not * * @return bool false if the image does not exists */ public function AddPage( $content = null, $file = null, $title = 'Untitled', $download_images = false ) { // Set the key for the page $this->key++; // If nothing to add, nothing to do if ( ! $content && ! $file ) { $this->error = 'No content or file added.'; return; } // If it's XHTML if ( $content ) { $this->pages[$this->key]['content'] = $content; } // If it's a file if ( $file ) { // If the file does not exists, won't do anything if ( ! file_exists( $file ) ) { $this->error = "File {$file} does not exists."; } $file = file_get_contents( $file ); $this->pages[$this->key]['content'] = $file; } // If the $download_images param is set to true, we'll try to download // images found and add it to you e-book if ( $download_images ) { $found_images = preg_match_all( '/(<img.*?src=['|"])(.*?)(['|"].*?>)/mis', $this->pages[$this->key]['content'], $image_matches ); // Just need the URLs if ( $found_images ) { if ( ! empty( $image_matches[2] ) ) { foreach ( $image_matches[2] as $img ) { $this->AddImage( $img ); } } } } // Set the page title $this->pages[$this->key]['title'] = $title; } /** * Create EPUB * * This creates the epub file. It uses lots of other methods to accomplish * its task. * * @since 1.0.0 * @access public */ public function CreateEPUB() { // Creates all the folders needed $this->CreateFolders(); // If there's no error we're good to go if ( $this->error ) { return; } // Open the content.opf file $this->OpenOPF(); // Open the toc.ncx file $this->OpenNCX(); // Open the css.css file $this->OpenCSS(); // Variables needed to put everything in the right place $ncx = null; $opf = null; $fill_opf_spine = null; // Loop the pages array and fill the content.opf and toc.ncx content foreach( $this->pages as $key => $value ) { // The page $page = 'page' . $key; // OPF $opf .= '<item id="' . $page . '" href="' . $page . '.xhtml" media-type="application/xhtml+xml" />' . "rn"; // NCX $ncx .= '<navPoint id="' . $page . '" playOrder="' . $key . '">' . "rn"; $ncx .= '<navLabel>' . "rn"; $ncx .= '<text>' . $value['title'] . '</text>' . "rn"; $ncx .= '</navLabel>' . "rn"; $ncx .= '<content src="' . $page . '.xhtml"/>' . "rn"; $ncx .= '</navPoint>' . "rn"; // Fill the spine $fill_opf_spine .= '<itemref idref="' . $page . '" />' . "rn"; } // If there are images, loop the values if ( ! empty( $this->images ) ) { foreach( $this->images as $image_key => $image_value ) { // New image have the same name as the old one $new_image = $this->temp_folder . '/OEBPS/images/'; $new_image .= mt_rand(0,9999) . '_'; $new_image .= basename( $image_value['path'] ); // Mime-type $image_type = $image_value['type']; // If we don't have a mimetype for the image // We'll try to get it if ( ! $image_type ) { $image_type = getimagesize( $image_value['path'] ); $image_type = $image_type['mime']; } // Try to copy the image if ( ! @copy( $image_value['path'], $new_image ) ) { $this->error = 'Cannot copy ' . $image_value . '.'; return; } // Set the new images name $this->new_images[$image_key]['path'] = $new_image; // If there is a cover, create another ID and XHTML page later if ( ! empty( $image_value['cover'] ) ) { $opf .= '<item id="cover" href="cover.xhtml" media-type="application/xhtml+xml" />' . "rn"; $opf .= '<item id="cover-image'; $this->cover_img = basename( $new_image ); } else { $opf .= '<item id="img' . $image_key; } // End the image <item> tag $opf .= '" href="images/' . basename( $new_image ); $opf .= '" media-type="' . $image_type . '" />' . "rn"; } } // Fill the NCX and OPF $this->ncx[] = $ncx; $this->opf[] = $opf; $this->opf[] = '</manifest><spine toc="ncx">' . "rn"; // If there's a cover, we'll need an <itemref idref="cover" /> if ( $this->cover_img ) { $this->opf[] = "<itemref idref="cover" />rn"; } // Fill the spine $this->opf[] = $fill_opf_spine; // Closes the OPF and NCX $this->CloseOPF(); $this->CloseNCX(); // Create the OPF and NCX files $this->CreateOPF(); $this->CreateNCX(); // XHTML default page header $page_content = "<?xml version='1.0' encoding='utf-8'?>" . "rn"; $page_content .= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' . "rn"; $page_content .= '<html xmlns="http://www.w3.org/1999/xhtml">' . "rn"; $page_content .= '<head>' . "rn"; $page_content .= '<meta content="application/xhtml+xml; charset=utf-8" http-equiv="Content-Type"/>' . "rn"; $page_content .= '<link href="css.css" type="text/css" rel="stylesheet"/>' . "rn"; $page_content .= '</head>' . "rn"; $page_content .= '<body>' . "rn"; // Loop the pages foreach( $this->pages as $key => $value ) { // Page file $page = 'page' . $key . '.xhtml'; // Replace unwanted tags (for now scripts and iframes) $value['content'] = preg_replace( '/<(script|iframe)[^>]*>.*?</(script|iframe)>/mis', '', $value['content'] ); // Fill the page content and ends the XHTML $value['content'] = $page_content . $value['content']; $value['content'] .= '</body></html>'; // Replace the HTML images to the new images foreach( $this->images as $check_image_key => $check_images ) { $value['content'] = str_replace ( $check_images['path'], 'images/' . basename( $this->new_images[$check_image_key]['path'] ), $value['content'] ); } // Create the file $this->CreateFile( $this->temp_folder . '/OEBPS/' . $page, $value['content'] ); } // If there's a cover, create its page if ( ! empty( $this->cover_img ) ) { $cover_page = $page_content; $cover_page .= '<img class="cover-image" width="600" height="800" src="images/' . $this->cover_img . '" />' . "rn"; $cover_page .= '</body></html>'; $this->CreateFile( $this->temp_folder . '/OEBPS/cover.xhtml', $cover_page ); } // Create the zip file $this->CreateZip(); } /** * Open CSS * * It will simply fill the $css property */ public function OpenCSS() { if ( ! $this->css ) { $this->css = 'body {'; $this->css .= 'margin-left: .5em;'; $this->css .= 'margin-right: .5em;'; $this->css .= 'text-align: left;'; $this->css .= 'direction: ltr;'; $this->css .= 'font-family: arial;'; $this->css .= 'direction: ltr;'; $this->css .= 'font-size:12pt;'; $this->css .= 'font-weight:400;'; $this->css .= '};'; } } /** * Open OPF * * Fill the content.opf file ($opf property) */ private function OpenOPF() { $this->opf[] = '<?xml version="1.0" encoding="UTF-8"?>' . "rn"; $this->opf[] = '<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookID" version="2.0" >' . "rn"; $this->opf[] = '<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">' . "rn"; $this->opf[] = '<dc:title>' . $this->title . '</dc:title>' . "rn"; $this->opf[] = '<dc:creator opf:file-as="' . $this->creator . '" opf:role="aut">' . $this->creator . '</dc:creator>' . "rn"; $this->opf[] = '<dc:language>' . $this->language . '</dc:language>' . "rn"; $this->opf[] = '<dc:rights>' . $this->rights . '</dc:rights>' . "rn"; $this->opf[] = '<dc:publisher>' . $this->publisher . '</dc:publisher>'; $this->opf[] = '<dc:identifier id="BookID" opf:scheme="UUID">' . $this->uuid . '</dc:identifier>' . "rn"; $this->opf[] = '<meta name="cover" content="cover" />' . "rn"; $this->opf[] = '</metadata><manifest>' . "rn"; $this->opf[] = '<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml" />' . "rn"; $this->opf[] = '<item id="style" href="css.css" media-type="text/css" />' . "rn"; } /** * Close OPF * * End of the content.opf file */ private function CloseOPF() { $this->opf[] = '</spine></package>' . "rn"; } /** * Create OPF * * Creates the content.opf file */ private function CreateOPF() { $opf = null; foreach( $this->opf as $lines ) { $opf .= "$linesrn"; } $this->CreateFile( $this->temp_folder . '/OEBPS/content.opf', $opf ); } /** * Open NCX * * Fill the toc.ncx content ($ncx property) */ private function OpenNCX() { $this->ncx[] = '<?xml version="1.0" encoding="UTF-8"?>' . "rn"; $this->ncx[] = '<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">' . "rn"; $this->ncx[] = '<meta name="dtb:uid" content="' . $this->uuid . '"/>' . "rn"; $this->ncx[] = '<head>' . "rn"; $this->ncx[] = '<meta name="dtb:depth" content="1"/>' . "rn"; $this->ncx[] = '<meta name="dtb:totalPageCount" content="0"/>' . "rn"; $this->ncx[] = '<meta name="dtb:maxPageNumber" content="0"/>' . "rn"; $this->ncx[] = '</head>' . "rn"; $this->ncx[] = '<docTitle><text>' . $this->title . '</text></docTitle>' . "rn"; $this->ncx[] = '<navMap>' . "rn"; } /** * Close NCX * * Closes the toc.ncx file content */ private function CloseNCX() { $this->ncx[] = '</navMap>' . "rn"; $this->ncx[] = '</ncx>' . "rn"; } /** * Create NCX * * Creates toc.ncx file */ private function CreateNCX() { $ncx = null; foreach( $this->ncx as $lines ) { $ncx .= "$linesrn"; } $this->CreateFile( $this->temp_folder . '/OEBPS/toc.ncx', $ncx ); } /** * Create folders * * Create all the temp folders needed */ private function CreateFolders() { // If the user do not specify a temp folder, we'll assume it. if ( ! $this->temp_folder ) { $this->temp_folder = preg_replace( '/[^A-Za-z0-9]/is', '', $this->title ); $this->temp_folder = strtolower( $this->temp_folder ); } // Temp folder is the book's uuid $this->temp_folder .= $this->uuid . '/'; // Check to see if there's no folder with the same name if( is_dir( $this->temp_folder ) ) { $this->error = 'Folder already exists.'; return; } // Creates the main temp folder mkdir( $this->temp_folder, 0777 ); // Check the folder if ( ! is_dir( $this->temp_folder ) ) { $this->error = "Cannot create EPUB folder "{$this->temp_folder}"."; return; } // Creates the other needed folders mkdir( $this->temp_folder . '/META-INF', 0777 ); mkdir( $this->temp_folder . '/OEBPS', 0777 ); mkdir( $this->temp_folder . '/OEBPS/images', 0777 ); // Open the CSS $this->OpenCSS(); // Creates the container.xml $this->CreateContainer(); // Creates the needed epub files $this->CreateFile( $this->temp_folder . '/mimetype', 'application/epub+zip'); $this->CreateFile( $this->temp_folder . '/OEBPS/css.css', $this->css); $this->CreateFile( $this->temp_folder . '/META-INF/container.xml', $this->container); } /** * Create container * * Creates the container.xml file */ private function CreateContainer() { $this->container = '<?xml version="1.0" encoding="UTF-8" ?>'; $this->container .= '<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">'; $this->container .= '<rootfiles>'; $this->container .= '<rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>'; $this->container .= '</rootfiles>'; $this->container .= '</container>'; } /** * Create Files */ private function CreateFile( $file, $content = null ) { $handle = fopen( $file, 'w+' ); $ler = fwrite( $handle, $content ); fclose($handle); } /** * Create Zip * * This creates the zip file as epub. * * @since 1.0.0 * @access private */ private function CreateZip () { // Checks the zip extension if ( ! extension_loaded('zip') ) { $this->error('zip extension is not loaded'); return false; } // If the user do not specify the epub file, we'll assume it. if ( ! $this->epub_file ) { $this->epub_file = preg_replace( '/[^A-Za-z0-9]/is', '', $this->title ); $this->epub_file .= '.epub'; } $zip = new ZipArchive(); if ( ! $zip->open( $this->epub_file, ZIPARCHIVE::CREATE ) ) { $this->error('Failed to create zip file.'); return false; } // Folders array $folders = array( $this->temp_folder . '/META-INF', $this->temp_folder . '/OEBPS', $this->temp_folder . '/OEBPS/images', $this->temp_folder, ); // Files we'll delete later $files_to_delete = array(); // Loop the folders foreach ( $folders as $folder ) { // The files inside the folders $scan = scandir( $folder ); // Loop the files foreach ( $scan as $subfolder ) { // Prevent . and .. paths if ( '.' === $subfolder || '..' === $subfolder ) continue; $full_path = $folder . '/' . $subfolder; // We just want files, not directories if ( is_dir( $full_path ) ) continue; // Add the file $zip->addFile( $full_path, str_replace( $this->temp_folder . '/', '', $full_path ) ); // Fill the array, so we'll know what to delete later $files_to_delete[] = $full_path; } } $zip->close(); // Delete the files foreach ( $files_to_delete as $delete ) unlink( $delete ); // Delete folders rmdir( $this->temp_folder . '/META-INF' ); rmdir( $this->temp_folder . '/OEBPS/images' ); rmdir( $this->temp_folder . '/OEBPS' ); rmdir( $this->temp_folder ); } }
A classe é tão extensa justamente para facilitar a sua vida. Vamos ver como utilizá-la a seguir.
Utilizando a classe TPEpubCreator
Veja como ficaria para criar um livro com
<?php // Só pra ter certeza que estamos utilizando UTF-8 // Você pode remover essa linha. header('Content-Type: text/html; charset=utf-8'); // A classe está pasta classes require 'classes/TPEpubCreator.php'; // Objeto da classe $epub = new TPEpubCreator(); // A pasta temporária e o caminho do arquivo final $epub->temp_folder = 'temp_folder/'; $epub->epub_file = 'epubs/epub_name.epub'; // Configurações do ebook $epub->title = 'Epub title'; $epub->creator = 'Luiz Otávio Miranda'; $epub->language = 'pt'; $epub->rights = 'Public Domain'; $epub->publisher = 'http://www.todoespacoonline.com/w/'; // Você pode adicionar seu próprio CSS $epub->css = file_get_contents('base.css'); // $epub->uuid = ''; // Você pode espeificar seu UUID se quiser // Adiciona a página de um arquivo (só o conteúdo do <body>) // Você deve remover o doctype, head, body e html tags // Sintax: $epub->AddImage( XHTML, arquivo, título, baixar imagens ); $epub->AddPage( false, 'file.txt', 'Título (check accent)' ); // Adiciona a página diretamente (só o conteúdo do <body>) // Não utilize tags como doctype, html, head ou body $epub->AddPage( '<b>Test</b>', false, 'Title 2' ); $epub->AddPage( '<img src="images/2.jpg" />', false, 'Title 3' ); // Aqui é apenas um exemplo com o último parâmetro, onde // mandamos a classe baixar a imagem. // A classe pode baixar qualquer tipo de imagem de qualquer // página disponível online (incluindo http://...) $epub->AddPage( '<img src="images/3.jpg" />', false, 'Title 4', true ); // Aqui uma inclusão de página normal (sem download de imagens) $epub->AddPage( '<img src="images/4.jpg" />', false, 'Title 5' ); // Adiciona uma imagem de cover // Lembre-se, apenas uma imagem pode ser cover ( último argumento = true). // Se mais de uma imagem for cover, alguns leitores não vão carregar o livro // Sintax: $epub->AddImage( caminho da image, mimetype, cover ); $epub->AddImage( 'images/1.jpg', false, true ); // Adiciona outra imagem (não é cover) $epub->AddImage( 'images/2.jpg', 'image/jpeg', false ); // Se você não enviar o mimetype, a classe fará isso por você $epub->AddImage( 'images/4.jpg', false, false ); // Cria o livro // Se tiver erro, testamos primeiro aqui if ( ! $epub->error ) { // Isso também pode gerar erros // Então vamos verificar novamente $epub->CreateEPUB(); // Se não tiver erro, seu ebook foi criado com sucesso if ( ! $epub->error ) { echo 'Success: Download your book <a href="' . $epub->epub_file . '">here</a>.'; } } else { // Se tiver erro, você poderá ver o que acontece aqui echo $epub->error; }
Veja nos comentários acima, tudo o que você precisa fazer para criar seus arquivos EPUB é seguir o modelo.
Concluindo
Falamos muito sobre arquivos EPUB neste artigo, na verdade eu descrevi tudo o que eu sei sobre tais formatos de arquivos. Tire um tempo para estudar todo o conteúdo e tire suas dúvidas nos comentários.
A classe está documentada em inglês, pois este é o formato que utilizamos para documentar nossos arquivos globalmente. Caso você entenda sobre os EPUBs e PHP, me ajude com o projeto no github.
E, novamente, caso tenha dúvidas, comente-as!