ArthurOutputDev: Rudimentary support for transparency groups

This patch adds minimal support for transparency groups.  With it,
the Arthur backend can render highlight annotations.
diff --git a/qt5/src/ArthurOutputDev.cc b/qt5/src/ArthurOutputDev.cc
index c5afc14..96774e5 100644
--- a/qt5/src/ArthurOutputDev.cc
+++ b/qt5/src/ArthurOutputDev.cc
@@ -55,6 +55,8 @@
 #include <QRawFont>
 #include <QGlyphRun>
 #include <QtGui/QPainterPath>
+#include <QPicture>
+
 //------------------------------------------------------------------------
 
 #ifdef HAVE_SPLASH
@@ -89,6 +91,7 @@
 //------------------------------------------------------------------------
 
 ArthurOutputDev::ArthurOutputDev(QPainter *painter):
+  m_lastTransparencyGroupPicture(nullptr),
   m_fontHinting(NoHinting)
 {
   m_painter.push(painter);
@@ -990,3 +993,46 @@
   m_painter.top()->drawImage( QRect(0,0,1,1), image );
 }
 
+void ArthurOutputDev::beginTransparencyGroup(GfxState * /*state*/, double * /*bbox*/,
+                                             GfxColorSpace * /*blendingColorSpace*/,
+                                             GBool /*isolated*/, GBool /*knockout*/,
+                                             GBool /*forSoftMask*/)
+{
+  // The entire transparency group will be painted into a
+  // freshly created QPicture object.  Since an existing painter
+  // cannot change its paint device, we need to construct a
+  // new QPainter object as well.
+  m_qpictures.push(new QPicture);
+  m_painter.push(new QPainter(m_qpictures.top()));
+}
+
+void ArthurOutputDev::endTransparencyGroup(GfxState * /*state*/)
+{
+  // Stop painting into the group
+  m_painter.top()->end();
+
+  // Kill the painter that has been used for the transparency group
+  delete(m_painter.top());
+  m_painter.pop();
+
+  // Store the QPicture object that holds the result of the transparency group
+  // painting.  It will be painted and deleted in the method paintTransparencyGroup.
+  if (m_lastTransparencyGroupPicture)
+  {
+    qDebug() << "Found a transparency group that has not been painted";
+    delete(m_lastTransparencyGroupPicture);
+  }
+  m_lastTransparencyGroupPicture = m_qpictures.top();
+  m_qpictures.pop();
+}
+
+void ArthurOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/)
+{
+  // Actually draw the transparency group
+  m_painter.top()->drawPicture(0,0,*m_lastTransparencyGroupPicture);
+
+  // And delete it
+  delete(m_lastTransparencyGroupPicture);
+  m_lastTransparencyGroupPicture = nullptr;
+}
+
diff --git a/qt5/src/ArthurOutputDev.h b/qt5/src/ArthurOutputDev.h
index 4fae0a5..b4ab3d7 100644
--- a/qt5/src/ArthurOutputDev.h
+++ b/qt5/src/ArthurOutputDev.h
@@ -164,6 +164,14 @@
   void type3D1(GfxState *state, double wx, double wy,
 	       double llx, double lly, double urx, double ury) override;
 
+  //----- transparency groups and soft masks
+  virtual void beginTransparencyGroup(GfxState *state, double *bbox,
+                                      GfxColorSpace *blendingColorSpace,
+                                      GBool isolated, GBool knockout,
+                                      GBool forSoftMask) override;
+  virtual void endTransparencyGroup(GfxState *state) override;
+  virtual void paintTransparencyGroup(GfxState *state, double *bbox) override;
+
   //----- special access
 
   // Called to indicate that a new PDF document has been loaded.
@@ -178,6 +186,13 @@
   // It is popped again when the transparency group ends.
   std::stack<QPainter*> m_painter;
 
+  // This is the corresponding stack of QPicture objects
+  std::stack<QPicture*> m_qpictures;
+
+  // endTransparencyGroup removes a QPicture from the stack, but stores
+  // it here for later use in paintTransparencyGroup.
+  QPicture* m_lastTransparencyGroupPicture;
+
   FontHinting m_fontHinting;
 
   QPen m_currentPen;