Este é um plugin do Gitbook, uma ferramenta para geração de livros.
Você vai precisar instalar:
- Node.js
- Instalação: http://nodejs.org/download/
- OBS: Testado na versão 0.10.35
- Gitbook 1.5.0
- Utilizado para transformar .md em .html, gerando site ou ebooks
- Instalação:
sudo npm install -g [email protected]
- PDFtk
- Utilizado para extrair sumário e mesclar PDFs.
- Instalação (Ubuntu):
sudo apt-get install pdftk - Instalação (Mac .pkg): https://www.pdflabs.com/tools/pdftk-server
- OBS: Testado na versão 2.0.1
- Ghostscript
- Utilizado para modificar página inicial de um PDF.
- Instalação (Ubuntu):
sudo apt-get install ghostscript - Instalação (Mac):
brew install ghostscript - OBS: Testado na versão 9.10
- Calibre
- Utilizado para gerar ebooks nos formatos .pdf, .mobi e .epub
- Instalação (Ubuntu e Mac): instruções em http://calibre-ebook.com/download
- (Ubuntu) Para instalar o Calibre você precisa ter instalado: xdg-utils, wget e python 2.6+
- (Ubuntu) Para rodar o Calibre, você precisa ter instalado: GLIBC 2.13+ e libstdc++.so.6.0.17 (from gcc 4.7.0), qt-sdk e python3-pyqt5
-
Configure o
package.jsondo seu livro da seguinte maneira:{ "name": "livro", "version": "0.0.1", "description": "livro", "private": true, "dependencies": { "gitbook-plugin-cdc": "casadocodigo/gitbook-plugin-cdc", "gitbook-plugin-cdc-tema": "casadocodigo/gitbook-plugin-cdc-tema" } } -
Execute
npm installpara baixar este plugin e as outras dependências. -
Configure o arquivo
book.jsondo seu livro, definindotitle,description,authorepublisher. Defina também emplugins, o valorescdcecdc-tema.{ "title": "Título do Livro", "description": "Descrição do Livro.", "author": "Fulano Silva & Ciclano Souza", "publisher": "Minha Editora", "plugins": ["cdc", "cdc-tema"] }Outras opções possíveis estão definidas abaixo.
-
Fizemos um patch para adicionar algumas funcionalidades no Gitbook que não podem ser feitas com plugins. Substitua o arquivo
/usr/local/lib/node_modules/gitbook/lib/generate/ebook/index.js, pelo conteúdo do patch.
Pronto! Agora podemos gerar o nosso livro. Temos algumas opções:
-
Para gerar um site no diretório
_book:gitbook build -
Para gerar um arquivo
book.pdf:$ gitbook pdf -
Para gerar um arquivo
book.epub:$ gitbook epub -
Para gerar um arquivo
book.mobi:$ gitbook mobi
Para ver os arquivos intermediários, você pode utilizar os comandos abaixo.
-
Para gerar um PDF, mantendo os arquivos intermediários, no diretório
book.pdf, execute:$ gitbook build -f ebook -o book.pdfO arquivo PDF final é o
index-with-toc-bookmarks-and-page-numbers.pdf.O PDF também pode ser gerado com o comando
$ gitbook build -f ebook. Serão mantidos os arquivos auxiliares no diretório_book. -
Para gerar um MOBI, no diretório
book.mobi, execute:$ gitbook build -f ebook -o book.mobiO arquivo MOBI gerado é o
index.mobi. -
Para gerar um EPUB, no diretório
book.epub, execute:$ gitbook build -f ebook -o book.epubO arquivo EPUB gerado é o
index.epub.
É possível mudar os valores de algumas opções através do arquivo book.json.
"pdf": {
"margin": {
"right": 56, //Number
"left": 56, //Number
"top": 56, //Number
"bottom": 56 //Number
},
"customSize": "210x280", //String no formato largura x altura
"fontSize": 11 //Number
"headerTemplate": "<p>_SECTION_</p>", //String com um template html para o cabeçalho de cada página
"footerTemplate": "<p>_PAGENUM_</p>", //String com um template html para o rodapé de cada página
"summary": {
"headerTemplate": "<p>Sumário</p>", //String com template do cabeçalho do sumário
"footerTemplate": "<p>Casa do Código</p>" //String com template do rodapé do sumário
}
}Tanto para o headerTemplate como para o footerTemplate, podem ser usadas as seguintes variáveis:
_PAGENUM_, que contém o número da página atual_SECTION, que contém o nome da seção atual_TITLE_, que contém o título do livro_AUTHOR_, que contém o nome do autor
Os valores padrão das propriedades são os seguintes:
pdf.fontSize: tamanho do texto. O padrão, definido pelo Gitbook, é 12.pdf.margin.left: define a margem esquerda do pdf em pts. O padrão, definido pelo Gitbook, é 62.pdf.margin.right: define a margem direita do pdf em pts. O padrão, definido pelo Gitbook, é 62.pdf.margin.top: define a margem de cima do pdf em pts. O padrão, definido pelo Gitbook, é 36.pdf.margin.bottom: define a margem de baixo do pdf em pts. O padrão, definido pelo Gitbook, é 36.
Existem alguns conteúdos que não fazem parte do texto do livro em si.
Alguns são conteúdos estáticos como copyright e propagandas.
Outros são conteúdos dinâmicos como prefácio, apresentação dos autores e agradecimentos. Estes conteúdos são parte do livro, mas vem antes do sumário.
Conteúdos estáticos como copyright e propagandas, que são sempre iguais para todos os livros, devem ser colocados em arquivos .pdf no diretório configurado pela variável de ambiente EXTRAS_DIR.
Os .pdf de EXTRAS_DIR serão ordenados por nome e inseridos logo depois da capa do livro.
Também é possível definir arquivos .pdf dentro do diretório do livro, no sub-diretório extras. Se o EXTRAS_DIR já estiver configurado, os .pdf de extras virão logo depois.
Conteúdos do livro como prefácio, apresentação dos autores e agradecimentos devem ser colocados em arquivos .md dentro do diretório intro.
Ao gerar um pdf, cada arquivo .md do diretório intro será transformado em um .pdf e inserido logo antes do sumário (mas depois dos extras).
Os .md serão ordenados pelo nome do arquivo antes de serem renderizados e inseridos.
Este plugin está organizado em dois sub-módulos:
- ebook: manipula conteúdo do livro como nomes das seções, legenda de imagens, etc...
- pre-content: insere conteúdo no início do livro como copyright, propagandas, apresentação dos autores, etc...
Configura hooks do gitbook.
Os hooks configurados são:
page: disparado logo depois ter renderizado o .md de cada página em um .html, chama a função handlePage do sub-módulo ebookpage:after: disparado depois do gitbook manipular o .html de cada página, chama a função handlePageAfter do sub-módulo ebookebook:before: hook customizado, criado a partir de um patch. É disparado logo antes da chamada do calibre para a geração do ebook. Chama a função handleEbookBefore do sub-módulo ebookfinish: disparado ao fim da geração do ebook ou site, chama a função finish do sub-módulo pre-content
As funções acima são chamadas com código do tipo funcao.call(this, argumento), para que o this do gitbook seja propagado para cada função.
Contém as funções comuns:
Função que obtém a extensão do livro a ser gerado.
Parâmetros:
- options -
Objectcom as configurações dobook.jsondo gitbook.
Retorno:
- uma
Stringcom a extensão do livro a ser gerado (pdf, mobi ou epub).
Se não houver como descobrir a extensão do livro e o formato for ebook, é considerado o "pdf".
Se o formato for site, o retorno é "", uma String vazia.
Manipula conteúdo do livro.
Dependências externas:
- cheerio, uma implementação enxuta do jQuery para Node.js.
Função que manipula conteúdo da página renderizada.
Parâmetros:
- page -
Objectcom as informações sobre a página que será renderizada.
Retorno:
- um
Objectcom a page com conteúdo manipulado.
Se o título capítulo começar com números, será lançada uma exceção. Essa limitação tem a ver com a maneira com que é feito o cabeçalho das páginas no calibre (pdf.headerTemplate do book.json).
O gitbook/calibre não coloca números antes das seções (p. ex.: 1.2 Integração com tecnologias do JavaEE). Por isso, para cada h2 de cada seção da página, é inserido o número do capítulo seguido por um número sequencial para cada seção.
Para cada img da página, é extraído do alt configurações de largura da imagem (ex.: {w=60%}). Se a extensão do livro for pdf e houver largura configurada, é colocado um width no img. Além disso, é inserido um div com um p que serve como legenda para a imagem.
Todos os comentários html são removidos do livro.
Função que manipula conteúdo da página renderizada.
Parâmetros:
- page -
Objectcom as informações sobre a página que será renderizada.
Retorno:
- um
Objectcom a page com conteúdo manipulado.
Antes de cada h1 que contém o título do capítulo, é inserido um div com o número do capítulo (ex.: Capítulo 1).
Função que altera opções do ebook-convert do calibre
Parâmetros:
- options -
Objectcom configurações que vão ser passadas para o calibre.
Retorno:
- um
Objectcom as opções do calibre alteradas.
Essa função é chamada no hook ebook:before, que não é padrão do gitbook. Esse hook foi criado a partir de um patch.
É disparado logo antes da chamada do calibre para a geração do ebook.
Não é chamado na geração de site.
São alteradas configurações do ebook-convert do calibre que não são inseridas pelo gitbook como:
--publisher: nome da editora, obtido a partir da propriedadepublisherdobook.json--chapter-mark: colocado paranone, para que não sejam colocadas quebras de página no início de cada capítulo. As quebras de página devem ser controladas por css.--level2-toc: configura a detecção de seções para considerarh2. Utilizado na geração do sumário pelo calibre.--level2-toc: setado paranull, de maneira a desconsiderar títulos (na verdade,h3) na geração do sumário pelo calibre.
Se a extensão do livro a ser gerado for mobi, são configuradas as seguintes opções:
--mobi-keep-original-images: setada paratrue, fazendo com que o calibre não comprima as imagens--toc-title: modificado para Sumário
Se a extensão do livro a ser gerado for pdf, são configuradas também as seguintes opções:
--pdf-page-numbers: setado para null--disable-font-rescaling: setado para true--paper-size: setado para null--custom-size: utilizado o valor da propriedadepdf.customSizedobook.json--unit: setada para millimeter
Insere conteúdo no início do livro.
Dependências externas:
- fs-extra, métodos extras para o módulo de sistemas de arquivo do Node.js.
- Q, biblioteca de promises para Node.js.
Função associada ao hook de finish do gitbook, que é chamada no fim da geração de um site ou ebook pelo gitbook.
Se a extensão do livro a ser gerado não for pdf, não faz nada.
Agora, se for pdf, faz os seguintes passos:
- cria sumário utilizando a função
renderTocPDF - adiciona conteúdo antes do sumário através da função
handlePreContent - junta conteúdos em um pdf só utilizando a função
joindo módulopdftk, gerando o arquivoindex-with-pre-content.pdf - atualiza índice do pdf usando a função
updateBookmarkInfodo módulopdftk, gerando o arquivoindex-with-pre-content-and-bookmarks.pdf - atualiza informações de número das páginas do pdf com a função
updatePageNumberInfodo módulogs, gerando o arquivoindex-with-pre-content-bookmarks-and-page-numbers.pdf - há um passo final, se o comando executado for
gitbook pdf. Os arquivos intermediários são gerados no diretório/tmpe logo em seguida apagados. Por isso, copiamos o/tmp/index-with-pre-content-bookmarks-and-page-numbers.pdfpara/tmp/index.pdf. O gitbook se encarrega de transformá-lo no arquivo, chamadobook.pdf.
Função privada de pre-content/index.js que é responsável por gerar um pdf com o sumário, a partir do pdf original do gitbook/calibre.
Parâmetros:
- outputDir -
Stringcom o caminho do diretório de saída - originalPDF -
Stringcom o caminho do pdf gerado pelo gitbook/calibre - pdfInfo -
Objectcom informações do livro como autor, editora e título, além das opções do gitbook.
Retorno:
Stringcom o caminho do pdf gerado que contém o sumário
Os passos para gerar o pdf com o sumário são os seguintes:
- extrair do índice do pdf original, através do função
extractTOCdo módulopdftk.js, umObjectcom capítulos e seções com suas respectivas páginas. - atualizar as páginas do
Objectobtida no passo anterior, utilizando a funçãoupdatedo módulotoc.js, para que o primeiro capítulo comece na página 1. Nas informações extraídas pelo pdftk, o primeiro capítulo começa na página 3, porque é considerada a capa e uma página com o sumário original (e incompleto) gerado pelo gitbook. - com o
Objectcom as páginas atualizadas, é renderizado um html através da funçãorenderdo módulohtmlRenderer.js. Para isso, é passado o templatebook/templates/toc.tpl.html. - a
Stringcom o html renderizado no passo anterior é salva em um arquivo - para gerar um pdf com o sumário é chamada a função
generatedo módulocalibrepassando o caminho do arquivo html, o caminho onde o arquivo pdf deve ser gerado e opções para geração do pdf. As opções do calibre vem do objetopdfInfo, que são obtidas dobook.json. São definidas as seguintes opções:--pdf-page-numbersfica comonullporque será usado o--pdf-footer-template--disable-font-rescalingfica comotruepara desabilitar mudança nas fontes--paper-sizefica comnullporque será usado o--custom-size--unitfica commillimeter--chapterfica com/para desligar a detecção de capítulos--page-breaks-beforefica com/para desabilitar quebras de página.--custom-sizeé obtido depdf.customSizedobook.json--margin-lefté obtido depdf.margin.leftdobook.json--margin-righté obtido depdf.margin.rightdobook.json--margin-topé obtido depdf.margin.topdobook.json--margin-bottomé obtido depdf.margin.bottomdobook.json--pdf-default-font-sizeé obtido depdf.fontSizedobook.json--pdf-mono-font-sizeé obtido depdf.fontSizedobook.json--pdf-header-templateé obtido depdf.summary.headerTemplatedobook.json--pdf-footer-templateé obtido depdf.summary.footerTemplatedobook.json
No fim desses passos, temos um pdf com o sumário do livro com capítulos e seções e as respectivas páginas.
Função privada de pre-content/index.js responsável por gerar pdfs com todo o conteúdo que precede o primeiro capítulo: conteúdo fixo como pdfs com copyright e propagandas, conteúdo .md que precisa ser renderizado e o pdf do sumário.
Parâmetros:
- inputDir -
Stringcom o caminho do diretório de entrada - outputDir -
Stringcom o caminho do diretório de saída - tocPDF -
Stringcom o caminho do pdf do sumário - pdfInfo -
Objectcom informações do livro como autor, editora e título, além das opções do gitbook.
Retorno:
Arraycom todos os caminhos de pdfs que devem ser inseridos antes do primeiro capítulo
Os passos para gerar o pdfs são os seguintes:
- obter todos os arquivos
.pdfordenados por nome do diretório apontado pela variável de ambienteEXTRAS_DIR, se presente - verificar se existe um diretório
extrasno livro e obter todos os arquivos.pdfdesse diretório ordenados por nome - obter todos os arquivos
.md, ordenados por nome, do diretóriointro - renderizar os
.md, transformado-os em.pdf - extrair a soma dos número de páginas de todos os
.pdfencontrados noEXTRAS_DIR, noextrasou gerados a partir do.mddeintro - atualizar o objeto
pdfInfocom um objetopreContentque tem a propriedadenumberOfPagescom a soma do número de páginas obtida anteriormente
Após a execução da função, é retornado um array com os caminhos dos .pdf de EXTRAS_DIR, extras e intro (renderizados). Além disso, o objeto pdfInfo terá o número de páginas desses .pdf.
Contém código de invocação da ferramenta pdftk.
Dependências externas:
- Q, biblioteca de promises para Node.js.
Função que usa o pdftk para extrair o índice de um pdf.
Parâmetros:
- pdfFile -
Stringcom o caminho de um pdf
Retorno:
ArraydeObjects com informações dos capítulos (title,pageNumberesections).
É executado o comando pdftk arquivo.pdf dump_data.
Cada linha da resposta do comando anterior é lida, buscando a página através do BookmarkPageNumber, se é seção ou capítulo através do BookmarkLevel e o título através do BookmarkTitle .
Então, é retornado um Array que contém objetos com as informações de cada capítulo. O retorno será algo como:
[
{ title: "Capítulo 1",
pageNumber: 1,
sections: [
{ title: "Seção 1.1", pageNumber: 1},
{ title: "Seção 1.2", pageNumber: 3}
]
},
{ title: "Capítulo 2",
pageNumber: 5,
sections: [
{ title: "Seção 2.1", pageNumber: 6}
]
}
]Em pre-content/test/integration/pdftk-test.js, há um teste de que executa a extração do índice em um pdf de exemplo.
Função que retorna a soma do número de páginas dos arquivos pdf passados como parâmetro, usando o pdftk.
Parâmetros:
- files -
Arraycom o caminho de arquivos pdf
Retorno:
Numbercom a soma do número de páginas dos pdfs.
Para extrair o número de páginas, é executado o comando pdftk arquivo.pdf dump_data e lida a informação NumberOfPages.
Como o comando pdftk é executado várias vezes, é criado um array de promises e são utilizadas as funções all e spread da biblioteca Q, para gerenciar a execução das promises.
Em pre-content/test/integration/pdftk-test.js, há um teste de que executa a extração do número de páginas de pdfs de exemplo.
Função que mescla vários arquivos pdf usando o pdftk.
Parâmetros:
- pdfFile -
Stringcom o caminho de um pdf principal - files -
Arraycom o caminho de arquivos pdf a serem inseridos no começo do pdf principal - outputFile -
Stringcom caminho do pdf de saída
Os arquivos pdf no array files são colocados logo depois da página 1 do arquivo principal (que contém a capa do livro). Depois de todos os arquivos pdf extras, é inserido o conteúdo do pdf principal.
Obs.: Na verdade, é retirada a página 2 do pdf principal, porque essa página contém um sumário incompleto que é gerado pelo gitbook.
É utilizada o comando cat do pdftk. Um exemplo de chamada é o seguinte:
pdftk A="input.pdf" B="copyright.pdf" C="ads.pdf" D="toc.pdf" cat A1 B C D A3-end output out.pdf
Em pre-content/test/unit/pdftk-join-test.js, há um teste de unidade com exemplos.
Função que, dado um pdf de entrada e um objeto com as páginas iniciais e títulos dos capítulos e seções, gera um pdf de saída com as informações atualizadas.
Parâmetros:
- inputFile -
Stringcom o caminho do pdf de entrada - info -
Objectcom informações atualizadas das páginas e títulos - infoFile -
Stringcom o caminho onde deve ser gravado o arquivo com informações do novo índice no formato do pdftk - outputFile -
Stringcom caminho do pdf de saída
Vamos supor que invocamos essa função com o objeto info, conforme a seguir:
{
"toc":[
{
"title":"1 Introdução",
"pageNumber":3,
"sections":[
{
"title":"1.1 Mantendo o histórico do código",
"pageNumber":3
}
]
}
],
"preContent": { "numberOfPages": 9 }
};Se o parâmetro infoFile for info.txt, esse arquivo será gerado com o seguinte conteúdo:
BookmarkBegin
BookmarkTitle: 1 Introdução
BookmarkLevel: 1
BookmarkPageNumber: 13
BookmarkBegin
BookmarkTitle: 1.1 Mantendo o histórico do código
BookmarkLevel: 2
BookmarkPageNumber: 13
Então, é invocada a opção update_info do comando pdftk da seguinte maneira:
pdftk input.pdf update_info info.txt output output.pdf
Depois dessa invocação, o índice do output.pdf estará com as páginas atualizadas de acordo com as informações de info.
Em pre-content/test/unit/pdftk-bookmarkInfo-test.js, há um teste de unidade com exemplos dos parâmetros e do tipo de arquivo que é gerado.
Atualiza páginas do sumário.
Função que atualiza a informação do número da página dos capítulo e seções, de maneira que o primeiro capítulo comece na página 1. Para isso, são descontadas 2 páginas: 1 para a capa e outra para o sumário original do gitbook.
Parâmetros:
- toc -
Objectcom informações dos capítulos (title,pageNumberesections).
Retorno:
Objectcom número das páginas dos capítulos e seções atualizados
Renderiza um html.
Dependências externas:
Função que renderiza um html utilizando a template engine swig, dados um conteúdo e um template.
Parâmetros:
- content -
Objectcom o conteúdo a ser renderizado. - templateLocation -
Stringcom o caminho de um template compatível com oswig
Retorno:
- uma
Stringque contém o html renderizado
Renderiza um pdf utilizando o comando ebook-convert do calibre.
Dependências externas:
- Q, biblioteca de promises para Node.js.
Função que gera um pdf a partir de um html, utilizando o ebook-convert do calibre.
Parâmetros:
- inputFilename -
Stringcom o caminho do html de entrada - outputFilename -
Stringcom o caminho do pdf de saída - options -
Objectcom opções doebook-convert
É gerada um chamada do tipo:
ebook-convert input.html output.pdf --disable-font-rescaling --chapter="/" --page-breaks-before="/"
O comando é executado através da função exec do módulo child_process do Node.js.
Lista os arquivos de um diretório.
Dependências externas:
- Q, biblioteca de promises para Node.js.
Função que retorna os caminhos de todos os arquivos de uma determinada extensão de um diretório, ordenados pelo nome do arquivo.
Parâmetros:
- dir -
Stringcom o caminho de um diretório. - extension -
Stringcom uma extensão de arquivos
Retorno:
Arraycom os caminhos dos arquivos da extensão, ordenados por nome.
Renderiza arquivos md em pdf.
Dependências externas:
Função que renderiza uma lista de arquivos md em pdf.
Parâmetros:
- files -
Arraycom caminhos de arquivos .md - template -
Stringcom o caminho de um template compatível comswig - pdfOptions -
Objectcom opções doebook-convert
Para cada arquivo .md do parâmetro files, são feitos os seguintes passos:
- renderizar um html a partir do md utilizando a biblioteca
kramed - é utilizada a função
renderdo módulohtmlRenderer.jspassando o parâmetrotemplate, para melhorar o html gerado no passo anterior. - é criado um arquivo com o conteúdo html com o mesmo nome do md, só que com extensão
.html - é utilizada a função
generatedo módulocalibre.jspara gerar um pdf a partir do arquivo html. São utilizadas as seguintes opções:--pdf-page-numbersfica comonullporque será usado o--pdf-footer-template--disable-font-rescalingfica comotruepara desabilitar mudança nas fontes--paper-sizefica comnullporque será usado o--custom-size--unitfica commillimeter--chapterfica com/para desligar a detecção de capítulos--page-breaks-beforefica com/para desabilitar quebras de página.--custom-sizeé obtido depdf.customSizedobook.json--margin-lefté obtido depdf.margin.leftdobook.json--margin-righté obtido depdf.margin.rightdobook.json--margin-topé obtido depdf.margin.topdobook.json--margin-bottomé obtido depdf.margin.bottomdobook.json--pdf-default-font-sizeé obtido depdf.fontSizedobook.json--pdf-mono-font-sizeé obtido depdf.fontSizedobook.json--pdf-header-templatefica como null, para não ter cabeçalho--pdf-footer-templatefica como null, para não ter rodapé
Código de chamada do ghostscript.
Dependências externas:
- Q, biblioteca de promises para Node.js.
Função que configura o pdf para usar números romanos (i, ii, iii, iv, ...) até antes do capítulo 1 e algarismos arábicos (1, 2, 3, 4, ...) do capítulo 1 em diante.
Parâmetros:
- inputFile -
Stringcom o caminho do pdf de entrada - pageInfo -
Objectcom informações do livro como título, autores, editora e número de páginas de pré-conteúdo - pageInfoFile -
Stringcom o caminho onde gravar o arquivo que será usado ogs - outputFile -
Stringcom o caminho do pdf de saída
Suponha que passamos o objeto pageInfo, conforme abaixo:
{
"book": {
"title": "Git e GitHub",
"author": "Alexandre",
"publisher": "Casa do Código"
},
"preContent": { "numberOfPages": 9 }
};O arquivo gerado teria o seguinte conteúdo:
[ /Title (Git e GitHub)
/Author (Alexandre)
/Creator (Casa do Código)
/Producer (Casa do Código)
/DOCINFO pdfmark
[/_objdef {pl} /type /dict /OBJ pdfmark
[{pl} <</Nums [
0 << /S /r >>
10 << /S /D /St 1 >>
]>> /PUT pdfmark
[{Catalog} <</PageLabels {pl}>> /PUT pdfmark
O conteúdo do arquivo anterior informa para o Ghostscript que da página 0 a 9 devem ser utilizados números romanos e da página 10 em diante devem ser usados algarismos arábicos.
Então, será chamado o Ghostscript de maneira parecida com:
gs -q -o output.pdf -sDEVICE=pdfwrite input.pdf info.txt
Em pre-content/test/unit/gs-pageInfo-test.js, há um teste de unidade.