1 #include "../include/fb2def.h"
2 #include "../include/crlog.h"
3 #include "odxutil.h"
4 
OnTagOpen(const lChar32 * nsname,const lChar32 * tagname)5 ldomNode * docXMLreader::OnTagOpen( const lChar32 * nsname, const lChar32 * tagname)
6 {
7     if ( m_state == xml_doc_in_start && !lStr_cmp(tagname, "?xml") )
8         m_state = xml_doc_in_xml_declaration;
9     else if( !isSkipping() ) {
10         if ( m_handler )
11             return m_handler->handleTagOpen(nsname, tagname);
12     } else
13         // skip nested tag
14         skip();
15     return NULL;
16 }
17 
OnStart(LVFileFormatParser *)18 void docXMLreader::OnStart(LVFileFormatParser *)
19 {
20     m_skipTag = 0;
21     m_state = xml_doc_in_start;
22 }
23 
OnTagBody()24 void docXMLreader::OnTagBody()
25 {
26     if( m_state != xml_doc_in_xml_declaration && !isSkipping() && m_handler )
27         m_handler->handleTagBody();
28 }
29 
OnTagClose(const lChar32 * nsname,const lChar32 * tagname,bool self_closing_tag)30 void docXMLreader::OnTagClose(const lChar32 * nsname, const lChar32 * tagname , bool self_closing_tag)
31 {
32     CR_UNUSED2(nsname, self_closing_tag);
33 
34     switch(m_state) {
35     case xml_doc_in_xml_declaration:
36         m_state = xml_doc_in_document;
37         break;
38     case xml_doc_in_document:
39         if( isSkipping() )
40             skipped();
41         else if ( m_handler )
42             m_handler->handleTagClose(U"", tagname);
43         break;
44     default:
45         CRLog::error("Unexpected state");
46         break;
47     }
48 }
49 
OnAttribute(const lChar32 * nsname,const lChar32 * attrname,const lChar32 * attrvalue)50 void docXMLreader::OnAttribute( const lChar32 * nsname, const lChar32 * attrname, const lChar32 * attrvalue )
51 {
52     switch(m_state) {
53     case xml_doc_in_xml_declaration:
54         if ( m_writer )
55             m_writer->OnAttribute(nsname, attrname, attrvalue);
56         break;
57     case xml_doc_in_document:
58         if ( !isSkipping() && m_handler )
59             m_handler->handleAttribute(nsname, attrname, attrvalue);
60         break;
61     default:
62         CRLog::error("Unexpected state");
63     }
64 }
65 
OnText(const lChar32 * text,int len,lUInt32 flags)66 void docXMLreader::OnText( const lChar32 * text, int len, lUInt32 flags )
67 {
68     if( !isSkipping() && m_handler )
69         m_handler->handleText(text, len, flags);
70 }
71 
OnBlob(lString32 name,const lUInt8 * data,int size)72 bool docXMLreader::OnBlob(lString32 name, const lUInt8 * data, int size)
73 {
74     if ( !isSkipping() && m_writer )
75         return m_writer->OnBlob(name, data, size);
76     return false;
77 }
78 
setChildrenInfo(const struct item_def_t * tags)79 void xml_ElementHandler::setChildrenInfo(const struct item_def_t *tags)
80 {
81     m_children = tags;
82 }
83 
parse_name(const struct item_def_t * tags,const lChar32 * nameValue)84 int xml_ElementHandler::parse_name(const struct item_def_t *tags, const lChar32 * nameValue)
85 {
86     for (int i=0; tags[i].name; i++) {
87         if ( !lStr_cmp( tags[i].name, nameValue )) {
88             // found!
89             return tags[i].id;
90         }
91     }
92     return -1;
93 }
94 
parse_int(const lChar32 * attrValue,css_length_t & result)95 void xml_ElementHandler::parse_int(const lChar32 * attrValue, css_length_t & result)
96 {
97     lString32 value = attrValue;
98 
99     result.type = css_val_unspecified;
100     if ( value.atoi(result.value) )
101         result.type = css_val_pt; //just to distinguish with unspecified value
102 }
103 
handleTagOpen(const lChar32 * nsname,const lChar32 * tagname)104 ldomNode *xml_ElementHandler::handleTagOpen(const lChar32 *nsname, const lChar32 *tagname)
105 {
106     int tag = parseTagName(tagname);
107 
108     CR_UNUSED(nsname);
109     if( -1 == tag) {
110         // skip the tag we are not interested in
111         m_reader->skip();
112         return NULL;
113     }
114     return handleTagOpen(tag);
115 }
116 
handleTagOpen(int tagId)117 ldomNode *xml_ElementHandler::handleTagOpen(int tagId)
118 {
119     m_state = tagId;
120     return NULL;
121 }
122 
start()123 void xml_ElementHandler::start()
124 {
125     m_savedHandler = m_reader->getHandler();
126     reset();
127     m_reader->setHandler(this);
128 }
129 
stop()130 void xml_ElementHandler::stop()
131 {
132     m_reader->setHandler(m_savedHandler);
133     m_savedHandler = NULL;
134 }
135 
reset()136 void xml_ElementHandler::reset()
137 {
138 }
139 
onBodyStart()140 ldomNode *odx_titleHandler::onBodyStart()
141 {
142     return m_writer->OnTagOpen(U"", U"body");
143 }
144 
onTitleStart(int level,bool noSection)145 void odx_titleHandler::onTitleStart(int level, bool noSection)
146 {
147     CR_UNUSED(noSection);
148 
149     m_titleLevel = level;
150     lString32 name = cs32("h") +  lString32::itoa(m_titleLevel);
151     if( m_useClassName ) {
152         m_writer->OnTagOpen(U"", U"p");
153         m_writer->OnAttribute(U"", U"class", name.c_str());
154     } else
155         m_writer->OnTagOpen(U"", name.c_str());
156 }
157 
onTitleEnd()158 void odx_titleHandler::onTitleEnd()
159 {
160     if( !m_useClassName ) {
161         lString32 tagName = cs32("h") +  lString32::itoa(m_titleLevel);
162         m_writer->OnTagClose(U"", tagName.c_str());
163     } else
164         m_writer->OnTagClose(U"", U"p");
165 }
166 
onBodyStart()167 ldomNode* odx_fb2TitleHandler::onBodyStart()
168 {
169     m_section = m_writer->OnTagOpen(U"", U"body");
170     return m_section;
171 }
172 
onTitleStart(int level,bool noSection)173 void odx_fb2TitleHandler::onTitleStart(int level, bool noSection)
174 {
175     if( noSection )
176         odx_titleHandler::onTitleStart(level, true);
177     else {
178         if( m_titleLevel < level ) {
179             int startIndex = m_hasTitle ? 1 : 0;
180             int contentCount = m_section->getChildCount();
181             if(contentCount > startIndex)
182                 makeSection(startIndex);
183         } else
184             closeSection(m_titleLevel - level + 1);
185         openSection(level);
186         m_writer->OnTagOpen(U"", U"title");
187         lString32 headingName = cs32("h") +  lString32::itoa(level);
188         if( m_useClassName ) {
189             m_writer->OnTagBody();
190             m_writer->OnTagOpen(U"", U"p");
191             m_writer->OnAttribute(U"", U"class", headingName.c_str());
192         } else {
193             m_writer->OnTagBody();
194             m_writer->OnTagOpen(U"", headingName.c_str());
195         }
196     }
197 }
198 
onTitleEnd()199 void odx_fb2TitleHandler::onTitleEnd()
200 {
201     if( !m_useClassName ) {
202         lString32 headingName = cs32("h") +  lString32::itoa(m_titleLevel);
203         m_writer->OnTagClose(U"", headingName.c_str());
204     } else
205         m_writer->OnTagClose(U"", U"p");
206 
207     m_writer->OnTagClose(U"", U"title");
208     m_hasTitle = true;
209 }
210 
makeSection(int startIndex)211 void odx_fb2TitleHandler::makeSection(int startIndex)
212 {
213     ldomNode *newSection = m_section->insertChildElement(startIndex, LXML_NS_NONE, el_section);
214     newSection->initNodeStyle();
215     m_section->moveItemsTo(newSection, startIndex + 1, m_section->getChildCount() - 1);
216     newSection->initNodeRendMethod( );
217     m_section = newSection;
218 }
219 
openSection(int level)220 void odx_fb2TitleHandler::openSection(int level)
221 {
222     for(int i = m_titleLevel; i < level; i++) {
223         m_section = m_writer->OnTagOpen(U"", U"section");
224         m_writer->OnTagBody();
225     }
226     m_titleLevel = level;
227     m_hasTitle = false;
228 }
229 
closeSection(int level)230 void odx_fb2TitleHandler::closeSection(int level)
231 {
232     for(int i = 0; i < level; i++) {
233         m_writer->OnTagClose(U"", U"section");
234         m_titleLevel--;
235     }
236     m_hasTitle = false;
237 }
238 
odx_rPr()239 odx_rPr::odx_rPr() : odx_StylePropertiesContainer(odx_character_style)
240 {
241 }
242 
getCss()243 lString32 odx_rPr::getCss()
244 {
245     lString32 style;
246 
247     if( isBold() )
248         style << " font-weight: bold;";
249     if( isItalic() )
250         style << " font-style: italic;";
251     if( isUnderline() )
252         style << " text-decoration: underline;";
253     if( isStrikeThrough() )
254         style << " text-decoration: line-through;";
255     return style;
256 }
257 
odx_pPr()258 odx_pPr::odx_pPr() : odx_StylePropertiesContainer(odx_paragraph_style)
259 {
260 }
261 
getCss()262 lString32 odx_pPr::getCss()
263 {
264     lString32 style;
265 
266     css_text_align_t align = getTextAlign();
267     if(align != css_ta_inherit)
268     {
269         style << "text-align: ";
270         switch(align)
271         {
272         case css_ta_left:
273             style << "left;";
274             break;
275         case css_ta_right:
276             style << "right;";
277             break;
278         case css_ta_center:
279             style << "center;";
280             break;
281         case css_ta_justify:
282             style << "justify;";
283             break;
284         case css_ta_end:
285             style << "end;";
286             break;
287         case css_ta_start:
288             style << "start;";
289             break;
290         default:
291             style << "inherited;";
292             break;
293         }
294     }
295     if( isPageBreakBefore() )
296         style << "page-break-before: always;";
297     else if ( isKeepNext() )
298         style << "page-break-before: avoid;";
299     return style;
300 }
301 
odx_Style()302 odx_Style::odx_Style() : m_type(odx_paragraph_style),
303     m_pPrMerged(false), m_rPrMerged(false)
304 {
305 }
306 
isValid() const307 bool odx_Style::isValid() const
308 {
309     return ( odx_invalid_style != m_type && !m_Id.empty() );
310 }
311 
getBaseStyle(odx_ImportContext * context)312 odx_Style *odx_Style::getBaseStyle(odx_ImportContext *context)
313 {
314     lString32 basedOn = getBasedOn();
315     if ( !basedOn.empty() ) {
316         odx_Style *pStyle = context->getStyle(basedOn);
317         if( pStyle && pStyle->getStyleType() == getStyleType() )
318             return pStyle;
319     }
320     return NULL;
321 }
322 
get_pPr(odx_ImportContext * context)323 odx_pPr *odx_Style::get_pPr(odx_ImportContext *context)
324 {
325     if( !m_pPrMerged ) {
326         odx_Style* pStyle = getBaseStyle(context);
327         if (pStyle ) {
328             m_pPr.combineWith(pStyle->get_pPr(context));
329         }
330         m_pPrMerged = true;
331     }
332     return &m_pPr;
333 }
334 
get_rPr(odx_ImportContext * context)335 odx_rPr *odx_Style::get_rPr(odx_ImportContext *context)
336 {
337     if( !m_rPrMerged ) {
338         odx_Style* pStyle = getBaseStyle(context);
339         if (pStyle ) {
340             m_rPr.combineWith(pStyle->get_rPr(context));
341         }
342         m_rPrMerged = true;
343     }
344     return &m_rPr;
345 }
346 
getStyleProperties(odx_ImportContext * context,odx_style_type styleType)347 odx_StylePropertiesGetter *odx_Style::getStyleProperties(odx_ImportContext *context, odx_style_type styleType)
348 {
349     switch(styleType) {
350     case odx_paragraph_style:
351         return get_pPr(context);
352     case odx_character_style:
353         return get_rPr(context);
354     default:
355         break;
356     }
357     return NULL;
358 }
359 
addStyle(odx_StyleRef style)360 void odx_ImportContext::addStyle(odx_StyleRef style)
361 {
362     odx_Style *referenced = style.get();
363     if ( NULL != referenced)
364     {
365         m_styles.set(referenced->getId(), style);
366     }
367 }
368 
setLanguage(const lChar32 * lang)369 void odx_ImportContext::setLanguage(const lChar32 *lang)
370 {
371     lString32 language(lang);
372 
373     int p = language.pos(cs32("-"));
374     if ( p > 0  ) {
375         language = language.substr(0, p);
376     }
377     m_doc->getProps()->setString(DOC_PROP_LANGUAGE, language);
378 }
379 
getListStyleCss(css_list_style_type_t listType)380 lString32 odx_ImportContext::getListStyleCss(css_list_style_type_t listType)
381 {
382     switch(listType) {
383     case css_lst_disc:
384         return cs32("list-style-type: disc;");
385     case css_lst_circle:
386         return cs32("list-style-type: circle;");
387     case css_lst_square:
388         return cs32("list-style-type: square;");
389     case css_lst_decimal:
390         return cs32("list-style-type: decimal;");
391     case css_lst_lower_roman:
392         return cs32("list-style-type: lower-roman;");
393     case css_lst_upper_roman:
394         return cs32("list-style-type: upper-roman;");
395     case css_lst_lower_alpha:
396         return cs32("list-style-type: lower-alpha;");
397     case css_lst_upper_alpha:
398         return cs32("list-style-type: upper-alpha;");
399     default:
400         break;
401     }
402     return cs32("list-style-type: none;");
403 }
404 
startDocument(ldomDocumentWriter & writer)405 void odx_ImportContext::startDocument(ldomDocumentWriter &writer)
406 {
407 #ifdef DOCX_FB2_DOM_STRUCTURE
408     writer.OnStart(NULL);
409     writer.OnTagOpen(NULL, U"?xml");
410     writer.OnAttribute(NULL, U"version", U"1.0");
411     writer.OnAttribute(NULL, U"encoding", U"utf-8");
412     writer.OnEncoding(U"utf-8", NULL);
413     writer.OnTagBody();
414     writer.OnTagClose(NULL, U"?xml");
415     writer.OnTagOpenNoAttr(NULL, U"FictionBook");
416     // DESCRIPTION
417     writer.OnTagOpenNoAttr(NULL, U"description");
418     writer.OnTagOpenNoAttr(NULL, U"title-info");
419     writer.OnTagOpenNoAttr(NULL, U"book-title");
420     writer.OnTagClose(NULL, U"book-title");
421     writer.OnTagClose(NULL, U"title-info");
422     writer.OnTagClose(NULL, U"description");
423 #else
424     writer.OnStart(NULL);
425     writer.OnTagOpen(NULL, U"?xml");
426     writer.OnAttribute(NULL, U"version", U"1.0");
427     writer.OnAttribute(NULL, U"encoding", U"utf-8");
428     writer.OnEncoding(U"utf-8", NULL);
429     writer.OnTagBody();
430     writer.OnTagClose(NULL, U"?xml");
431     writer.OnTagOpenNoAttr(NULL, U"html");
432 #endif
433 }
434 
endDocument(ldomDocumentWriter & writer)435 void odx_ImportContext::endDocument(ldomDocumentWriter &writer)
436 {
437 #ifdef DOCX_FB2_DOM_STRUCTURE
438     writer.OnTagClose(NULL, U"FictionBook");
439 #else
440     writer.OnTagClose(NULL, U"html");
441 #endif
442 }
443 
styleTagPos(lChar32 ch)444 int odx_styleTagsHandler::styleTagPos(lChar32 ch)
445 {
446     for (int i=0; i < m_styleTags.length(); i++)
447         if (m_styleTags[i] == ch)
448             return i;
449     return -1;
450 }
451 
getStyleTagName(lChar32 ch)452 const lChar32 *odx_styleTagsHandler::getStyleTagName(lChar32 ch)
453 {
454     switch ( ch ) {
455     case 'b':
456         return U"strong";
457     case 'i':
458         return U"em";
459     case 'u':
460         return U"u";
461     case 's':
462         return U"s";
463     case 't':
464         return U"sup";
465     case 'd':
466         return U"sub";
467     default:
468         return NULL;
469     }
470 }
471 
closeStyleTag(lChar32 ch,ldomDocumentWriter * writer)472 void odx_styleTagsHandler::closeStyleTag(lChar32 ch, ldomDocumentWriter *writer)
473 {
474     int pos = styleTagPos( ch );
475     if (pos >= 0) {
476         for (int i = m_styleTags.length() - 1; i >= pos; i--) {
477             const lChar32 * tag = getStyleTagName(m_styleTags[i]);
478             m_styleTags.erase(m_styleTags.length() - 1, 1);
479             if ( tag ) {
480                 writer->OnTagClose(U"", tag);
481             }
482         }
483     }
484 }
485 
openStyleTag(lChar32 ch,ldomDocumentWriter * writer)486 void odx_styleTagsHandler::openStyleTag(lChar32 ch, ldomDocumentWriter *writer)
487 {
488     int pos = styleTagPos( ch );
489     if (pos < 0) {
490         const lChar32 * tag = getStyleTagName(ch);
491         if ( tag ) {
492             writer->OnTagOpenNoAttr(U"", tag);
493             m_styleTags.append( 1,  ch );
494         }
495     }
496 }
497 
openStyleTags(odx_rPr * runProps,ldomDocumentWriter * writer)498 void odx_styleTagsHandler::openStyleTags(odx_rPr *runProps, ldomDocumentWriter *writer)
499 {
500     if(runProps->isBold())
501         openStyleTag('b', writer);
502     if(runProps->isItalic())
503         openStyleTag('i', writer);
504     if(runProps->isUnderline())
505         openStyleTag('u', writer);
506     if(runProps->isStrikeThrough())
507         openStyleTag('s', writer);
508     if(runProps->isSubScript())
509         openStyleTag('d', writer);
510     if(runProps->isSuperScript())
511         openStyleTag('t', writer);
512 }
513 
closeStyleTags(odx_rPr * runProps,ldomDocumentWriter * writer)514 void odx_styleTagsHandler::closeStyleTags(odx_rPr *runProps, ldomDocumentWriter *writer)
515 {
516     if(!runProps->isBold())
517         closeStyleTag('b', writer);
518     if(!runProps->isItalic())
519         closeStyleTag('i', writer);
520     if(!runProps->isUnderline())
521         closeStyleTag('u', writer);
522     if(!runProps->isStrikeThrough())
523         closeStyleTag('s', writer);
524     if(!runProps->isSubScript())
525         closeStyleTag('d', writer);
526     if(!runProps->isSuperScript())
527         closeStyleTag('t', writer);
528 }
529 
closeStyleTags(ldomDocumentWriter * writer)530 void odx_styleTagsHandler::closeStyleTags(ldomDocumentWriter *writer)
531 {
532     for(int i = m_styleTags.length() - 1; i >= 0; i--)
533         closeStyleTag(m_styleTags[i], writer);
534     m_styleTags.clear();
535 }
536