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

xslfofootnote.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 "utilities.h"
#include "xslfofootnote.h"
#include "usfmtools.h"
#include "constants.h"
#include "gwrappers.h"


XslFoFootnote::XslFoFootnote (const Usfm& usfm, bool show)
// Stores the properties for all the footnote related styles.
{
  // Store and initialize variables.
  myshow = show;
  NoteNumberingType note_numbering_type = nntNumerical;
  note_numbering_restart = nnrtPage;
  ustring note_numbering_user_sequence;
  bool spacious = false;
  standardparagraph = NULL;
  extraparagraph = NULL;
  
  // Go through all the styles.
  for (unsigned int i = 0; i < usfm.styles.size(); i++) {
    if (usfm.styles[i].type == stFootEndNote) {
      // Check subtype.
      FootEndNoteType footnotetype = (FootEndNoteType) usfm.styles[i].subtype;
      switch (footnotetype) {
        case fentFootnote:
        {
          // Store data for the markers and the anchor.
          opening_marker = usfm_get_full_opening_marker (usfm.styles[i].marker);
          closing_marker = usfm_get_full_closing_marker (usfm.styles[i].marker);
          anchor_fontpercentage = usfm.styles[i].fontpercentage;
          anchor_italic = usfm.styles[i].italic;
          anchor_bold = usfm.styles[i].bold;
          anchor_underline = usfm.styles[i].underline;
          anchor_smallcaps = usfm.styles[i].smallcaps;
          anchor_superscript = usfm.styles[i].superscript;
          note_numbering_type = (NoteNumberingType) usfm.styles[i].userint1;
          note_numbering_restart = (NoteNumberingRestartType) usfm.styles[i].userint2;
          note_numbering_user_sequence = usfm.styles[i].userstring1;
          spacious = usfm.styles[i].userbool2;
          break;
        }
        case fentEndnote:
        {
          // Endnotes not dealt with here.
          break;
        }
        case fentStandardContent:
        {
          // Standard content.
          if (!standardparagraph)
            standardparagraph = new XslFoFootnoteParagraph (
              usfm.styles[i].marker, usfm.styles[i].fontsize, 
              usfm.styles[i].italic, usfm.styles[i].bold, usfm.styles[i].underline, usfm.styles[i].smallcaps,
              usfm.styles[i].justification, 
              usfm.styles[i].spacebefore, usfm.styles[i].spaceafter, 
              usfm.styles[i].leftmargin, usfm.styles[i].rightmargin, usfm.styles[i].firstlineindent, 
              usfm.styles[i].userbool1);
          footnote_markers.insert (usfm.styles[i].marker);
          break;
        }
        case fentContent:
        case fentContentWithEndmarker:
        {
          // Store data for the footnote body.
          content_marker.push_back (usfm_get_full_opening_marker (usfm.styles[i].marker));
          content_apocrypha.push_back (usfm.styles[i].userbool1);
          footnote_markers.insert (usfm.styles[i].marker);
          break;
        }
        case fentParagraph:
        {
          // Store relevant data for a paragraph marker.
          if (!extraparagraph)
            extraparagraph = new XslFoFootnoteParagraph (
              usfm.styles[i].marker, usfm.styles[i].fontsize, 
              usfm.styles[i].italic, usfm.styles[i].bold, usfm.styles[i].underline, usfm.styles[i].smallcaps,
              usfm.styles[i].justification, 
              usfm.styles[i].spacebefore, usfm.styles[i].spaceafter, 
              usfm.styles[i].leftmargin, usfm.styles[i].rightmargin, usfm.styles[i].firstlineindent, 
              usfm.styles[i].userbool1);
          footnote_markers.insert (usfm.styles[i].marker);
          break;
        }
      }
    }
  }
  // Ensure that both of the paragraph styles are there.
  if (!standardparagraph)
    standardparagraph = new XslFoFootnoteParagraph ("ft", 11, OFF, OFF, OFF, OFF, JUSTIFIED, 0, 0, 3, 0, 0, false);
  if (!extraparagraph)
    extraparagraph =    new XslFoFootnoteParagraph ("fp", 11, OFF, OFF, OFF, OFF, JUSTIFIED, 0, 0, 3, 0, 3, false);
  // Create footnote caller object.
  footnotecaller = new NoteCaller (note_numbering_type, note_numbering_user_sequence, spacious);
}


