Bookchef

A library to convert bookchef.xml file tree into an actual book (currently HTML and PDF are supported). bookchef.xml is A VERY SIMPLE xml format that I designed to write books. Yes, I've actually written and successfully published a book with it.

Installation

  1. Install wkhtmltopdf as explained in this Wiki. It's a utility that converts html to pdf that Bookchef is going to need.

  2. gem install bookchef

Now a bookchef command is available on your system. Type it to see a short summary on how to use it. You will be able to do everything from the command line, but, of course, you may load necessary classes in your ruby program.

Bookchef XML basics

Once I decided to write a book. I didn't want to use any propritary software -- I wanted to use git, my text-editor and a simple xml format (and I mean, really simple) that you can write by hand, so that later I could maybe build some kind of gui-editor around it. In order to make git useful, I realized that each chapter and each section of the chapter should be in a separate file and a seperate directory. Thus we have <chapter> and <section> tags, each may or may not have an src= attribute. Here's an example:

<book>
  <chapter>
      <section src="section1.xml"/>
      <section src="section2.xml"/>
      <section>And this one doesn't have a seperate file</section>
  </chapter>

  <!-- Automatically loads chapter2/index.xml
  if file name is not specified, e.g. doesn't end on .xml -->
  <chapter src="chapter2"></chapter>
</book>

Because chapters and sections may be in seperate files, it will be much easier to parse git diffs in the future, when and if this gui editor arrives. Also, it makes it easy to to manually navigate through the book with just your text editor. Assuming this file is called index.xml (and for now it is a requirement for a file which contains a <book> tag) I don't recommend using any kind of internal links (like footnotes and references, see below) in this file, because for now they will be broken. index.xml in the root book directory should source all the parts from other files.

