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

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


XslFoXref::XslFoXref (const Usfm& usfm, bool show)
// Stores the properties for all the crossreference related styles.
{
  // Store and initialize variables.
  myshow = show;
  NoteNumberingType note_numbering_type = nntAlphabetical;
  note_numbering_restart = nnrtChapter;
  ustring note_numbering_user_sequence;
  bool spacious = false;
  standardparagraph = NULL;

  // Go through all the styles.
  for (unsigned int i = 0; i < usfm.styles.size(); i++) {
    if (usfm.styles[i].type == stCrossreference) {
      // Check subtype.
      CrossreferenceType xreftype = (CrossreferenceType) usfm.styles[i].subtype;
      switch (xreftype) {
        case ctCrossreference:
        {
          // 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 ctStandardContent:
        {
          // 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);
          xref_markers.insert (usfm.styles[i].marker);
          break;
        }
        case ctContent:
        case ctContentWithEndmarker:
        {
          // 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);
          xref_markers.insert (usfm.styles[i].marker);
          break;
        }
      }
    }
  }
  
  // Ensure that the paragraph style is there.
  if (!standardparagraph)
    standardparagraph = new XslFoFootnoteParagraph ("ft", 11, OFF, OFF, OFF, OFF, JUSTIFIED, 0, 0, 3, 0, 0, false);
  // Create footnote caller object.
  footnotecaller = new NoteCaller (note_numbering_type, note_numbering_user_sequence, spacious);
}


XslFoXref::~XslFoXref ()
{
  delete footnotecaller;
  delete standardparagraph;
}


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


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


void XslFoXref::transform (XmlFoBlock * xmlfoblock, ustring& line)
// Replace all crossreference 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 crossreferences, 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. If not found bail out.
    size_t closing_position;
    closing_position = line.find (closing_marker, opening_position);    
    if (closing_position == string::npos) {
      gw_warning ("Crossreference: missing endmarker");
      return;
    }

    // Take out this bit of the line, transform it, and insert it again.
    ustring xref;
    xref = 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) {
      xref = transform_main_parts (xmlfoblock, xref);
      line.insert (opening_position, xref);
    }

    // Search for another crossreference.
    opening_position = line.find (opening_marker, opening_position);
  }
  
}


ustring XslFoXref::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 = CROSSREFERENCE_CALLER_NUMBERING_PER_PAGE_TEXT_SPACIOUS;
        else
          caller_in_text = CROSSREFERENCE_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 == CROSSREFERENCE_CALLER_NUMBERING_PER_PAGE_TEXT)
    caller_in_note = CROSSREFERENCE_CALLER_NUMBERING_PER_PAGE_NOTE;
  if (caller_in_note == CROSSREFERENCE_CALLER_NUMBERING_PER_PAGE_TEXT_SPACIOUS)
    caller_in_note = CROSSREFERENCE_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()\">");
  
  // Deal with the paragraph.
  {
    // Format actual text, and comply with footnote nesting, see USFM standard.
    footnote = handle_nesting (footnote);
    // Insert the paragraph as another block.
    lines.clear();
    {
      XmlFoBlock block (&lines, standardparagraph->fontsize,
                        standardparagraph->italic, standardparagraph->bold,
                        standardparagraph->underline, standardparagraph->smallcaps,
                        standardparagraph->justification, 
                        standardparagraph->spacebefore, standardparagraph->spaceafter, 
                        standardparagraph->leftmargin, standardparagraph->rightmargin, 
                        standardparagraph->firstlineindent, false);
      lines.push_back (footnote);
    }
    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 XslFoXref::handle_nesting (const ustring& line)
/*
Handle compliance with the following bit extracted from Usfm 2.03:

  By default cross reference 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. \x - \xo...\xo*...\x*.). The 
  examples below include variants which use the "nested", and "non-nested" 
  approach. If the nested approach is used, each code must be explicitly closed, 
  and the unmarked text within \x ..\x* is assumed to be target reference text.

  TEV Mat 1.11:

  \p
  \v 2-6a From Abraham to King David, the following ancestors
  are listed: Abraham, Isaac, Jacob, Judah and his brothers;
  then Perez and Zerah (their mother was Tamar), Hezron, Ram,
  Amminadab, Nahshon, Salmon, Boaz (his mother was Rahab), Obed
  (his mother was Ruth), Jesse, and King David.
  \p
  \v 6b-11 From \x - \xo 1.11: \xt 2Ki 24.14,15; 2Ch 36.10;
  Jer 27.20.\x* David to the time when the people of Israel were
  taken into exile in Babylon, the following ancestors are listed: 

  Cross reference with multiple "parts" (TEV Gen 49.31):

  \v 31 
  \x - \xo 49.31: a \xt Gen 25.9,10; \xo b \xt Gen 35.29.\x* 
  That is where they buried Abraham and his wife Sarah; that is where they buried  
  Isaac and his wife Rebecca; and that is where I buried Leah.
  
  Cross reference with references to the Apocrypha marked (TEV Gen 1.26):

  \p 
  \v 26 \x - \xo 1.26: \xt \xdc Wis 2.23; Sir 17.3,4;\xdc* 1Co 11.7.\x* Then God said,
  ?And now we will make human beings; they will be like us and resemble us.

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.
  See for examples the footnote handling, as crossreferences work the same.
  
  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 xrefs?
      if (xref_markers.find (marker) != xref_markers.end()) {
        // Marker is 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