1 #include "../include/odtfmt.h"
2 #include "../include/lvtinydom.h"
3 #include "../include/fb2def.h"
4 #include "../include/lvopc.h"
5 #include "../include/crlog.h"
6 #include "odxutil.h"
7
8 // If you add new element - update odt_elements_mapping table below
9 #define ODT_TAGS \
10 ODT_TAG(a)\
11 ODT_TAG2(automaticStyles, "automatic-styles")\
12 ODT_TAG(body)\
13 ODT_TAG(bookmark)\
14 ODT_TAG2(bookmarkRef, "bookmark-ref")\
15 ODT_TAG2(bookmarkStart, "bookmark-start")\
16 ODT_TAG2(defaultStyle, "default-style")\
17 ODT_TAG2(documentContent, "document-content")\
18 ODT_TAG2(documentStyles, "document-styles")\
19 ODT_TAG(frame)\
20 ODT_TAG(h)\
21 ODT_TAG(image)\
22 ODT_TAG2(indexBody, "index-body")\
23 ODT_TAG2(lineBreak, "line-break")\
24 ODT_TAG(list)\
25 ODT_TAG2(listStyle, "list-style")\
26 ODT_TAG2(listLevelStyleBullet, "list-level-style-bullet")\
27 ODT_TAG2(listLevelStyleNumber, "list-level-style-number")\
28 ODT_TAG2(listItem, "list-item")\
29 ODT_TAG(note)\
30 ODT_TAG2(noteBody, "note-body")\
31 ODT_TAG2(noteCitation, "note-citation")\
32 ODT_TAG2(noteRef, "note-ref")\
33 ODT_TAG(p)\
34 ODT_TAG2(paragraphProperties, "paragraph-properties")\
35 ODT_TAG2(referenceMark, "reference-mark")\
36 ODT_TAG2(referenceMarkStart, "reference-mark-start")\
37 ODT_TAG2(referenceRef, "reference-ref")\
38 ODT_TAG(s)\
39 ODT_TAG(span)\
40 ODT_TAG(style)\
41 ODT_TAG(styles)\
42 ODT_TAG(tab)\
43 ODT_TAG(table)\
44 ODT_TAG2(tableHeaderRows, "table-header-rows")\
45 ODT_TAG2(tableOfContent, "table-of-content")\
46 ODT_TAG2(tableRow, "table-row")\
47 ODT_TAG2(tableCell, "table-cell")\
48 ODT_TAG(text)\
49 ODT_TAG2(textBox, "text-box")\
50 ODT_TAG2(textProperties, "text-properties")
51
52 #define ODT_TAG_NAME(itm) odt_el_##itm##_name
53 #define ODT_TAG_ID(itm) odt_el_##itm
54 #define ODT_TAG_CHILD(itm) { ODT_TAG_ID(itm), ODT_TAG_NAME(itm) }
55 #define ODT_TAG_CHILD2(itm, name) { ODT_TAG_ID(itm), U ## name }
56 #define ODT_LAST_ITEM { -1, NULL }
57
58 enum {
59 #define ODT_TAG(itm) ODT_TAG_ID(itm),
60 #define ODT_TAG2(itm, name) ODT_TAG_ID(itm),
61 odt_el_NULL = 0,
62 ODT_TAGS
63 odt_el_MAX_ID
64 };
65
66 #undef ODT_TAG
67 #undef ODT_TAG2
68 #define ODT_TAG(itm) static const lChar32 * const ODT_TAG_NAME(itm) = U ## #itm;
69 #define ODT_TAG2(itm, name) static const lChar32 * const ODT_TAG_NAME(itm) = U ## name;
70 ODT_TAGS
71
72 #undef ODT_TAG
73 #undef ODT_TAG2
74 #define ODT_TAG(itm) ODT_TAG_CHILD(itm),
75 #define ODT_TAG2(itm, name) ODT_TAG_CHILD2(itm, name),
76
77 const struct item_def_t odt_elements_mapping[] = {
78 { odt_el_NULL, NULL },
79 { odt_el_a, U"a" },
80 { odt_el_automaticStyles, NULL },
81 { odt_el_body, U"body" },
82 { odt_el_bookmark, U"a" },
83 { odt_el_bookmarkRef, U"a" },
84 { odt_el_bookmarkStart, U"a" },
85 { odt_el_defaultStyle, NULL },
86 { odt_el_documentContent, NULL },
87 { odt_el_documentStyles, NULL },
88 { odt_el_frame, NULL },
89 { odt_el_h, NULL /*Special processing*/},
90 { odt_el_image, U"img" },
91 { odt_el_indexBody, NULL },
92 { odt_el_lineBreak, U"br" },
93 { odt_el_list, U"ol" },
94 { odt_el_listStyle, NULL },
95 { odt_el_listLevelStyleBullet, NULL },
96 { odt_el_listLevelStyleNumber, NULL },
97 { odt_el_listItem, U"li" },
98 { odt_el_note, NULL },
99 { odt_el_noteBody, NULL /*Special Processing */ },
100 { odt_el_noteCitation, NULL /*Special Processing */ },
101 { odt_el_noteRef, U"a" },
102 { odt_el_p, U"p" },
103 { odt_el_paragraphProperties, NULL },
104 { odt_el_referenceMark, U"a" },
105 { odt_el_referenceMarkStart, U"a" },
106 { odt_el_referenceRef, U"a" },
107 { odt_el_s, NULL },
108 { odt_el_span, NULL },
109 { odt_el_style, NULL },
110 { odt_el_styles, NULL },
111 { odt_el_tab, NULL },
112 { odt_el_table, U"table" },
113 { odt_el_tableHeaderRows, U"thead" },
114 { odt_el_tableOfContent, NULL },
115 { odt_el_tableRow, U"tr" },
116 { odt_el_tableCell, U"td" },
117 { odt_el_text, NULL },
118 { odt_el_textBox, NULL },
119 { odt_el_textProperties, NULL }
120 };
121
122 const struct item_def_t odt_elements[] = {
123 ODT_TAG_CHILD(a),
124 ODT_TAG_CHILD(automaticStyles),
125 ODT_TAG_CHILD(body),
126 ODT_TAG_CHILD(bookmark),
127 ODT_TAG_CHILD(bookmarkRef),
128 ODT_TAG_CHILD(bookmarkStart),
129 ODT_TAG_CHILD(documentContent),
130 ODT_TAG_CHILD(frame),
131 ODT_TAG_CHILD(h),
132 ODT_TAG_CHILD(image),
133 ODT_TAG_CHILD(indexBody),
134 ODT_TAG_CHILD(lineBreak),
135 ODT_TAG_CHILD(list),
136 ODT_TAG_CHILD(listItem),
137 ODT_TAG_CHILD(note),
138 ODT_TAG_CHILD(noteBody),
139 ODT_TAG_CHILD(noteCitation),
140 ODT_TAG_CHILD(noteRef),
141 ODT_TAG_CHILD(p),
142 ODT_TAG_CHILD(referenceMark),
143 ODT_TAG_CHILD(referenceMarkStart),
144 ODT_TAG_CHILD(referenceRef),
145 ODT_TAG_CHILD(s),
146 ODT_TAG_CHILD(span),
147 ODT_TAG_CHILD(tab),
148 ODT_TAG_CHILD(table),
149 ODT_TAG_CHILD(tableHeaderRows),
150 ODT_TAG_CHILD(tableOfContent),
151 ODT_TAG_CHILD(tableRow),
152 ODT_TAG_CHILD(tableCell),
153 ODT_TAG_CHILD(text),
154 ODT_TAG_CHILD(textBox),
155 ODT_LAST_ITEM
156 };
157
158 const struct item_def_t odt_style_elements[] = {
159 ODT_TAG_CHILD(automaticStyles),
160 ODT_TAG_CHILD(defaultStyle),
161 ODT_TAG_CHILD(documentStyles),
162 ODT_TAG_CHILD(listStyle),
163 ODT_TAG_CHILD(listLevelStyleBullet),
164 ODT_TAG_CHILD(listLevelStyleNumber),
165 ODT_TAG_CHILD(paragraphProperties),
166 ODT_TAG_CHILD(style),
167 ODT_TAG_CHILD(styles),
168 ODT_TAG_CHILD(textProperties),
169 ODT_LAST_ITEM
170 };
171
172 static const struct item_def_t styleFamily_attr_values[] = {
173 { odx_paragraph_style, U"paragraph" },
174 { odx_character_style, U"text"},
175 ODT_LAST_ITEM
176 };
177
178 const struct item_def_t odt_fontWeigth_attr_values[] = {
179 { 400, U"normal" },
180 { 600, U"bold" },
181 { 100, U"100" },
182 { 200, U"200" },
183 { 300, U"300" },
184 { 400, U"400" },
185 { 500, U"500" },
186 { 600, U"600" },
187 { 700, U"700" },
188 { 800, U"800" },
189 { 900, U"900" },
190 ODT_LAST_ITEM
191 };
192
193 static const struct item_def_t odt_textAlign_attr_values[] =
194 {
195 { css_ta_left, U"left" },
196 { css_ta_right, U"right" },
197 { css_ta_center, U"center" },
198 { css_ta_justify, U"justify" },
199 { css_ta_start, U"start" },
200 { css_ta_end, U"end" },
201 ODT_LAST_ITEM
202 };
203
DetectOpenDocumentFormat(LVStreamRef stream)204 bool DetectOpenDocumentFormat( LVStreamRef stream )
205 {
206 LVContainerRef arc = LVOpenArchieve( stream );
207 if ( arc.isNull() )
208 return false; // not a ZIP archive
209 lString32 mimeType;
210 {
211 LVStreamRef mtStream = arc->OpenStream(U"mimetype", LVOM_READ );
212 if ( !mtStream.isNull() ) {
213 lvsize_t size = mtStream->GetSize();
214 if ( size>4 && size<100 ) {
215 LVArray<char> buf( size+1, '\0' );
216 if ( mtStream->Read( buf.get(), size, NULL )==LVERR_OK ) {
217 for ( lvsize_t i=0; i<size; i++ )
218 if ( buf[i]<32 || ((unsigned char)buf[i])>127 )
219 buf[i] = 0;
220 buf[size] = 0;
221 if ( buf[0] )
222 mimeType = Utf8ToUnicode( lString8( buf.get() ) );
223 }
224 }
225 }
226 }
227 return ( mimeType == U"application/vnd.oasis.opendocument.text" );
228 }
229
230 class odt_ListLevelStyle : public LVRefCounter
231 {
232 css_length_t m_lvlStart;
233 css_list_style_type_t m_levelType;
234 int m_level;
235 public:
odt_ListLevelStyle()236 odt_ListLevelStyle() :
237 m_lvlStart(css_val_unspecified, 0), m_levelType(css_lst_disc), m_level(0) { }
~odt_ListLevelStyle()238 virtual ~odt_ListLevelStyle() {}
getLevelStart() const239 inline css_length_t getLevelStart() const { return m_lvlStart; }
setLevelStart(int value)240 inline void setLevelStart( int value) {
241 m_lvlStart.type = css_val_pt;
242 m_lvlStart.value = value;
243 }
getLevel()244 inline int getLevel() { return m_level; }
setLevel(const lChar32 * value)245 inline void setLevel( const lChar32* value) { m_level = lString32(value).atoi(); }
getLevelType()246 inline css_list_style_type_t getLevelType() { return m_levelType; }
setLevelType(css_list_style_type_t levelType)247 inline void setLevelType(css_list_style_type_t levelType) { m_levelType = levelType; }
reset()248 void reset() {}
249 };
250
251 typedef LVFastRef< odt_ListLevelStyle > odt_ListLevelStyleRef;
252
253 class odt_ListStyle : public LVRefCounter
254 {
255 LVHashTable<lUInt32, odt_ListLevelStyleRef> m_levels;
256 bool m_consecutiveNumbering;
257 lString32 m_Id;
258 public:
odt_ListStyle()259 odt_ListStyle() : m_levels(10), m_consecutiveNumbering(false) {}
~odt_ListStyle()260 virtual ~odt_ListStyle() {}
getLevel(int level)261 odt_ListLevelStyle* getLevel(int level) {
262 return m_levels.get(level).get();
263 }
getId() const264 inline lString32 getId() const { return m_Id; }
setId(const lChar32 * value)265 inline void setId(const lChar32 * value) { m_Id = value; }
getConsecutiveNumbering()266 bool getConsecutiveNumbering() { return m_consecutiveNumbering; }
setConsecutiveNumbering(bool value)267 void setConsecutiveNumbering(bool value) {
268 m_consecutiveNumbering = value;
269 }
270 void addLevel(odt_ListLevelStyleRef listLevel);
reset()271 void reset() { m_levels.clear(); }
272 };
273
274 typedef LVFastRef< odt_ListStyle > odt_ListStyleRef;
275
276 class odtImportContext : public odx_ImportContext
277 {
278 LVHashTable<lString32, odt_ListStyleRef> m_ListStyles;
279 public:
odtImportContext(ldomDocument * doc)280 explicit odtImportContext(ldomDocument* doc) : odx_ImportContext(doc), m_ListStyles(64) { }
281 void addListStyle(odt_ListStyleRef listStyle);
getListStyle(lString32 id)282 odt_ListStyle* getListStyle(lString32 id) {
283 if( !id.empty() )
284 return m_ListStyles.get(id).get();
285 return NULL;
286 }
287 LVStreamRef openFile(const lChar32 * const fileName);
288 };
289
290 class odt_stylesHandler : public xml_ElementHandler
291 {
292 private:
293 LVArray<int> m_levels;
294 odx_StyleRef m_styleRef;
295 odx_Style *m_style;
296 odt_ListStyleRef m_ListStyleRef;
297 odt_ListStyle *m_ListStyle;
298 odt_ListLevelStyleRef m_ListLevelStyleRef;
299 odt_ListLevelStyle *m_ListLevelStyle;
300 odx_pPr* m_pPr;
301 odx_rPr* m_rPr;
302 odtImportContext *m_context;
303 public:
304 /// constructor
odt_stylesHandler(docXMLreader * reader,ldomDocumentWriter * writer,int element,odtImportContext * context)305 odt_stylesHandler(docXMLreader * reader, ldomDocumentWriter *writer, int element,
306 odtImportContext *context) :
307 xml_ElementHandler(reader, writer, element, odt_style_elements),
308 m_style(NULL), m_ListStyle(NULL), m_pPr(NULL), m_rPr(NULL), m_context(context)
309 {
310 }
311 ldomNode * handleTagOpen(int tagId);
312 void handleAttribute(const lChar32 * attrname, const lChar32 * attrvalue);
313 void handleTagClose( const lChar32 * nsname, const lChar32 * tagname );
314 };
315
316 class odt_documentHandler : public xml_ElementHandler, odx_styleTagsHandler
317 {
318 LVArray<int> m_levels;
319 LVArray<bool> m_listItems;
320 LVArray<odt_ListStyle*> m_ListLevels;
321 ldomDocumentWriter m_footNotesWriter;
322 ldomDocumentWriter m_endNotesWriter;
323 ldomDocumentWriter *m_saveWriter;
324 odtImportContext * m_context;
325 ldomNode *m_footNotes;
326 ldomNode *m_endNotes;
327 ldomNode *m_body;
328 lString32 m_noteId;
329 lString32 m_noteRefText;
330 lString32 m_StyleName;
331 lString32 m_spanStyleName;
332 bool m_isEndNote;
333 bool m_paragraphStarted;
334 odt_stylesHandler m_stylesHandler;
335 private:
startNotes(const lChar32 * notesKind)336 ldomNode* startNotes(const lChar32 * notesKind) {
337 m_writer->OnStart(NULL);
338 #ifdef ODX_CRENGINE_IN_PAGE_FOOTNOTES
339 ldomNode* notes = m_writer->OnTagOpen(U"", U"body");
340 m_writer->OnAttribute(U"", U"name", notesKind);
341 #else
342 ldomNode* notes = m_writer->OnTagOpen(U"", U"div");
343 m_writer->OnAttribute(U"", U"style", U"page-break-before: always");
344 #endif
345 m_writer->OnTagBody();
346 return notes;
347 }
finishNotes(ldomNode * notes,ldomDocumentWriter & writer)348 void finishNotes(ldomNode* notes, ldomDocumentWriter& writer) {
349 ldomNode* parent = notes->getParentNode();
350 int index = notes->getNodeIndex();
351 #ifdef ODX_CRENGINE_IN_PAGE_FOOTNOTES
352 writer.OnTagClose(U"", U"body");
353 #else
354 writer.OnTagClose(U"", U"div");
355 #endif
356 writer.OnStop();
357 parent->moveItemsTo(m_body->getParentNode(), index, index);
358 }
checkParagraphStart()359 inline void checkParagraphStart() {
360 if( !m_paragraphStarted ) {
361 startParagraph();
362 }
363 }
364 void startParagraph();
365 protected:
366 odx_titleHandler* m_titleHandler;
367 int m_outlineLevel;
368 bool m_inTable;
369 bool m_inListItem;
370 bool m_listItemHadContent;
371 public:
odt_documentHandler(ldomDocument * doc,docXMLreader * reader,ldomDocumentWriter * writer,odx_titleHandler * titleHandler,odtImportContext * context)372 odt_documentHandler(ldomDocument * doc, docXMLreader * reader,
373 ldomDocumentWriter * writer, odx_titleHandler* titleHandler,
374 odtImportContext * context) :
375 xml_ElementHandler(reader, writer, odt_el_NULL, odt_elements),
376 m_footNotesWriter(doc), m_endNotesWriter(doc), m_saveWriter(NULL),
377 m_context(context), m_footNotes(NULL), m_endNotes(NULL), m_body(NULL),
378 m_isEndNote(false), m_titleHandler(titleHandler),
379 m_outlineLevel(0), m_inTable(false), m_inListItem(false),
380 m_stylesHandler(reader, NULL, odt_el_automaticStyles, context),
381 m_listItemHadContent(false), m_paragraphStarted(false) {
382 }
isInList()383 inline bool isInList() { return m_ListLevels.length() != 0; }
384 ldomNode *handleTagOpen(int tagId);
385 void handleAttribute(const lChar32 *attrname, const lChar32 *attrValue);
386 void handleTagBody();
387 void handleTagClose(const lChar32 *nsname, const lChar32 *tagname);
388 void handleText(const lChar32 *text, int len, lUInt32 flags);
389 void reset();
390 };
391
parseStyles(odtImportContext * context)392 static bool parseStyles(odtImportContext *context)
393 {
394 LVStreamRef stream = context->openFile(U"styles.xml");
395 if ( stream.isNull() )
396 return false;
397
398 docXMLreader docReader(NULL);
399 odt_stylesHandler stylesHandler(&docReader, NULL, odt_el_documentStyles, context);
400 docReader.setHandler(&stylesHandler);
401
402 LVXMLParser parser(stream, &docReader);
403
404 if ( !parser.Parse() )
405 return false;
406 return true;
407 }
408
ImportOpenDocument(LVStreamRef stream,ldomDocument * doc,LVDocViewCallback * progressCallback,CacheLoadingCallback * formatCallback)409 bool ImportOpenDocument( LVStreamRef stream, ldomDocument * doc, LVDocViewCallback * progressCallback, CacheLoadingCallback * formatCallback )
410 {
411 LVContainerRef arc = LVOpenArchieve( stream );
412 if ( arc.isNull() )
413 return false; // not a ZIP archive
414
415 doc->setContainer(arc);
416
417 //Read document metadata
418 LVStreamRef meta_stream = arc->OpenStream(U"meta.xml", LVOM_READ);
419 if ( meta_stream.isNull() )
420 return false;
421 ldomDocument * metaDoc = LVParseXMLStream( meta_stream );
422 if ( metaDoc ) {
423 CRPropRef doc_props = doc->getProps();
424
425 lString32 author = metaDoc->textFromXPath( cs32("document-meta/meta/creator") );
426 lString32 title = metaDoc->textFromXPath( cs32("document-meta/meta/title") );
427 lString32 description = metaDoc->textFromXPath( cs32("document-meta/meta/description") );
428 doc_props->setString(DOC_PROP_TITLE, title);
429 doc_props->setString(DOC_PROP_AUTHORS, author );
430 doc_props->setString(DOC_PROP_DESCRIPTION, description );
431 delete metaDoc;
432 } else {
433 CRLog::error("Couldn't parse document meta data");
434 }
435
436 #if BUILD_LITE!=1
437 if ( doc->openFromCache(formatCallback) ) {
438 if ( progressCallback ) {
439 progressCallback->OnLoadFileEnd( );
440 }
441 return true;
442 }
443 #endif
444
445 ldomDocumentWriter writer(doc);
446 docXMLreader docReader(&writer);
447
448 odtImportContext importContext(doc);
449 if( !parseStyles(&importContext) )
450 return false;
451
452 LVStreamRef m_stream = arc->OpenStream(U"content.xml", LVOM_READ);
453 if ( m_stream.isNull() )
454 return false;
455
456 importContext.startDocument(writer);
457
458 #ifdef DOCX_FB2_DOM_STRUCTURE
459 //Two options when dealing with titles: (FB2|HTML)
460 odx_fb2TitleHandler titleHandler(&writer, DOCX_USE_CLASS_FOR_HEADING); //<section><title>..</title></section>
461 #else
462 odx_titleHandler titleHandler(&writer); //<hx>..</hx>
463 #endif
464
465 odt_documentHandler documentHandler(doc, &docReader, &writer, &titleHandler, &importContext);
466 docReader.setHandler(&documentHandler);
467
468 LVXMLParser parser(m_stream, &docReader);
469
470 if ( !parser.Parse() )
471 return false;
472
473 importContext.endDocument(writer);
474 writer.OnStop();
475
476 if ( progressCallback ) {
477 progressCallback->OnLoadFileEnd( );
478 doc->compact();
479 doc->dumpStatistics();
480 }
481
482 return true;
483 }
484
startParagraph()485 void odt_documentHandler::startParagraph()
486 {
487 if(m_inListItem) {
488 m_listItemHadContent = true;
489 m_writer->OnTagOpenNoAttr(U"", U"li");
490 }
491 m_writer->OnTagOpen(U"", U"p");
492 odx_Style* style = m_context->getStyle(m_StyleName);
493 if( style ) {
494 odx_pPr pPr;
495
496 pPr.combineWith(style->get_pPr(m_context));
497 pPr.combineWith(m_context->get_pPrDefault());
498 lString32 css = pPr.getCss();
499 if( !css.empty() )
500 m_writer->OnAttribute(U"", U"style", css.c_str());
501 }
502 m_writer->OnTagBody();
503 #ifndef DOCX_FB2_DOM_STRUCTURE
504 if(m_state == odt_el_noteBody) {
505 m_writer->OnTagOpen(U"", U"sup");
506 m_writer->OnTagBody();
507 m_writer->OnText(m_noteRefText.c_str(), m_noteRefText.length(), 0);
508 m_writer->OnTagClose(U"", U"sup");
509 }
510 #endif
511 m_paragraphStarted = true;
512 }
513
handleTagOpen(int tagId)514 ldomNode *odt_documentHandler::handleTagOpen(int tagId)
515 {
516 bool elementHandled = false;
517 switch(tagId) {
518 case odt_el_automaticStyles:
519 m_stylesHandler.start();
520 elementHandled = true;
521 break;
522 case odt_el_note:
523 m_isEndNote = false;
524 m_writer->OnTagOpen(U"", U"a");
525 break;
526 case odt_el_body:
527 m_body = m_titleHandler->onBodyStart();
528 m_writer->OnTagBody();
529 break;
530 case odt_el_h:
531 if( odt_el_p == m_state)
532 checkParagraphStart();
533 m_StyleName.clear();
534 m_outlineLevel = 0;
535 break;
536 case odt_el_p:
537 #ifndef DOCX_FB2_DOM_STRUCTURE
538 if( odt_el_p == m_state || odt_el_noteBody == m_state)
539 checkParagraphStart();
540 m_paragraphStarted = odt_el_noteBody == m_state;
541 #else
542 if( odt_el_p == m_state)
543 checkParagraphStart();
544 m_paragraphStarted = false;
545 #endif
546 m_StyleName.clear();
547 break;
548 case odt_el_span:
549 m_spanStyleName.clear();
550 break;
551 case odt_el_list:
552 if( odt_el_p == m_state)
553 checkParagraphStart();
554 m_StyleName.clear();
555 m_writer->OnTagOpen(U"", U"ol");
556 if (isInList())
557 m_listItems.add(m_listItemHadContent);
558 break;
559 case odt_el_listItem:
560 m_inListItem = true;
561 m_listItemHadContent = false;
562 break;
563 case odt_el_tab:
564 case odt_el_s:
565 if( odt_el_p == m_state)
566 checkParagraphStart();
567 m_writer->OnText(U" ", 1, TXTFLG_PRE);
568 break;
569 case odt_el_noteBody:
570 m_saveWriter = m_writer;
571 if(m_isEndNote) {
572 m_writer = &m_endNotesWriter;
573 if(!m_endNotes)
574 m_endNotes = startNotes(U"comments");
575 } else {
576 m_writer = &m_footNotesWriter;
577 if(!m_footNotes)
578 m_footNotes = startNotes(U"notes");;
579 }
580 m_writer->OnTagOpen(U"", U"section");
581 m_writer->OnAttribute(U"", U"id", m_noteId.c_str());
582 m_writer->OnAttribute(U"", U"role", m_isEndNote ? U"doc-rearnote" : U"doc-footnote");
583 m_writer->OnTagBody();
584 #ifdef DOCX_FB2_DOM_STRUCTURE
585 m_writer->OnTagOpenNoAttr(U"", U"title");
586 m_writer->OnTagOpenNoAttr(U"", U"p");
587 m_writer->OnText(m_noteRefText.c_str(), m_noteRefText.length(), 0);
588 m_writer->OnTagClose(U"", U"p");
589 m_writer->OnTagClose(U"", U"title");
590 #else
591 m_paragraphStarted = false;
592 #endif
593 break;
594 case odt_el_table:
595 m_inTable = true;
596 default:
597 if( odt_elements_mapping[tagId].name ) {
598 if( odt_el_p == m_state)
599 checkParagraphStart();
600 m_writer->OnTagOpen(U"", odt_elements_mapping[tagId].name);
601 }
602 break;
603 }
604 if( !elementHandled ) {
605 m_state = tagId;
606 m_levels.add(tagId);
607 }
608 return NULL;
609 }
610
handleAttribute(const lChar32 * attrname,const lChar32 * attrValue)611 void odt_documentHandler::handleAttribute(const lChar32 *attrname, const lChar32 *attrValue)
612 {
613 switch (m_state) {
614 case odt_el_bookmark:
615 case odt_el_bookmarkStart:
616 case odt_el_referenceMark:
617 case odt_el_referenceMarkStart:
618 if(!lStr_cmp(attrname, "name") ) {
619 m_writer->OnAttribute(U"", U"id", attrValue);
620 }
621 break;
622 case odt_el_bookmarkRef:
623 case odt_el_noteRef:
624 case odt_el_referenceRef:
625 if(!lStr_cmp(attrname, "ref-name") ) {
626 lString32 target = cs32("#") + lString32(attrValue);
627 m_writer->OnAttribute(U"", U"href", target.c_str());
628 }
629 break;
630 case odt_el_h:
631 if(!lStr_cmp(attrname, "outline-level") ) {
632 lString32 value = attrValue;
633 int tmp;
634
635 if(value.atoi(tmp))
636 m_outlineLevel = tmp - 1;
637 break;
638 }
639 if( !lStr_cmp(attrname, "style-name") ) {
640 m_StyleName = attrValue;
641 }
642 break;
643 case odt_el_note:
644 if(!lStr_cmp(attrname, "note-class")) {
645 if(!lStr_cmp(attrValue, "endnote")) {
646 m_writer->OnAttribute(U"", U"type", U"comment");
647 m_isEndNote = true;
648 } else if(!lStr_cmp(attrValue, "footnote")) {
649 m_writer->OnAttribute(U"", U"type", U"note");
650 }
651 m_writer->OnAttribute(U"", U"role", U"doc-noteref");
652 } else if(!lStr_cmp(attrname, "id")) {
653 m_noteId = lString32(attrValue);
654 lString32 target = cs32("#") + m_noteId;
655 m_writer->OnAttribute(U"", U"href", target.c_str());
656 }
657 break;
658 case odt_el_tableCell:
659 if(!lStr_cmp(attrname, "number-columns-spanned"))
660 m_writer->OnAttribute(U"", U"colspan", attrValue);
661 else if(!lStr_cmp(attrname, "number-rows-spanned"))
662 m_writer->OnAttribute(U"", U"rowspan", attrValue);
663 break;
664 case odt_el_a:
665 case odt_el_image:
666 if( !lStr_cmp(attrname, "href") )
667 m_writer->OnAttribute(U"", attrname, attrValue);
668 break;
669 case odt_el_p:
670 case odt_el_list:
671 case odt_el_span:
672 if( !lStr_cmp(attrname, "style-name") ) {
673 if( m_state == odt_el_span)
674 m_spanStyleName = attrValue;
675 else
676 m_StyleName = attrValue;
677 }
678 break;
679 default:
680 break;
681 }
682 }
683
handleTagBody()684 void odt_documentHandler::handleTagBody()
685 {
686 switch(m_state) {
687 case odt_el_span:
688 case odt_el_p:
689 break;
690 case odt_el_list:
691 {
692 css_list_style_type_t listType = css_lst_none;
693 css_length_t levelStart(css_val_unspecified, 0);
694 odt_ListStyle* listStyle = m_context->getListStyle(m_StyleName);
695 if( !listStyle && !m_ListLevels.empty() ) {
696 listStyle = m_ListLevels.get( m_ListLevels.length() -1);
697 }
698 m_ListLevels.add(listStyle);
699 if(listStyle) {
700 odt_ListLevelStyle* levelStyle = listStyle->getLevel(m_ListLevels.length());
701 if(levelStyle) {
702 listType = levelStyle->getLevelType();
703 levelStart = levelStyle->getLevelStart();
704 }
705 }
706 m_writer->OnAttribute(U"", U"style", m_context->getListStyleCss(listType).c_str());
707 if(levelStart.type != css_val_unspecified)
708 m_writer->OnAttribute(U"", U"start", lString32::itoa(levelStart.value).c_str());
709 m_writer->OnTagBody();
710 }
711 break;
712 case odt_el_h:
713 if(m_inListItem) {
714 m_listItemHadContent = true;
715 m_writer->OnTagOpenNoAttr(U"", U"li");
716 }
717 m_titleHandler->onTitleStart(m_outlineLevel + 1, m_inTable || m_inListItem);
718 m_writer->OnTagBody();
719 break;
720 default:
721 if( odt_elements_mapping[m_state].name )
722 m_writer->OnTagBody();
723 break;
724 }
725 }
726
handleTagClose(const lChar32 * nsname,const lChar32 * tagname)727 void odt_documentHandler::handleTagClose(const lChar32 *nsname, const lChar32 *tagname)
728 {
729 switch(m_state) {
730 case odt_el_body:
731 m_titleHandler->onBodyEnd();
732 m_writer->OnTagClose(nsname, tagname);
733 if(m_footNotes)
734 finishNotes(m_footNotes, m_footNotesWriter);
735 if(m_endNotes)
736 finishNotes(m_endNotes, m_endNotesWriter);
737 break;
738 case odt_el_h:
739 closeStyleTags(m_writer);
740 m_titleHandler->onTitleEnd();
741 break;
742 case odt_el_p:
743 if( !m_paragraphStarted ) {
744 if( m_inListItem) {
745 m_writer->OnTagOpen(U"", U"li");
746 m_writer->OnAttribute(U"", U"style", U"display:none");
747 m_writer->OnTagBody();
748 m_writer->OnTagClose(U"", U"li");
749 } else
750 m_writer->OnTagOpenNoAttr(U"", U"p");
751 m_paragraphStarted = true;
752 } else
753 closeStyleTags(m_writer);
754 m_writer->OnTagClose(nsname, tagname);
755 break;
756 case odt_el_list:
757 m_ListLevels.remove(m_ListLevels.length() - 1);
758 m_writer->OnTagClose(U"", U"ol");
759 break;
760 case odt_el_listItem:
761 if(m_listItemHadContent)
762 m_writer->OnTagClose(U"", U"li");
763 if(!m_listItems.empty()) {
764 m_listItemHadContent = m_listItems[m_listItems.length() - 1];
765 m_listItems.erase(m_listItems.length() - 1, 1);
766 }
767 m_inListItem = false;
768 break;
769 case odt_el_noteBody:
770 m_writer->OnTagClose(U"", U"section");
771 m_writer = m_saveWriter;
772 break;
773 case odt_el_noteCitation:
774 m_writer->OnTagClose(U"", U"a");
775 break;
776 case odt_el_table:
777 m_inTable = false;
778 default:
779 if( odt_elements_mapping[m_state].name )
780 m_writer->OnTagClose(U"", odt_elements_mapping[m_state].name);
781 break;
782 }
783 m_levels.erase(m_levels.length() - 1, 1);
784 if( !m_levels.empty() )
785 m_state = m_levels[m_levels.length() - 1];
786 else
787 m_state = odt_el_NULL;
788 }
789
handleText(const lChar32 * text,int len,lUInt32 flags)790 void odt_documentHandler::handleText(const lChar32 *text, int len, lUInt32 flags)
791 {
792 odx_Style* style;
793
794 switch(m_state) {
795 case odt_el_h:
796 case odt_el_p:
797 case odt_el_span:
798 style = m_context->getStyle(m_state == odt_el_span ? m_spanStyleName : m_StyleName);
799 if(style) {
800 odx_rPr rPr;
801
802 rPr.combineWith(style->get_rPr(m_context));
803 rPr.combineWith(m_context->get_rPrDefault());
804 if( rPr.isHidden() )
805 break;
806 checkParagraphStart();
807 closeStyleTags(&rPr, m_writer);
808 openStyleTags(&rPr, m_writer);
809 } else
810 checkParagraphStart();
811 m_writer->OnText(text, len, flags);
812 break;
813 case odt_el_noteCitation:
814 m_noteRefText = text;
815 m_writer->OnTagBody();
816 case odt_el_bookmarkRef:
817 case odt_el_noteRef:
818 case odt_el_referenceRef:
819 case odt_el_a:
820 m_writer->OnText(text, len, flags);
821 break;
822 default:
823 break;
824 }
825 }
826
reset()827 void odt_documentHandler::reset()
828 {
829 m_levels.clear();
830 m_ListLevels.clear();
831 m_listItems.clear();
832 m_state = odt_el_NULL;
833 m_outlineLevel = 0;
834 m_inTable = false;
835 m_inListItem = false;
836 m_listItemHadContent = false;
837 m_paragraphStarted = false;
838 }
839
addListStyle(odt_ListStyleRef listStyle)840 void odtImportContext::addListStyle(odt_ListStyleRef listStyle)
841 {
842 if( !listStyle.isNull())
843 m_ListStyles.set(listStyle->getId(), listStyle);
844 }
845
openFile(const lChar32 * const fileName)846 LVStreamRef odtImportContext::openFile(const lChar32 * const fileName)
847 {
848 LVContainerRef container = m_doc->getContainer();
849 if( !container.isNull() )
850 return container->OpenStream(fileName, LVOM_READ);
851 return LVStreamRef();
852 }
853
handleTagOpen(int tagId)854 ldomNode *odt_stylesHandler::handleTagOpen(int tagId)
855 {
856 switch(tagId) {
857 case odt_el_defaultStyle:
858 m_style = NULL;
859 break;
860 case odt_el_paragraphProperties:
861 m_pPr = m_style ? m_style->get_pPrPointer() : m_context->get_pPrDefault();
862 break;
863 case odt_el_textProperties:
864 m_rPr = m_style ? m_style->get_rPrPointer() : m_context->get_rPrDefault();
865 break;
866 case odt_el_style:
867 m_styleRef = odx_StyleRef( new odx_Style );
868 m_style = m_styleRef.get();
869 case odt_el_listStyle:
870 m_ListStyleRef = odt_ListStyleRef( new odt_ListStyle );
871 m_ListStyle = m_ListStyleRef.get();
872 break;
873 case odt_el_listLevelStyleBullet:
874 case odt_el_listLevelStyleNumber:
875 m_ListLevelStyleRef = odt_ListLevelStyleRef( new odt_ListLevelStyle );
876 m_ListLevelStyle = m_ListLevelStyleRef.get();
877 break;
878 default:
879 break;
880 }
881 m_state = tagId;
882 m_levels.add(tagId);
883 return NULL;
884 }
885
handleAttribute(const lChar32 * attrname,const lChar32 * attrvalue)886 void odt_stylesHandler::handleAttribute(const lChar32 *attrname, const lChar32 *attrvalue)
887 {
888 switch(m_state) {
889 case odt_el_style:
890 if( !lStr_cmp(attrname, "name") ) {
891 m_style->setId(attrvalue);
892 } else if( !lStr_cmp(attrname, "display-name") ) {
893 m_style->setName(attrvalue);
894 } else if( !lStr_cmp(attrname, "family") ) {
895 int n = parse_name(styleFamily_attr_values, attrvalue);
896 if(n != -1)
897 m_style->setStyleType((odx_style_type)n);
898 } else if( !lStr_cmp(attrname, "parent-style-name") ) {
899 m_style->setBasedOn(attrvalue);
900 }
901 break;
902 case odt_el_listStyle:
903 if( !lStr_cmp(attrname, "name") )
904 m_ListStyle->setId(attrvalue);
905 break;
906 case odt_el_listLevelStyleNumber:
907 if( !lStr_cmp(attrname, "num-format") ) {
908 lString32 format(attrvalue);
909
910 if( format.length() == 1) {
911 switch(attrvalue[0]) {
912 case '1':
913 m_ListLevelStyle->setLevelType(css_lst_decimal);
914 break;
915 case 'a':
916 m_ListLevelStyle->setLevelType(css_lst_lower_alpha);
917 break;
918 case 'A':
919 m_ListLevelStyle->setLevelType(css_lst_upper_alpha);
920 break;
921 case 'i':
922 m_ListLevelStyle->setLevelType(css_lst_lower_roman);
923 break;
924 case 'I':
925 m_ListLevelStyle->setLevelType(css_lst_upper_roman);
926 break;
927 default:
928 m_ListLevelStyle->setLevelType(css_lst_none);
929 break;
930 }
931 } else if( format.empty() )
932 m_ListLevelStyle->setLevelType(css_lst_none);
933 break;
934 }
935 if( !lStr_cmp(attrname, "start-value") ) {
936 int startValue;
937
938 if( lString32(attrvalue).atoi( startValue) )
939 m_ListLevelStyle->setLevelStart( startValue );
940 break;
941 }
942 case odt_el_listLevelStyleBullet:
943 if( !lStr_cmp(attrname, "level") )
944 m_ListLevelStyle->setLevel(attrvalue);
945 break;
946 case odt_el_paragraphProperties:
947 if( !lStr_cmp(attrname, "break-before") ) {
948 m_pPr->setPageBreakBefore( !lStr_cmp(attrvalue, "page") );
949 } else if( !lStr_cmp(attrname, "text-align") ) {
950 int n = parse_name(odt_textAlign_attr_values, attrvalue);
951 if(n != -1)
952 m_pPr->setTextAlign((css_text_align_t)n);
953 } else if( !lStr_cmp(attrname, "keep-with-next") ) {
954 m_pPr->setKeepNext( !lStr_cmp(attrvalue, "always") );
955 }
956 break;
957 case odt_el_textProperties:
958 if( NULL == m_style && !lStr_cmp(attrname, "language") ) {
959 m_context->setLanguage(attrvalue);
960 } else if( !lStr_cmp(attrname, "font-style") ) {
961 m_rPr->setItalic( lStr_cmp(attrvalue, "normal") !=0 );
962 } else if( !lStr_cmp(attrname, "font-weight") ) {
963 int n = parse_name(odt_fontWeigth_attr_values, attrvalue);
964 if( n != -1 )
965 m_rPr->setBold( n >= 600 );
966 } else if( !lStr_cmp(attrname, "text-underline-style") ) {
967 m_rPr->setUnderline( lStr_cmp( attrvalue, "none") !=0 );
968 } else if( !lStr_cmp(attrname, "text-line-through-type") ) {
969 m_rPr->setStrikeThrough( lStr_cmp( attrvalue, "none") !=0 );
970 } else if( !lStr_cmp(attrname, "text-position") ) {
971 lString32 val = attrvalue;
972
973 if( val.startsWith(U"super") ) {
974 m_rPr->setVertAlign(css_va_super);
975 } else if( val.startsWith(U"sub") ) {
976 m_rPr->setVertAlign(css_va_sub);
977 }
978 }
979 break;
980 default:
981 break;
982 }
983 }
984
handleTagClose(const lChar32 * nsname,const lChar32 * tagname)985 void odt_stylesHandler::handleTagClose(const lChar32 *nsname, const lChar32 *tagname)
986 {
987 CR_UNUSED2(nsname, tagname);
988
989 switch(m_state) {
990 case odt_el_listStyle:
991 m_context->addListStyle(m_ListStyleRef);
992 break;
993 case odt_el_listLevelStyleBullet:
994 case odt_el_listLevelStyleNumber:
995 m_ListStyle->addLevel(m_ListLevelStyleRef);
996 break;
997 case odt_el_style:
998 if(m_style && m_style->isValid()) {
999 m_context->addStyle(m_styleRef);
1000 }
1001 default:
1002 break;
1003 }
1004 if( !m_levels.empty() ) {
1005 m_levels.erase(m_levels.length() - 1, 1);
1006 if(! m_levels.empty() )
1007 m_state = m_levels[m_levels.length() - 1];
1008 else
1009 m_state = m_element;
1010 } else
1011 stop();
1012 }
1013
addLevel(odt_ListLevelStyleRef listLevel)1014 void odt_ListStyle::addLevel(odt_ListLevelStyleRef listLevel)
1015 {
1016 if( !listLevel.isNull() )
1017 m_levels.set( listLevel->getLevel(), listLevel );
1018 }
1019