Skip to content

Commit ee2f9f6

Browse files
authored
fix editing (#448)
1 parent 27c8ef2 commit ee2f9f6

8 files changed

Lines changed: 240 additions & 40 deletions

File tree

cli/src/server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ int main(int argc, char **argv) {
2121
DecodePreference decode_preference;
2222
decode_preference.engine_priority = {
2323
DecoderEngine::poppler, DecoderEngine::wvware, DecoderEngine::odr};
24+
decode_preference.as_file_type = FileType::zip;
2425

2526
DecodedFile decoded_file{input, decode_preference, *logger};
2627

src/odr/document_element.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,17 @@ TextStyle ListItem::style() const {
305305
return exists_() ? m_element->style(m_document) : TextStyle();
306306
}
307307

308+
TableRow Table::first_row() const {
309+
return exists_() ? TableRow(m_document, m_element->first_row(m_document))
310+
: TableRow();
311+
}
312+
313+
TableColumn Table::first_column() const {
314+
return exists_()
315+
? TableColumn(m_document, m_element->first_column(m_document))
316+
: TableColumn();
317+
}
318+
308319
ElementRange Table::columns() const {
309320
return exists_() ? ElementRange(ElementIterator(
310321
m_document, m_element->first_column(m_document)))

src/odr/document_element.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,9 @@ class Table final : public TypedElement<internal::abstract::Table> {
418418
public:
419419
using TypedElement::TypedElement;
420420

421+
[[nodiscard]] TableRow first_row() const;
422+
[[nodiscard]] TableColumn first_column() const;
423+
421424
[[nodiscard]] ElementRange columns() const;
422425
[[nodiscard]] ElementRange rows() const;
423426

src/odr/document_path.cpp

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,41 @@ DocumentPath DocumentPath::extract(Element element, Element root) {
102102
return DocumentPath(reverse);
103103
}
104104

105-
Element DocumentPath::find(Element root, const DocumentPath & /*path*/) {
106-
return root; // TODO
105+
Element DocumentPath::find(Element root, const DocumentPath &path) {
106+
Element element = root;
107+
108+
for (const DocumentPath::Component &c : path) {
109+
std::uint32_t number = 0;
110+
if (const auto child = std::get_if<DocumentPath::Child>(&c)) {
111+
if (!element.first_child()) {
112+
throw std::invalid_argument("child not found");
113+
}
114+
element = element.first_child();
115+
number = child->number;
116+
} else if (const auto column = std::get_if<DocumentPath::Column>(&c)) {
117+
if (!element.as_table().first_column()) {
118+
throw std::invalid_argument("column not found");
119+
}
120+
element = Element(element.as_table().first_column());
121+
number = column->number;
122+
} else if (const auto row = std::get_if<DocumentPath::Row>(&c)) {
123+
if (!element.as_table().first_row()) {
124+
throw std::invalid_argument("row not found");
125+
}
126+
element = Element(element.as_table().first_row());
127+
number = row->number;
128+
} else {
129+
throw std::invalid_argument("unknown component");
130+
}
131+
for (std::uint32_t i = 0; i < number; ++i) {
132+
if (!element.next_sibling()) {
133+
throw std::invalid_argument("sibling not found");
134+
}
135+
element = element.next_sibling();
136+
}
137+
}
138+
139+
return element;
107140
}
108141

109142
DocumentPath::DocumentPath() noexcept = default;

src/odr/html.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,13 @@ void html::edit(const Document &document, const char *diff,
350350
for (const auto &[key, value] : json["modifiedText"].items()) {
351351
auto element =
352352
DocumentPath::find(document.root_element(), DocumentPath(key));
353+
if (!element) {
354+
throw std::invalid_argument("element with path " + key + " not found");
355+
}
356+
if (!element.as_text()) {
357+
throw std::invalid_argument("element with path " + key +
358+
" is not a text element");
359+
}
353360
element.as_text().set_content(value);
354361
}
355362
}

src/odr/internal/odf/odf_element.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,30 +183,57 @@ std::string Text::content(const abstract::Document *) const {
183183

184184
void Text::set_content(const abstract::Document *, const std::string &text) {
185185
auto parent = m_node.parent();
186-
auto old_start = m_node;
186+
auto old_first = m_node;
187+
auto old_last = m_last;
188+
auto new_first = old_first;
189+
auto new_last = m_last;
190+
191+
const auto insert_pcdata = [&]() {
192+
auto new_node =
193+
parent.insert_child_before(pugi::xml_node_type::node_pcdata, old_first);
194+
if (new_first == old_first) {
195+
new_first = new_node;
196+
}
197+
new_last = new_node;
198+
return new_node;
199+
};
200+
const auto insert_node = [&](const char *node) {
201+
auto new_node = parent.insert_child_before(node, old_first);
202+
if (new_first == old_first) {
203+
new_first = new_node;
204+
}
205+
new_last = new_node;
206+
return new_node;
207+
};
187208

188-
for (auto &&token : util::xml::tokenize_text(text)) {
209+
for (const util::xml::StringToken &token : util::xml::tokenize_text(text)) {
189210
switch (token.type) {
190211
case util::xml::StringToken::Type::none:
191212
break;
192213
case util::xml::StringToken::Type::string: {
193-
auto text_node = parent.insert_child_before(
194-
pugi::xml_node_type::node_pcdata, old_start);
214+
auto text_node = insert_pcdata();
195215
text_node.text().set(token.string.c_str());
196216
} break;
197217
case util::xml::StringToken::Type::spaces: {
198-
auto space_node = parent.insert_child_before("text:s", old_start);
218+
auto space_node = insert_node("text:s");
199219
space_node.prepend_attribute("text:c").set_value(token.string.size());
200220
} break;
201221
case util::xml::StringToken::Type::tabs: {
202222
for (std::size_t i = 0; i < token.string.size(); ++i) {
203-
parent.insert_child_before("text:tab", old_start);
223+
insert_node("text:tab");
204224
}
205225
} break;
206226
}
207227
}
208228

209-
// TODO set first and last
229+
m_node = new_first;
230+
m_last = new_last;
231+
232+
for (auto node = old_first; node != old_last.next_sibling();) {
233+
auto next = node.next_sibling();
234+
parent.remove_child(node);
235+
node = next;
236+
}
210237
}
211238

212239
TextStyle Text::style(const abstract::Document *document) const {

src/odr/internal/ooxml/text/ooxml_text_element.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,36 +110,55 @@ void Text::set_content(const abstract::Document *, const std::string &text) {
110110
// TODO http://officeopenxml.com/WPtextSpacing.php
111111
// <w:t xml:space="preserve">
112112
// use `xml:space`
113+
113114
auto parent = m_node.parent();
114-
auto old_start = m_node;
115+
auto old_first = m_node;
116+
auto old_last = m_last;
117+
auto new_first = old_first;
118+
auto new_last = m_last;
119+
120+
const auto insert_node = [&](const char *node) {
121+
auto new_node = parent.insert_child_before(node, old_first);
122+
if (new_first == old_first) {
123+
new_first = new_node;
124+
}
125+
new_last = new_node;
126+
return new_node;
127+
};
115128

116-
auto tokens = util::xml::tokenize_text(text);
117-
for (auto &&token : tokens) {
129+
for (const util::xml::StringToken &token : util::xml::tokenize_text(text)) {
118130
switch (token.type) {
119131
case util::xml::StringToken::Type::none:
120132
break;
121133
case util::xml::StringToken::Type::string: {
122-
auto text_node = parent.insert_child_before("w:t", old_start);
134+
auto text_node = insert_node("w:t");
123135
text_node.append_child(pugi::xml_node_type::node_pcdata)
124136
.text()
125137
.set(token.string.c_str());
126138
} break;
127139
case util::xml::StringToken::Type::spaces: {
128-
auto text_node = parent.insert_child_before("w:t", old_start);
140+
auto text_node = insert_node("w:t");
129141
text_node.append_attribute("xml:space").set_value("preserve");
130142
text_node.append_child(pugi::xml_node_type::node_pcdata)
131143
.text()
132144
.set(token.string.c_str());
133145
} break;
134146
case util::xml::StringToken::Type::tabs: {
135147
for (std::size_t i = 0; i < token.string.size(); ++i) {
136-
parent.insert_child_before("w:tab", old_start);
148+
insert_node("w:tab");
137149
}
138150
} break;
139151
}
140152
}
141153

142-
// TODO remove other
154+
m_node = new_first;
155+
m_last = new_last;
156+
157+
for (auto node = old_first; node != old_last.next_sibling();) {
158+
auto next = node.next_sibling();
159+
parent.remove_child(node);
160+
node = next;
161+
}
143162
}
144163

145164
TextStyle Text::style(const abstract::Document *document) const {

0 commit comments

Comments
 (0)