XslFoFootnote::~XslFoFootnote ()
{
  delete footnotecaller;
  delete standardparagraph;
  delete extraparagraph;
}


void XslFoFootnote::new_book ()
{
  if (note_numbering_restart == nnrtBook)
    footnotecaller->reset ();
  new_chapter ();
}


void XslFoFootnote::new_chapter ()
{
  if (note_numbering_restart == nnrtChapter)
    footnotecaller->reset ();
}


void XslFoFootnote::transform (XmlFoBlock * xmlfoblock, ustring& line)
// Replace all footnote related content with corresponding xslfo code.
{
  // If no opening marker in stylesheet, bail out.
  if (opening_marker.empty())
    return;
  
  // Variables.
  size_t opening_position;
  
  // Look for footnotes, but only deal with them if they have the endmarker too.
  opening_position = line.find (opening_marker);
  while (opening_position != string::npos) {
    
    // Look for the endmarker.
    size_t closing_position;
    closing_position = line.find (closing_marker, opening_position);    
    if (closing_position == string::npos) {
      gw_warning ("Footnote: missing endmarker");
      return;
    }

    // Take out this bit of the line, transform it, and insert it again.
    ustring footnote;
    footnote = line.substr (opening_position + opening_marker.length(), closing_position - opening_position - closing_marker.length());
    line.erase (opening_position, closing_position - opening_position + closing_marker.length());
    if (myshow) {
      footnote = transform_main_parts (xmlfoblock, footnote);
      line.insert (opening_position, footnote);
    }
    
    // Search for another footnote.
    opening_position = line.find (opening_marker, opening_position);
  }
}


