1 /* This file is part of the KDE project
2  * Copyright (C) 2001-2007 by OpenMFG, LLC (info@openmfg.com)
3  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 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  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "KReportHTMLCSSRenderer_p.h"
20 #include "KReportRenderObjects.h"
21 #include "kreport_debug.h"
22 
23 #include <QTemporaryFile>
24 #include <QDir>
25 #include <QPainter>
26 #include <QString>
27 
28 namespace KReportPrivate {
29 
HTMLCSSRenderer()30 HTMLCSSRenderer::HTMLCSSRenderer()
31 {
32 
33 }
34 
~HTMLCSSRenderer()35 HTMLCSSRenderer::~HTMLCSSRenderer()
36 {
37 
38 }
39 
render(const KReportRendererContext & context,ORODocument * document,int page)40 bool HTMLCSSRenderer::render(const KReportRendererContext& context, ORODocument *document, int page)
41 {
42     Q_UNUSED(page);
43     QTemporaryFile tempHtmlFile; // auto removed by default on destruction
44     if (!tempHtmlFile.open()) {
45         kreportWarning() << "Couldn't create temporary file to write into";
46         return false;
47     }
48 
49     QTextStream out(&tempHtmlFile);
50 
51     QString dirSuffix = QLatin1String(".files");
52     QDir tempDir;
53     QFileInfo fi(tempHtmlFile);
54 
55     QString tempFileName = fi.absoluteFilePath();
56     m_tempDirName = tempFileName + dirSuffix;
57     m_actualDirName = context.url().fileName() + dirSuffix;
58 
59     if (!tempDir.mkpath(m_tempDirName))
60         return false;
61 
62     out << renderCSS(document);
63 
64     out.flush();
65     tempHtmlFile.close();
66 
67     bool status = true; //!< @todo KIO
68 //    if (KIO::NetAccess::upload(tempFileName, context.destinationUrl, 0) && KIO::NetAccess::dircopy(QUrl(m_tempDirName),  QUrl(context.destinationUrl.url() + dirSuffix), 0)) {
69 //        status = true;
70 //    }
71 
72     // cleanup the temporary directory
73     tempDir.setPath(m_tempDirName);
74     QStringList fileList = tempDir.entryList();
75     foreach(const QString& fileName, fileList) {
76         tempDir.remove(fileName);
77     }
78     tempDir.rmdir(m_tempDirName);
79 
80     return status;
81 }
82 
83 //! @todo use QTextStream for efficiency
renderCSS(ORODocument * document)84 QString HTMLCSSRenderer::renderCSS(ORODocument *document)
85 {
86     QString html;
87     QString body;
88     QString style;
89     QStringList styles;
90     int styleindex;
91     bool renderedPageHead = false;
92     bool renderedPageFoot = false;
93 
94     QDir d(m_tempDirName);
95     // Render Each Section
96     for (int s = 0; s < document->sectionCount(); s++) {
97         OROSection *section = document->section(s);
98 
99         if (section->type() == KReportSectionData::Type::GroupHeader
100             || section->type() == KReportSectionData::Type::GroupFooter
101             || section->type() == KReportSectionData::Type::Detail
102             || section->type() == KReportSectionData::Type::ReportHeader
103             || section->type() == KReportSectionData::Type::ReportFooter
104             || (section->type() == KReportSectionData::Type::PageHeaderAny && !renderedPageHead)
105             || (section->type() == KReportSectionData::Type::PageFooterAny && !renderedPageFoot
106                 && s > document->sectionCount() - 2))
107         { // render the page foot right at the end, it
108           // will either be the last or second last
109           // section if there is a report footer
110             if (section->type() == KReportSectionData::Type::PageHeaderAny)
111                 renderedPageHead = true;
112 
113             if (section->type() == KReportSectionData::Type::PageFooterAny)
114                 renderedPageFoot = true;
115 
116             style = QLatin1String("position: relative; top: 0pt; left: 0pt; background-color: ") + section->backgroundColor().name() + QLatin1String("; height: ") + QString::number(section->height()) + QLatin1String("pt;");
117 
118             if (!styles.contains(style)) {
119                 styles << style;
120             }
121             styleindex = styles.indexOf(style);
122 
123             body += QLatin1String("<div class=\"style") + QString::number(styleindex) + QLatin1String("\">\n");
124             //Render the objects in each section
125             for (int i = 0; i < section->primitiveCount(); i++) {
126                 OROPrimitive * prim = section->primitive(i);
127                 //kreportDebug() << "Got object type" << prim->type();
128                 if (OROTextBox *tb = dynamic_cast<OROTextBox*>(prim)) {
129                     QColor bg = tb->textStyle().backgroundColor;
130                     style = QLatin1String("position: absolute; ") +
131                             QLatin1String("background-color: ") + QString::fromLatin1("rgba(%1,%2,%3,%4)")
132                                                             .arg(bg.red())
133                                                             .arg(bg.green())
134                                                             .arg(bg.blue())
135                                                             .arg(0.01 * tb->textStyle().backgroundOpacity) +QLatin1String( "; ") +
136                             QLatin1String("top: ") + QString::number(tb->position().y()) + QLatin1String("pt; ") +
137                             QLatin1String("left: ") + QString::number(tb->position().x()) + QLatin1String("pt; ") +
138                             QLatin1String("font-size: ") + QString::number(tb->textStyle().font.pointSize()) + QLatin1String("pt; ") +
139                             QLatin1String("color: ") + tb->textStyle().foregroundColor.name() + QLatin1String("; ") +
140                             QLatin1String("width: ") + QString::number(tb->size().width()) + QLatin1String("px;") +
141                             QLatin1String("height: ") + QString::number(tb->size().height()) + QLatin1String("px;") ;
142                     //! @todo opaque text + translucent background
143                     //it looks a pain to implement
144                     //http://developer.mozilla.org/en/docs/Useful_CSS_tips:Color_and_Background
145                     //style += "filter:alpha(opacity=" + QString::number((tb->textStyle().bgOpacity / 255) * 100) + ");"; //ie opacity
146                     //style += "opacity: " + QString::number(tb->textStyle().bgOpacity / 255.0) + ";";
147 
148                     if (!styles.contains(style)) {
149                         styles << style;
150                     }
151                     styleindex = styles.indexOf(style);
152 
153                     body += QLatin1String("<div class=\"style") + QString::number(styleindex) + QLatin1String("\">") +
154                             tb->text() +
155                             QLatin1String("</div>\n");
156                 } else if (OROImage *im = dynamic_cast<OROImage*>(prim)) {
157                     style = QLatin1String("position: absolute; ") +
158                             QLatin1String("top: ") + QString::number(im->position().y()) + QLatin1String("pt; ") +
159                             QLatin1String("left: ") + QString::number(im->position().x()) + QLatin1String("pt; ");
160                     if (!styles.contains(style)) {
161                         styles << style;
162                     }
163                     styleindex = styles.indexOf(style);
164 
165                     body += QLatin1String("<div class=\"style") + QString::number(styleindex) + QLatin1String("\">") +
166                             QLatin1String("<img width=\"") + QString::number(im->size().width()) + QLatin1String("px") + QLatin1String("\" height=\"") + QString::number(im->size().height()) + QLatin1String("px") + QLatin1String("\" src=\"./") + m_actualDirName + QLatin1String("/object") + QString::number(s) + QString::number(i) + QLatin1String(".png\"></img>") +
167                             QLatin1String("</div>\n");
168 
169 
170                     im->image().save(m_tempDirName + QLatin1String("/object") + QString::number(s) + QString::number(i) + QLatin1String(".png"));
171                 } else if (OROPicture *im = dynamic_cast<OROPicture*>(prim)) {
172                     style = QLatin1String("position: absolute; ") +
173                             QLatin1String("top: ") + QString::number(im->position().y()) + QLatin1String("pt; ") +
174                             QLatin1String("left: ") + QString::number(im->position().x()) + QLatin1String("pt; ");
175                     if (!styles.contains(style)) {
176                         styles << style;
177                     }
178                     styleindex = styles.indexOf(style);
179 
180                     body += QLatin1String("<div class=\"style") + QString::number(styleindex) + QLatin1String("\">") +
181                             QLatin1String("<img width=\"") + QString::number(im->size().width()) + QLatin1String("px") + QLatin1String("\" height=\"") + QString::number(im->size().height()) + QLatin1String("px") + QLatin1String("\" src=\"./") + m_actualDirName + QLatin1String("/object") + QString::number(s) + QString::number(i) + QLatin1String(".png\"></img>") +
182                             QLatin1String("</div>\n");
183 
184                     QImage image(im->size().toSize(), QImage::Format_RGB32);
185                     QPainter painter(&image);
186                     im->picture()->play(&painter);
187                     image.save(m_tempDirName + QLatin1String("/object") + QString::number(s) + QString::number(i) + QLatin1String(".png"));
188                 } else {
189                     kreportWarning() << "unrecognized primitive type";
190                 }
191             }
192             body += QLatin1String("</div>\n");
193         }
194     }
195 
196     //! @todo add option for creating separate css file
197     html = QLatin1String("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n"
198         "<html>\n"
199         "<head>\n"
200         "<style type=\"text/css\">\n");
201 
202     for (int i = 0; i < styles.count(); ++i) {
203         html += QLatin1String(".style") + QString::number(i) + QLatin1String("{") + styles[i] + QLatin1String("}\n");
204     }
205 
206     html += QLatin1String("</style>\n") +
207         QLatin1String("<title>") + document->title() + QLatin1String("</title>\n") +
208         QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\">\n") +
209         QLatin1String("<meta name=\"generator\" content=\"Kexi\">\n") +
210         QLatin1String("</head>\n") +
211         QLatin1String("<body>\n") +
212         body +
213         QLatin1String("\n</body>\n") +
214         QLatin1String("</html>\n");
215 
216     return html;
217 }
218 
219 }
220