[sword-svn] r3068 - in trunk: include src/modules/filters
refdoc at crosswire.org
refdoc at crosswire.org
Tue Mar 4 17:05:47 MST 2014
Author: refdoc
Date: 2014-03-04 17:05:47 -0700 (Tue, 04 Mar 2014)
New Revision: 3068
Modified:
trunk/include/osislatex.h
trunk/src/modules/filters/osislatex.cpp
Log:
filter for LaTeX now based on xhtml - needs fleshing out
Modified: trunk/include/osislatex.h
===================================================================
--- trunk/include/osislatex.h 2014-03-04 18:32:24 UTC (rev 3067)
+++ trunk/include/osislatex.h 2014-03-05 00:05:47 UTC (rev 3068)
@@ -1,10 +1,10 @@
/******************************************************************************
*
- * osislatex.h - Implementation of OSISLaTeX
+ * osislatex.h - Render filter for LaTeX of an OSIS module
*
* $Id$
*
- * Copyright 2013 CrossWire Bible Society (http://www.crosswire.org)
+ * Copyright 2011-2013 CrossWire Bible Society (http://www.crosswire.org)
* CrossWire Bible Society
* P. O. Box 2528
* Tempe, AZ 85280-2528
@@ -20,23 +20,54 @@
*
*/
-#ifndef OSISLATEX_H
-#define OSISLATEX_H
+#ifndef OSISLaTeX_H
+#define OSISLaTeX_H
#include <swbasicfilter.h>
-#include <utilxml.h>
SWORD_NAMESPACE_START
-/** this filter converts OSIS text to LaTeX text
+/** this filter converts OSIS text to classed XHTML
*/
class SWDLLEXPORT OSISLaTeX : public SWBasicFilter {
-public:
+private:
+ bool morphFirst;
+ bool renderNoteNumbers;
protected:
+
+ class TagStack;
+ // used by derived classes so we have it in the header
virtual BasicFilterUserData *createUserData(const SWModule *module, const SWKey *key);
virtual bool handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData);
+
+
+ class MyUserData : public BasicFilterUserData {
+ public:
+ bool osisQToTick;
+ bool inXRefNote;
+ bool BiblicalText;
+ int suspendLevel;
+ SWBuf wordsOfChristStart;
+ SWBuf wordsOfChristEnd;
+ TagStack *quoteStack;
+ TagStack *hiStack;
+ TagStack *titleStack;
+ TagStack *lineStack;
+ int consecutiveNewlines;
+ SWBuf lastTransChange;
+ SWBuf w;
+ SWBuf fn;
+ SWBuf version;
+
+ MyUserData(const SWModule *module, const SWKey *key);
+ ~MyUserData();
+ void outputNewline(SWBuf &buf);
+ };
public:
OSISLaTeX();
+ void setMorphFirst(bool val = true) { morphFirst = val; }
+ void setRenderNoteNumbers(bool val = true) { renderNoteNumbers = val; }
+ virtual const char *getHeader() const;
};
SWORD_NAMESPACE_END
Modified: trunk/src/modules/filters/osislatex.cpp
===================================================================
--- trunk/src/modules/filters/osislatex.cpp 2014-03-04 18:32:24 UTC (rev 3067)
+++ trunk/src/modules/filters/osislatex.cpp 2014-03-05 00:05:47 UTC (rev 3068)
@@ -1,10 +1,10 @@
/******************************************************************************
*
- * osislatex.cpp - An SWFilter that provides conversion of OSIS to LaTeX
+ * osislatex.cpp - Render filter for LaTeX of an OSIS module
*
* $Id$
*
- * Copyright 2013 CrossWire Bible Society (http://www.crosswire.org)
+ * Copyright 2011-2014 CrossWire Bible Society (http://www.crosswire.org)
* CrossWire Bible Society
* P. O. Box 2528
* Tempe, AZ 85280-2528
@@ -21,28 +21,115 @@
*/
#include <stdlib.h>
+#include <ctype.h>
#include <osislatex.h>
-#include <ctype.h>
+#include <utilxml.h>
+#include <utilstr.h>
#include <versekey.h>
+#include <swmodule.h>
+#include <url.h>
#include <stringmgr.h>
+#include <stack>
SWORD_NAMESPACE_START
+const char *OSISLaTeX::getHeader() const {
+ const static char *header = "\
+ .divineName { font-variant: small-caps; }\n\
+ .wordsOfJesus { color: red; }\n\
+ .transChangeSupplied { font-style: italic; }\n\
+ .small, .sub, .sup { font-size: .83em }\n\
+ .sub { vertical-align: sub }\n\
+ .sup { vertical-align: super }\n\
+ .indent1 { margin-left: 10px }\n\
+ .indent2 { margin-left: 20px }\n\
+ .indent3 { margin-left: 30px }\n\
+ .indent4 { margin-left: 40px }\n\
+ ";
+ return header;
+}
+
namespace {
- class MyUserData : public BasicFilterUserData {
- public:
- SWBuf w;
- XMLTag tag;
- VerseKey *vk;
- char testament;
- SWBuf hiType;
- MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key) {}
- };
+// though this might be slightly slower, possibly causing an extra bool check, this is a renderFilter
+// so speed isn't the absolute highest priority, and this is a very minor possible hit
+static inline void outText(const char *t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
+static inline void outText(char t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
+
+void processLemma(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf) {
+ const char *attrib;
+ const char *val;
+ if ((attrib = tag.getAttribute("lemma"))) {
+ int count = tag.getAttributePartCount("lemma", ' ');
+ int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0
+ do {
+ attrib = tag.getAttribute("lemma", i, ' ');
+ if (i < 0) i = 0; // to handle our -1 condition
+ val = strchr(attrib, ':');
+ val = (val) ? (val + 1) : attrib;
+ SWBuf gh;
+ if(*val == 'G')
+ gh = "Greek";
+ if(*val == 'H')
+ gh = "Hebrew";
+ const char *val2 = val;
+ if ((strchr("GH", *val)) && (isdigit(val[1])))
+ val2++;
+ //if ((!strcmp(val2, "3588")) && (lastText.length() < 1))
+ // show = false;
+ //else {
+ if (!suspendTextPassThru) {
+ buf.appendFormatted("<small><em class=\"strongs\"><<a href=\"passagestudy.jsp?action=showStrongs&type=%s&value=%s\" class=\"strongs\">%s</a>></em></small>",
+ (gh.length()) ? gh.c_str() : "",
+ URL::encode(val2).c_str(),
+ val2);
+ }
+ //}
+
+ } while (++i < count);
+ }
}
+
+void processMorph(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf) {
+ const char * attrib;
+ const char *val;
+ if ((attrib = tag.getAttribute("morph"))) { // && (show)) {
+ SWBuf savelemma = tag.getAttribute("savlm");
+ //if ((strstr(savelemma.c_str(), "3588")) && (lastText.length() < 1))
+ // show = false;
+ //if (show) {
+ int count = tag.getAttributePartCount("morph", ' ');
+ int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0
+ do {
+ attrib = tag.getAttribute("morph", i, ' ');
+ if (i < 0) i = 0; // to handle our -1 condition
+ val = strchr(attrib, ':');
+ val = (val) ? (val + 1) : attrib;
+ const char *val2 = val;
+ if ((*val == 'T') && (strchr("GH", val[1])) && (isdigit(val[2])))
+ val2+=2;
+ if (!suspendTextPassThru) {
+ buf.appendFormatted("<small><em class=\"morph\">(<a href=\"passagestudy.jsp?action=showMorph&type=%s&value=%s\" class=\"morph\">%s</a>)</em></small>",
+ URL::encode(tag.getAttribute("morph")).c_str(),
+ URL::encode(val).c_str(),
+ val2);
+ }
+ } while (++i < count);
+ //}
+ }
+}
+
+
+} // end anonymous namespace
+
+BasicFilterUserData *OSISLaTeX::createUserData(const SWModule *module, const SWKey *key) {
+ return new MyUserData(module, key);
+}
+
+
OSISLaTeX::OSISLaTeX() {
setTokenStart("<");
setTokenEnd(">");
@@ -51,209 +138,633 @@
setEscapeEnd(";");
setEscapeStringCaseSensitive(true);
+ setPassThruNumericEscapeString(true);
- addEscapeStringSubstitute("amp", "&");
- addEscapeStringSubstitute("apos", "'");
- addEscapeStringSubstitute("lt", "<");
- addEscapeStringSubstitute("gt", ">");
- addEscapeStringSubstitute("quot", "\"");
+ addAllowedEscapeString("quot");
+ addAllowedEscapeString("apos");
+ addAllowedEscapeString("amp");
+ addAllowedEscapeString("lt");
+ addAllowedEscapeString("gt");
- setTokenCaseSensitive(true);
- addTokenSubstitute("title", "\n");
- addTokenSubstitute("/title", "\n");
- addTokenSubstitute("/l", "\n");
- addTokenSubstitute("lg", "\n");
- addTokenSubstitute("/lg", "\n");
+ setTokenCaseSensitive(true);
+
+ // addTokenSubstitute("lg", "<br />");
+ // addTokenSubstitute("/lg", "<br />");
+
+ morphFirst = false;
+ renderNoteNumbers = false;
}
-BasicFilterUserData *OSISLaTeX::createUserData(const SWModule *module, const SWKey *key) {
- MyUserData *u = new MyUserData(module, key);
- u->vk = SWDYNAMIC_CAST(VerseKey, u->key);
- u->testament = (u->vk) ? u->vk->getTestament() : 2; // default to NT
- return u;
+class OSISLaTeX::TagStack : public std::stack<SWBuf> {
+};
+
+OSISLaTeX::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key), quoteStack(new TagStack()), hiStack(new TagStack()), titleStack(new TagStack()), lineStack(new TagStack()) {
+ inXRefNote = false;
+ suspendLevel = 0;
+ wordsOfChristStart = "<span class=\"wordsOfJesus\"> ";
+ wordsOfChristEnd = "</span> ";
+ if (module) {
+ osisQToTick = ((!module->getConfigEntry("OSISqToTick")) || (strcmp(module->getConfigEntry("OSISqToTick"), "false")));
+ version = module->getName();
+ BiblicalText = (!strcmp(module->getType(), "Biblical Texts"));
+ }
+ else {
+ osisQToTick = true; // default
+ version = "";
+ }
+ consecutiveNewlines = 0;
}
+OSISLaTeX::MyUserData::~MyUserData() {
+ delete quoteStack;
+ delete hiStack;
+ delete titleStack;
+ delete lineStack;
+}
+void OSISLaTeX::MyUserData::outputNewline(SWBuf &buf) {
+ if (++consecutiveNewlines <= 2) {
+ outText("<br />\n", buf, this);
+ supressAdjacentWhitespace = true;
+ }
+}
bool OSISLaTeX::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
- // manually process if it wasn't a simple substitution
- if (!substituteToken(buf, token)) {
- MyUserData *u = (MyUserData *)userData;
- if (((*token == 'w') && (token[1] == ' ')) ||
- ((*token == '/') && (token[1] == 'w') && (!token[2]))) {
- u->tag = token;
-
- bool start = false;
- if (*token == 'w') {
- if (token[strlen(token)-1] != '/') {
- u->w = token;
- return true;
+ MyUserData *u = (MyUserData *)userData;
+ SWBuf scratch;
+ bool sub = (u->suspendTextPassThru) ? substituteToken(scratch, token) : substituteToken(buf, token);
+ if (!sub) {
+ // manually process if it wasn't a simple substitution
+ XMLTag tag(token);
+
+ // <w> tag
+ if (!strcmp(tag.getName(), "w")) {
+
+ // start <w> tag
+ if ((!tag.isEmpty()) && (!tag.isEndTag())) {
+ u->w = token;
+ }
+
+ // end or empty <w> tag
+ else {
+ bool endTag = tag.isEndTag();
+ SWBuf lastText;
+ //bool show = true; // to handle unplaced article in kjv2003-- temporary till combined
+
+ if (endTag) {
+ tag = u->w.c_str();
+ lastText = u->lastTextNode.c_str();
}
- start = true;
+ else lastText = "stuff";
+
+ const char *attrib;
+ const char *val;
+ if ((attrib = tag.getAttribute("xlit"))) {
+ val = strchr(attrib, ':');
+ val = (val) ? (val + 1) : attrib;
+ outText(" ", buf, u);
+ outText(val, buf, u);
+ }
+ if ((attrib = tag.getAttribute("gloss"))) {
+ // I'm sure this is not the cleanest way to do it, but it gets the job done
+ // for rendering ruby chars properly ^_^
+ buf -= lastText.length();
+
+ outText("<ruby><rb>", buf, u);
+ outText(lastText, buf, u);
+ val = strchr(attrib, ':');
+ val = (val) ? (val + 1) : attrib;
+ outText("</rb><rp>(</rp><rt>", buf, u);
+ outText(val, buf, u);
+ outText("</rt><rp>)</rp></ruby>", buf, u);
+ }
+ if (!morphFirst) {
+ processLemma(u->suspendTextPassThru, tag, buf);
+ processMorph(u->suspendTextPassThru, tag, buf);
+ }
+ else {
+ processMorph(u->suspendTextPassThru, tag, buf);
+ processLemma(u->suspendTextPassThru, tag, buf);
+ }
+ if ((attrib = tag.getAttribute("POS"))) {
+ val = strchr(attrib, ':');
+ val = (val) ? (val + 1) : attrib;
+ outText(" ", buf, u);
+ outText(val, buf, u);
+ }
+
+ /*if (endTag)
+ buf += "}";*/
}
- u->tag = (start) ? token : u->w.c_str();
- bool show = true; // to handle unplaced article in kjv2003-- temporary till combined
+ }
- SWBuf lastText = (start) ? "stuff" : u->lastTextNode.c_str();
+ // <note> tag
+ else if (!strcmp(tag.getName(), "note")) {
+ if (!tag.isEndTag()) {
+ SWBuf type = tag.getAttribute("type");
+ bool strongsMarkup = (type == "x-strongsMarkup" || type == "strongsMarkup"); // the latter is deprecated
+ if (strongsMarkup) {
+ tag.setEmpty(false); // handle bug in KJV2003 module where some note open tags were <note ... />
+ }
- const char *attrib;
- const char *val;
- if ((attrib = u->tag.getAttribute("xlit"))) {
- val = strchr(attrib, ':');
- val = (val) ? (val + 1) : attrib;
- buf.append(" <");
- buf.append(val);
- buf.append('>');
+ if (!tag.isEmpty()) {
+
+ if (!strongsMarkup) { // leave strong's markup notes out, in the future we'll probably have different option filters to turn different note types on or off
+ SWBuf footnoteNumber = tag.getAttribute("swordFootnote");
+ SWBuf noteName = tag.getAttribute("n");
+ VerseKey *vkey = NULL;
+ char ch = ((tag.getAttribute("type") && ((!strcmp(tag.getAttribute("type"), "crossReference")) || (!strcmp(tag.getAttribute("type"), "x-cross-ref")))) ? 'x':'n');
+
+ u->inXRefNote = true; // Why this change? Ben Morgan: Any note can have references in, so we need to set this to true for all notes
+// u->inXRefNote = (ch == 'x');
+
+ // see if we have a VerseKey * or descendant
+ SWTRY {
+ vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+ }
+ SWCATCH ( ... ) { }
+ if (vkey) {
+ //printf("URL = %s\n",URL::encode(vkey->getText()).c_str());
+ buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=%c&value=%s&module=%s&passage=%s\"><small><sup class=\"%c\">*%c%s</sup></small></a>",
+ ch,
+ URL::encode(footnoteNumber.c_str()).c_str(),
+ URL::encode(u->version.c_str()).c_str(),
+ URL::encode(vkey->getText()).c_str(),
+ ch,
+ ch,
+ (renderNoteNumbers ? URL::encode(noteName.c_str()).c_str() : ""));
+ }
+ else {
+ buf.appendFormatted("<a href=\"passagestudy.jsp?action=showNote&type=%c&value=%s&module=%s&passage=%s\"><small><sup class=\"%c\">*%c%s</sup></small></a>",
+ ch,
+ URL::encode(footnoteNumber.c_str()).c_str(),
+ URL::encode(u->version.c_str()).c_str(),
+ URL::encode(u->key->getText()).c_str(),
+ ch,
+ ch,
+ (renderNoteNumbers ? URL::encode(noteName.c_str()).c_str() : ""));
+ }
+ }
+ }
+ u->suspendTextPassThru = (++u->suspendLevel);
}
- if ((attrib = u->tag.getAttribute("gloss"))) {
- val = strchr(attrib, ':');
- val = (val) ? (val + 1) : attrib;
- buf.append(" <");
- buf.append(val);
- buf.append('>');
+ if (tag.isEndTag()) {
+ u->suspendTextPassThru = (--u->suspendLevel);
+ u->inXRefNote = false;
+ u->lastSuspendSegment = ""; // fix/work-around for nasb devineName in note bug
}
- if ((attrib = u->tag.getAttribute("lemma"))) {
- int count = u->tag.getAttributePartCount("lemma", ' ');
- int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0
- do {
- char gh;
- attrib = u->tag.getAttribute("lemma", i, ' ');
- if (i < 0) i = 0; // to handle our -1 condition
- val = strchr(attrib, ':');
- val = (val) ? (val + 1) : attrib;
- if ((strchr("GH", *val)) && (isdigit(val[1]))) {
- gh = *val;
- val++;
+ }
+
+ // <p> paragraph and <lg> linegroup tags
+ else if (!strcmp(tag.getName(), "p") || !strcmp(tag.getName(), "lg")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) { // non-empty start tag
+ u->outputNewline(buf);
+ }
+ else if (tag.isEndTag()) { // end tag
+ u->outputNewline(buf);
+ }
+ else { // empty paragraph break marker
+ u->outputNewline(buf);
+ }
+ }
+
+ // Milestoned paragraphs, created by osis2mod
+ // <div type="paragraph" sID.../>
+ // <div type="paragraph" eID.../>
+ else if (tag.isEmpty() && !strcmp(tag.getName(), "div") && tag.getAttribute("type") && (!strcmp(tag.getAttribute("type"), "x-p") || !strcmp(tag.getAttribute("type"), "paragraph"))) {
+ // <div type="paragraph" sID... />
+ if (tag.getAttribute("sID")) { // non-empty start tag
+ u->outputNewline(buf);
+ }
+ // <div type="paragraph" eID... />
+ else if (tag.getAttribute("eID")) {
+ u->outputNewline(buf);
+ }
+ }
+
+ // <reference> tag
+ else if (!strcmp(tag.getName(), "reference")) {
+ if (!u->inXRefNote) { // only show these if we're not in an xref note
+ if (!tag.isEndTag()) {
+ SWBuf target;
+ SWBuf work;
+ SWBuf ref;
+ bool is_scripRef = false;
+
+ target = tag.getAttribute("osisRef");
+ const char* the_ref = strchr(target, ':');
+
+ if(!the_ref) {
+ // No work
+ ref = target;
+ is_scripRef = true;
}
else {
- gh = (u->testament>1) ? 'G' : 'H';
+ // Compensate for starting :
+ ref = the_ref + 1;
+
+ int size = target.size() - ref.size() - 1;
+ work.setSize(size);
+ strncpy(work.getRawData(), target, size);
+
+ // For Bible:Gen.3.15 or Bible.vulgate:Gen.3.15
+ if(!strncmp(work, "Bible", 5))
+ is_scripRef = true;
}
- if ((!strcmp(val, "3588")) && (lastText.length() < 1))
- show = false;
- else {
- buf.append(" <");
- buf.append(gh);
- buf.append(val);
- buf.append(">");
+
+ if(is_scripRef)
+ {
+ buf.appendFormatted("<a href=\"passagestudy.jsp?action=showRef&type=scripRef&value=%s&module=\">",
+ URL::encode(ref.c_str()).c_str()
+// (work.size()) ? URL::encode(work.c_str()).c_str() : "")
+ );
}
- } while (++i < count);
+ else
+ {
+ // Dictionary link, or something
+ buf.appendFormatted("<a href=\"sword://%s/%s\">",
+ URL::encode(work.c_str()).c_str(),
+ URL::encode(ref.c_str()).c_str()
+ );
+ }
+ }
+ else {
+ outText("</a>", buf, u);
+ }
}
- if ((attrib = u->tag.getAttribute("morph")) && (show)) {
- int count = u->tag.getAttributePartCount("morph", ' ');
- int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0
- do {
- attrib = u->tag.getAttribute("morph", i, ' ');
- if (i < 0) i = 0; // to handle our -1 condition
- val = strchr(attrib, ':');
- val = (val) ? (val + 1) : attrib;
- if ((*val == 'T') && (strchr("GH", val[1])) && (isdigit(val[2])))
- val+=2;
- buf.append(" (");
- buf.append(val);
- buf.append(')');
- } while (++i < count);
+ }
+
+ // <l> poetry, etc
+ else if (!strcmp(tag.getName(), "l")) {
+ // start line marker
+ if (tag.getAttribute("sID") || (!tag.isEndTag() && !tag.isEmpty())) {
+ // nested lines plus if the line itself has an x-indent type attribute value
+ outText(SWBuf("<span class=\"line indent").appendFormatted("%d\">", u->lineStack->size() + (SWBuf("x-indent") == tag.getAttribute("type")?1:0)).c_str(), buf, u);
+ u->lineStack->push(tag.toString());
}
- if ((attrib = u->tag.getAttribute("POS"))) {
- val = strchr(attrib, ':');
- val = (val) ? (val + 1) : attrib;
-
- buf.append(" <");
- buf.append(val);
- buf.append('>');
+ // end line marker
+ else if (tag.getAttribute("eID") || tag.isEndTag()) {
+ outText("</span>", buf, u);
+ u->outputNewline(buf);
+ if (u->lineStack->size()) u->lineStack->pop();
}
+ // <l/> without eID or sID
+ // Note: this is improper osis. This should be <lb/>
+ else if (tag.isEmpty() && !tag.getAttribute("sID")) {
+ u->outputNewline(buf);
+ }
}
- // <note> tag
- else if (!strncmp(token, "note", 4)) {
- if (!strstr(token, "strongsMarkup")) { // leave strong's markup notes out, in the future we'll probably have different option filters to turn different note types on or off
- buf.append(" \\footnote{");
+ // <lb.../>
+ else if (!strcmp(tag.getName(), "lb") && (!tag.getAttribute("type") || strcmp(tag.getAttribute("type"), "x-optional"))) {
+ u->outputNewline(buf);
+ }
+ // <milestone type="line"/>
+ // <milestone type="x-p"/>
+ // <milestone type="cQuote" marker="x"/>
+ else if ((!strcmp(tag.getName(), "milestone")) && (tag.getAttribute("type"))) {
+ if (!strcmp(tag.getAttribute("type"), "line")) {
+ u->outputNewline(buf);
+ if (tag.getAttribute("subType") && !strcmp(tag.getAttribute("subType"), "x-PM")) {
+ u->outputNewline(buf);
}
- else u->suspendTextPassThru = true;
}
- else if (!strncmp(token, "/note", 5)) {
- if (!u->suspendTextPassThru)
- buf.append("} ");
- else u->suspendTextPassThru = false;
- }
+ else if (!strcmp(tag.getAttribute("type"),"x-p")) {
+ if (tag.getAttribute("marker"))
+ outText(tag.getAttribute("marker"), buf, u);
+ else outText("<!p>", buf, u);
+ }
+ else if (!strcmp(tag.getAttribute("type"), "cQuote")) {
+ const char *tmp = tag.getAttribute("marker");
+ bool hasMark = tmp;
+ SWBuf mark = tmp;
+ tmp = tag.getAttribute("level");
+ int level = (tmp) ? atoi(tmp) : 1;
- // <p> paragraph tag
- else if (((*token == 'p') && ((token[1] == ' ') || (!token[1]))) ||
- ((*token == '/') && (token[1] == 'p') && (!token[2]))) {
- userData->supressAdjacentWhitespace = true;
- buf.append('\n');
+ // first check to see if we've been given an explicit mark
+ if (hasMark)
+ outText(mark, buf, u);
+ // finally, alternate " and ', if config says we should supply a mark
+ else if (u->osisQToTick)
+ outText((level % 2) ? '\"' : '\'', buf, u);
+ }
}
- // Milestoned paragraph, created by osis2mod
- // <div type="paragraph" sID... />
- // <div type="paragraph" eID... />
- else if (!strcmp(u->tag.getName(), "div") && u->tag.getAttribute("type") && (!strcmp(u->tag.getAttribute("type"), "x-p") || !strcmp(u->tag.getAttribute("type"), "paragraph")) &&
- (u->tag.isEmpty() && (u->tag.getAttribute("sID") || u->tag.getAttribute("eID")))) {
- userData->supressAdjacentWhitespace = true;
- buf.append('\n');
+ // <title>
+ else if (!strcmp(tag.getName(), "title")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ VerseKey *vkey = SWDYNAMIC_CAST(VerseKey, u->key);
+ if (vkey && !vkey->getVerse()) {
+ if (!vkey->getChapter()) {
+ if (!vkey->getBook()) {
+ if (!vkey->getTestament()) {
+ buf += "<h1 class=\"moduleHeader\">";
+ tag.setAttribute("pushed", "h1");
+ }
+ else {
+ buf += "<h1 class=\"testamentHeader\">";
+ tag.setAttribute("pushed", "h1");
+ }
+ }
+ else {
+ buf += "<h1 class=\"bookHeader\">";
+ tag.setAttribute("pushed", "h1");
+ }
+ }
+ else {
+ buf += "<h2 class=\"chapterHeader\">";
+ tag.setAttribute("pushed", "h2");
+ }
+ }
+ else {
+ buf += "<h3>";
+ tag.setAttribute("pushed", "h3");
+ }
+ u->titleStack->push(tag.toString());
+ }
+ else if (tag.isEndTag()) {
+ if (!u->titleStack->empty()) {
+ XMLTag tag(u->titleStack->top());
+ if (u->titleStack->size()) u->titleStack->pop();
+ SWBuf pushed = tag.getAttribute("pushed");
+ if (pushed.size()) {
+ buf += (SWBuf)"</" + pushed + ">\n\n";
+ }
+ else {
+ buf += "</h3>\n\n";
+ }
+ ++u->consecutiveNewlines;
+ u->supressAdjacentWhitespace = true;
+ }
+ }
}
+
+ // <list>
+ else if (!strcmp(tag.getName(), "list")) {
+ if((!tag.isEndTag()) && (!tag.isEmpty())) {
+ outText("<ul>\n", buf, u);
+ }
+ else if (tag.isEndTag()) {
+ outText("</ul>\n", buf, u);
+ ++u->consecutiveNewlines;
+ u->supressAdjacentWhitespace = true;
+ }
+ }
- // <lb .../>
- else if (!strncmp(token, "lb", 2)) {
- userData->supressAdjacentWhitespace = true;
- buf.append('\n');
+ // <item>
+ else if (!strcmp(tag.getName(), "item")) {
+ if((!tag.isEndTag()) && (!tag.isEmpty())) {
+ outText("\t<li>", buf, u);
+ }
+ else if (tag.isEndTag()) {
+ outText("</li>\n", buf, u);
+ ++u->consecutiveNewlines;
+ u->supressAdjacentWhitespace = true;
+ }
}
- else if (!strncmp(token, "l", 1) && strstr(token, "eID")) {
- userData->supressAdjacentWhitespace = true;
- buf.append('\n');
+ // <catchWord> & <rdg> tags (italicize)
+ else if (!strcmp(tag.getName(), "rdg") || !strcmp(tag.getName(), "catchWord")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ outText("<i>", buf, u);
+ }
+ else if (tag.isEndTag()) {
+ outText("</i>", buf, u);
+ }
}
- else if (!strncmp(token, "/divineName", 11)) {
- // Get the end portion of the string, and upper case it
- char* end = buf.getRawData();
- end += buf.size() - u->lastTextNode.size();
- toupperstr(end);
+
+ // divineName
+ else if (!strcmp(tag.getName(), "divineName")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ u->suspendTextPassThru = (++u->suspendLevel);
+ }
+ else if (tag.isEndTag()) {
+ SWBuf lastText = u->lastSuspendSegment.c_str();
+ u->suspendTextPassThru = (--u->suspendLevel);
+ if (lastText.size()) {
+ scratch.setFormatted("<span class=\"divineName\">%s</span>", lastText.c_str());
+ outText(scratch.c_str(), buf, u);
+ }
+ }
}
- else if (!strncmp(token, "hi", 2)) {
- // handle both OSIS 'type' and TEI 'rend' attributes
+ // <hi> text highlighting
+ else if (!strcmp(tag.getName(), "hi")) {
+ SWBuf type = tag.getAttribute("type");
+
+ // handle tei rend attribute if type doesn't exist
+ if (!type.length()) type = tag.getAttribute("rend");
+
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ if (type == "bold" || type == "b" || type == "x-b") {
+ outText("<b>", buf, u);
+ }
+
// there is no officially supported OSIS overline attribute,
// thus either TEI overline or OSIS x-overline would be best,
// but we have used "ol" in the past, as well. Once a valid
// OSIS overline attribute is made available, these should all
// eventually be deprecated and never documented that they are supported.
- if (strstr(token, "rend=\"ol\"") || strstr(token, "rend=\"x-overline\"") || strstr(token, "rend=\"overline\"")
- || strstr(token, "type=\"ol\"") || strstr(token, "type=\"x-overline\"") || strstr(token, "type=\"overline\"")) {
- u->hiType = "overline";
+ else if (type == "ol" || type == "overline" || type == "x-overline") {
+ outText("<span style=\"text-decoration:overline\">", buf, u);
}
- else u->hiType = "";
- u->suspendTextPassThru = true;
+
+ else if (type == "super") {
+ outText("<span class=\"sup\">", buf, u);
+ }
+ else if (type == "sub") {
+ outText("<span class=\"sub\">", buf, u);
+ }
+ else { // all other types
+ outText("<i>", buf, u);
+ }
+ u->hiStack->push(tag.toString());
}
- else if (!strncmp(token, "/hi", 3)) {
- if (u->hiType == "overline") {
- const unsigned char *b = (const unsigned char *)u->lastTextNode.c_str();
- while (*b) {
- const unsigned char *o = b;
- if (getUniCharFromUTF8(&b)) {
- while (o != b) buf.append(*(o++));
- buf.append((unsigned char)0xCC);
- buf.append((unsigned char)0x85);
- }
+ else if (tag.isEndTag()) {
+ SWBuf type = "";
+ if (!u->hiStack->empty()) {
+ XMLTag tag(u->hiStack->top());
+ if (u->hiStack->size()) u->hiStack->pop();
+ type = tag.getAttribute("type");
+ if (!type.length()) type = tag.getAttribute("rend");
}
+ if (type == "bold" || type == "b" || type == "x-b") {
+ outText("</b>", buf, u);
+ }
+ else if ( type == "ol"
+ || type == "super"
+ || type == "sub") {
+ outText("</span>", buf, u);
+ }
+ else outText("</i>", buf, u);
}
- else {
- buf.append("*");
- buf.append(u->lastTextNode);
- buf.append("*");
+ }
+
+ // <q> quote
+ // Rules for a quote element:
+ // If the tag is empty with an sID or an eID then use whatever it specifies for quoting.
+ // Note: empty elements without sID or eID are ignored.
+ // If the tag is <q> then use it's specifications and push it onto a stack for </q>
+ // If the tag is </q> then use the pushed <q> for specification
+ // If there is a marker attribute, possibly empty, this overrides osisQToTick.
+ // If osisQToTick, then output the marker, using level to determine the type of mark.
+ else if (!strcmp(tag.getName(), "q")) {
+ SWBuf type = tag.getAttribute("type");
+ SWBuf who = tag.getAttribute("who");
+ const char *tmp = tag.getAttribute("level");
+ int level = (tmp) ? atoi(tmp) : 1;
+ tmp = tag.getAttribute("marker");
+ bool hasMark = tmp;
+ SWBuf mark = tmp;
+
+ // open <q> or <q sID... />
+ if ((!tag.isEmpty() && !tag.isEndTag()) || (tag.isEmpty() && tag.getAttribute("sID"))) {
+ // if <q> then remember it for the </q>
+ if (!tag.isEmpty()) {
+ u->quoteStack->push(tag.toString());
+ }
+
+ // Do this first so quote marks are included as WoC
+ if (who == "Jesus")
+ outText(u->wordsOfChristStart, buf, u);
+
+ // first check to see if we've been given an explicit mark
+ if (hasMark)
+ outText(mark, buf, u);
+ //alternate " and '
+ else if (u->osisQToTick)
+ outText((level % 2) ? '\"' : '\'', buf, u);
}
- u->suspendTextPassThru = false;
+ // close </q> or <q eID... />
+ else if ((tag.isEndTag()) || (tag.isEmpty() && tag.getAttribute("eID"))) {
+ // if it is </q> then pop the stack for the attributes
+ if (tag.isEndTag() && !u->quoteStack->empty()) {
+ XMLTag qTag(u->quoteStack->top());
+ if (u->quoteStack->size()) u->quoteStack->pop();
+
+ type = qTag.getAttribute("type");
+ who = qTag.getAttribute("who");
+ tmp = qTag.getAttribute("level");
+ level = (tmp) ? atoi(tmp) : 1;
+ tmp = qTag.getAttribute("marker");
+ hasMark = tmp;
+ mark = tmp;
+ }
+
+ // first check to see if we've been given an explicit mark
+ if (hasMark)
+ outText(mark, buf, u);
+ // finally, alternate " and ', if config says we should supply a mark
+ else if (u->osisQToTick)
+ outText((level % 2) ? '\"' : '\'', buf, u);
+
+ // Do this last so quote marks are included as WoC
+ if (who == "Jesus")
+ outText(u->wordsOfChristEnd, buf, u);
+ }
}
- // <milestone type="line"/>
- else if (!strncmp(token, "milestone", 9)) {
- const char* type = strstr(token+10, "type=\"");
- if (type && strncmp(type+6, "line", 4)) { //we check for type != line
- userData->supressAdjacentWhitespace = true;
- buf.append('\n');
+ // <transChange>
+ else if (!strcmp(tag.getName(), "transChange")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ SWBuf type = tag.getAttribute("type");
+ u->lastTransChange = type;
+
+ // just do all transChange tags this way for now
+ if ((type == "added") || (type == "supplied"))
+ outText("<span class=\"transChangeSupplied\">", buf, u);
+ else if (type == "tenseChange")
+ buf += "*";
}
- }
+ else if (tag.isEndTag()) {
+ SWBuf type = u->lastTransChange;
+ if ((type == "added") || (type == "supplied"))
+ outText("</span>", buf, u);
+ }
+ else { // empty transChange marker?
+ }
+ }
+ // image
+ else if (!strcmp(tag.getName(), "figure")) {
+ const char *src = tag.getAttribute("src");
+ if (src) { // assert we have a src attribute
+ SWBuf filepath;
+ if (userData->module) {
+ filepath = userData->module->getConfigEntry("AbsoluteDataPath");
+ if ((filepath.size()) && (filepath[filepath.size()-1] != '/') && (src[0] != '/'))
+ filepath += '/';
+ }
+ filepath += src;
+
+ // images become clickable, if the UI supports showImage.
+ outText("<a href=\"passagestudy.jsp?action=showImage&value=", buf, u);
+ outText(URL::encode(filepath.c_str()).c_str(), buf, u);
+ outText("&module=", buf, u);
+ outText(URL::encode(u->version.c_str()).c_str(), buf, u);
+ outText("\">", buf, u);
+
+ outText("<img src=\"file:", buf, u);
+ outText(filepath, buf, u);
+ outText("\" border=\"0\" />", buf, u);
+
+ outText("</a>", buf, u);
+ }
+ }
+
+ // ok to leave these in
+ else if (!strcmp(tag.getName(), "div")) {
+ SWBuf type = tag.getAttribute("type");
+ if (type == "bookGroup") {
+ }
+ else if (type == "book") {
+ }
+ else if (type == "section") {
+ }
+ else if (type == "majorSection") {
+ }
+ else {
+ buf += tag;
+ }
+ }
+ else if (!strcmp(tag.getName(), "span")) {
+ buf += tag;
+ }
+ else if (!strcmp(tag.getName(), "br")) {
+ buf += tag;
+ }
+ else if (!strcmp(tag.getName(), "table")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ buf += "<table><tbody>\n";
+ }
+ else if (tag.isEndTag()) {
+ buf += "</tbody></table>\n";
+ ++u->consecutiveNewlines;
+ u->supressAdjacentWhitespace = true;
+ }
+
+ }
+ else if (!strcmp(tag.getName(), "row")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ buf += "\t<tr>";
+ }
+ else if (tag.isEndTag()) {
+ buf += "</tr>\n";
+ }
+
+ }
+ else if (!strcmp(tag.getName(), "cell")) {
+ if ((!tag.isEndTag()) && (!tag.isEmpty())) {
+ buf += "<td>";
+ }
+ else if (tag.isEndTag()) {
+ buf += "</td>";
+ }
+ }
else {
+ if (!u->supressAdjacentWhitespace) u->consecutiveNewlines = 0;
return false; // we still didn't handle token
}
}
+ if (!u->supressAdjacentWhitespace) u->consecutiveNewlines = 0;
return true;
}
More information about the sword-cvs
mailing list