File: logic.cpp

package info (click to toggle)
bibledit 5.1.036-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 252,000 kB
  • sloc: xml: 915,984; ansic: 261,349; cpp: 92,794; javascript: 32,542; sh: 4,527; makefile: 514; php: 69
file content (212 lines) | stat: -rw-r--r-- 7,769 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/*
Copyright (©) 2003-2025 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


#include <editone/logic.h>
#include <editor/usfm2html.h>
#include <editor/html2usfm.h>
#include <filter/string.h>
#include <filter/url.h>


void editone_logic_prefix_html (const std::string& usfm, const std::string& stylesheet,
                                std::string& html, std::string& last_p_style)
{
  if (!usfm.empty ()) {
    Editor_Usfm2Html editor_usfm2html;
    editor_usfm2html.load (usfm);
    editor_usfm2html.stylesheet (stylesheet);
    editor_usfm2html.run ();
    html = editor_usfm2html.get ();
    // No identical id's in the same DOM.
    html = filter::strings::replace (R"( id="notes")", R"( id="prefixnotes")", std::move(html));
    // The last paragraph style in this USFM fragment, the prefix to the editable fragment.
    // If the last paragraph has any content in it,
    // for correct visual representation of the editable fragment, that follows this,
    // clear that style.
    last_p_style = editor_usfm2html.m_current_paragraph_style;
    if (!editor_usfm2html.m_current_paragraph_content.empty ())
      last_p_style.clear ();
  }
}


void editone_logic_editable_html (const std::string& usfm, const std::string& stylesheet,
                                  std::string& html)
{
  if (!usfm.empty ()) {
    Editor_Usfm2Html editor_usfm2html;
    editor_usfm2html.load (usfm);
    editor_usfm2html.stylesheet (stylesheet);
    editor_usfm2html.run ();
    html = editor_usfm2html.get ();
  }
}


void editone_logic_suffix_html (const std::string& editable_last_p_style, const std::string& usfm,
                                const std::string& stylesheet, std::string& html)
{
  if (!usfm.empty ()) {
    Editor_Usfm2Html editor_usfm2html;
    editor_usfm2html.load (usfm);
    editor_usfm2html.stylesheet (stylesheet);
    editor_usfm2html.run ();
    html = editor_usfm2html.get ();
    // No identical id in the same DOM.
    html = filter::strings::replace (R"( id="notes")", R"( id="suffixnotes")", std::move(html));
  }
  
  // If the first paragraph of the suffix does not have a paragraph style applied,
  // apply the last paragraph style of the focused verse to the first paragraph of the suffix.
  // For example, html like this:
  // <p><span class="v">7</span><span> </span><span>For Yahweh knows the way of the righteous,</span></p><p class="q2"><span>but the way of the wicked shall perish.</span></p>
  // ... will become like this:
  // <p class="q1"><span class="v">7</span><span /><span>For Yahweh knows the way of the righteous,</span></p><p class="q2"><span>but the way of the wicked shall perish.</span></p>
  if (!html.empty ()) {
    if (!editable_last_p_style.empty ()) {
      pugi::xml_document document;
      html = filter::strings::html2xml (std::move(html));
      document.load_string (std::move(html).c_str(), pugi::parse_ws_pcdata_single);
      pugi::xml_node p_node = document.first_child ();
      const std::string p_style = p_node.attribute ("class").value ();
      if (p_style.empty ()) {
        p_node.append_attribute ("class") = editable_last_p_style.c_str ();
      }
      std::stringstream output;
      document.print (output, "", pugi::format_raw);
      html = output.str ();
    }
  }
}


std::string editone_logic_html_to_usfm (const std::string& stylesheet, std::string html)
{
  // It used to convert XML entities to normal characters.
  // For example, it used to convert "&lt;" to "<".
  // But doing this too early in the conversion chain led to the following problem:
  // The XML parser was taking the "<" character as part of an XML element.
  // It then didn't find the closing ">" marker, and then complained about parsing errors.
  // And it dropped whatever followed the "<" marker.
  // So it now no longer unescapes the XML special characters this early in the chain.
  // It does it much later now, before saving the USFM that the converter produces.
  
  // Convert special spaces to normal ones.
  html = filter::strings::any_space_to_standard_space (std::move(html));

  // Convert the html back to USFM in the special way for editing one verse.
  const std::string usfm = editor_export_verse_quill (stylesheet, std::move(html));

  // Done.
  return usfm;
}


// Move the notes from the $prefix to the $suffix.
void editone_logic_move_notes (std::string& prefix, std::string& suffix)
{
  // No input: Ready.
  if (prefix.empty ())
    return;
  
  // Do a html to xml conversion to avoid a mismatched tag error.
  prefix = filter::strings::html2xml (prefix);

  // Load the prefix.
  pugi::xml_document document;
  document.load_string (prefix.c_str(), pugi::parse_ws_pcdata_single);

  // The notes separator class.
  const char* b_notes_class = "b-notes";
  
  // Iterate over the document to find:
  // - the possible notes separator.
  // - any possible subsequent note nodes.
  bool within_notes = false;
  pugi::xml_node prefix_separator_node;
  std::vector <pugi::xml_node> prefix_note_nodes;
  for (pugi::xml_node p_node : document.children ()) {
    if (within_notes) {
      prefix_note_nodes.push_back (p_node);
    }
    const std::string cls = p_node.attribute ("class").value ();
    if (cls == b_notes_class) {
      within_notes = true;
      prefix_separator_node = p_node;
    }
  }

  // No notes: Ready.
  if (prefix_note_nodes.empty())
    return;

  // Get the note(s) text from the note node(s).
  // Remove the note node(s) from the prefix.
  // Remove the notes separator node from the prefix.
  std::string notes_text;
  for (pugi::xml_node p_node : prefix_note_nodes) {
    std::stringstream ss;
    p_node.print (ss, "", pugi::format_raw);
    std::string note = ss.str ();
    notes_text.append (note);
    document.remove_child (p_node);
  }
  document.remove_child (prefix_separator_node);

  // Convert the XML document back to a possibly cleaned prefix without notes.
  {
    std::stringstream ss;
    document.print (ss, "", pugi::format_raw);
    prefix = ss.str ();
  }

  // Do a html to xml conversion in the suffix to avoid a mismatched tag error.
  suffix = filter::strings::html2xml (std::move(suffix));

  // Load the suffix.
  document.load_string (std::move(suffix).c_str(), pugi::parse_ws_pcdata_single);

  // Iterate over the document to find the possible notes separator.
  pugi::xml_node suffix_separator_node;
  for (pugi::xml_node p_node : document.children ()) {
    std::string cls = p_node.attribute ("class").value ();
    if (cls == b_notes_class) {
      suffix_separator_node = p_node;
    }
  }

  // If there's no notes container, add it.
  if (!suffix_separator_node) {
    suffix_separator_node = document.append_child ("p");
    suffix_separator_node.append_attribute ("class") = b_notes_class;
    suffix_separator_node.append_child ("br");
  }

  // Contain the prefix's notes and add them to the suffix's notes container.
  suffix_separator_node.append_buffer (notes_text.c_str (), notes_text.size ());

  // Convert the DOM to suffix text.
  {
    std::stringstream ss;
    document.print (ss, "", pugi::format_raw);
    suffix = std::move(ss).str ();
  }
}