1 /* This file is part of the KDE project
2  * Copyright (C) 2008 Girish Ramakrishnan <girish@forwardbias.in>
3  * Copyright (C) 2009 Elvis Stansvik <elvstone@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "KoTextDebug.h"
22 
23 #include <QTextDocument>
24 #include <QTextDocumentFragment>
25 #include <QTextFrame>
26 #include <QTextBlock>
27 #include <QTextTable>
28 #include <QTextFragment>
29 #include <QTextList>
30 #include <QTextStream>
31 #include <QTextCursor>
32 
33 #include "styles/KoParagraphStyle.h"
34 #include "styles/KoCharacterStyle.h"
35 #include "styles/KoListStyle.h"
36 #include "styles/KoTableStyle.h"
37 #include "styles/KoTableCellStyle.h"
38 #include "styles/KoStyleManager.h"
39 #include "KoTextDocument.h"
40 #include "KoTextBlockData.h"
41 #include <KoInlineTextObjectManager.h>
42 #include <KoInlineNote.h>
43 #include <KoImageData.h>
44 
45 #define PARAGRAPH_BORDER_DEBUG
46 
47 int KoTextDebug::depth = 0;
48 const int KoTextDebug::INDENT = 2;
49 const QTextDocument *KoTextDebug::document = 0;
50 
51 #define dumpIndent(T) { for (int i=0; i<T; ++i) out << ' '; }
52 #define dumpList(T) { foreach (const QString &x, T) out << x << ' '; }
53 
Q_DECLARE_METATYPE(QList<KoText::Tab>)54 Q_DECLARE_METATYPE(QList<KoText::Tab>)
55 
56 static QString fontProperties(const QTextCharFormat &textFormat)
57 {
58     QMap<int, QVariant> properties = textFormat.properties();
59     QStringList fontProps;
60     // add only font properties here
61     Q_FOREACH (int id, properties.keys()) {
62         QString key, value;
63         switch (id) {
64         case QTextFormat::FontFamily:
65             fontProps.append(properties[id].toString());
66             break;
67         case QTextFormat::FontPointSize:
68             fontProps.append(QString("%1pt").arg(properties[id].toDouble()));
69             break;
70         case QTextFormat::FontSizeAdjustment:
71             fontProps.append(QString("%1adj").arg(properties[id].toDouble()));
72             break;
73         case QTextFormat::FontWeight:
74             fontProps.append(QString("weight %1").arg(properties[id].toInt()));
75             break;
76         case QTextFormat::FontItalic:
77             fontProps.append(properties[id].toBool() ? "italic" : "non-italic");
78             break;
79         case QTextFormat::FontPixelSize:
80             fontProps.append(QString("%1px").arg(properties[id].toDouble()));
81             break;
82         case QTextFormat::FontFixedPitch:
83             fontProps.append(properties[id].toBool() ? "fixedpitch" : "varpitch");
84             break;
85         case QTextFormat::FontCapitalization:
86             fontProps.append(QString("caps %1").arg(properties[id].toInt()));
87             break;
88         case KoCharacterStyle::FontCharset:
89             fontProps.append(properties[id].toString());
90             break;
91         case QTextFormat::FontStyleHint:
92             fontProps.append(QString::number(properties[id].toInt()));
93             break;
94         case QTextFormat::FontKerning:
95             fontProps.append(QString("kerning %1").arg(properties[id].toInt()));
96             break;
97         default:
98             break;
99         }
100     }
101     return fontProps.join(",");
102 }
103 
dumpDocument(const QTextDocument * doc,QTextStream & out)104 void KoTextDebug::dumpDocument(const QTextDocument *doc, QTextStream &out)
105 {
106     Q_ASSERT(doc);
107     document = doc;
108     out << QString("<document defaultfont=\"%1\">").arg(doc->defaultFont().toString());
109     dumpFrame(document->rootFrame(), out);
110     out << "</document>";
111     document = 0;
112 }
113 
textAttributes(const KoCharacterStyle & style)114 QString KoTextDebug::textAttributes(const KoCharacterStyle &style)
115 {
116     QTextCharFormat format;
117     style.applyStyle(format);
118     return textAttributes(format);
119 }
120 
inlineObjectAttributes(const QTextCharFormat & textFormat)121 QString KoTextDebug::inlineObjectAttributes(const QTextCharFormat &textFormat)
122 {
123     QString attrs;
124 
125     if (textFormat.objectType() == QTextFormat::UserObject + 1) {
126         KoInlineTextObjectManager *inlineObjectManager = KoTextDocument(document).inlineTextObjectManager();
127         KoInlineObject *inlineObject = inlineObjectManager->inlineTextObject(textFormat);
128         if (KoInlineNote *note = dynamic_cast<KoInlineNote *>(inlineObject)) {
129             attrs.append(QString(" id=\"%1\"").arg(note->id()));
130             if (note->type() == KoInlineNote::Footnote) {
131                 attrs.append(" type=\"footnote\"");
132             } else if (note->type() == KoInlineNote::Endnote) {
133                 attrs.append(" type=\"endnote\"");
134             }
135             attrs.append(QString(" label=\"%1\"").arg(note->label()));
136         } else {
137             attrs.append(" type=\"inlineobject\">");
138         }
139     }
140 
141     return attrs;
142 }
143 
textAttributes(const QTextCharFormat & textFormat)144 QString KoTextDebug::textAttributes(const QTextCharFormat &textFormat)
145 {
146     QString attrs;
147 
148     QTextImageFormat imageFormat = textFormat.toImageFormat();
149 
150     if (imageFormat.isValid()) {
151         attrs.append(" type=\"image\">");
152         return attrs;
153     }
154 
155     KoStyleManager *styleManager = document ? KoTextDocument(document).styleManager() : 0;
156     if (styleManager && textFormat.hasProperty(KoCharacterStyle::StyleId)) {
157         int id = textFormat.intProperty(KoCharacterStyle::StyleId);
158         KoCharacterStyle *characterStyle = styleManager->characterStyle(id);
159         attrs.append(" characterStyle=\"id:").append(QString::number(id));
160         if (characterStyle)
161             attrs.append(" name:").append(characterStyle->name());
162         attrs.append("\"");
163     }
164 
165     QMap<int, QVariant> properties = textFormat.properties();
166     attrs.append(" type=\"char\"");
167     QString fontProps = fontProperties(textFormat);
168     if (!fontProps.isEmpty())
169         attrs.append(QString(" font=\"%1\"").arg(fontProps));
170 
171     if (textFormat.isAnchor()) {
172         attrs.append(QString(" achorHref=\"%1\"").arg(textFormat.anchorHref()));
173         attrs.append(QString(" achorName=\"%1\"").arg(textFormat.anchorName()));
174     }
175 
176     Q_FOREACH (int id, properties.keys()) {
177         QString key, value;
178         switch (id) {
179         case QTextFormat::TextOutline: {
180             key = "outline";
181             QPen pen = qvariant_cast<QPen>(properties[id]);
182             if (pen.style() == Qt::NoPen)
183                 value = "false";
184             else
185                 value = pen.color().name();
186             break;
187         }
188         case KoCharacterStyle::UnderlineStyle:
189             key = "underlinestyle";
190             value = QString::number(properties[id].toInt());
191             break;
192         case QTextFormat::TextUnderlineColor:
193             key = "underlinecolor";
194             value = qvariant_cast<QColor>(properties[id]).name();
195             break;
196         case KoCharacterStyle::UnderlineType:
197             key = "underlinetype";
198             value = QString::number(properties[id].toInt());
199             break;
200         case KoCharacterStyle::UnderlineMode:
201             key = "underlinemode";
202             value = QString::number(properties[id].toInt());
203             break;
204         case KoCharacterStyle::UnderlineWeight:
205             key = "underlineweight";
206             value = QString::number(properties[id].toInt());
207             break;
208         case KoCharacterStyle::UnderlineWidth:
209             key = "underlinewidth";
210             value = QString::number(properties[id].toDouble());
211             break;
212         case KoCharacterStyle::StrikeOutStyle:
213             key = "strikeoutstyle";
214             value = QString::number(properties[id].toInt());
215             break;
216         case KoCharacterStyle::StrikeOutColor:
217             key = "strikeoutcolor";
218             value = qvariant_cast<QColor>(properties[id]).name();
219             break;
220         case KoCharacterStyle::StrikeOutType:
221             key = "strikeouttype";
222             value = QString::number(properties[id].toInt());
223             break;
224         case KoCharacterStyle::StrikeOutMode:
225             key = "strikeoutmode";
226             value = QString::number(properties[id].toInt());
227             break;
228         case KoCharacterStyle::StrikeOutWeight:
229             key = "strikeoutweight";
230             value = QString::number(properties[id].toInt());
231             break;
232         case KoCharacterStyle::StrikeOutWidth:
233             key = "strikeoutwidth";
234             value = QString::number(properties[id].toDouble());
235             break;
236         case QTextFormat::ForegroundBrush:
237             key = "foreground";
238             value = qvariant_cast<QBrush>(properties[id]).color().name(); // beware!
239             break;
240         case QTextFormat::BackgroundBrush:
241             key = "background";
242             value = qvariant_cast<QBrush>(properties[id]).color().name(); // beware!
243             break;
244         case QTextFormat::BlockAlignment:
245             key = "align";
246             value = QString::number(properties[id].toInt());
247             break;
248         case QTextFormat::TextIndent:
249             key = "textindent";
250             value = QString::number(properties[id].toInt());
251             break;
252         case QTextFormat::BlockIndent:
253             key = "indent";
254             value = QString::number(properties[id].toInt());
255             break;
256         case KoCharacterStyle::Country:
257             key = "country";
258             value = properties[id].toString();
259             break;
260         case KoCharacterStyle::Language:
261             key = "language";
262             value = properties[id].toString();
263             break;
264         case KoCharacterStyle::HasHyphenation:
265             key = "hyphenation";
266             value = QString::number(properties[id].toBool());
267             break;
268         case KoCharacterStyle::StrikeOutText:
269             key = "strikeout-text";
270             value = properties[id].toString();
271             break;
272         case KoCharacterStyle::FontCharset:
273             key = "font-charset";
274             value = properties[id].toString();
275             break;
276         case KoCharacterStyle::TextRotationAngle:
277             key = "rotation-angle";
278             value = QString::number(properties[id].toInt());
279             break;
280         case KoCharacterStyle::TextRotationScale:
281             key = "text-rotation-scale";
282             value = properties[id].toInt() == KoCharacterStyle::Fixed ? "Fixed" : "LineHeight";
283             break;
284         case KoCharacterStyle::TextScale:
285             key = "text-scale";
286             value = QString::number(properties[id].toInt());
287             break;
288         case KoCharacterStyle::InlineRdf:
289             key = "inline-rdf";
290             value = QString::number(properties[id].toInt());
291             break;
292         default:
293             key = "unknown"+QString::number(id);
294             value = QString::number(properties[id].toInt());
295             break;
296         }
297         if (!key.isEmpty())
298             attrs.append(" ").append(key).append("=\"").append(value).append("\"");
299     }
300     return attrs;
301 }
302 
paraAttributes(const KoParagraphStyle & style)303 QString KoTextDebug::paraAttributes(const KoParagraphStyle &style)
304 {
305     QTextBlockFormat format;
306     style.applyStyle(format);
307     return paraAttributes(format);
308 }
309 
paraAttributes(const QTextBlockFormat & blockFormat)310 QString KoTextDebug::paraAttributes(const QTextBlockFormat &blockFormat)
311 {
312     QString attrs;
313     KoStyleManager *styleManager = document ? KoTextDocument(document).styleManager() : 0;
314     if (styleManager && blockFormat.hasProperty(KoParagraphStyle::StyleId)) {
315         int id = blockFormat.intProperty(KoParagraphStyle::StyleId);
316         KoParagraphStyle *paragraphStyle = styleManager->paragraphStyle(id);
317         attrs.append(" paragraphStyle=\"id:").append(QString::number(id));
318         if (paragraphStyle)
319             attrs.append(" name:").append(paragraphStyle->name());
320         attrs.append("\"");
321     }
322 
323     QMap<int, QVariant> properties = blockFormat.properties();
324     Q_FOREACH (int id, properties.keys()) {
325         QString key, value;
326         switch (id) {
327         // the following are 'todo'
328         case KoParagraphStyle::PercentLineHeight:
329         case KoParagraphStyle::FixedLineHeight:
330         case KoParagraphStyle::MinimumLineHeight:
331         case KoParagraphStyle::LineSpacing:
332         case KoParagraphStyle::LineSpacingFromFont:
333         case KoParagraphStyle::AlignLastLine:
334         case KoParagraphStyle::WidowThreshold:
335         case KoParagraphStyle::OrphanThreshold:
336         case KoParagraphStyle::DropCapsTextStyle:
337         case KoParagraphStyle::FollowDocBaseline:
338         case KoParagraphStyle::HasLeftBorder:
339         case KoParagraphStyle::HasTopBorder:
340         case KoParagraphStyle::HasRightBorder:
341         case KoParagraphStyle::HasBottomBorder:
342         case KoParagraphStyle::BorderLineWidth:
343         case KoParagraphStyle::SecondBorderLineWidth:
344         case KoParagraphStyle::DistanceToSecondBorder:
345         case KoParagraphStyle::LeftPadding:
346         case KoParagraphStyle::TopPadding:
347         case KoParagraphStyle::RightPadding:
348         case KoParagraphStyle::BottomPadding:
349         case KoParagraphStyle::LeftBorderColor:
350         case KoParagraphStyle::TopInnerBorderWidth:
351         case KoParagraphStyle::TopBorderSpacing:
352         case KoParagraphStyle::TopBorderStyle:
353         case KoParagraphStyle::TopBorderColor:
354         case KoParagraphStyle::RightInnerBorderWidth:
355         case KoParagraphStyle::RightBorderSpacing:
356         case KoParagraphStyle::RightBorderStyle:
357         case KoParagraphStyle::RightBorderColor:
358         case KoParagraphStyle::BottomInnerBorderWidth:
359         case KoParagraphStyle::BottomBorderSpacing:
360         case KoParagraphStyle::BottomBorderStyle:
361         case KoParagraphStyle::BottomBorderColor:
362         case KoParagraphStyle::ListStyleId:
363         case KoParagraphStyle::ListStartValue:
364         case KoParagraphStyle::RestartListNumbering:
365         case KoParagraphStyle::TextProgressionDirection:
366         case KoParagraphStyle::MasterPageName:
367         case KoParagraphStyle::OutlineLevel:
368             break;
369         case KoParagraphStyle::AutoTextIndent:
370             key = "autotextindent";
371             value = properties[id].toBool() ? "true" : "false" ;
372             break;
373 #ifdef PARAGRAPH_BORDER_DEBUG // because it tends to get annoyingly long :)
374         case KoParagraphStyle::LeftBorderWidth:
375             key = "border-width-left";
376             value = QString::number(properties[id].toDouble()) ;
377             break;
378         case KoParagraphStyle::TopBorderWidth:
379             key = "border-width-top";
380             value = QString::number(properties[id].toDouble()) ;
381             break;
382         case KoParagraphStyle::RightBorderWidth:
383             key = "border-width-right";
384             value = QString::number(properties[id].toDouble()) ;
385             break;
386         case KoParagraphStyle::BottomBorderWidth:
387             key = "border-width-bottom";
388             value = QString::number(properties[id].toDouble()) ;
389             break;
390         case KoParagraphStyle::LeftBorderStyle:
391             key = "border-style-left";
392             value = QString::number(properties[id].toDouble()) ;
393             break;
394         case KoParagraphStyle::LeftBorderSpacing:
395             key = "inner-border-spacing-left";
396             value = QString::number(properties[id].toDouble()) ;
397             break;
398         case KoParagraphStyle::LeftInnerBorderWidth:
399             key = "inner-border-width-left";
400             value = QString::number(properties[id].toDouble()) ;
401             break;
402 #endif
403         case KoParagraphStyle::TabStopDistance:
404             key = "tab-stop-distance";
405             value = QString::number(properties[id].toDouble());
406             break;
407         case KoParagraphStyle::TabPositions:
408             key = "tab-stops";
409             value.clear();
410             Q_FOREACH (const QVariant & qvtab, qvariant_cast<QList<QVariant> >(properties[id])) {
411                 KoText::Tab tab = qvtab.value<KoText::Tab>();
412                 value.append("{");
413                 value.append(" pos:").append(QString::number(tab.position));
414                 value.append(" type:").append(QString::number(tab.type));
415                 if (! tab.delimiter.isNull())
416                     value.append(" delim:").append(QString(tab.delimiter));
417                 value.append(" leadertype:").append(QString::number(tab.leaderType));
418                 value.append(" leaderstyle:").append(QString::number(tab.leaderStyle));
419                 value.append(" leaderweight:").append(QString::number(tab.leaderWeight));
420                 value.append(" leaderwidth:").append(QString().setNum(tab.leaderWidth));
421                 value.append(" leadercolor:").append(tab.leaderColor.name());
422                 if (! tab.leaderText.isEmpty())
423                     value.append(" leadertext:").append(QString(tab.leaderText));
424                 value.append("}, ");
425             }
426             break;
427         case KoParagraphStyle::DropCaps:
428             key = "drop-caps";
429             value = QString::number(properties[id].toBool());
430             break;
431         case KoParagraphStyle::DropCapsLines:
432             key = "drop-caps-lines";
433             value = QString::number(properties[id].toInt());
434             break;
435         case KoParagraphStyle::DropCapsLength:
436             key = "drop-caps-length";
437             value = QString::number(properties[id].toInt());
438             break;
439         case KoParagraphStyle::DropCapsDistance:
440             key = "drop-caps-distance";
441             value = QString::number(properties[id].toDouble());
442             break;
443         case QTextFormat::BlockBottomMargin:
444             value = QString::number(properties[id].toDouble());
445             if (value != "0")
446                 key = "block-bottom-margin";
447             break;
448         case QTextFormat::BlockTopMargin:
449             value = QString::number(properties[id].toDouble());
450             if (value != "0")
451                 key = "block-top-margin";
452             break;
453         case QTextFormat::BlockLeftMargin:
454             value = QString::number(properties[id].toDouble());
455             if (value != "0")
456                 key = "block-left-margin";
457             break;
458         case QTextFormat::BlockRightMargin:
459             value = QString::number(properties[id].toDouble());
460             if (value != "0")
461                 key = "block-right-margin";
462             break;
463         case KoParagraphStyle::UnnumberedListItem:
464             key = "unnumbered-list-item";
465             value = QString::number(properties[id].toBool());
466             break;
467         case KoParagraphStyle::IsListHeader:
468             key = "list-header";
469             value = '1';
470             break;
471         case KoParagraphStyle::ListLevel:
472             key = "list-level";
473             value = QString::number(properties[id].toInt());
474             break;
475         default:
476             break;
477         }
478         if (!key.isEmpty())
479             attrs.append(" ").append(key).append("=\"").append(value).append("\"");
480     }
481     return attrs;
482 }
483 
listAttributes(const QTextListFormat & listFormat)484 QString KoTextDebug::listAttributes(const QTextListFormat &listFormat)
485 {
486     QString attrs;
487     KoStyleManager *styleManager = document ? KoTextDocument(document).styleManager() : 0;
488     if (styleManager && listFormat.hasProperty(KoListStyle::StyleId)) {
489         int id = listFormat.intProperty(KoListStyle::StyleId);
490         KoListStyle *listStyle = styleManager->listStyle(id);
491         attrs.append(" listStyle=\"id:").append(QString::number(id));
492         if (listStyle)
493             attrs.append(" name:").append(listStyle->name());
494         attrs.append("\"");
495     }
496 
497     QMap<int, QVariant> properties = listFormat.properties();
498     Q_FOREACH (int id, properties.keys()) {
499         QString key, value;
500         switch (id) {
501         case QTextListFormat::ListStyle:
502             key = "type";
503             value = QString::number(properties[id].toInt());
504             break;
505         case QTextListFormat::ListIndent:
506             key = "indent";
507             value = QString::number(properties[id].toDouble());
508             break;
509         case KoListStyle::ListItemPrefix:
510             key = "prefix";
511             value = properties[id].toString();
512             break;
513         case KoListStyle::ListItemSuffix:
514             key = "suffix";
515             value = properties[id].toString();
516             break;
517         case KoListStyle::StartValue:
518             key = "start-value";
519             value = QString::number(properties[id].toInt());
520             break;
521         case KoListStyle::Level:
522             key = "level";
523             value = QString::number(properties[id].toInt());
524             break;
525         case KoListStyle::DisplayLevel:
526             key = "display-level";
527             value = QString::number(properties[id].toInt());
528             break;
529         case KoListStyle::Alignment:
530             key = "alignment";
531             value = QString::number(properties[id].toInt());
532             break;
533         case KoListStyle::RelativeBulletSize:
534             key = "bullet-size";
535             value = QString::number(properties[id].toInt());
536             break;
537         case KoListStyle::BulletCharacter:
538             key = "bullet-char";
539             value = properties[id].toString();
540             break;
541         case KoListStyle::LetterSynchronization:
542             key = "letter-sync";
543             value = QString::number(properties[id].toInt());
544             break;
545         case KoListStyle::StyleId:
546             key = "styleid";
547             value = QString::number(properties[id].toInt());
548             break;
549         case KoListStyle::MinimumWidth:
550             key = "minimum-width";
551             value = QString::number(properties[id].toDouble());
552             break;
553         case KoListStyle::ListId:
554             key = "list-id";
555             value = QString::number(properties[id].toInt());
556             break;
557         case KoListStyle::IsOutline:
558             key = "is-outline";
559             value = QString::number(properties[id].toBool());
560             break;
561         case KoListStyle::Indent:
562             key = "indent";
563             value = QString::number(properties[id].toInt());
564             break;
565         case KoListStyle::MinimumDistance:
566             key = "minimum-distance";
567             value = QString::number(properties[id].toDouble());
568             break;
569         case KoListStyle::Width:
570             key = "width";
571             value = QString::number(properties[id].toDouble());
572             break;
573         case KoListStyle::Height:
574             key = "height";
575             value = QString::number(properties[id].toDouble());
576             break;
577         case KoListStyle::BulletImage:
578             key = "bullet-image";
579             value = QString::number((quintptr)(properties[id].value<KoImageData*>()));
580             break;
581         case KoListStyle::Margin:
582             key="margin-left";
583             value =QString::number(properties[id].toInt());
584             break;
585         case KoListStyle::TextIndent:
586             key="text-indent";
587             value =QString::number(properties[id].toInt());
588             break;
589         case KoListStyle::AlignmentMode:
590             key="label-alignment";
591             value=QString(properties[id].toBool()? "true":"false");
592             break;
593         case KoListStyle::LabelFollowedBy:
594             key="label-followed-by";
595             value =QString::number(properties[id].toInt());
596             break;
597         case KoListStyle::TabStopPosition:
598             key="tab-stop-position";
599             value =QString::number(properties[id].toInt());
600             break;
601         default:
602             break;
603         }
604         if (!key.isEmpty())
605             attrs.append(" ").append(key).append("=\"").append(value).append("\"");
606     }
607     return attrs;
608 }
609 
tableAttributes(const KoTableStyle & tableStyle)610 QString KoTextDebug::tableAttributes(const KoTableStyle &tableStyle)
611 {
612     QTextTableFormat format;
613     tableStyle.applyStyle(format);
614     return tableAttributes(format);
615 }
616 
tableAttributes(const QTextTableFormat & tableFormat)617 QString KoTextDebug::tableAttributes(const QTextTableFormat &tableFormat)
618 {
619     QString attrs;
620     KoStyleManager *styleManager = document ? KoTextDocument(document).styleManager() : 0;
621     if (styleManager) {
622         int id = tableFormat.intProperty(KoTableStyle::StyleId);
623         KoTableStyle *tableStyle = styleManager->tableStyle(id);
624         attrs.append(" tableStyle=\"id:").append(QString::number(id));
625         if (tableStyle)
626             attrs.append(" name:").append(tableStyle->name());
627         attrs.append("\"");
628     }
629 
630     QMap<int, QVariant> properties = tableFormat.properties();
631     Q_FOREACH (int id, properties.keys()) {
632         QString key, value;
633         switch (id) {
634         case QTextTableFormat::TableColumnWidthConstraints:
635         case QTextFormat::BackgroundBrush:
636             key = "background";
637             value = qvariant_cast<QBrush>(properties[id]).color().name(); // beware!
638             break;
639         case QTextFormat::BlockAlignment:
640             key = "alignment";
641             switch (properties[id].toInt()) {
642                 case Qt::AlignLeft:
643                     value = "left";
644                     break;
645                 case Qt::AlignRight:
646                     value = "right";
647                     break;
648                 case Qt::AlignHCenter:
649                     value = "center";
650                     break;
651                 case Qt::AlignJustify:
652                     value = "justify";
653                     break;
654                 default:
655                     value.clear();
656                     break;
657             }
658             break;
659         case KoTableStyle::KeepWithNext:
660             key = "keep-with-next";
661             value = properties[id].toBool() ? "true" : "false";
662             break;
663         case KoTableStyle::BreakBefore:
664             key = "break-before";
665             value = properties[id].toBool() ? "true" : "false";
666             break;
667         case KoTableStyle::BreakAfter:
668             key = "break-after";
669             value = properties[id].toBool() ? "true" : "false";
670             break;
671         case KoTableStyle::MayBreakBetweenRows:
672             key = "may-break-between-rows";
673             value = properties[id].toBool() ? "true" : "false";
674             break;
675         case KoTableStyle::MasterPageName:
676             key = "master-page-name";
677             value = properties[id].toString();
678             break;
679         case QTextTableFormat::TableColumns:
680             key = "columns";
681             value = QString::number(properties[id].toInt());
682             break;
683         case QTextTableFormat::TableCellSpacing:
684             key = "cell-spacing";
685             value = QString::number(properties[id].toDouble());
686             break;
687         case QTextTableFormat::TableHeaderRowCount:
688             key = "header-row-count";
689             value = QString::number(properties[id].toInt());
690             break;
691         default:
692             break;
693         }
694         if (!key.isEmpty())
695             attrs.append(" ").append(key).append("=\"").append(value).append("\"");
696     }
697     return attrs;
698 }
699 
frameAttributes(const QTextFrameFormat & frameFormat)700 QString KoTextDebug::frameAttributes(const QTextFrameFormat &frameFormat)
701 {
702     QString attrs;
703 
704     QMap<int, QVariant> properties = frameFormat.properties();
705     Q_FOREACH (int id, properties.keys()) {
706         QString key, value;
707         switch (id) {
708         case QTextFrameFormat::FrameBorderBrush:
709             break;
710         case QTextFrameFormat::FrameBorderStyle:
711             key = "border-style";
712             // determine border style.
713             switch (properties[id].toInt()) {
714             case QTextFrameFormat::BorderStyle_None:
715                 value = "None";
716                 break;
717             case QTextFrameFormat::BorderStyle_Dotted:
718                 value = "Dotted";
719                 break;
720             case QTextFrameFormat::BorderStyle_Dashed:
721                 value = "Dashed";
722                 break;
723             case QTextFrameFormat::BorderStyle_Solid:
724                 value = "Solid";
725                 break;
726             case QTextFrameFormat::BorderStyle_Double:
727                 value = "Double";
728                 break;
729             case QTextFrameFormat::BorderStyle_DotDash:
730                 value = "DotDash";
731                 break;
732             case QTextFrameFormat::BorderStyle_DotDotDash:
733                 value = "DotDotDash";
734                 break;
735             case QTextFrameFormat::BorderStyle_Groove:
736                 value = "Groove";
737                 break;
738             case QTextFrameFormat::BorderStyle_Ridge:
739                 value = "Ridge";
740                 break;
741             case QTextFrameFormat::BorderStyle_Inset:
742                 value = "Inset";
743                 break;
744             case QTextFrameFormat::BorderStyle_Outset:
745                 value = "Outset";
746                 break;
747             default:
748                 value = "Unknown";
749                 break;
750             }
751             break;
752         case QTextFrameFormat::FrameBorder:
753             key = "border";
754             value = QString::number(properties[id].toDouble());
755             break;
756         case QTextFrameFormat::FrameMargin:
757             key = "margin";
758             value = QString::number(properties[id].toDouble());
759             break;
760         case QTextFrameFormat::FramePadding:
761             key = "padding";
762             value = QString::number(properties[id].toDouble());
763             break;
764         case QTextFrameFormat::FrameWidth:
765             key = "width";
766             value = QString::number(properties[id].toDouble());
767             break;
768         case QTextFrameFormat::FrameHeight:
769             key = "height";
770             value = QString::number(properties[id].toDouble());
771             break;
772         case QTextFrameFormat::FrameTopMargin:
773             key = "top-margin";
774             value = QString::number(properties[id].toDouble());
775             break;
776         case QTextFrameFormat::FrameBottomMargin:
777             key = "bottom-margin";
778             value = QString::number(properties[id].toDouble());
779             break;
780         case QTextFrameFormat::FrameLeftMargin:
781             key = "left-margin";
782             value = QString::number(properties[id].toDouble());
783             break;
784         case QTextFrameFormat::FrameRightMargin:
785             key = "right-margin";
786             value = QString::number(properties[id].toDouble());
787             break;
788         default:
789             break;
790         }
791         if (!key.isEmpty())
792             attrs.append(" ").append(key).append("=\"").append(value).append("\"");
793     }
794     return attrs;
795 }
796 
tableCellAttributes(const KoTableCellStyle & tableCellStyle)797 QString KoTextDebug::tableCellAttributes(const KoTableCellStyle &tableCellStyle)
798 {
799     QTextTableCellFormat format;
800     tableCellStyle.applyStyle(format);
801     return tableCellAttributes(format);
802 }
803 
tableCellAttributes(const QTextTableCellFormat & tableCellFormat)804 QString KoTextDebug::tableCellAttributes(const QTextTableCellFormat &tableCellFormat)
805 {
806     QString attrs;
807     KoStyleManager *styleManager = document ? KoTextDocument(document).styleManager() : 0;
808     if (styleManager) {
809         int id = tableCellFormat.intProperty(KoTableCellStyle::StyleId);
810         KoTableCellStyle *tableCellStyle = styleManager->tableCellStyle(id);
811         attrs.append(" tableCellStyle=\"id:").append(QString::number(id));
812         if (tableCellStyle)
813             attrs.append(" name:").append(tableCellStyle->name());
814         attrs.append("\"");
815     }
816 
817     QMap<int, QVariant> properties = tableCellFormat.properties();
818     Q_FOREACH (int id, properties.keys()) {
819         QString key, value;
820         switch (id) {
821         case QTextTableCellFormat::TableCellRowSpan:
822             key = "row-span";
823             value = QString::number(properties[id].toInt());
824             break;
825         case QTextTableCellFormat::TableCellColumnSpan:
826             key = "column-span";
827             value = QString::number(properties[id].toInt());
828             break;
829         case QTextFormat::TableCellTopPadding:
830             key = "top-padding";
831             value = QString::number(properties[id].toDouble());
832             break;
833         case QTextFormat::TableCellBottomPadding:
834             key = "bottom-padding";
835             value = QString::number(properties[id].toDouble());
836             break;
837         case QTextFormat::TableCellLeftPadding:
838             key = "left-padding";
839             value = QString::number(properties[id].toDouble());
840             break;
841         case QTextFormat::TableCellRightPadding:
842             key = "right-padding";
843             value = QString::number(properties[id].toDouble());
844             break;
845         case KoTableCellStyle::MasterPageName:
846             key = "master-page-name";
847             value = properties[id].toString();
848             break;
849         default:
850             break;
851         }
852         if (!key.isEmpty())
853             attrs.append(" ").append(key).append("=\"").append(value).append("\"");
854     }
855     return attrs;
856 }
857 
dumpFrame(const QTextFrame * frame,QTextStream & out)858 void KoTextDebug::dumpFrame(const QTextFrame *frame, QTextStream &out)
859 {
860     depth += INDENT;
861 
862     dumpIndent(depth);
863     out << "<frame" << frameAttributes(frame->frameFormat()) << '>' << endl;
864 
865     QTextFrame::iterator iterator = frame->begin();
866 
867     for (; !iterator.atEnd(); ++iterator) {
868         QTextFrame *childFrame = iterator.currentFrame();
869         QTextBlock textBlock = iterator.currentBlock();
870 
871         if (childFrame) {
872             QTextTable *table = qobject_cast<QTextTable *>(childFrame);
873             if (table) {
874                 dumpTable(table, out);
875             } else {
876                 dumpFrame(frame, out);
877             }
878         } else if (textBlock.isValid()) {
879             dumpBlock(textBlock, out);
880         }
881     }
882 
883     dumpIndent(depth);
884     out << "</frame>" << endl;
885     depth -= INDENT;
886 }
887 
dumpBlock(const QTextBlock & block,QTextStream & out)888 void KoTextDebug::dumpBlock(const QTextBlock &block, QTextStream &out)
889 {
890     depth += INDENT;
891 
892     QString attrs;
893     attrs.append(paraAttributes(block.blockFormat()));
894     //attrs.append(" blockcharformat=\"").append(textAttributes(QTextCursor(block).blockCharFormat())).append('\"');
895     attrs.append(textAttributes(QTextCursor(block).blockCharFormat()));
896 
897     QTextList *list = block.textList();
898     if (list) {
899         attrs.append(" list=\"item:").append(QString::number(list->itemNumber(block) + 1)).append('/')
900         .append(QString::number(list->count()));
901         attrs.append('"');
902         attrs.append(listAttributes(list->format()));
903     }
904 
905     dumpIndent(depth);
906     out << "<block" << attrs << '>' << endl;
907 
908     QTextBlock::Iterator iterator = block.begin();
909     for (; !iterator.atEnd(); ++iterator) {
910         QTextFragment fragment = iterator.fragment();
911         if (fragment.isValid()) {
912             dumpFragment(fragment, out);
913         }
914     }
915     dumpIndent(depth);
916     out << "</block>" << endl;
917     depth -= INDENT;
918     if (block.next().isValid())
919         out << ' ';
920 }
921 
dumpTable(const QTextTable * table,QTextStream & out)922 void KoTextDebug::dumpTable(const QTextTable *table, QTextStream &out)
923 {
924     depth += INDENT;
925 
926     QString attrs;
927     attrs.append(tableAttributes(table->format()));
928     attrs.append(frameAttributes(table->frameFormat())); // include frame attributes too.
929 
930     dumpIndent(depth);
931     out << "<table" << attrs << '>' << endl;
932 
933     // loop through all the cells in the table and dump the cells.
934     for (int row = 0; row < table->rows(); ++row) {
935         for (int column = 0; column < table->columns(); ++column) {
936             dumpTableCell(table->cellAt(row, column), out);
937         }
938     }
939 
940     dumpIndent(depth);
941     out << "</table>" << endl;
942     depth -= INDENT;
943 }
944 
dumpTableCell(const QTextTableCell & cell,QTextStream & out)945 void KoTextDebug::dumpTableCell(const QTextTableCell &cell, QTextStream &out)
946 {
947     depth += INDENT;
948 
949     QString attrs;
950     attrs.append(textAttributes(cell.format()));
951     attrs.append(tableCellAttributes(cell.format().toTableCellFormat()));
952 
953     dumpIndent(depth);
954     out << "<cell" << attrs << '>' << endl;
955 
956     // iterate through the cell content.
957     QTextFrame::iterator cellIter = cell.begin();
958     while (!cellIter.atEnd()) {
959         if (cellIter.currentFrame() != 0) {
960             // content is a frame or table.
961             dumpFrame(cellIter.currentFrame(), out);
962         } else {
963             // content is a block.
964             dumpBlock(cellIter.currentBlock(), out);
965         }
966         ++cellIter;
967     }
968 
969     dumpIndent(depth);
970     out << "</cell>\n";
971 
972     depth -= INDENT;
973 }
974 
dumpFragment(const QTextFragment & fragment,QTextStream & out)975 void KoTextDebug::dumpFragment(const QTextFragment &fragment, QTextStream &out)
976 {
977     depth += INDENT;
978 
979     QTextCharFormat charFormat = fragment.charFormat();
980     KoInlineObject *inlineObject = KoTextDocument(document).inlineTextObjectManager()->inlineTextObject(charFormat);
981     if (inlineObject) {
982         QString cf = inlineObjectAttributes(charFormat);
983 
984         dumpIndent(depth);
985         out << "<fragment" << cf << ">\n";
986     } else {
987         QString cf = textAttributes(charFormat);
988 
989         dumpIndent(depth);
990         out << "<fragment" << cf << ">\n";
991         dumpIndent(depth + INDENT);
992         out << '|' << fragment.text() << "|\n";
993         dumpIndent(depth);
994         out << "</fragment>\n";
995     }
996 
997     depth -= INDENT;
998 }
999 
1000