21
21
#include < QPainter>
22
22
#include < QWebFrame>
23
23
#include < QWebPage>
24
+ #include < QImage>
24
25
25
26
QgsComposerHtml::QgsComposerHtml ( QgsComposition* c, bool createUndoCommands ): QgsComposerMultiFrame( c, createUndoCommands ),
26
- mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 )
27
+ mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 ), mRenderedPage( 0 )
27
28
{
28
29
mHtmlUnitsToMM = htmlUnitsToMM ();
29
30
mWebPage = new QWebPage ();
@@ -34,13 +35,14 @@ QgsComposerHtml::QgsComposerHtml( QgsComposition* c, bool createUndoCommands ):
34
35
}
35
36
}
36
37
37
- QgsComposerHtml::QgsComposerHtml (): QgsComposerMultiFrame( 0 , false ), mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 )
38
+ QgsComposerHtml::QgsComposerHtml (): QgsComposerMultiFrame( 0 , false ), mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 ), mRenderedPage( 0 )
38
39
{
39
40
}
40
41
41
42
QgsComposerHtml::~QgsComposerHtml ()
42
43
{
43
44
delete mWebPage ;
45
+ delete mRenderedPage ;
44
46
}
45
47
46
48
void QgsComposerHtml::setUrl ( const QUrl& url )
@@ -67,6 +69,9 @@ void QgsComposerHtml::setUrl( const QUrl& url )
67
69
mWebPage ->mainFrame ()->setScrollBarPolicy ( Qt::Vertical, Qt::ScrollBarAlwaysOff );
68
70
mSize .setWidth ( contentsSize.width () / mHtmlUnitsToMM );
69
71
mSize .setHeight ( contentsSize.height () / mHtmlUnitsToMM );
72
+
73
+ renderCachedImage ();
74
+
70
75
recalculateFrameSizes ();
71
76
emit changed ();
72
77
}
@@ -77,6 +82,20 @@ void QgsComposerHtml::frameLoaded( bool ok )
77
82
mLoaded = true ;
78
83
}
79
84
85
+ void QgsComposerHtml::renderCachedImage ()
86
+ {
87
+ // render page to cache image
88
+ if ( mRenderedPage )
89
+ {
90
+ delete mRenderedPage ;
91
+ }
92
+ mRenderedPage = new QImage ( mWebPage ->viewportSize (), QImage::Format_ARGB32 );
93
+ QPainter painter;
94
+ painter.begin ( mRenderedPage );
95
+ mWebPage ->mainFrame ()->render ( &painter );
96
+ painter.end ();
97
+ }
98
+
80
99
QSizeF QgsComposerHtml::totalSize () const
81
100
{
82
101
return mSize ;
@@ -121,6 +140,94 @@ void QgsComposerHtml::addFrame( QgsComposerFrame* frame, bool recalcFrameSizes )
121
140
}
122
141
}
123
142
143
+ bool candidateSort ( const QPair<int , int > &c1, const QPair<int , int > &c2 )
144
+ {
145
+ if ( c1.second < c2.second )
146
+ return true ;
147
+ else if ( c1.second > c2.second )
148
+ return false ;
149
+ else if ( c1.first > c2.first )
150
+ return true ;
151
+ else
152
+ return false ;
153
+ }
154
+
155
+ double QgsComposerHtml::findNearbyPageBreak ( double yPos )
156
+ {
157
+ if ( !mWebPage || !mRenderedPage )
158
+ {
159
+ return yPos;
160
+ }
161
+
162
+ // convert yPos to pixels
163
+ int idealPos = yPos * htmlUnitsToMM ();
164
+
165
+ // if ideal break pos is past end of page, there's nothing we need to do
166
+ if ( idealPos >= mRenderedPage ->height () )
167
+ {
168
+ return yPos;
169
+ }
170
+
171
+ int maxSearchDistance = 200 ;
172
+
173
+ // loop through all lines just before ideal break location, up to max distance
174
+ // of maxSearchDistance
175
+ int changes = 0 ;
176
+ QRgb currentColor;
177
+ QRgb pixelColor;
178
+ QList< QPair<int , int > > candidates;
179
+ for ( int candidateRow = idealPos; candidateRow >= idealPos - maxSearchDistance; --candidateRow )
180
+ {
181
+ changes = 0 ;
182
+ currentColor = qRgba ( 0 , 0 , 0 , 0 );
183
+ // check all pixels in this line
184
+ for ( int col = 0 ; col < mRenderedPage ->width (); ++col )
185
+ {
186
+ // count how many times the pixels change color in this row
187
+ // eventually, we select a row to break at with the minimum number of color changes
188
+ // since this is likely a line break, or gap between table cells, etc
189
+ // but very unlikely to be midway through a text line or picture
190
+ pixelColor = mRenderedPage ->pixel ( col, candidateRow );
191
+ if ( pixelColor != currentColor )
192
+ {
193
+ // color has changed
194
+ currentColor = pixelColor;
195
+ changes++;
196
+ }
197
+ }
198
+ candidates.append ( qMakePair ( candidateRow, changes ) );
199
+ }
200
+
201
+ // sort candidate rows by number of changes ascending, row number descending
202
+ qSort ( candidates.begin (), candidates.end (), candidateSort );
203
+ // first candidate is now the largest row with smallest number of changes
204
+
205
+ // ok, now take the mid point of the best candidate position
206
+ // we do this so that the spacing between text lines is likely to be split in half
207
+ // otherwise the html will be broken immediately above a line of text, which
208
+ // looks a little messy
209
+ int maxCandidateRow = candidates[0 ].first ;
210
+ int minCandidateRow = maxCandidateRow + 1 ;
211
+ int minCandidateChanges = candidates[0 ].second ;
212
+
213
+ QList< QPair<int , int > >::iterator it;
214
+ for ( it = candidates.begin (); it != candidates.end (); ++it )
215
+ {
216
+ if (( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
217
+ {
218
+ // no longer in a consecutive block of rows of minimum pixel colour changes
219
+ // so return the row mid-way through the block
220
+ // first converting back to mm
221
+ return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToMM ();
222
+ }
223
+ minCandidateRow = ( *it ).first ;
224
+ }
225
+
226
+ // above loop didn't work for some reason
227
+ // return first candidate converted to mm
228
+ return candidates[0 ].first / htmlUnitsToMM ();
229
+ }
230
+
124
231
bool QgsComposerHtml::writeXML ( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
125
232
{
126
233
QDomElement htmlElem = doc.createElement ( " ComposerHtml" );
0 commit comments