The SWORD Project  1.9.0.svnversion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
osislatex.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * osislatex.cpp - Render filter for LaTeX of an OSIS module
4  *
5  * $Id: osislatex.cpp 3547 2017-12-10 05:06:48Z scribe $
6  *
7  * Copyright 2011-2014 CrossWire Bible Society (http://www.crosswire.org)
8  * CrossWire Bible Society
9  * P. O. Box 2528
10  * Tempe, AZ 85280-2528
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  */
22 
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <osislatex.h>
26 #include <utilxml.h>
27 #include <utilstr.h>
28 #include <versekey.h>
29 #include <swmodule.h>
30 #include <url.h>
31 #include <stringmgr.h>
32 #include <stack>
33 
35 
36 const char *OSISLaTeX::getHeader() const {
37 // can be used to return static start-up info, like packages to load. Not sure yet if I want to retain it.
38 
39  const static char *header = "\
40  \\LoadClass[11pt,a4paper,twoside,headinclude=true,footinclude=true,BCOR=0mm,DIV=calc]{scrbook}\n\
41  \\LoadClass[11pt,a4paper,twoside,headinclude=true,footinclude=true,BCOR=0mm,DIV=calc]{scrbook}\n\
42  \\NeedsTeXFormat{LaTeX2e}\n\
43  \\ProvidesClass{sword}[2015/03/29 CrossWire LaTeX class for Biblical texts]\n\
44  %\\sworddiclink{%s}{%s}{\n\
45  %\\sworddictref{%s}{%s}{\n\
46  %\\sworddict{%s}{\n\
47  %\\sworddivinename}{%s}{\n\
48  %\\swordfont{\n\
49  %\\swordfootnote[%c]{%s}{%s}{%s}{%s}{\n\
50  %\\swordfootnote{%s}{%s}{%s}{\n\
51  %\\swordfootnote{%s}{%s}{%s}{%s}{\n\
52  %\\swordmorph{\n\
53  %\\swordmorph[Greek]{%s}\n\
54  %\\swordmorph[lemma]{%s}\n\
55  %\\swordmorph{%s}\n\
56  %\\swordpoetryline{\n\
57  %\\swordquote{\n\
58  %\\swordref{%s}{%s}{\n\
59  %\\swordsection{\n\
60  %\\swordsection{}{\n\
61  %\\swordsection{book}{\n\
62  %\\swordsection{sechead}{\n\
63  %\\swordstrong[Greek]{\n\
64  %\\swordstrong[Greektense]{\n\
65  %\\swordstrong[Hebrew]{\n\
66  %\\swordstrong[Hebrewtense]{\n\
67  %\\swordstrong[%s]{%s}{\n\
68  %\\swordstrong{%s}{%s}\n\
69  %\\swordtitle{\n\
70  %\\swordtranschange{supplied}{\n\
71  %\\swordtranschange{tense}{\n\
72  %\\swordwoj{\n\
73  %\\swordxref{\n\
74  %\\swordxref{%s}{\n\
75  %\\swordxref{%s}{%s}{\n\
76  ";
77  return header;
78 }
79 
80 
81 namespace {
82 
83 // though this might be slightly slower, possibly causing an extra bool check, this is a renderFilter
84 // so speed isn't the absolute highest priority, and this is a very minor possible hit
85 static inline void outText(const char *t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
86 static inline void outText(char t, SWBuf &o, BasicFilterUserData *u) { if (!u->suspendTextPassThru) o += t; else u->lastSuspendSegment += t; }
87 
88 void processLemma(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf) {
89  const char *attrib;
90  const char *val;
91  if ((attrib = tag.getAttribute("lemma"))) {
92  int count = tag.getAttributePartCount("lemma", ' ');
93  int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0
94  do {
95  attrib = tag.getAttribute("lemma", i, ' ');
96  if (i < 0) i = 0; // to handle our -1 condition
97  val = strchr(attrib, ':');
98  val = (val) ? (val + 1) : attrib;
99  SWBuf gh;
100  if(*val == 'G')
101  gh = "Greek";
102  if(*val == 'H')
103  gh = "Hebrew";
104  const char *val2 = val;
105  if ((strchr("GH", *val)) && (isdigit(val[1])))
106  val2++;
107  //if ((!strcmp(val2, "3588")) && (lastText.length() < 1))
108  // show = false;
109  //else {
110  if (!suspendTextPassThru) {
111  buf.appendFormatted("\\swordstrong{%s}{%s}",
112  (gh.length()) ? gh.c_str() : "",
113  val2);
114  }
115  //}
116 
117  } while (++i < count);
118  }
119 }
120 
121 
122 
123 void processMorph(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf) {
124  const char * attrib;
125  const char *val;
126  if ((attrib = tag.getAttribute("morph"))) { // && (show)) {
127  SWBuf savelemma = tag.getAttribute("savlm");
128  //if ((strstr(savelemma.c_str(), "3588")) && (lastText.length() < 1))
129  // show = false;
130  //if (show) {
131  int count = tag.getAttributePartCount("morph", ' ');
132  int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0
133  do {
134  attrib = tag.getAttribute("morph", i, ' ');
135  if (i < 0) i = 0; // to handle our -1 condition
136  val = strchr(attrib, ':');
137  val = (val) ? (val + 1) : attrib;
138  const char *val2 = val;
139  if ((*val == 'T') && (strchr("GH", val[1])) && (isdigit(val[2])))
140  val2+=2;
141  if (!suspendTextPassThru) {
142  buf.appendFormatted("\\swordmorph{%s}",
143  tag.getAttribute("morph")
144  );
145  }
146  } while (++i < count);
147  //}
148  }
149 }
150 
151 
152 } // end anonymous namespace
153 
155  return new MyUserData(module, key);
156 }
157 
158 
160  setTokenStart("<");
161  setTokenEnd(">");
162 
163  setEscapeStart("&");
164  setEscapeEnd(";");
165 
168 
169  addAllowedEscapeString("quot");
170  addAllowedEscapeString("apos");
171  addAllowedEscapeString("amp");
174 
175  setTokenCaseSensitive(true);
176 
177  // addTokenSubstitute("lg", "<br />");
178  // addTokenSubstitute("/lg", "<br />");
179 
180  morphFirst = false;
181  renderNoteNumbers = false;
182 }
183 
184 class OSISLaTeX::TagStack : public std::stack<SWBuf> {
185 };
186 
187 OSISLaTeX::MyUserData::MyUserData(const SWModule *module, const SWKey *key) : BasicFilterUserData(module, key), quoteStack(new TagStack()), hiStack(new TagStack()), titleStack(new TagStack()), lineStack(new TagStack()) {
188  inXRefNote = false;
189  suspendLevel = 0;
190  divLevel = "module";
191  wordsOfChristStart = "\\swordwoj{";
192  wordsOfChristEnd = "}";
194  firstCell = false;
195 }
196 
198  delete quoteStack;
199  delete hiStack;
200  delete titleStack;
201  delete lineStack;
202 }
203 
205  if (++consecutiveNewlines <= 2) {
206  outText("//\n", buf, this);
207  supressAdjacentWhitespace = true;
208  }
209 }
210 bool OSISLaTeX::handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData) {
211  MyUserData *u = (MyUserData *)userData;
212  SWBuf scratch;
213 
214  bool sub = (u->suspendTextPassThru) ? substituteToken(scratch, token) : substituteToken(buf, token);
215  if (!sub) {
216  // manually process if it wasn't a simple substitution
217  XMLTag tag(token);
218 
219  // <w> tag
220  if (!strcmp(tag.getName(), "w")) {
221 
222  // start <w> tag
223  if ((!tag.isEmpty()) && (!tag.isEndTag())) {
224  u->w = token;
225  }
226 
227  // end or empty <w> tag
228  else {
229  bool endTag = tag.isEndTag();
230  SWBuf lastText;
231  //bool show = true; // to handle unplaced article in kjv2003-- temporary till combined
232 
233  if (endTag) {
234  tag = u->w.c_str();
235  lastText = u->lastTextNode.c_str();
236  }
237  else lastText = "stuff";
238 
239  const char *attrib;
240  const char *val;
241  if ((attrib = tag.getAttribute("xlit"))) {
242  val = strchr(attrib, ':');
243  val = (val) ? (val + 1) : attrib;
244  outText(" ", buf, u);
245  outText(val, buf, u);
246  }
247  if ((attrib = tag.getAttribute("gloss"))) {
248  // I'm sure this is not the cleanest way to do it, but it gets the job done
249  // for rendering ruby chars properly ^_^
250  buf -= lastText.length();
251 
252  outText("\\ruby{", buf, u);
253  outText(lastText, buf, u);
254  outText("}{", buf, u);
255  outText(attrib, buf, u);
256  outText("}", buf, u);
257  }
258  if (!morphFirst) {
259  processLemma(u->suspendTextPassThru, tag, buf);
260  processMorph(u->suspendTextPassThru, tag, buf);
261  }
262  else {
263  processMorph(u->suspendTextPassThru, tag, buf);
264  processLemma(u->suspendTextPassThru, tag, buf);
265  }
266  if ((attrib = tag.getAttribute("POS"))) {
267  val = strchr(attrib, ':');
268  val = (val) ? (val + 1) : attrib;
269  outText(" ", buf, u);
270  outText(val, buf, u);
271  }
272 
273 
274  }
275  }
276 
277  // <note> tag
278 
279  else if (!strcmp(tag.getName(), "note")) {
280  if (!tag.isEndTag()) {
281  SWBuf type = tag.getAttribute("type");
282  bool strongsMarkup = (type == "x-strongsMarkup" || type == "strongsMarkup"); // the latter is deprecated
283  if (strongsMarkup) {
284  tag.setEmpty(false); // handle bug in KJV2003 module where some note open tags were <note ... />
285  }
286 
287  if (!tag.isEmpty()) {
288 
289  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
290  SWBuf footnoteNumber = tag.getAttribute("swordFootnote");
291  SWBuf footnoteBody = "";
292  if (u->module){
293  footnoteBody += u->module->getEntryAttributes()["Footnote"][footnoteNumber]["body"];
294  }
295  SWBuf noteName = tag.getAttribute("n");
296 
297  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
298 // u->inXRefNote = (ch == 'x');
299 
300  if (u->vkey) {
301  //printf("URL = %s\n",URL::encode(u->vkey->getText()).c_str());
302  buf.appendFormatted("\\swordfootnote{%s}{%s}{%s}{%s}{%s}{",
303 
304  footnoteNumber.c_str(),
305  u->version.c_str(),
306  u->vkey->getText(),
307  tag.getAttribute("type"),
308  (renderNoteNumbers ? noteName.c_str() : ""));
309  if (u->module) {
310  outText( u->module->renderText(footnoteBody).c_str(), buf, u);
311  }
312  }
313  else {
314  buf.appendFormatted("\\swordfootnote{%s}{%s}{%s}{%s}{%s}{",
315  footnoteNumber.c_str(),
316  u->version.c_str(),
317  u->key->getText(),
318  tag.getAttribute("type"),
319  (renderNoteNumbers ? noteName.c_str() : ""));
320  if (u->module) {
321  outText( u->module->renderText(footnoteBody).c_str(), buf, u);
322  }
323  }
324  }
325  }
326  u->suspendTextPassThru = (++u->suspendLevel);
327  }
328  if (tag.isEndTag()) {
329  u->suspendTextPassThru = (--u->suspendLevel);
330  u->inXRefNote = false;
331  u->lastSuspendSegment = ""; // fix/work-around for nasb divineName in note bug
332  outText("}", buf, u);
333  }
334  }
335 
336  // <p> paragraph and <lg> linegroup tags
337  else if (!strcmp(tag.getName(), "p") || !strcmp(tag.getName(), "lg")) {
338  if ((!tag.isEndTag()) && (!tag.isEmpty())) { // non-empty start tag
339  u->outputNewline(buf);
340  }
341  else if (tag.isEndTag()) { // end tag
342  u->outputNewline(buf);
343  }
344  else { // empty paragraph break marker
345  u->outputNewline(buf);
346  }
347  }
348 
349  // Milestoned paragraphs, created by osis2mod
350  // <div type="paragraph" sID.../>
351  // <div type="paragraph" eID.../>
352  else if (tag.isEmpty() && !strcmp(tag.getName(), "div") && tag.getAttribute("type") && (!strcmp(tag.getAttribute("type"), "x-p") || !strcmp(tag.getAttribute("type"), "paragraph"))) {
353  // <div type="paragraph" sID... />
354  if (tag.getAttribute("sID")) { // non-empty start tag
355  u->outputNewline(buf);
356  }
357  // <div type="paragraph" eID... />
358  else if (tag.getAttribute("eID")) {
359  u->outputNewline(buf);
360  }
361  }
362 
363  // <reference> tag
364  else if (!strcmp(tag.getName(), "reference")) {
365  if (!u->inXRefNote) { // only show these if we're not in an xref note
366  if (!tag.isEndTag()) {
367  SWBuf target;
368  SWBuf work;
369  SWBuf ref;
370  bool is_scripRef = false;
371 
372  target = tag.getAttribute("osisRef");
373  const char* the_ref = strchr(target, ':');
374 
375  if(!the_ref) {
376  // No work
377  ref = target;
378  is_scripRef = true;
379  }
380  else {
381  // Compensate for starting :
382  ref = the_ref + 1;
383 
384  int size = (int)(target.size() - ref.size() - 1);
385  work.setSize(size);
386  strncpy(work.getRawData(), target, size);
387 
388  // For Bible:Gen.3.15 or Bible.vulgate:Gen.3.15
389  if(!strncmp(work, "Bible", 5))
390  is_scripRef = true;
391  }
392 
393  if(is_scripRef)
394  {
395  buf.appendFormatted("\\swordxref{%s}{",
396  ref.c_str()
397 // (work.size()) ? URL::encode(work.c_str()).c_str() : "")
398  );
399  }
400  else
401  {
402  // Dictionary link, or something
403  buf.appendFormatted("\\sworddiclink{%s}{%s}{", // work, entry
404  work.c_str(),
405  ref.c_str()
406  );
407  }
408  }
409  else {
410  outText("}", buf, u);
411  }
412  }
413  }
414 
415  // <l> poetry, etc
416  else if (!strcmp(tag.getName(), "l")) {
417  // start line marker
418  if (tag.getAttribute("sID") || (!tag.isEndTag() && !tag.isEmpty())) {
419  // nested lines plus if the line itself has an x-indent type attribute value
420  outText("\\swordpoetryline{", buf, u);
421  u->lineStack->push(tag.toString());
422  }
423  // end line marker
424  else if (tag.getAttribute("eID") || tag.isEndTag()) {
425  outText("}", buf, u);
426  u->outputNewline(buf);
427  if (u->lineStack->size()) u->lineStack->pop();
428  }
429  // <l/> without eID or sID
430  // Note: this is improper osis. This should be <lb/>
431  else if (tag.isEmpty() && !tag.getAttribute("sID")) {
432  u->outputNewline(buf);
433  }
434  }
435 
436  // <lb.../>
437  else if (!strcmp(tag.getName(), "lb") && (!tag.getAttribute("type") || strcmp(tag.getAttribute("type"), "x-optional"))) {
438  u->outputNewline(buf);
439  }
440  // <milestone type="line"/>
441  // <milestone type="x-p"/>
442  // <milestone type="cQuote" marker="x"/>
443  else if ((!strcmp(tag.getName(), "milestone")) && (tag.getAttribute("type"))) {
444  if (!strcmp(tag.getAttribute("type"), "line")) {
445  u->outputNewline(buf);
446  if (tag.getAttribute("subType") && !strcmp(tag.getAttribute("subType"), "x-PM")) {
447  u->outputNewline(buf);
448  }
449  }
450  else if (!strcmp(tag.getAttribute("type"),"x-p")) {
451  if (tag.getAttribute("marker"))
452  outText(tag.getAttribute("marker"), buf, u);
453  else outText("<!p>", buf, u);
454  }
455  else if (!strcmp(tag.getAttribute("type"), "cQuote")) {
456  const char *tmp = tag.getAttribute("marker");
457  bool hasMark = tmp;
458  SWBuf mark = tmp;
459  tmp = tag.getAttribute("level");
460  int level = (tmp) ? atoi(tmp) : 1;
461 
462  // first check to see if we've been given an explicit mark
463  if (hasMark)
464  outText(mark, buf, u);
465  // finally, alternate " and ', if config says we should supply a mark
466  else if (u->osisQToTick)
467  outText((level % 2) ? '\"' : '\'', buf, u);
468  }
469  }
470 
471  // <title>
472 
473  else if (!strcmp(tag.getName(), "title")) {
474  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
475  const char *tmp = tag.getAttribute("type");
476  bool hasType = tmp;
477  SWBuf type = tmp;
478 
479  outText("\n\\swordtitle{", buf, u);
480  outText(u->divLevel, buf, u);
481  outText("}{", buf, u);
482 
483  if (hasType) outText(type, buf, u);
484  else outText("", buf, u);
485 
486  outText("}{", buf, u);
487  }
488  else if (tag.isEndTag()) {
489  outText( "}", buf, u);
490  ++u->consecutiveNewlines;
491  u->supressAdjacentWhitespace = true;
492  }
493  }
494 
495  // <list>
496  else if (!strcmp(tag.getName(), "list")) {
497  if((!tag.isEndTag()) && (!tag.isEmpty())) {
498  outText("\n\\begin{itemize}", buf, u);
499  }
500  else if (tag.isEndTag()) {
501  outText("\n\\end{itemize}", buf, u);
502  ++u->consecutiveNewlines;
503  u->supressAdjacentWhitespace = true;
504  }
505  }
506 
507  // <item>
508  else if (!strcmp(tag.getName(), "item")) {
509  if((!tag.isEndTag()) && (!tag.isEmpty())) {
510  outText("\n\\item ", buf, u);
511  }
512  else if (tag.isEndTag()) {
513  ++u->consecutiveNewlines;
514  u->supressAdjacentWhitespace = true;
515  }
516  }
517  // <catchWord> & <rdg> tags (italicize)
518  else if (!strcmp(tag.getName(), "rdg") || !strcmp(tag.getName(), "catchWord")) {
519  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
520  outText("\\emph{", buf, u);
521  }
522  else if (tag.isEndTag()) {
523  outText("}", buf, u);
524  }
525  }
526 
527  // divineName
528  else if (!strcmp(tag.getName(), "divineName")) {
529  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
530  outText( "\\sworddivinename{", buf, u);
531  u->suspendTextPassThru = (++u->suspendLevel);
532  }
533  else if (tag.isEndTag()) {
534  SWBuf lastText = u->lastSuspendSegment.c_str();
535  u->suspendTextPassThru = (--u->suspendLevel);
536  if (lastText.size()) {
537  scratch.setFormatted("%s}", lastText.c_str());
538  outText(scratch.c_str(), buf, u);
539  }
540  }
541  }
542 
543  // <hi> text highlighting
544  else if (!strcmp(tag.getName(), "hi")) {
545  SWBuf type = tag.getAttribute("type");
546 
547  // handle tei rend attribute if type doesn't exist
548  if (!type.length()) type = tag.getAttribute("rend");
549 
550  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
551  if (type == "bold" || type == "b" || type == "x-b") {
552  outText("\\textbold{", buf, u);
553  }
554 
555  // there is no officially supported OSIS overline attribute,
556  // thus either TEI overline or OSIS x-overline would be best,
557  // but we have used "ol" in the past, as well. Once a valid
558  // OSIS overline attribute is made available, these should all
559  // eventually be deprecated and never documented that they are supported.
560  else if (type == "ol" || type == "overline" || type == "x-overline") {
561  outText("\\textoverline{", buf, u);
562  }
563 
564  else if (type == "super") {
565  outText("\\textsuperscript{", buf, u);
566  }
567  else if (type == "sub") {
568  outText("\\textsubscript{", buf, u);
569  }
570  else { // all other types
571  outText("\\emph {", buf, u);
572  }
573  u->hiStack->push(tag.toString());
574  }
575  else if (tag.isEndTag()) {
576  outText("}", buf, u);
577  }
578  }
579 
580  // <q> quote
581  // Rules for a quote element:
582  // If the tag is empty with an sID or an eID then use whatever it specifies for quoting.
583  // Note: empty elements without sID or eID are ignored.
584  // If the tag is <q> then use it's specifications and push it onto a stack for </q>
585  // If the tag is </q> then use the pushed <q> for specification
586  // If there is a marker attribute, possibly empty, this overrides osisQToTick.
587  // If osisQToTick, then output the marker, using level to determine the type of mark.
588  else if (!strcmp(tag.getName(), "q")) {
589  SWBuf type = tag.getAttribute("type");
590  SWBuf who = tag.getAttribute("who");
591  const char *tmp = tag.getAttribute("level");
592  int level = (tmp) ? atoi(tmp) : 1;
593  tmp = tag.getAttribute("marker");
594  bool hasMark = tmp;
595  SWBuf mark = tmp;
596 
597  // open <q> or <q sID... />
598  if ((!tag.isEmpty() && !tag.isEndTag()) || (tag.isEmpty() && tag.getAttribute("sID"))) {
599  // if <q> then remember it for the </q>
600  if (!tag.isEmpty()) {
601  u->quoteStack->push(tag.toString());
602  }
603 
604  // Do this first so quote marks are included as WoC
605  if (who == "Jesus")
606  outText(u->wordsOfChristStart, buf, u);
607 
608  // first check to see if we've been given an explicit mark
609  if (hasMark)
610  outText(mark, buf, u);
611  //alternate " and '
612  else if (u->osisQToTick)
613  outText((level % 2) ? '\"' : '\'', buf, u);
614  }
615  // close </q> or <q eID... />
616  else if ((tag.isEndTag()) || (tag.isEmpty() && tag.getAttribute("eID"))) {
617  // if it is </q> then pop the stack for the attributes
618  if (tag.isEndTag() && !u->quoteStack->empty()) {
619  XMLTag qTag(u->quoteStack->top());
620  if (u->quoteStack->size()) u->quoteStack->pop();
621 
622  type = qTag.getAttribute("type");
623  who = qTag.getAttribute("who");
624  tmp = qTag.getAttribute("level");
625  level = (tmp) ? atoi(tmp) : 1;
626  tmp = qTag.getAttribute("marker");
627  hasMark = tmp;
628  mark = tmp;
629  }
630 
631  // first check to see if we've been given an explicit mark
632  if (hasMark)
633  outText(mark, buf, u);
634  // finally, alternate " and ', if config says we should supply a mark
635  else if (u->osisQToTick)
636  outText((level % 2) ? '\"' : '\'', buf, u);
637 
638  // Do this last so quote marks are included as WoC
639  if (who == "Jesus")
640  outText(u->wordsOfChristEnd, buf, u);
641  }
642  }
643 
644  // <transChange>
645  else if (!strcmp(tag.getName(), "transChange")) {
646  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
647  SWBuf type = tag.getAttribute("type");
648  u->lastTransChange = type;
649 
650  // just do all transChange tags this way for now
651  if ((type == "added") || (type == "supplied"))
652  outText("\\swordtranschange{supplied}{", buf, u);
653  else if (type == "tenseChange")
654  outText( "\\swordtranschange{tense}{", buf, u);
655  }
656  else if (tag.isEndTag()) {
657  outText("}", buf, u);
658  }
659  else { // empty transChange marker?
660  }
661  }
662 
663  // image
664  else if (!strcmp(tag.getName(), "figure")) {
665  const char *src = tag.getAttribute("src");
666  if (src) { // assert we have a src attribute
667  SWBuf filepath;
668  if (userData->module) {
669  filepath = userData->module->getConfigEntry("AbsoluteDataPath");
670  if ((filepath.size()) && (filepath[filepath.size()-1] != '/') && (src[0] != '/'))
671  filepath += '/';
672  }
673  filepath += src;
674 
675  outText("\\figure{", buf, u);
676  outText("\\includegraphics{", buf, u);
677  outText(filepath.c_str(), buf, u);
678  outText("}}", buf, u);
679 
680  }
681  }
682 
683  // ok to leave these in
684  else if (!strcmp(tag.getName(), "div")) {
685  SWBuf type = tag.getAttribute("type");
686  if (type == "module") {
687  u->divLevel = type;
688  outText("\n", buf, u);
689  }
690  else if (type == "testament") {
691  u->divLevel = type;
692  outText("\n", buf, u);
693  }
694  else if (type == "bookGroup") {
695  u->divLevel = type;
696  outText("\n", buf, u);
697  }
698  else if (type == "book") {
699  u->divLevel = type;
700  outText("\n", buf, u);
701  }
702  else if (type == "majorSection") {
703  u->divLevel = type;
704  outText("\n", buf, u);
705  }
706  else if (type == "section") {
707  u->divLevel = type;
708  outText("\n", buf, u);
709  }
710  else if (type == "paragraph") {
711  u->divLevel = type;
712  outText("\n", buf, u);
713  }
714  }
715  else if (!strcmp(tag.getName(), "span")) {
716  outText( "", buf, u);
717  }
718  else if (!strcmp(tag.getName(), "br")) {
719  outText( "\\", buf, u);
720  }
721  else if (!strcmp(tag.getName(), "table")) {
722  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
723  outText( "\n\\begin{tabular}", buf, u);
724  }
725  else if (tag.isEndTag()) {
726  outText( "\n\\end{tabular}", buf, u);
727  ++u->consecutiveNewlines;
728  u->supressAdjacentWhitespace = true;
729  }
730 
731  }
732  else if (!strcmp(tag.getName(), "row")) {
733  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
734  outText( "\n", buf, u);
735  u->firstCell = true;
736  }
737  else if (tag.isEndTag()) {
738  outText( "//", buf, u);
739  u->firstCell = false;
740  }
741 
742  }
743  else if (!strcmp(tag.getName(), "cell")) {
744  if ((!tag.isEndTag()) && (!tag.isEmpty())) {
745  if (u->firstCell == false) {
746  outText( " & ", buf, u);
747  }
748  else {
749  u->firstCell = false;
750  }
751  }
752  else if (tag.isEndTag()) {
753  outText( "", buf, u);
754  }
755  }
756  else {
758  return false; // we still didn't handle token
759  }
760  }
762  return true;
763 }
764 
765 
#define SWORD_NAMESPACE_START
Definition: defs.h:39
SWBuf & appendFormatted(const char *format,...)
Definition: swbuf.cpp:81
void setTokenEnd(const char *tokenEnd)
void addAllowedEscapeString(const char *findString)
Definition: swbuf.h:47
unsigned long length() const
Definition: swbuf.h:197
void setEmpty(bool value)
Definition: utilxml.h:66
virtual const char * getConfigEntry(const char *key) const
Definition: swmodule.cpp:1159
MyUserData(const SWModule *module, const SWKey *key)
Definition: osislatex.cpp:187
bool renderNoteNumbers
Definition: osislatex.h:36
const SWModule * module
Definition: swbasicfilter.h:42
const char * getName() const
Definition: utilxml.h:58
SWText * module
Definition: osis2mod.cpp:105
Definition: utilxml.h:38
TagStack * hiStack
Definition: osislatex.h:57
const char * toString() const
Definition: utilxml.cpp:285
void setTokenCaseSensitive(bool val)
void setEscapeStart(const char *escStart)
SWBuf renderText(const char *buf, int len=-1, bool render=true) const
Definition: swmodule.cpp:1038
bool isEmpty() const
Definition: utilxml.h:60
bool substituteToken(SWBuf &buf, const char *token)
const VerseKey * vkey
Definition: swbasicfilter.h:44
virtual const char * getText() const
Definition: versekey.cpp:1242
virtual const char * getText() const
Definition: swkey.cpp:184
void processLemma(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf)
void setTokenStart(const char *tokenStart)
char * getRawData()
Definition: swbuf.h:379
const char * c_str() const
Definition: swbuf.h:158
virtual BasicFilterUserData * createUserData(const SWModule *module, const SWKey *key)
Definition: osislatex.cpp:154
void setPassThruNumericEscapeString(bool val)
static void outText(const char *t, SWBuf &o, BasicFilterUserData *u)
void processMorph(bool suspendTextPassThru, XMLTag &tag, SWBuf &buf)
TagStack * lineStack
Definition: osislatex.h:59
unsigned long size() const
Definition: swbuf.h:185
const SWKey * key
Definition: swbasicfilter.h:43
virtual AttributeTypeList & getEntryAttributes() const
Definition: swmodule.h:817
const char * getAttribute(const char *attribName, int partNum=-1, char partSplit= '|') const
Definition: utilxml.cpp:230
int size
Definition: regex.c:5043
void setEscapeStringCaseSensitive(bool val)
bool isEndTag(const char *eID=0) const
Definition: utilxml.cpp:323
void outputNewline(SWBuf &buf)
Definition: osislatex.cpp:204
void setEscapeEnd(const char *escEnd)
#define SWORD_NAMESPACE_END
Definition: defs.h:40
TagStack * quoteStack
Definition: osislatex.h:56
SWBuf & setFormatted(const char *format,...)
Definition: swbuf.cpp:50
Definition: swkey.h:77
virtual bool handleToken(SWBuf &buf, const char *token, BasicFilterUserData *userData)
Definition: osislatex.cpp:210
bool morphFirst
Definition: osislatex.h:35
virtual const char * getHeader() const
Definition: osislatex.cpp:36
void setSize(unsigned long len)
Definition: swbuf.h:255
int getAttributePartCount(const char *attribName, char partSplit= '|') const
Definition: utilxml.cpp:218