Skip to content
Permalink
Browse files

[FEATURE][composer] HTML frames no longer cut through lines of text w…

…hen splitting over multiple pages (sponsored by City of Uster, Switzerland)
  • Loading branch information
nyalldawson committed Apr 28, 2014
1 parent 4885289 commit a9af35ba52f56a10373710e76298f87db76e46ab
Showing with 117 additions and 3 deletions.
  1. +0 −1 src/core/composer/qgscomposerframe.h
  2. +109 −2 src/core/composer/qgscomposerhtml.cpp
  3. +8 −0 src/core/composer/qgscomposerhtml.h
@@ -43,7 +43,6 @@ class CORE_EXPORT QgsComposerFrame: public QgsComposerItem

QgsComposerMultiFrame* multiFrame() const { return mMultiFrame; }


private:
QgsComposerFrame(); //forbidden
QgsComposerMultiFrame* mMultiFrame;
@@ -21,9 +21,10 @@
#include <QPainter>
#include <QWebFrame>
#include <QWebPage>
#include <QImage>

QgsComposerHtml::QgsComposerHtml( QgsComposition* c, bool createUndoCommands ): QgsComposerMultiFrame( c, createUndoCommands ),
mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 )
mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 ), mRenderedPage( 0 )
{
mHtmlUnitsToMM = htmlUnitsToMM();
mWebPage = new QWebPage();
@@ -34,13 +35,14 @@ QgsComposerHtml::QgsComposerHtml( QgsComposition* c, bool createUndoCommands ):
}
}

QgsComposerHtml::QgsComposerHtml(): QgsComposerMultiFrame( 0, false ), mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 )
QgsComposerHtml::QgsComposerHtml(): QgsComposerMultiFrame( 0, false ), mWebPage( 0 ), mLoaded( false ), mHtmlUnitsToMM( 1.0 ), mRenderedPage( 0 )
{
}

QgsComposerHtml::~QgsComposerHtml()
{
delete mWebPage;
delete mRenderedPage;
}

void QgsComposerHtml::setUrl( const QUrl& url )
@@ -67,6 +69,9 @@ void QgsComposerHtml::setUrl( const QUrl& url )
mWebPage->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
mSize.setWidth( contentsSize.width() / mHtmlUnitsToMM );
mSize.setHeight( contentsSize.height() / mHtmlUnitsToMM );

renderCachedImage();

recalculateFrameSizes();
emit changed();
}
@@ -77,6 +82,20 @@ void QgsComposerHtml::frameLoaded( bool ok )
mLoaded = true;
}

void QgsComposerHtml::renderCachedImage()
{
//render page to cache image
if ( mRenderedPage )
{
delete mRenderedPage;
}
mRenderedPage = new QImage( mWebPage->viewportSize(), QImage::Format_ARGB32 );
QPainter painter;
painter.begin( mRenderedPage );
mWebPage->mainFrame()->render( &painter );
painter.end();
}

QSizeF QgsComposerHtml::totalSize() const
{
return mSize;
@@ -121,6 +140,94 @@ void QgsComposerHtml::addFrame( QgsComposerFrame* frame, bool recalcFrameSizes )
}
}

bool candidateSort( const QPair<int, int> &c1, const QPair<int, int> &c2 )
{
if ( c1.second < c2.second )
return true;
else if ( c1.second > c2.second )
return false;
else if ( c1.first > c2.first )
return true;
else
return false;
}

double QgsComposerHtml::findNearbyPageBreak( double yPos )
{
if ( !mWebPage || !mRenderedPage )
{
return yPos;
}

//convert yPos to pixels
int idealPos = yPos * htmlUnitsToMM();

//if ideal break pos is past end of page, there's nothing we need to do
if ( idealPos >= mRenderedPage->height() )
{
return yPos;
}

int maxSearchDistance = 200;

//loop through all lines just before ideal break location, up to max distance
//of maxSearchDistance
int changes = 0;
QRgb currentColor;
QRgb pixelColor;
QList< QPair<int, int> > candidates;
for ( int candidateRow = idealPos; candidateRow >= idealPos - maxSearchDistance; --candidateRow )
{
changes = 0;
currentColor = qRgba( 0, 0, 0, 0 );
//check all pixels in this line
for ( int col = 0; col < mRenderedPage->width(); ++col )
{
//count how many times the pixels change color in this row
//eventually, we select a row to break at with the minimum number of color changes
//since this is likely a line break, or gap between table cells, etc
//but very unlikely to be midway through a text line or picture
pixelColor = mRenderedPage->pixel( col, candidateRow );
if ( pixelColor != currentColor )
{
//color has changed
currentColor = pixelColor;
changes++;
}
}
candidates.append( qMakePair( candidateRow, changes ) );
}

//sort candidate rows by number of changes ascending, row number descending
qSort( candidates.begin(), candidates.end(), candidateSort );
//first candidate is now the largest row with smallest number of changes

//ok, now take the mid point of the best candidate position
//we do this so that the spacing between text lines is likely to be split in half
//otherwise the html will be broken immediately above a line of text, which
//looks a little messy
int maxCandidateRow = candidates[0].first;
int minCandidateRow = maxCandidateRow + 1;
int minCandidateChanges = candidates[0].second;

QList< QPair<int, int> >::iterator it;
for ( it = candidates.begin(); it != candidates.end(); ++it )
{
if (( *it ).second != minCandidateChanges || ( *it ).first != minCandidateRow - 1 )
{
//no longer in a consecutive block of rows of minimum pixel colour changes
//so return the row mid-way through the block
//first converting back to mm
return ( minCandidateRow + ( maxCandidateRow - minCandidateRow ) / 2 ) / htmlUnitsToMM();
}
minCandidateRow = ( *it ).first;
}

//above loop didn't work for some reason
//return first candidate converted to mm
return candidates[0].first / htmlUnitsToMM();
}

bool QgsComposerHtml::writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
{
QDomElement htmlElem = doc.createElement( "ComposerHtml" );
@@ -20,6 +20,7 @@
#include <QUrl>

class QWebPage;
class QImage;

class CORE_EXPORT QgsComposerHtml: public QgsComposerMultiFrame
{
@@ -40,6 +41,9 @@ class CORE_EXPORT QgsComposerHtml: public QgsComposerMultiFrame

void addFrame( QgsComposerFrame* frame, bool recalcFrameSizes = true );

//overriden to break frames without dividing lines of text
double findNearbyPageBreak( double yPos );

private slots:
void frameLoaded( bool ok );

@@ -49,8 +53,12 @@ class CORE_EXPORT QgsComposerHtml: public QgsComposerMultiFrame
bool mLoaded;
QSizeF mSize; //total size in mm
double mHtmlUnitsToMM;
QImage* mRenderedPage;

double htmlUnitsToMM(); //calculate scale factor

//renders a snapshot of the page to a cached image
void renderCachedImage();
};

#endif // QGSCOMPOSERHTML_H

0 comments on commit a9af35b

Please sign in to comment.
You can’t perform that action at this time.