Within the book, you may use various elements, listed below:

  • <title> may appear once within a chapter or a section, but doesn't have to. While compiling, each chapter automatically gets a number before its title
  • <code-inline> allows you to include an inline code
  • <code> allow a multi-line block of code. Remember that within it other tags don't work.
  • <p> is self-exlpanatory
  • <term> - wrap a term with it, with standard styles used by compiler the text will appear in italics.
  • <filename> is used to indicate either a filename or a url (when it's not supposed to be clicked, for example in the case of http://localhost:3000)

Then we also have two special blocks that may appear at the bottom of each section, which are called <references> and <footnotes>. Here's how it may look:

Minimum wage: why it doesn't work

Minimum wage doesn't work because people getting jobs for this wage are doing so at the expense of all others who didn't get the same job, when employers were either unable to pay for that many employees or simply went out of business. Or, they may indeed comply with the law and hire just the same number of employees, in which case they simply redistribute these costs on their customers, who then redistribute these costs on their employers demanding more pay and the circle continues. This circle inevitably causes either inflation or loss of demand. Thus, minimum wage always has its costs, but those costs are not payed by people who are expected to pay.

By raising prices People either start demanding more pay, causing businesses to borrow more from banks or, if it's government employees, causing government to print more money. Article about minimum wage on Wikipedia
As you can see, we referenced both footnotes and references by wrapping some text in `` tags and using either a `footnote=` or `reference=` attributes. In fact, you can add those attributes to other tags, not just ``, but also `` or ``. It will look like this when compiled: ![bookchef_compiled_example](https://github.com/snitko/frontend_notifier/raw/master/bookchef_compiled_example.png) Of course, readers are able to click the links and go straight to the footnote or the reference they chose. You can also link to other chapters and sections which are placed in a separate file, for example: we discussed this in the chapter on supply and demand or you can even say this we discussed this in the chapter on in which case the content in the `` tags from that section will be used to create a link. You can have as many subdirectories as you want for each chapter, so if there's a large section within a chapter it makes sense to put it in a separate subdirectory. Just don't forget that each subdirectory must have its `index.xml` file. That's basically it. </code></pre> <h2 id="How_to_compile_your_XML_into_an_actual_book">How to compile your XML into an actual book</h2> <p>You've written a book using BookChef.xml tags, now you want to make a pdf out of it. That's rather simple to do from the command line:</p> <h3 id="Step_0">Step 0</h3> <p>make sure there's a git repo in your book dir. Otherwise there will be an error (to be fixed).</p> <h3 id="Step_1__Merge_xml_files_tree_into_one_big__xml_file">Step 1: Merge xml files tree into one big .xml file</h3> <pre class="code ruby"><code class="ruby">bookchef merge_tree path/to/your/book merged_book.xml </code></pre> <h3 id="Step_2__Compile_this_file_into_an__html_file_using_css_styles">Step 2: Compile this file into an .html file using css styles</h3> <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_bookchef'>bookchef</span> <span class='id identifier rubyid_make_html'>make_html</span> <span class='id identifier rubyid_merged_book'>merged_book</span><span class='period'>.</span><span class='id identifier rubyid_xml'>xml</span> <span class='id identifier rubyid_compiled_book'>compiled_book</span><span class='period'>.</span><span class='id identifier rubyid_html'>html</span> </code></pre> <h3 id="Step_3__Compile_PDF_out_of_this_HTML_">Step 3: Compile PDF out of this HTML:</h3> <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_bookchef'>bookchef</span> <span class='id identifier rubyid_make_pdf'>make_pdf</span> <span class='id identifier rubyid_compiled_book'>compiled_book</span><span class='period'>.</span><span class='id identifier rubyid_html'>html</span> <span class='id identifier rubyid_compiled_book'>compiled_book</span><span class='period'>.</span><span class='id identifier rubyid_pdf'>pdf</span> </code></pre> <p>Same steps can be achieved with Ruby code:</p> <pre class="code ruby"><code class="ruby"><span class='comment'># Step 1 </span><span class='id identifier rubyid_merger'>merger</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef" title="BookChef (class)">BookChef</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/TreeMerger" title="BookChef::TreeMerger (class)">TreeMerger</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="/gems/bookchef/BookChef/TreeMerger#initialize-instance_method" title="BookChef::TreeMerger#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>path/to/your/book</span><span class='tstring_end'>'</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>index.xml</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span> <span class='id identifier rubyid_merger'>merger</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span> <span class='id identifier rubyid_merger'>merger</span><span class='period'>.</span><span class='id identifier rubyid_save_to'>save_to</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>merged_book.xml</span><span class='tstring_end'>"</span></span> <span class='comment'># Step 2 </span><span class='id identifier rubyid_html_compiler'>html_compiler</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef" title="BookChef (class)">BookChef</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler" title="BookChef::Compiler (class)">Compiler</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/HTML" title="BookChef::Compiler::HTML (class)">HTML</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/HTML#initialize-instance_method" title="BookChef::Compiler::HTML#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>merged_book.xml</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span> <span class='id identifier rubyid_html_compiler'>html_compiler</span><span class='period'>.</span><span class='id identifier rubyid_run'>run</span> <span class='id identifier rubyid_html_compiler'>html_compiler</span><span class='period'>.</span><span class='id identifier rubyid_save_to'>save_to</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>compiled_book.html</span><span class='tstring_end'>"</span></span> <span class='comment'># Step 3 </span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef" title="BookChef (class)">BookChef</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler" title="BookChef::Compiler (class)">Compiler</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/PDF" title="BookChef::Compiler::PDF (class)">PDF</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/PDF#initialize-instance_method" title="BookChef::Compiler::PDF#initialize (method)">new</a></span></span><span class='lparen'>(</span> <span class='const'>File</span><span class='period'>.</span><span class='id identifier rubyid_read'>read</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>compiled_book.html</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span><span class='comma'>,</span> <span class='label'>output_file:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>compiled_book.pdf</span><span class='tstring_end'>'</span></span><span class='comma'>,</span> <span class='label'>footer_custom_html:</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>This text will appear in the footer of every page</span><span class='tstring_end'>"</span></span> <span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_compile'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/PDF#compile-instance_method" title="BookChef::Compiler::PDF#compile (method)">compile</a></span></span> </code></pre> <h2 id="Customizable_styles_and_compiling_to_different_formats">Customizable styles and compiling to different formats</h2> <p>There to directories that you might be interested in to customize the output. First, there's a <code>lib/bookchef/stylesheets/scss</code> where you may find a <code>default.scss</code> file. This file determines, obviously, what the HTML output will look like in a browser (use <code>rake compile_css</code> to create a correposnding css in <code>lib/bookchef/stylesheets/css</code>.</p> <p>To tell compiler which css file to use you'd need to create a new xslt-stylesheet in <code>lib/bookchef/stylesheets/xslt</code>. Just copy the default one, find the part where it creates a stylesheet link tag and change the css filename. Then you can tell compiler to use your xslt instead of a default one:</p> <pre class="code ruby"><code class="ruby"><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef" title="BookChef (class)">BookChef</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler" title="BookChef::Compiler (class)">Compiler</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/HTML" title="BookChef::Compiler::HTML (class)">HTML</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="/gems/bookchef/BookChef/Compiler/HTML#initialize-instance_method" title="BookChef::Compiler::HTML#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>merged_book.xml</span><span class='tstring_end'>"</span></span><span class='comma'>,</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='embexpr_beg'>#{</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef" title="BookChef (class)">BookChef</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="/gems/bookchef/BookChef#LIB_PATH-constant" title="BookChef::LIB_PATH (constant)">LIB_PATH</a></span></span><span class='embexpr_end'>}</span><span class='tstring_content'>/stylesheets/xslt/my_styles.xsl</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span> </code></pre> <p>Because compiler mostly relies on xslt styles to convert xml into html, you can add you own xslt-styles to create any kind of resulting files, for instance epub or Kindle.</p></div></div> <div id="footer"> Generated on Sun Sep 20 19:18:57 2026 by <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a> 0.9.45 (ruby-4.0.7). </div> </div> </body> </html>