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