1 /* AbiSource
2  *
3  * Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
4  * Copyright (C) 2004 Robert Staudinger <robsta@stereolyzer.net>
5  * Copyright (C) 2005 Daniel d'Andrada T. de Carvalho
6  * <daniel.carvalho@indt.org.br>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21  * 02110-1301 USA.
22  */
23 
24 
25 // Class definition include
26 #include "ODi_Style_Style.h"
27 
28 // Internal includes
29 #include "ODi_FontFaceDecls.h"
30 #include "ODi_ListenerStateAction.h"
31 #include "ODi_ElementStack.h"
32 #include "ODi_StartTag.h"
33 #include "ODi_Abi_Data.h"
34 
35 // AbiWord includes
36 #include <pd_Document.h>
37 #include <ut_math.h>
38 #include <ut_locale.h>
39 #include <ut_std_string.h>
40 
41 // External includes
42 #include <ctype.h>
43 
44 
45 /**
46  * Constructor
47  */
ODi_Style_Style(ODi_ElementStack & rElementStack,ODi_Abi_Data & rAbiData)48 ODi_Style_Style::ODi_Style_Style(ODi_ElementStack& rElementStack,
49 				  ODi_Abi_Data & rAbiData) :
50                        ODi_ListenerState("StyleStyle", rElementStack),
51                        m_pParentStyle(NULL),
52                        m_pNextStyle(NULL),
53                        m_haveTopBorder(HAVE_BORDER_UNSPECIFIED),
54                        m_haveBottomBorder(HAVE_BORDER_UNSPECIFIED),
55                        m_haveLeftBorder(HAVE_BORDER_UNSPECIFIED),
56                        m_haveRightBorder(HAVE_BORDER_UNSPECIFIED),
57 		       m_rAbiData(rAbiData)
58 {
59     if (rElementStack.hasElement("office:automatic-styles")) {
60         m_bAutomatic = true;
61     } else {
62         m_bAutomatic = false;
63     }
64 }
65 
66 
67 /**
68  *
69  */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction &)70 void ODi_Style_Style::startElement(const gchar* pName,
71                                   const gchar** ppAtts,
72 								   ODi_ListenerStateAction& /*rAction*/)
73 {
74 
75     if (!strcmp("style:style", pName)) {
76 
77         _parse_style_style(ppAtts);
78 
79     } else if (!strcmp("style:paragraph-properties", pName)) {
80 
81         _parse_style_paragraphProperties(ppAtts);
82 
83     } else if (!strcmp("style:tab-stop", pName)) {
84 
85         if (m_rElementStack.getStackSize() >= 2 &&
86             !strcmp(m_rElementStack.getStartTag(1)->getName(), "style:paragraph-properties") &&
87             !strcmp(m_rElementStack.getStartTag(0)->getName(), "style:tab-stops")) {
88 
89             _parse_style_tabStopProperties(ppAtts);
90 
91         } else {
92 
93             // we only know tabstops inside paragraphs
94             UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
95 
96         }
97 
98     } else if (!strcmp("style:text-properties", pName)) {
99 
100         _parse_style_textProperties(ppAtts);
101 
102     } else if (!strcmp("style:section-properties", pName)) {
103 
104         _parse_style_sectionProperties(ppAtts);
105 
106     } else if (!strcmp("style:graphic-properties", pName)) {
107 
108         _parse_style_graphicProperties(ppAtts);
109 
110     } else if (!strcmp("style:table-properties", pName)) {
111 
112         _parse_style_tableProperties(ppAtts);
113 
114     } else if (!strcmp("style:table-column-properties", pName)) {
115 
116         _parse_style_tableColumnProperties(ppAtts);
117 
118     } else if (!strcmp("style:table-row-properties", pName)) {
119 
120         _parse_style_tableRowProperties(ppAtts);
121 
122     } else if (!strcmp("style:table-cell-properties", pName)) {
123 
124         _parse_style_tableCellProperties(ppAtts);
125 
126     } else if (!strcmp("style:background-image", pName)) {
127 
128         _parse_style_background_image(ppAtts);
129 
130     } else if (!strcmp("style:default-style", pName)) {
131 
132         const gchar* pAttr;
133 
134         pAttr = UT_getAttribute("style:family", ppAtts);
135         UT_ASSERT(pAttr);
136         m_family = pAttr;
137 
138         // In AbiWord, the default style is called "Normal"
139         m_displayName = m_name = "Normal";
140         m_parentStyleName = "None";
141 
142     } else if (!strcmp("style:columns", pName)) {
143 
144         const gchar* pVal;
145 
146         pVal = UT_getAttribute("fo:column-count", ppAtts);
147         if (pVal) {
148             // A column count of "0" (zero) crashes AbiWord.
149             // Instead we just leave the column count empty.
150             if (atoi(pVal) > 0) {
151                 m_columns = pVal;
152             }
153         }
154 
155         pVal = UT_getAttribute("fo:column-gap", ppAtts);
156         if (pVal) {
157             m_columnGap = pVal;
158         }
159     }
160 
161 }
162 
163 
164 
165 
166 
167 /**
168  *
169  */
endElement(const gchar * pName,ODi_ListenerStateAction & rAction)170 void ODi_Style_Style::endElement(const gchar* pName,
171                                 ODi_ListenerStateAction& rAction) {
172 
173     if (!strcmp("style:style", pName)) {
174         rAction.popState();
175 
176     } else if (!strcmp("style:default-style", pName)) {
177         // I'm a default style.
178         rAction.popState();
179     }
180 }
181 
182 
183 
184 
185 
186 /**
187  *
188  */
_parse_style_style(const gchar ** ppAtts)189 void ODi_Style_Style::_parse_style_style(const gchar** ppAtts) {
190 
191     const gchar* pAttr;
192 
193     if (m_name.empty()) {
194         pAttr = UT_getAttribute("style:name", ppAtts);
195         UT_ASSERT(pAttr);
196         m_name = pAttr;
197     }
198 
199     pAttr = UT_getAttribute("style:family", ppAtts);
200     UT_ASSERT(pAttr);
201     m_family = pAttr;
202 
203     if (m_displayName.empty()) {
204         pAttr = UT_getAttribute("style:display-name", ppAtts);
205         if (pAttr) {
206             m_displayName = pAttr;
207         } else {
208             m_displayName = m_name;
209         }
210     }
211 
212     pAttr = UT_getAttribute("style:parent-style-name", ppAtts);
213     if (pAttr) {
214         m_parentStyleName = pAttr;
215     } else {
216         m_parentStyleName.clear();
217     }
218 
219     pAttr = UT_getAttribute("style:next-style-name", ppAtts);
220     if (pAttr) {
221         m_nextStyleName = pAttr;
222     } else {
223         m_nextStyleName = m_name;
224     }
225 
226     pAttr = UT_getAttribute("style:list-style-name", ppAtts);
227     if (pAttr) {
228         m_listStyleName = pAttr;
229     } else {
230         m_listStyleName.clear();
231     }
232 
233     pAttr = UT_getAttribute("style:master-page-name", ppAtts);
234     if (pAttr) {
235         m_masterPageName = pAttr;
236     } else {
237         m_masterPageName.clear();
238     }
239 }
240 
241 
242 
243 
244 
245 /**
246  *
247  */
_parse_style_paragraphProperties(const gchar ** ppProps)248 void ODi_Style_Style::_parse_style_paragraphProperties(const gchar** ppProps) {
249 
250     const gchar* pVal;
251 
252     pVal = UT_getAttribute ("style:line-height-at-least", ppProps);
253     if (pVal) {
254         m_lineHeight = UT_std_string_sprintf ("%s+", pVal);
255     }
256 
257     pVal = UT_getAttribute ("fo:line-height", ppProps);
258     if (pVal) {
259         if (strstr(pVal, "%") != NULL) {
260             int spacing;
261 	    UT_LocaleTransactor lt(LC_NUMERIC, "C");
262 
263             sscanf(pVal, "%d%%", &spacing);
264             m_lineHeight = UT_std_string_sprintf ("%f",
265                                               (double)spacing/100.);
266         } else {
267             m_lineHeight.assign(pVal);
268         }
269     }
270 
271     pVal = UT_getAttribute ("fo:text-align", ppProps);
272     if (pVal) {
273         if (!strcmp(pVal, "end")) {
274             m_align = "right";
275         } else if (!strcmp(pVal, "center")) {
276             m_align = "center";
277         } else if (!strcmp(pVal, "justify")) {
278             m_align = "justify";
279         } else {
280             m_align = "left";
281         }
282     }
283 
284     pVal = UT_getAttribute ("fo:break-after", ppProps);
285     if (pVal) {
286         m_breakAfter.assign(pVal);
287     }
288 
289     pVal = UT_getAttribute("fo:widows", ppProps);
290     if (pVal) {
291         int widows = 0;
292         sscanf(pVal, "%d", &widows);
293         m_widows = UT_std_string_sprintf ("%d", widows);
294     }
295 
296     pVal = UT_getAttribute("fo:orphans", ppProps);
297     if (pVal) {
298         int orphans = 0;
299         sscanf (pVal, "%d", &orphans);
300         m_orphans = UT_std_string_sprintf ("%d", orphans);
301     }
302 
303     pVal = UT_getAttribute ("fo:margin-left", ppProps);
304     if(pVal) {
305         m_marginLeft.assign(pVal);
306     }
307 
308     pVal = UT_getAttribute ("fo:margin-right", ppProps);
309     if(pVal) {
310         m_marginRight.assign(pVal);
311     }
312 
313     pVal = UT_getAttribute ("fo:margin-top", ppProps);
314     if(pVal) {
315         m_marginTop.assign(pVal);
316     }
317 
318     pVal = UT_getAttribute ("fo:margin-bottom", ppProps);
319     if(pVal) {
320         m_marginBottom.assign(pVal);
321     }
322 
323     pVal = UT_getAttribute ("fo:break-before", ppProps);
324     if (pVal) {
325         m_breakBefore = pVal;
326     }
327 
328     pVal = UT_getAttribute ("fo:background-color", ppProps);
329     if(pVal) {
330         m_bgcolor.assign(pVal);
331     }
332 
333     pVal = UT_getAttribute("fo:keep-with-next", ppProps);
334     if (pVal) {
335         if ( !strcmp(pVal, "always") ) {
336             m_keepWithNext = "yes";
337         } else {
338             m_keepWithNext.clear();
339         }
340     }
341 
342     pVal = UT_getAttribute("fo:text-indent", ppProps);
343     if (pVal) {
344         m_textIndent = pVal;
345     }
346 
347     pVal = UT_getAttribute("style:writing-mode", ppProps);
348     if (pVal) {
349         if(!strcmp(pVal,"rl") || !strcmp(pVal,"rl-tb") || !strcmp(pVal,"tb-rl")) {
350             m_direction = "rtl";
351         } else {
352             m_direction = "ltr";
353         }
354     }
355 
356     // If "fo:border" is defined, its value will fill all "fo:border-*"
357     pVal = UT_getAttribute("fo:border", ppProps);
358     if (pVal) {
359         _stripColorLength(m_borderTop_color, m_borderTop_thickness,
360                           m_haveTopBorder, pVal);
361 
362         m_borderBottom_color = m_borderTop_color;
363         m_borderBottom_thickness = m_borderTop_thickness;
364         m_haveBottomBorder = m_haveTopBorder;
365 
366         m_borderLeft_color = m_borderTop_color;
367         m_borderLeft_thickness = m_borderTop_thickness;
368         m_haveLeftBorder = m_haveTopBorder;
369 
370         m_borderRight_color = m_borderTop_color;
371         m_borderRight_thickness = m_borderTop_thickness;
372         m_haveRightBorder = m_haveTopBorder;
373 
374     } else {
375         pVal = UT_getAttribute("fo:border-top", ppProps);
376         if (pVal) {
377             _stripColorLength(m_borderTop_color, m_borderTop_thickness,
378                               m_haveTopBorder, pVal);
379         }
380 
381         pVal = UT_getAttribute("fo:border-bottom", ppProps);
382         if (pVal) {
383             _stripColorLength(m_borderBottom_color, m_borderBottom_thickness,
384                               m_haveBottomBorder, pVal);
385         }
386 
387         pVal = UT_getAttribute("fo:border-left", ppProps);
388         if (pVal) {
389             _stripColorLength(m_borderLeft_color, m_borderLeft_thickness,
390                               m_haveLeftBorder, pVal);
391         }
392 
393         pVal = UT_getAttribute("fo:border-right", ppProps);
394         if (pVal) {
395             _stripColorLength(m_borderRight_color, m_borderRight_thickness,
396                               m_haveRightBorder, pVal);
397         }
398     }
399     pVal=  UT_getAttribute("style:join-border", ppProps);
400     m_mergeBorders.clear();
401     if(pVal)
402     {
403 	m_mergeBorders = pVal;
404     }
405     // If "fo:padding" is defined, its value will fill all "fo:padding-*"
406     pVal = UT_getAttribute("fo:padding", ppProps);
407     if (pVal)
408     {
409          m_paddingLeft = pVal;
410 	 m_paddingRight = pVal;
411 	 m_paddingTop = pVal;
412 	 m_paddingBot = pVal;
413     }
414     else
415     {
416          pVal = UT_getAttribute("fo:padding-left", ppProps);
417 	 if(pVal)
418 	 {
419 	     m_paddingLeft = pVal;
420 	 }
421          pVal = UT_getAttribute("fo:padding-right", ppProps);
422 	 if(pVal)
423 	 {
424 	     m_paddingRight = pVal;
425 	 }
426          pVal = UT_getAttribute("fo:padding-top", ppProps);
427 	 if(pVal)
428 	 {
429 	     m_paddingTop = pVal;
430 	 }
431          pVal = UT_getAttribute("fo:padding-bot", ppProps);
432 	 if(pVal)
433 	 {
434 	     m_paddingBot = pVal;
435 	 }
436     }
437 
438     // style:tab-stop-distance
439     pVal = UT_getAttribute("style:tab-stop-distance", ppProps);
440 	if (pVal) {
441         m_defaultTabInterval = pVal;
442     }
443 }
444 
445 
446 
447 
448 /**
449  * <style:tab-stop />
450  */
_parse_style_tabStopProperties(const gchar ** ppProps)451 void ODi_Style_Style::_parse_style_tabStopProperties(const gchar** ppProps) {
452 
453     const gchar* pVal = NULL;
454     std::string type;
455     std::string position;
456     std::string leaderStyle;
457     std::string leaderText;
458 
459     pVal = UT_getAttribute("style:type", ppProps);
460     if (pVal) {
461         type = pVal;
462     }
463 
464     pVal = UT_getAttribute("style:position", ppProps);
465     if (pVal) {
466         position = pVal;
467     }
468 
469     pVal = UT_getAttribute("style:leader-style", ppProps);
470     if (pVal) {
471         leaderStyle = pVal;
472     }
473 
474     pVal = UT_getAttribute("style:leader-text", ppProps);
475     if (pVal) {
476         leaderText = pVal;
477     }
478 
479     pVal = UT_getAttribute("style:char", ppProps);
480     if (pVal) {
481         // We ignore the "style:char" attribute if it exists, as abiword has no
482         // equivalent: it will always use the locale-defined decimal point as the
483         // decimal tab character. See fp_Line::_calculateWidthOfRun() for details.
484     }
485 
486     // convert the tab information into an AbiWord property value
487 
488     UT_return_if_fail(!position.empty()); // a tab position is required (at least for AbiWord)
489 
490     if (!m_tabStops.empty())
491         m_tabStops += ",";
492 
493     // tab position
494     m_tabStops += position;
495     m_tabStops += "/";
496 
497     // tab type
498     if (type == "left") {
499         m_tabStops += "L";
500     } else if (type == "center") {
501         m_tabStops += "C";
502     } else if (type == "right") {
503         m_tabStops += "R";
504     } else if (type == "char") {
505         m_tabStops += "D";
506     } else {
507         m_tabStops += "L";
508     }
509 
510     // tab leader style: AbiWord's 4 tab styles map not to ODF's leader-styles but
511     // to leader-text's, with style 1 mapping to character ".", 2 to "-" and 3 to "_".
512     // AbiWord tab style 0 means no leader style. ODF's leader-styles denoting a
513     // line style are not supported.
514     //
515     // NOTE: in ODF, leader text (if present) *always* has a higher priority than
516     // leader styles, even if the text character is not supported.
517     if (!leaderText.empty()) {
518         UT_UCS4String leaderTextUCS4 = leaderText;
519         UT_UCS4Char ucs4char = leaderTextUCS4[0];
520 
521         switch (ucs4char) {
522             case '.':
523                 m_tabStops += "1";
524                 break;
525             case '-':
526                 m_tabStops += "2";
527                 break;
528             case '_':
529                 m_tabStops += "3";
530                 break;
531             default:
532                 m_tabStops += "0";
533                 break;
534         }
535     } else if (!leaderStyle.empty()) {
536 
537         // AbiWord does not really support leader-styles, so do a best effort conversion.
538         // Note: leader-styles describe the *underlining* line style. This means that we
539         //       won't map "dash" for example to AbiWord's tab style "2" (dashed), as that
540         //       does not represent underlining.
541 
542         if (leaderStyle == "none") {
543 		     m_tabStops += "0";
544         } else if (leaderStyle == "solid") {
545 		     m_tabStops += "3";
546         } else if (leaderStyle == "dotted") {
547 	    	m_tabStops += "1";
548         } else if (leaderStyle == "dash" || leaderStyle == "long-dash" ||
549                    leaderStyle == "dot-dash" || leaderStyle == "dot-dot-dash" ||
550                    leaderStyle == "wave") {
551             //
552 	    	m_tabStops += "3";
553         } else {
554              m_tabStops += "0";
555         }
556     } else {
557         // fall back to style "none"
558         m_tabStops += "0";
559     }
560 }
561 
562 
563 
564 
565 
566 /**
567  * <style:text-properties />
568  */
_parse_style_textProperties(const gchar ** ppProps)569 void ODi_Style_Style::_parse_style_textProperties(const gchar** ppProps) {
570 
571     const gchar* pVal = NULL;
572     const gchar* pVal2 = NULL;
573 
574     pVal = UT_getAttribute("fo:color", ppProps);
575     if (pVal) {
576         m_color.assign(pVal);
577     }
578 
579     const gchar* undrStyle = UT_getAttribute("style:text-underline-style", ppProps);
580     const gchar* undrType = UT_getAttribute("style:text-underline-type", ppProps);
581 
582     if ((undrStyle && (strcmp(undrStyle, "none") != 0)) ||
583         (undrType && (strcmp(undrType, "none") != 0))) {
584 
585         m_textDecoration += "underline";
586     }
587 
588     const gchar* ovrStyle = UT_getAttribute("style:text-overline-style", ppProps);
589     const gchar* ovrType = UT_getAttribute("style:text-overline-type", ppProps);
590 
591     if ((ovrStyle && (strcmp(ovrStyle, "none") != 0)) ||
592         (ovrType && (strcmp(ovrType, "none") != 0))) {
593 
594         if(!m_textDecoration.empty())
595             m_textDecoration += " "; //separate the props with a space, not a comma
596 
597         m_textDecoration += "overline";
598     }
599 
600     const gchar* strkStyle = UT_getAttribute("style:text-line-through-style", ppProps);
601     const gchar* strkType = UT_getAttribute("style:text-line-through-type", ppProps);
602 
603     if ((strkStyle && (strcmp(strkStyle, "none") != 0)) ||
604         (strkType && (strcmp(strkType, "none") != 0))) {
605 
606         if(!m_textDecoration.empty())
607             m_textDecoration += " "; //separate the props with a space, not a comma
608 
609         m_textDecoration += "line-through";
610     }
611 
612     pVal = UT_getAttribute("style:text-position", ppProps);
613     if(pVal) {
614         int position = 0;
615 
616         if (strstr(pVal, "sub") || strstr(pVal, "-"))
617             m_textPos = "subscript";
618         else if (strstr(pVal, "super") || ((sscanf(pVal, "%d%%", &position) == 1) && (position > 0)))
619             m_textPos = "superscript";
620         else
621             m_textPos = "normal";
622     }
623 
624     pVal = UT_getAttribute("style:font-name", ppProps);
625     if(!pVal) {
626       pVal = UT_getAttribute("fo:font-family", ppProps);
627     }
628 
629     if(pVal) {
630         m_fontName.assign(pVal);
631     }
632 
633 
634     pVal = UT_getAttribute("fo:font-size", ppProps);
635     if(pVal) {
636         m_fontSize.assign(pVal);
637     }
638 
639     pVal = UT_getAttribute("fo:language", ppProps);
640     pVal2 = UT_getAttribute("fo:country", ppProps);
641     if ( pVal && pVal2 ) {
642 
643         if (!strcmp(pVal, "none") && !strcmp(pVal2, "none")) {
644             // AbiWord uses "-none-" instead of "none-none";
645             m_lang = "-none-";
646         } else {
647             m_lang = UT_std_string_sprintf ("%s-%s", pVal, pVal2);
648         }
649     }
650 
651 
652     pVal = UT_getAttribute("fo:font-style", ppProps);
653     if (pVal) {
654         if (!strcmp(pVal, "italic") || !strcmp(pVal, "normal")) {
655             m_fontStyle = pVal;
656         }
657     }
658 
659 
660     pVal = UT_getAttribute ("fo:font-weight", ppProps);
661     if(pVal) {
662         if (!strcmp(pVal, "bold")) {
663             m_fontWeight = "bold";
664         } else {
665             m_fontWeight = "normal";
666         }
667     }
668 
669     // Note (for testing interoperability): OO.org doesn't correctly support hidden text:
670     // http://qa.openoffice.org/issues/show_bug.cgi?id=64237 [Fixed in OO.org 3.0]
671 
672     pVal = UT_getAttribute ("text:display", ppProps);
673     if(pVal) {
674         if (!strcmp(pVal, "none")) {
675             m_display = pVal;
676         }
677     }
678 
679     pVal = UT_getAttribute ("fo:background-color", ppProps);
680     if(pVal) {
681         m_bgcolor.assign(pVal);
682     }
683 
684     pVal = UT_getAttribute ("fo:text-transform", ppProps);
685     if(pVal && *pVal &&
686        (!strcmp(pVal, "none") || !strcmp(pVal, "lowercase") ||
687         !strcmp(pVal, "uppercase") || !strcmp(pVal, "capitalize"))) {
688 
689         m_transform = pVal;
690     }
691 }
692 
693 
694 
695 
696 
697 /**
698  *
699  */
_parse_style_sectionProperties(const gchar ** ppProps)700 void ODi_Style_Style::_parse_style_sectionProperties(const gchar** ppProps) {
701 
702     const gchar* pVal;
703 
704     pVal = UT_getAttribute("fo:column-count", ppProps);
705     if (pVal) {
706         int columns = 0;
707         sscanf (pVal, "%d", &columns);
708 
709         m_columns = UT_std_string_sprintf ("%d", columns);
710     }
711 }
712 
713 
714 /**
715  * <style:graphic-properties />
716  */
_parse_style_graphicProperties(const gchar ** ppProps)717 void ODi_Style_Style::_parse_style_graphicProperties(const gchar** ppProps) {
718     const gchar* pVal;
719 
720     pVal = UT_getAttribute("style:wrap", ppProps);
721     if (pVal) {
722         m_wrap = pVal;
723     }
724 
725     pVal = UT_getAttribute("style:horizontal-rel", ppProps);
726     if (pVal) {
727         m_HorizRel = pVal;
728     }
729 
730 
731     pVal = UT_getAttribute("style:horizontal-pos", ppProps);
732     if (pVal) {
733         m_HorizPos = pVal;
734     }
735 
736 
737     pVal = UT_getAttribute("style:vertical-rel", ppProps);
738     if (pVal) {
739         m_VerticalRel = pVal;
740     }
741 
742 
743     pVal = UT_getAttribute("style:vertical-pos", ppProps);
744     if (pVal) {
745         m_VerticalPos = pVal;
746     }
747 
748     pVal = UT_getAttribute("style:parent-style-name", ppProps);
749     if (pVal && *pVal) {
750         m_parentStyleName = pVal;
751     }
752 
753     pVal = UT_getAttribute("fo:border-top", ppProps);
754     if (pVal) {
755         _stripColorLength(m_borderTop_color, m_borderTop_thickness, m_haveTopBorder, pVal);
756     }
757 
758     pVal = UT_getAttribute("fo:border-bottom", ppProps);
759     if (pVal) {
760         _stripColorLength(m_borderBottom_color, m_borderBottom_thickness, m_haveBottomBorder, pVal);
761     }
762 
763     pVal = UT_getAttribute("fo:border-left", ppProps);
764     if (pVal) {
765         _stripColorLength(m_borderLeft_color, m_borderLeft_thickness, m_haveLeftBorder, pVal);
766     }
767 
768     pVal = UT_getAttribute("fo:border-right", ppProps);
769     if (pVal) {
770         _stripColorLength(m_borderRight_color, m_borderRight_thickness, m_haveRightBorder, pVal);
771     }
772 
773     pVal = UT_getAttribute("fo:background-color", ppProps);
774     if (pVal) {
775         m_backgroundColor = pVal;
776     }
777 }
778 
779 
780 /**
781  * <style:table-properties />
782  */
_parse_style_tableProperties(const gchar ** ppProps)783 void ODi_Style_Style::_parse_style_tableProperties(const gchar** ppProps) {
784     const gchar* pVal;
785 
786     pVal = UT_getAttribute("fo:background-color", ppProps);
787     if (pVal) {
788         m_backgroundColor = pVal;
789     }
790 
791     pVal = UT_getAttribute("fo:margin-left",ppProps);
792     if (pVal) {
793         m_TableMarginLeft = pVal;
794     }
795 
796     pVal = UT_getAttribute("fo:margin-right",ppProps);
797     if (pVal) {
798         m_TableMarginRight = pVal;
799     }
800 
801     pVal = UT_getAttribute("style:width",ppProps);
802     if (pVal) {
803         m_TableWidth = pVal;
804     }
805 
806     pVal = UT_getAttribute("style:rel-width",ppProps);
807     if (pVal) {
808         m_TableRelWidth = pVal;
809     }
810 }
811 
812 
813 /**
814  * <style:table-column-properties />
815  */
_parse_style_tableColumnProperties(const gchar ** ppProps)816 void ODi_Style_Style::_parse_style_tableColumnProperties(const gchar** ppProps) {
817     const gchar* pVal;
818 
819     pVal = UT_getAttribute("style:column-width", ppProps);
820     if (pVal) {
821         m_columnWidth = pVal;
822     }
823 
824     pVal = UT_getAttribute("style:rel-column-width", ppProps);
825     if (pVal) {
826         m_columnRelWidth = pVal;
827 	UT_DEBUGMSG(("style %p name %s style:rel-column-width found with %s \n",this, m_name.c_str(), pVal));
828     }
829 }
830 
831 
832 /**
833  * <style:table-row-properties />
834  */
_parse_style_tableRowProperties(const gchar ** ppProps)835 void ODi_Style_Style::_parse_style_tableRowProperties(const gchar** ppProps) {
836     const gchar* pVal;
837 
838     pVal = UT_getAttribute("style:min-row-height", ppProps);
839     if (pVal) {
840         m_minRowHeight = pVal;
841     }
842 
843     pVal = UT_getAttribute("style:row-height", ppProps);
844     if (pVal) {
845         m_rowHeight = pVal;
846     }
847 }
848 
849 
850 /**
851  * <style:background-image />
852  */
_parse_style_background_image(const gchar ** ppProps)853 void ODi_Style_Style::_parse_style_background_image(const gchar** ppProps)
854 {
855     const gchar* pVal;
856 
857     // only implement link:href for now
858     pVal = UT_getAttribute("xlink:href", ppProps);
859     if (pVal)
860     {
861          UT_String dataId; // id of the data item that contains the image.
862 	 if(!m_rAbiData.addImageDataItem(dataId, ppProps))
863 	 {
864 	     UT_DEBUGMSG(("ODT import: no suitable image importer found\n"));
865 	     return;
866 	 }
867 	 m_backgroundImageID = dataId.c_str();
868     }
869 }
870 
871 
872 /**
873  * <style:table-cell-properties />
874  */
_parse_style_tableCellProperties(const gchar ** ppProps)875 void ODi_Style_Style::_parse_style_tableCellProperties(const gchar** ppProps) {
876     const gchar* pVal;
877 
878     // If "fo:border" is defined, its value will fill all "fo:border-*"
879     pVal = UT_getAttribute("fo:border", ppProps);
880     if (pVal) {
881         _stripColorLength(m_borderTop_color, m_borderTop_thickness,
882                           m_haveTopBorder, pVal);
883 
884         m_borderBottom_color = m_borderTop_color;
885         m_borderBottom_thickness = m_borderTop_thickness;
886         m_haveBottomBorder = m_haveTopBorder;
887 
888         m_borderLeft_color = m_borderTop_color;
889         m_borderLeft_thickness = m_borderTop_thickness;
890         m_haveLeftBorder = m_haveTopBorder;
891 
892         m_borderRight_color = m_borderTop_color;
893         m_borderRight_thickness = m_borderTop_thickness;
894         m_haveRightBorder = m_haveTopBorder;
895 
896     } else {
897         pVal = UT_getAttribute("fo:border-top", ppProps);
898         if (pVal) {
899             _stripColorLength(m_borderTop_color, m_borderTop_thickness,
900                               m_haveTopBorder, pVal);
901         }
902 
903         pVal = UT_getAttribute("fo:border-bottom", ppProps);
904         if (pVal) {
905             _stripColorLength(m_borderBottom_color, m_borderBottom_thickness,
906                               m_haveBottomBorder, pVal);
907         }
908 
909         pVal = UT_getAttribute("fo:border-left", ppProps);
910         if (pVal) {
911             _stripColorLength(m_borderLeft_color, m_borderLeft_thickness,
912                               m_haveLeftBorder, pVal);
913         }
914 
915         pVal = UT_getAttribute("fo:border-right", ppProps);
916         if (pVal) {
917             _stripColorLength(m_borderRight_color, m_borderRight_thickness,
918                               m_haveRightBorder, pVal);
919         }
920     }
921 
922 
923     pVal = UT_getAttribute("fo:background-color", ppProps);
924     if (pVal) {
925         m_backgroundColor = pVal;
926     }
927 
928     pVal = UT_getAttribute("style:vertical-align", ppProps);
929     if(pVal) {
930 	m_VerticalAlign = pVal;
931     }
932 }
933 
934 
935 /**
936  * Defines an AbiWord style that is equivalent to this
937  * OpenDocument style.
938  *
939  * @param pDocument The AbiWord document on which the style will be defined.
940  */
defineAbiStyle(PD_Document * pDocument)941 void ODi_Style_Style::defineAbiStyle(PD_Document* pDocument) {
942 
943     if (m_bAutomatic) {
944         // Automatic styles must be invisible to the user.
945         // That's (in other words) is what the OpenDocument standard says.
946         // They are created for the sake of organization on the OpenDocument file.
947         //
948         // When they are referenced by the OpenDocument content, on AbiWord
949         // their properties are just pasted into the text element.
950         //
951         // So, invisibility means that the user can't see this style
952         // on the styles list. In fact, on the AbiWord document, it doesn't
953         // even exist.
954         return;
955     }
956 
957     if (m_family == "graphic") {
958         // AbiWord don't have graphic styles.
959         return;
960     }
961 
962     /*
963      *   An array of strings (array of array of chars)
964      *
965      *         e.g.:
966      *           a[0] = "type"
967      *           a[1] = "P"
968      *           a[2] = "name"
969      *           a[3] = "Subtitle"
970      *           a[4] = "props"
971      *           a[5] = "text-indent:0in; margin-top:0pt; margin-left:0pt; ..."
972      *           ...
973      *           a[n] = 0 (NULL character)
974      */
975     const gchar* pAttr[11];
976     UT_uint32 i = 0;
977     bool ok;
978 
979     pAttr[i++] = "type";
980     if (!strcmp("paragraph", m_family.c_str())) {
981         pAttr[i++] = "P";
982     } else if (!strcmp("text", m_family.c_str())) {
983         pAttr[i++] = "C";
984     } else {
985         // Really shouldn't happen
986         UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
987     }
988 
989     // AbiWord uses the display name
990     pAttr[i++] = "name";
991     pAttr[i++] = m_displayName.c_str();
992 
993     if (m_pParentStyle) {
994         pAttr[i++] = "basedon";
995         pAttr[i++] = m_pParentStyle->getDisplayName().c_str();
996     }
997 
998     if (m_pNextStyle) {
999         pAttr[i++] = "followedby";
1000         pAttr[i++] = m_pNextStyle->getDisplayName().c_str();
1001     }
1002 
1003 
1004     pAttr[i++] = "props";
1005     pAttr[i++] = m_abiPropsAttr.c_str();
1006 
1007     pAttr[i] = 0; // Signal the end of the array
1008 
1009     ok = pDocument->appendStyle(pAttr);
1010     UT_ASSERT_HARMLESS(ok);
1011 }
1012 
1013 
1014 
1015 
1016 
1017 /**
1018  * Builds the AbiWord "props" attribute value that describes this
1019  * Style.
1020  */
buildAbiPropsAttrString(ODi_FontFaceDecls & rFontFaceDecls)1021 void ODi_Style_Style::buildAbiPropsAttrString(ODi_FontFaceDecls& rFontFaceDecls) {
1022 
1023     if (!m_fontSize.empty()) {
1024         UT_Dimension dim = UT_determineDimension(m_fontSize.c_str(), DIM_none);
1025 
1026         if (dim == DIM_PERCENT && !m_pParentStyle) {
1027 
1028             UT_DEBUGMSG(("*** [OpenDocument] no parent style to resolve '%s'\n",
1029                 m_fontSize.c_str()));
1030 
1031             UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
1032 
1033             // Ignore its value then
1034             m_fontSize.clear();
1035 
1036         } else if (dim == DIM_PERCENT && m_pParentStyle) {
1037             // calculate font-size based on parent's
1038             double fontSize = 12;
1039 	    UT_LocaleTransactor lt(LC_NUMERIC, "C");
1040 
1041             if (m_pParentStyle->m_fontSize.size()) {
1042                 fontSize = atoi(m_pParentStyle->m_fontSize.c_str()) *
1043                            atoi(m_fontSize.c_str()) / 100.0;
1044             } else {
1045                 UT_DEBUGMSG(
1046                     ("*** [OpenDocument] using fallback font-size '%f'\n",
1047                      fontSize));
1048             }
1049             m_fontSize = UT_std_string_sprintf ("%gpt", rint(fontSize));
1050         }
1051     }
1052 
1053     m_abiPropsAttr.clear();
1054 
1055 #define APPEND_STYLE(styName, styValue) if (styValue.size()) { \
1056                                             if(m_abiPropsAttr.size()) { \
1057                                                 m_abiPropsAttr += ";"; \
1058                                             } \
1059                                             m_abiPropsAttr += styName; \
1060                                             m_abiPropsAttr += styValue; \
1061                                         }
1062 
1063     // <style:paragraph-properties>
1064     APPEND_STYLE("line-height: ", m_lineHeight);
1065     APPEND_STYLE("text-align: ", m_align);
1066     APPEND_STYLE("widows: ", m_widows);
1067     APPEND_STYLE("orphans: ", m_orphans);
1068     APPEND_STYLE("margin-left: ", m_marginLeft);
1069     APPEND_STYLE("margin-right: ", m_marginRight);
1070     APPEND_STYLE("margin-top: ", m_marginTop);
1071     APPEND_STYLE("margin-bottom: ", m_marginBottom);
1072     if(m_bgcolor.size() > 0)
1073     {
1074         std::string sPat("1");
1075         APPEND_STYLE("shading-pattern: ",sPat);
1076         APPEND_STYLE("shading-foreground-color: ",m_bgcolor);
1077     }
1078     APPEND_STYLE("bgcolor: ", m_bgcolor);
1079     APPEND_STYLE("keep-with-next: ", m_keepWithNext);
1080     APPEND_STYLE("text-indent: ", m_textIndent);
1081     APPEND_STYLE("dom-dir: ", m_direction);
1082     APPEND_STYLE("default-tab-interval: ", m_defaultTabInterval);
1083     APPEND_STYLE("tabstops: ", m_tabStops)
1084 
1085     APPEND_STYLE("bot-space: ", m_paddingBot);
1086     if(m_haveBottomBorder == HAVE_BORDER_YES)
1087     {
1088         std::string solid("1");
1089         APPEND_STYLE("bot-style: ", solid);
1090     }
1091     APPEND_STYLE("bot-thickness: ", m_borderBottom_thickness);
1092     APPEND_STYLE("bot-color: ", m_borderBottom_color);
1093 
1094     APPEND_STYLE("left-space: ", m_paddingLeft);
1095     if(m_haveLeftBorder == HAVE_BORDER_YES)
1096     {
1097         std::string solid("1");
1098         APPEND_STYLE("left-style: ", solid);
1099     }
1100     APPEND_STYLE("left-thickness: ", m_borderLeft_thickness);
1101     APPEND_STYLE("left-color: ", m_borderLeft_color);
1102 
1103     APPEND_STYLE("right-space: ", m_paddingRight);
1104     if(m_haveRightBorder == HAVE_BORDER_YES)
1105     {
1106         std::string solid("1");
1107         APPEND_STYLE("right-style: ", solid);
1108     }
1109     APPEND_STYLE("right-thickness: ", m_borderRight_thickness);
1110     APPEND_STYLE("right-color: ", m_borderRight_color);
1111 
1112     APPEND_STYLE("top-space: ", m_paddingTop);
1113     if(m_haveTopBorder == HAVE_BORDER_YES)
1114     {
1115         std::string solid("1");
1116         APPEND_STYLE("top-style: ", solid);
1117     }
1118     APPEND_STYLE("top-thickness: ", m_borderTop_thickness);
1119     APPEND_STYLE("top-color: ", m_borderTop_color);
1120     APPEND_STYLE("border-merge: ", m_mergeBorders);
1121 
1122     // <style:text-properties />
1123     APPEND_STYLE("color: ", m_color);
1124     APPEND_STYLE("text-decoration: ", m_textDecoration);
1125     APPEND_STYLE("text-position: ", m_textPos);
1126 
1127     if (!m_fontName.empty()) {
1128         const std::string & fontFamily = rFontFaceDecls.getFontFamily(m_fontName.c_str());
1129 
1130         UT_ASSERT_HARMLESS(!fontFamily.empty());
1131         if (!fontFamily.empty()) {
1132             APPEND_STYLE("font-family: ", fontFamily);
1133         }
1134     }
1135 
1136     APPEND_STYLE("font-size: ", m_fontSize);
1137     APPEND_STYLE("lang: ", m_lang);
1138     APPEND_STYLE("font-style: ", m_fontStyle);
1139     APPEND_STYLE("font-weight: ", m_fontWeight);
1140 
1141     // AbiWord hangs when a paragraph has a "display:none" property
1142     if (m_family.length() && !strcmp("text", m_family.c_str())) {
1143         APPEND_STYLE("display: ", m_display);
1144     }
1145 
1146     APPEND_STYLE("text-transform: ", m_transform);
1147 
1148     // <style:section-properties />
1149     APPEND_STYLE("columns: ", m_columns);
1150     APPEND_STYLE("column-gap: ", m_columnGap);
1151 
1152 #undef APPEND_STYLE
1153 
1154 }
1155 
1156 
1157 /**
1158  * @param rProps The string that will have appended to it the properties of this
1159  *               style.
1160  */
getAbiPropsAttrString(std::string & rProps,bool appendParentProps) const1161 void ODi_Style_Style::getAbiPropsAttrString(std::string& rProps, bool appendParentProps) const {
1162 
1163         if (appendParentProps && m_pParentStyle) {
1164             m_pParentStyle->getAbiPropsAttrString(rProps);
1165         }
1166 
1167         if (!m_abiPropsAttr.empty()) {
1168 
1169             if (!rProps.empty()) {
1170                 rProps += "; ";
1171             }
1172 
1173             rProps += m_abiPropsAttr;
1174         }
1175 }
1176 
1177 
1178 /**
1179  * @param local If "true", It returns the plain value of the corresponding
1180  *              variable. Otherwise, it considers the final value of this
1181  *              property, taking into account its value on the parent styles.
1182  */
getWrap(bool local) const1183 const std::string* ODi_Style_Style::getWrap(bool local) const {
1184     if (local) {
1185         return &m_wrap;
1186     } else {
1187         if (m_wrap.empty() && m_pParentStyle) {
1188             return m_pParentStyle->getWrap(false);
1189         } else {
1190             return &m_wrap;
1191         }
1192     }
1193 }
1194 
1195 
1196 /**
1197  * @param local If "true", It returns the plain value of the corresponding
1198  *              variable. Otherwise, it considers the final value of this
1199  *              property, taking into account its value on the parent styles.
1200  */
getHorizPos(bool local) const1201 const std::string* ODi_Style_Style::getHorizPos(bool local) const {
1202     if (local) {
1203         return &m_HorizPos;
1204     } else {
1205         if (m_HorizPos.empty() && m_pParentStyle) {
1206             return m_pParentStyle->getHorizPos(false);
1207         } else {
1208             return &m_HorizPos;
1209         }
1210     }
1211 }
1212 
1213 
1214 /**
1215  * @param local If "true", It returns the plain value of the corresponding
1216  *              variable. Otherwise, it considers the final value of this
1217  *              property, taking into account its value on the parent styles.
1218  */
getVerticalPos(bool local) const1219 const std::string* ODi_Style_Style::getVerticalPos(bool local) const {
1220     if (local) {
1221         return &m_VerticalPos;
1222     } else {
1223         if (m_VerticalPos.empty() && m_pParentStyle) {
1224             return m_pParentStyle->getVerticalPos(false);
1225         } else {
1226             return &m_VerticalPos;
1227         }
1228     }
1229 }
1230 
1231 
getBackgroundColor() const1232 const std::string* ODi_Style_Style::getBackgroundColor() const
1233 {
1234     if (m_backgroundColor.empty() && m_pParentStyle) {
1235         return m_pParentStyle->getBackgroundColor();
1236     }
1237 
1238     return &m_backgroundColor;
1239 }
1240 
1241 
getBackgroundImageID() const1242 const std::string* ODi_Style_Style::getBackgroundImageID() const
1243 {
1244     if (m_backgroundImageID.empty() && m_pParentStyle) {
1245         return m_pParentStyle->getBackgroundImageID();
1246     }
1247 
1248     return &m_backgroundImageID;
1249 }
1250 
1251 
1252 /**
1253  * If pString is "0.0556in solid #0000ff", rColor will receive "#0000ff",
1254  * rLength "0.0556in" and rHaveBorder "yes".
1255  *
1256  * If pString is "none", both rColor and rLenght will be empty and
1257  * rHaveBorder will be "no"
1258  */
_stripColorLength(std::string & rColor,std::string & rLength,ODi_Style_Style::HAVE_BORDER & rHaveBorder,const gchar * pString) const1259 void ODi_Style_Style::_stripColorLength(std::string& rColor,
1260                                   std::string& rLength,
1261                                   ODi_Style_Style::HAVE_BORDER& rHaveBorder,
1262                                   const gchar* pString) const {
1263 
1264     UT_uint16 i, start;
1265     bool hasWord;
1266 
1267     rColor.clear();
1268     rLength.clear();
1269 
1270     if (!strcmp(pString, "none")) {
1271         // Color and length remain empty.
1272         rHaveBorder = HAVE_BORDER_NO;
1273         return;
1274     } else {
1275         rHaveBorder = HAVE_BORDER_YES;
1276     }
1277 
1278     i = 0;
1279     start = 0;
1280     hasWord = true;
1281     while (pString[i] != 0) {
1282 
1283         if (hasWord) {
1284             if (isspace(pString[i])) {
1285                 if (_isValidDimensionString(&(pString[start]), i-start)) {
1286                     rLength.assign(&(pString[start]), i-start);
1287                 } else if (pString[start] == '#') {
1288                     rColor.assign(&(pString[start]), i-start);
1289                 }
1290                 hasWord = false;
1291             }
1292         } else {
1293             if (!isspace(pString[i])) {
1294                 start = i;
1295                 hasWord = true;
1296             }
1297         }
1298 
1299         i++;
1300     };
1301 
1302     // Process the last word.
1303     if (hasWord) {
1304         if (_isValidDimensionString(&(pString[start]), i-start)) {
1305             rLength.assign(&(pString[start]), i-start);
1306         } else if (pString[start] == '#') {
1307             rColor.assign(&(pString[start]), i-start);
1308         }
1309     }
1310 }
1311 
1312 
1313 /**
1314  * This function shouldn't exist. The code should use
1315  * UT_isValidDimensionString instead. The problem with the UT function is
1316  * that it doesn't check the dimension specifier and only accepts NULL
1317  * terminated strings.
1318  *
1319  * @param length 0 for NULL terminated strings.
1320  */
_isValidDimensionString(const gchar * pString,UT_uint32 length) const1321 bool ODi_Style_Style::_isValidDimensionString(const gchar* pString,
1322                                              UT_uint32 length) const {
1323     UT_uint32 i;
1324     bool gotDecimalSeparator;
1325 
1326     gotDecimalSeparator = false;
1327 
1328     if (length == 0) {
1329         length = strlen(pString);
1330     }
1331 
1332     if (length < 3) {
1333         // We need at least two characters for the dimension specifier and one
1334         // for the number.
1335         return false;
1336     }
1337 
1338     for (i=0; i<length; i++) {
1339         if ( !isdigit(pString[i]) ) {
1340             if (gotDecimalSeparator) {
1341                 // Already have a decimal separator.
1342                 // It should be the start of the dimension specifier.
1343                 break;
1344             } else {
1345                 if (pString[i] == '.' || pString[i] == ',') {
1346                     gotDecimalSeparator = true;
1347                 } else {
1348                     // Invalid decimal separator character.
1349                     return false;
1350                 }
1351             }
1352         }
1353     }
1354 
1355 
1356     gchar dimStr[100];
1357     UT_Dimension dim;
1358     UT_uint32 j;
1359 
1360     if (length - i > 99)  {
1361         // A dimension specifier can't be that big.
1362         return false;
1363     }
1364 
1365     j = 0;
1366     while (i < length) {
1367         dimStr[j] = pString[i];
1368 
1369         i++;
1370         j++;
1371     }
1372     dimStr[j] = 0; // A null terminated string.
1373 
1374     dim = UT_determineDimension(dimStr, DIM_none);
1375 
1376     if (dim == DIM_none) {
1377         return false;
1378     } else {
1379         return true;
1380     }
1381 }
1382