ustring XslFoFootnote::transform_main_parts (XmlFoBlock * xmlfoblock, const ustring& line)
{
  // Variables.
  ustring xslfo_code;
  
  // Work on a copy.
  ustring footnote (line);
  
  // Add first bit of code.
  xslfo_code.append ("<fo:footnote>");
  
  // Extract the footnote caller.    
  ustring caller_in_text;
  ustring caller_in_note;
  if (footnote.length() > 0) {
    caller_in_text = footnote.substr (0, 1);
    caller_in_text = trim (caller_in_text);
    if (caller_in_text == "+") {
      caller_in_text = footnotecaller->get_caller();
      if (note_numbering_restart == nnrtPage)
        if (footnotecaller->get_spacious())
          caller_in_text = FOOTNOTE_CALLER_NUMBERING_PER_PAGE_TEXT_SPACIOUS;
        else
          caller_in_text = FOOTNOTE_CALLER_NUMBERING_PER_PAGE_TEXT;
    } else if (caller_in_text == "-") {
      caller_in_text.clear();
    }
    footnote.erase (0, 1);
    footnote = trim (footnote);
  }
  caller_in_note = caller_in_text;
  if (caller_in_note == FOOTNOTE_CALLER_NUMBERING_PER_PAGE_TEXT)
    caller_in_note = FOOTNOTE_CALLER_NUMBERING_PER_PAGE_NOTE;
  if (caller_in_note == FOOTNOTE_CALLER_NUMBERING_PER_PAGE_TEXT_SPACIOUS)
    caller_in_note = FOOTNOTE_CALLER_NUMBERING_PER_PAGE_NOTE_SPACIOUS;
  // Insert the xslfo code.
  xslfo_code.append (XmlFoInlineText (caller_in_text, xmlfoblock, anchor_fontpercentage, anchor_italic, anchor_bold, anchor_underline, anchor_smallcaps, anchor_superscript));

  // We now come to the footnote body.
  xslfo_code.append ("<fo:footnote-body>");
  xslfo_code.append ("<fo:list-block provisional-label-separation=\"0pt\" provisional-distance-between-starts=\"18pt\">");
  xslfo_code.append ("<fo:list-item>");
  
  // Insert the caller in the footnote body too.
  xslfo_code.append ("<fo:list-item-label end-indent=\"label-end()\">");
  vector<ustring> lines;
  {
    XmlFoBlock block (&lines, standardparagraph->fontsize,
                      standardparagraph->italic, standardparagraph->bold,
                      standardparagraph->underline, standardparagraph->smallcaps,
                      standardparagraph->justification, 
                      standardparagraph->spacebefore, standardparagraph->spaceafter, 
                      0, 0, 0, false);
    if (!caller_in_note.empty()) {
      lines.push_back (XmlFoInlineText (caller_in_note, &block, anchor_fontpercentage, anchor_italic, anchor_bold, anchor_underline, anchor_smallcaps, false));
    }
  }
  xslfo_code.append ("\n");
  for (unsigned int i = 0; i < lines.size(); i++) {
    xslfo_code.append (lines[i]);
    xslfo_code.append ("\n");
  }
  xslfo_code.append ("</fo:list-item-label>");

  // Insert some code for this footnote into the body.
  xslfo_code.append ("<fo:list-item-body start-indent=\"body-start()\">");
  
  // Divide the footnote into paragraphs, if there are more than one.
  vector<ustring> paragraphs;
  size_t pos;
  pos = footnote.find (extraparagraph->marker_open);
  while (pos != string::npos) {
    paragraphs.push_back (footnote.substr (0, pos));
    footnote.erase (0, pos + extraparagraph->marker_open.length());
    pos = footnote.find (extraparagraph->marker_open);
  }
  paragraphs.push_back (footnote);

  // Deal with each paragraph.
  for (unsigned int i = 0; i < paragraphs.size(); i++) {
    // Choose the right paragraph.
    XslFoFootnoteParagraph * paragraph;
    if (i == 0)
      paragraph = standardparagraph;
    else
      paragraph = extraparagraph;
    // Format actual text, and comply with footnote nesting, see USFM standard.
    paragraphs[i] = handle_nesting (paragraphs[i]);
    // Insert the paragraph as another block.
    lines.clear();
    {
      XmlFoBlock block (&lines, paragraph->fontsize,
                        paragraph->italic, paragraph->bold,
                        paragraph->underline, paragraph->smallcaps,
                        paragraph->justification, 
                        paragraph->spacebefore, paragraph->spaceafter, 
                        paragraph->leftmargin, paragraph->rightmargin, 
                        paragraph->firstlineindent, false);
      lines.push_back (paragraphs[i]);
    }
    xslfo_code.append ("\n");
    for (unsigned int i = 0; i < lines.size(); i++) {
      xslfo_code.append (lines[i]);
      xslfo_code.append ("\n");
    }
  }    
  
  // Close footnote and foot notebody.
  xslfo_code.append ("</fo:list-item-body>");
  xslfo_code.append ("</fo:list-item>");
  xslfo_code.append ("</fo:list-block>");
  xslfo_code.append ("</fo:footnote-body>");
  xslfo_code.append ("</fo:footnote>");

  // Return the code.
  return xslfo_code;  
}


