Logo Search packages:      
Sourcecode: bibledit version File versions  Download package

printproject.cpp

/*
** Copyright (C) 2003-2006 Teus Benschop.
**  
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**  
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**  
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**  
*/


#include "libraries.h"
#include <glib.h>
#include "printproject.h"
#include "utilities.h"
#include "bible.h"
#include "usfmtools.h"
#include <config.h>
#include "pdfviewer.h"
#include "xmlutils.h"
#include "paper.h"
#include "xmlfo-utils.h"
#include "style.h"
#include "stylesheetutils.h"
#include "formatter.h"
#include "constants.h"
#include "gwrappers.h"
#include "gtkwrappers.h"
#include "directories.h"
#include "fonts.h"
#include "xslfofootnote.h"
#include "xslfoendnote.h"
#include "xslfoxref.h"
#include "portion_utils.h"
#include "scripture_reordered.h"
#include "projectutils.h"
#include "generalconfig.h"
#include "projectconfig.h"


void view_project_pdf (ProgressWindow& progresswindow)
// Formats the project and shows it in a pdf viewer.
{
  // Log.
  gw_message ("Starting to print the project");
  
  // Progressbar.
  progresswindow.set_text ("Starting");
  if (progresswindow.cancel) {
    return;
  }

  // Configuration
  GeneralConfiguration genconfig (0);
  
  // Storage for xsl-fo file.
  vector<ustring> xslfo_lines;

  // Usfm data.
  ProjectConfiguration projectconfig ("");
  Usfm usfm (projectconfig.stylesheet());

  // Check the three basic usfm's are there: id, c, v.
  {
    vector <string> missing_styles;
    string style;
    style = "id";
    if (!usfm.is_identifier (style))
      missing_styles.push_back (style + " - an identifier");
    style = "c";
    if (!usfm.is_chapter_number (style))
      missing_styles.push_back (style + " - a chapter number");
    style = "v";
    if (!usfm.is_verse_number (style))
      missing_styles.push_back (style + " - a verse number");
    if (!missing_styles.empty()) {
      string message;
      message = "Not all basic styles are in the stylesheet.\n"
                "Printing cannot continue.\n"
                "Please add the following styles to the stylesheet:";
      for (unsigned int i = 0; i < missing_styles.size(); i++)
        message.append ("\n" + missing_styles[i]);
      gtkw_dialog_error (NULL, message);
      return;
    }
  }

  // Scripture related data.
  // Possible reordering or exclusion of books.
  ScriptureReordered scripture_reordered (genconfig.project());
  vector<ustring> running_headers;
  vector<string> table_of_contents_ids;
  if (scripture_reordered.books.empty()) {
    gtkw_dialog_info (NULL, "There were no books to print");
    return;
  }

  // Data for progress bar.
  progresswindow.set_iterate (0, 1, scripture_reordered.books.size());
  
  // Prepare for the inline text markers.
  UsfmInlineMarkers usfm_inline_markers (usfm);

  // Prepare for printing footnotes.
  XslFoFootnote xslfofootnote (usfm, true);
  
  // Prepare for printing endnotes.
  XslFoEndnote xslfoendnote (usfm, true);
  
  // Prepare for printing crossreferences.
  XslFoXref xslfoxref (usfm, true);
  
  // Start building the xml-fo file.
  // The various elements of this file are created using objects.
  // When the object goes out of scope, it writes the closing elements.
  {
    XmlFoRoot xmlforoot (&xslfo_lines);
    {
      XmlFoLayoutMasterSet layoutmasterset (&xslfo_lines, true);
    }
    {
      XmlFoPageSequence pagesequence (&xslfo_lines, true);
      // Generate the table of contents.
      {
        XmlFoFlow flow (&xslfo_lines);
        {
          XmlFoBlock block (&xslfo_lines, 18, OFF, ON, OFF, OFF, "", 0, 5, 0, 0, 0, true);
          xslfo_lines.push_back("Table of Contents");
        }
        // Go through all the books and collect the running header for each book.
        progresswindow.set_text ("Table of contents");
        if (progresswindow.cancel) {
          return;
        }
        for (unsigned int i = 0; i < scripture_reordered.books.size(); i++) {
          // Update progress bar.
          progresswindow.iterate();
          if (progresswindow.cancel) {
            return;
          }
          // Get the running header.
          string booktitle;
          // Get the text of the chapter zero.
          vector <ustring> lines;
          lines = project_retrieve_chapter (genconfig.project(), scripture_reordered.books[i], 0);
          // Search for data in these lines.
          for (unsigned int i2 = 0; i2 < lines.size(); i2++) {
            ustring line = lines[i2];
            ustring marker = usfm_extract_marker (line);
            if (usfm.is_running_header (marker)) {
              booktitle = line;
            }
          }      
          // No running header found? Take English name.
          if (booktitle.empty()) 
            booktitle = scripture_reordered.books[i];
          // Store for later use.
          running_headers.push_back (booktitle);
          // Here are the ids to be used for the table of content.
          // Spaces are not allowed in the ids, neither can they start with a number.
          ustring toc_id ("book_" + convert_to_string (i));
          table_of_contents_ids.push_back (toc_id);
          // Write the xsl-fo data.
          XmlFoBlock block (&xslfo_lines, 12, OFF, OFF, OFF, OFF, "last-justify", 0, 0, 0, 0, 0, true);
          xslfo_lines.push_back(booktitle);
          xslfo_lines.push_back("        <fo:leader leader-pattern=\"dots\"/>");
          ustring line;
          line = "        <fo:page-number-citation ref-id=\"";
          line.append (toc_id);
          line.append ("\"/>");
          xslfo_lines.push_back(line);
        }
      }
    }

    // Progress bar.
    progresswindow.set_iterate (0, 1, scripture_reordered.books.size());
    
    // Generate all the books.
    for (unsigned int i = 0; i < scripture_reordered.books.size(); i++) {
      // Update progress bar.
      progresswindow.iterate ();
      progresswindow.set_text (running_headers[i]);
      if (progresswindow.cancel) {
        return;
      }
      // Signal new book to footnotes object.
      xslfofootnote.new_book ();
      // Make page sequence, static content, and flow for the book.        
      XmlFoPageSequence pagesequence (&xslfo_lines, true);
      {
        XmlFoStaticContent staticcontent (&xslfo_lines);
      }
      {
        XmlFoFlow flow (&xslfo_lines);
        // Generate markers for the table of contents.
        ustring line;
        line = "      <fo:block id=\"toc-id\" span=\"all\">";
        // (The span="all" is to work around a bug in FOP).
        replace_text (line, "toc-id", table_of_contents_ids[i]);
        xslfo_lines.push_back (line);
        xslfo_lines.push_back ("        <fo:marker marker-class-name=\"book\">");
        xslfo_lines.push_back(running_headers[i]);
        xslfo_lines.push_back("        </fo:marker>");
        xslfo_lines.push_back("      </fo:block>");
        // Open the book (it cleans the lines on its own).
        vector<ustring> book_lines;
        book_lines = project_retrieve_book (genconfig.project(), scripture_reordered.books[i]);
        // Deal with inclusion of a possible portion only.
        bool portion_print = false;
        bool portion_print_off_next_verse = false;
        unsigned int portion_chapter_from, portion_chapter_to;
        ustring portion_verse_from, portion_verse_to;
        select_portion_get_values (genconfig.project(), scripture_reordered.books[i], 
                                   scripture_reordered.portions[i],
                                   portion_chapter_from, portion_verse_from, 
                                   portion_chapter_to, portion_verse_to);
        if (portion_chapter_from == 0)
          if (portion_verse_from == "0") {
            portion_print = true;
          }
        unsigned int current_chapter = 0;
        // Go through all the lines of the book.
        XmlFoBlock * xmlfoblock = NULL;
        for (unsigned int i2 = 0; i2 < book_lines.size(); i2++) {
          ustring line = book_lines[i2];
          // Change certain characters to xml entities.
          vector <size_t> dummy;
          xml_handle_entities (line, dummy);
          // Deal with possible "inserted" and "removed" marks.
          replace_text (line, STRIKE_THROUGH_BEGIN, "<fo:inline text-decoration=\"line-through\">");
          replace_text (line, STRIKE_THROUGH_END, "</fo:inline>");
          replace_text (line, BOLD_BEGIN, "<fo:inline font-weight=\"bold\">");
          replace_text (line, BOLD_END, "</fo:inline>");
          // Deal with footnotes.
          xslfofootnote.transform (xmlfoblock, line);
          // Deal with endnotes.
          xslfoendnote.transform (xmlfoblock, line);
          // Deal with crossreferences.
          xslfoxref.transform (xmlfoblock, line);
          // Deal with inline text.
          usfm_handle_inline_text (line, &usfm_inline_markers, xmlfoblock);
          // Get the style belonging to the marker.
          ustring marker = usfm_extract_marker (line);
          if (usfm.is_identifier (marker)) {
            // Do nothing with an identifier.
          } else if (usfm.is_comment (marker)) {
            // Skip comments.
          } else if (usfm.is_running_header (marker)) {
            // Skip running header, because it has been dealth with above.
          } else if (usfm.is_verse_number (marker)) {
            // Because of dealing with portions to include/exclude, handle verse first.
            // Get verse number. Handle combined verses too, e.g. 10-12b, etc.
            size_t position = line.find (" ");
            position = CLAMP (position, 0, line.length());
            ustring versenumber = line.substr (0, position);
            position++;
            line.erase (0, position);
            // See whether this chapter.verse is within our portion to print.
            if (portion_print_off_next_verse)
              portion_print = false;
            if ((current_chapter == portion_chapter_from) && (versenumber == portion_verse_from)) {
              portion_print = true;
            }
            if ((current_chapter >= portion_chapter_to) && (versenumber == portion_verse_to))
              portion_print_off_next_verse = true;
            if (!portion_print)
              continue;
            // There was a bug that the chapter number was missing from a book,
            // and that results in inline text put straight under the flow, 
            // which is an error in XSL. Solve that here by checking whether
            // a block has been opened. If need be, open a default block.
            if (xmlfoblock == NULL) {
              xmlfoblock = new XmlFoBlock (&xslfo_lines, 12, OFF, OFF, OFF,
                OFF, JUSTIFIED, 0, 0, 0, 0, 0, false);
            }
            // When the usfm is a verse number, then the number is put in the 
            // format specified by the stylesheet, but the remaining part of the
            // line inherits the formatting from the block it is in, usually
            // a paragraph block.
            xslfo_lines.push_back(XmlFoInlineText (versenumber, xmlfoblock, usfm.fontpercentage(), usfm.italic(), usfm.bold(), usfm.underline(), usfm.smallcaps(), usfm.superscript()));
            xslfo_lines.push_back(line);
          } else if (usfm.is_starting_paragraph (marker)) {
            if (!portion_print)
              continue;
            if (xmlfoblock) {
              delete xmlfoblock;
            }
            xmlfoblock = new XmlFoBlock (&xslfo_lines, 
              usfm.fontsize(), usfm.italic(), usfm.bold(), usfm.underline(), 
              usfm.smallcaps(), usfm.justification(),
              usfm.spacebefore(), usfm.spaceafter(), usfm.leftmargin(),
              usfm.rightmargin(), usfm.firstlineindent(), usfm.spancolumns());
            xslfo_lines.push_back(line);
          } else if (usfm.is_inline_text (marker)) {
            if (!portion_print)
              continue;
            // Inline text, has been dealt with before (therefore should never occur here).
            xslfo_lines.push_back(XmlFoInlineText (line, xmlfoblock, usfm.fontpercentage(), usfm.italic(), usfm.bold(), usfm.underline(), usfm.smallcaps(), usfm.superscript()));
          } else if (usfm.is_chapter_number (marker)) {
            // See whether this chapter.0 is within our portion to print.
            current_chapter = convert_to_int (line);
            if ((current_chapter == portion_chapter_from) && (portion_verse_from == "0")) {
              portion_print = true;
            }
            if (current_chapter > portion_chapter_to)
              portion_print = false;
            if (!portion_print)
              continue;
            // Close any open block.
            if (xmlfoblock) {
              delete xmlfoblock;
            }
            // Signal new chapter to footnotes object.
            xslfofootnote.new_chapter ();            
            // Insert marker for chapter number.
            xslfo_lines.push_back ("      <fo:block>");
            xslfo_lines.push_back ("        <fo:marker marker-class-name=\"chapter\">");
            xslfo_lines.push_back (line);
            xslfo_lines.push_back ("        </fo:marker>");
            xslfo_lines.push_back ("      </fo:block>");
            // Insert chapter text.
            xmlfoblock = new XmlFoBlock (&xslfo_lines, 
              usfm.fontsize(), usfm.italic(), usfm.bold(), usfm.underline(),
              usfm.smallcaps(), usfm.justification(),
              usfm.spacebefore(), usfm.spaceafter(), usfm.leftmargin(),
              usfm.rightmargin(), usfm.firstlineindent(), usfm.spancolumns());
            xslfo_lines.push_back(line);
          } else {
            if (!portion_print)
              continue;
            // Fallback for unknown marker, or no marker.
            xslfo_lines.push_back(line);
          }
        } 
        // Close possible last block of text.
        if (xmlfoblock) {
          delete xmlfoblock;
        }
        // Balance text columns using a block that spans the columns.
        xslfo_lines.push_back ("<fo:block span=\"all\"></fo:block>");
      }
    }
  }    
  
  // Make a temporary directory where to put the working files and the resulting
  // .pdf file.information. This directory is not removed, because the pdf viewer 
  // needs the .pdf file to be there during viewing or printing.
  string working_directory = gw_build_filename(directories_get_temp(), "project");
  create_directory (working_directory);
  // Produce filename of .fo file.
  string fofilename = gw_build_filename(working_directory, "document.fo");
  // Write the document.
  write_lines (fofilename, xslfo_lines);
  // Show progress.
  if (progresswindow.cancel) {
    return;
  }
  string pdffilename = gw_build_filename(working_directory, "document.pdf");
  // Convert the xsl-fo document to .pdf.
  int conversion_result = formatter_convert_to_pdf (fofilename, pdffilename, progresswindow, xslfofootnote.footnotecaller, xslfoxref.footnotecaller);

  // Progressbar: ready.
  progresswindow.set_fraction (1);
  progresswindow.set_text ("Ready");
  if (progresswindow.cancel) {
    return;
  }
  
  // View the .pdf document.
  pdfviewer (pdffilename);

  // Give message if there were errors.
  if (conversion_result != 0) {
    string message = "While formatting the text problems were encountered.\n"
                     "See menu Help - System log for more details.\n"
                     "See the helpfile for a possible solution.";
    gtkw_dialog_error (NULL, message);
  };
  
  // Log: ready.
  gw_message ("Ready printing the project");
}

Generated by  Doxygen 1.6.0   Back to index