2121#include < QPainter>
2222#include < QWebFrame>
2323#include < QWebPage>
24+ #include < QImage>
2425
2526QgsComposerHtml::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 )
2728{
2829 mHtmlUnitsToMM = htmlUnitsToMM ();
2930 mWebPage = new QWebPage ();
@@ -34,13 +35,14 @@ QgsComposerHtml::QgsComposerHtml( QgsComposition* c, bool createUndoCommands ):
3435 }
3536}
3637
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 )
3839{
3940}
4041
4142QgsComposerHtml::~QgsComposerHtml ()
4243{
4344 delete mWebPage ;
45+ delete mRenderedPage ;
4446}
4547
4648void QgsComposerHtml::setUrl ( const QUrl& url )
@@ -67,6 +69,9 @@ void QgsComposerHtml::setUrl( const QUrl& url )
6769 mWebPage ->mainFrame ()->setScrollBarPolicy ( Qt::Vertical, Qt::ScrollBarAlwaysOff );
6870 mSize .setWidth ( contentsSize.width () / mHtmlUnitsToMM );
6971 mSize .setHeight ( contentsSize.height () / mHtmlUnitsToMM );
72+
73+ renderCachedImage ();
74+
7075 recalculateFrameSizes ();
7176 emit changed ();
7277}
@@ -77,6 +82,20 @@ void QgsComposerHtml::frameLoaded( bool ok )
7782 mLoaded = true ;
7883}
7984
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+
8099QSizeF QgsComposerHtml::totalSize () const
81100{
82101 return mSize ;
@@ -121,6 +140,94 @@ void QgsComposerHtml::addFrame( QgsComposerFrame* frame, bool recalcFrameSizes )
121140 }
122141}
123142
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+
124231bool QgsComposerHtml::writeXML ( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
125232{
126233 QDomElement htmlElem = doc.createElement ( " ComposerHtml" );
0 commit comments