ustring XslFoFootnote::handle_nesting (const ustring& line)
/*
Handle compliance with the following bit extracted from Usfm 2.03:

  By default footnote codes are not nested (i.e. they are not embedded within 
  each other). The use of each new code cancels out the 'environment' of the 
  previous. However, an alternative markup may be used requiring each internal 
  marker to be explicitly closed (e.g. \f + \fk...\fk*...\f*.). The examples 
  below include variants which use the "nested", and "non-nested" approach. The 
  \ft code is used in the non-nested markup to indicate a return to normal 
  explanatory footnote text. If the nested approach is used, each code must be 
  explicitly closed, and the unmarked text within \f ..\f* is assumed to be 
  normal footnote text.

  \f + \fk Issac:\ft In Hebrew means "laughter"\f*
  \f + \fk Issac:\fk* In Hebrew means "laughter"\f*

  \f + \fr 1.14 \fq religious festivals;\ft or \fq seasons.\f*
  \f + \fr 1.14\fr* \fq religious festivals;\fq* or \fq seasons.\fq*\f*
 
  \f + \fr 2.4 \fk The \nd Lord\nd*: \ft See \nd Lord\nd* in Word List.\f*
  \f + \fr 2.4\fr* \fk The \nd Lord\nd*:\fk* See \nd Lord\nd* in Word List.\f*
 
  \f + \fr 3.20 \fk Adam: \ft This name in Hebrew means "humanity".\f*
  \f + \fr 3.20\fr* \fk Adam:\fk* This name in Hebrew means "humanity".\f*
  
The strategy to handle this is:
* As the \ft is now being put in the xslfo block as the standard format, this
  marker can be disregarded here and hence removed.
* Make everything to behave as inline text. This implies everything is closed
  with an endmarker.

*/
{
  // Variables.
  ustring result;
  size_t position;
  
  // Work on a copy.
  ustring note (line);

  // If the note starts with the normal text marker, take it out, as this is 
  // the default format already set in the fo-block.
  if (note.find (standardparagraph->marker_open) == 0) {
    note.erase (0, standardparagraph->marker_open.length());
  }
  // Also remove any endmarkers for the normal text.
  position = note.find (standardparagraph->marker_close);
  while (position != string::npos) {
    note.erase (position, standardparagraph->marker_close.length());
    position = note.find (standardparagraph->marker_close);
  }

  /*
  Deal with the markers. 
  USFM allows two ways of positioning the styles. 
  One where the markers have no endmarker, and one where they have. 
  A mixture can occur too.
  
  For example, this code ...:
    \fr 1.7 \fq untie his sandals: \ft This was the duty of a slave.
  ... becomes:
    \fr 1.7 \fr*\fq untie his sandals: \fq*This was the duty of a slave.

  Another example, this code ...: 
    \fr 1.7 \fq untie \nd Jesus'\nd* sandals: \ft This was the duty of a slave.
  ... becomes:
    \fr 1.7 \fr*\fq untie \fq*\nd Jesus'\nd*\fq sandals: \fq*This was the duty of a slave.

  Steps:
  - Look for the next marker.
  - If it is an opening marker, 
      see if anything needs to be closed, close it, and move all to the output.
  - If it is a closing marker, 
      just copy it to the output, with the text. If anything needs to be reopened, do that too.
  */

  // Variables.
  ustring current_open_marker;
  // Deal with all note text until ready.
  while (!note.empty()) {
    // Find next marker.
    ustring marker;
    size_t length;
    bool opener;
    if (usfm_search_marker (note, marker, position, length, opener)) {
      // Another marker found.
      // If there's any text before the marker, copy that to the output.
      if (position > 0) {
        result.append (note.substr (0, position));
      }
      // Store the full marker.
      ustring full_marker = note.substr (position, length);
      // Take that text out from the note.
      note.erase (0, position + length);
      // Is the marker specific for footnotes?
      if (footnote_markers.find (marker) != footnote_markers.end()) {
        // Marker is footnote related: handle it.
        if (opener) {
          // It's an opening marker.
          // Close anything previous.
          if (!current_open_marker.empty()) {
            result.append (usfm_get_full_closing_marker (current_open_marker));
          }
          // Add this opener too.
          result.append (usfm_get_full_opening_marker (marker));
          // Set currently opened marker for next cycle.
          current_open_marker = marker;
        } else {
          // It is a closing marker.
          // Add it.
          result.append (usfm_get_full_closing_marker (marker));
          // Set currently opened marker for next cycle.
          current_open_marker.clear();
        }
      } else {
        // It is a foreign marker. Just copy it to the output.
        result.append (full_marker);
      }
    } else {
      // No more markers: copy remaining text to output.
      result.append (note);
      note.clear ();
      // If any marker was open, close it.
      if (!current_open_marker.empty())
        result.append (usfm_get_full_closing_marker (current_open_marker));
    }
  }

  // Take out all opening and closing markers for the standard note text.
  // They are not needed because the fo:block, that is the paragraph, already
  // defaults to this style.
  replace_text (result, standardparagraph->marker_open, "");
  replace_text (result, standardparagraph->marker_close, "");
 
  // Return the result.
  return result;
}

Generated by  Doxygen 1.6.0   Back to index