cairo: support embedding CCITT image data

Bug 103399
diff --git a/poppler/CairoOutputDev.cc b/poppler/CairoOutputDev.cc
index cc8a161..01cc0d0 100644
--- a/poppler/CairoOutputDev.cc
+++ b/poppler/CairoOutputDev.cc
@@ -2761,7 +2761,7 @@
 
   cairo_surface_mark_dirty (image);
 
-  setMimeData(state, str, ref, colorMap, image);
+  setMimeData(state, str, ref, colorMap, image, height);
 
   pattern = cairo_pattern_create_for_surface (image);
   cairo_surface_destroy (image);
@@ -2935,8 +2935,38 @@
 }
 #endif
 
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10)
+GBool CairoOutputDev::setMimeDataForCCITTParams(Stream  *str,
+						cairo_surface_t *image, int height)
+{
+  CCITTFaxStream *ccittStr = static_cast<CCITTFaxStream *>(str);
+
+  GooString params;
+  params.appendf("Columns={0:d}", ccittStr->getColumns());
+  params.appendf(" Rows={0:d}", height);
+  params.appendf(" K={0:d}", ccittStr->getEncoding());
+  params.appendf(" EndOfLine={0:d}", ccittStr->getEndOfLine() ? 1 : 0);
+  params.appendf(" EncodedByteAlign={0:d}", ccittStr->getEncodedByteAlign() ? 1 : 0);
+  params.appendf(" EndOfBlock={0:d}", ccittStr->getEndOfBlock() ? 1 : 0);
+  params.appendf(" BlackIs1={0:d}", ccittStr->getBlackIs1() ? 1 : 0);
+  params.appendf(" DamagedRowsBeforeError={0:d}", ccittStr->getDamagedRowsBeforeError());
+
+  char *p = strdup(params.getCString());
+  if (cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_CCITT_FAX_PARAMS,
+                                   (const unsigned char*)p,
+                                   params.getLength(),
+                                   gfree, (void*)p))
+  {
+    gfree (p);
+    return gFalse;
+  }
+
+  return gTrue;
+}
+#endif
+
 void CairoOutputDev::setMimeData(GfxState *state, Stream *str, Object *ref,
-				 GfxImageColorMap *colorMap, cairo_surface_t *image)
+				 GfxImageColorMap *colorMap, cairo_surface_t *image, int height)
 {
   char *strBuffer;
   int len;
@@ -2960,6 +2990,11 @@
       mime_type = CAIRO_MIME_TYPE_JBIG2;
       break;
 #endif
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10)
+    case strCCITTFax:
+      mime_type = CAIRO_MIME_TYPE_CCITT_FAX;
+      break;
+#endif
     default:
       return;
   }
@@ -3002,6 +3037,11 @@
     return;
 #endif
 
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10)
+  if (strKind == strCCITTFax && !setMimeDataForCCITTParams(str, image, height))
+    return;
+#endif
+
   if (getStreamData (str->getNextStream(), &strBuffer, &len)) {
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
@@ -3216,7 +3256,7 @@
     filter = getFilterForSurface (image, interpolate);
 
   if (!inlineImg) /* don't read stream twice if it is an inline image */
-    setMimeData(state, str, ref, colorMap, image);
+    setMimeData(state, str, ref, colorMap, image, heightA);
 
   pattern = cairo_pattern_create_for_surface (image);
   cairo_surface_destroy (image);
diff --git a/poppler/CairoOutputDev.h b/poppler/CairoOutputDev.h
index 1fee812..83ed1ab 100644
--- a/poppler/CairoOutputDev.h
+++ b/poppler/CairoOutputDev.h
@@ -283,12 +283,15 @@
 				     GBool interpolate);
   GBool getStreamData (Stream *str, char **buffer, int *length);
   void setMimeData(GfxState *state, Stream *str, Object *ref,
-		   GfxImageColorMap *colorMap, cairo_surface_t *image);
+		   GfxImageColorMap *colorMap, cairo_surface_t *image, int height);
   void fillToStrokePathClip(GfxState *state);
   void alignStrokeCoords(GfxSubpath *subpath, int i, double *x, double *y);
 #if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0)
   GBool setMimeDataForJBIG2Globals (Stream *str, cairo_surface_t *image);
 #endif
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 15, 10)
+  GBool setMimeDataForCCITTParams(Stream  *str, cairo_surface_t *image, int height);
+#endif
   static void setContextAntialias(cairo_t *cr, cairo_antialias_t antialias);
 
   GfxRGB fill_color, stroke_color;
diff --git a/poppler/Stream.cc b/poppler/Stream.cc
index da1d926..2dbd6c8 100644
--- a/poppler/Stream.cc
+++ b/poppler/Stream.cc
@@ -214,7 +214,7 @@
   int bits;
   int early;
   int encoding;
-  GBool endOfLine, byteAlign, endOfBlock, black;
+  GBool endOfLine, byteAlign, endOfBlock, black, damagedRowsBeforeError;
   int columns, rows;
   Object globals, obj;
 
@@ -256,7 +256,8 @@
     rows = 0;
     endOfBlock = gTrue;
     black = gFalse;
-    if (params->isDict()) {
+    damagedRowsBeforeError = 0;
+  if (params->isDict()) {
       obj = params->dictLookup("K", recursion);
       if (obj.isInt()) {
 	encoding = obj.getInt();
@@ -285,9 +286,13 @@
       if (obj.isBool()) {
 	black = obj.getBool();
       }
+      obj = params->dictLookup("DamagedRowsBeforeError", recursion);
+      if (obj.isInt()) {
+	damagedRowsBeforeError = obj.getInt();
+      }
     }
     str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign,
-			     columns, rows, endOfBlock, black);
+			     columns, rows, endOfBlock, black, damagedRowsBeforeError);
   } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
 #ifdef HAVE_DCT_DECODER
     int colorXform = -1;
@@ -1641,12 +1646,13 @@
 
 CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
 			       GBool byteAlignA, int columnsA, int rowsA,
-			       GBool endOfBlockA, GBool blackA):
+			       GBool endOfBlockA, GBool blackA, int damagedRowsBeforeErrorA):
     FilterStream(strA) {
   encoding = encodingA;
   endOfLine = endOfLineA;
   byteAlign = byteAlignA;
   columns = columnsA;
+  damagedRowsBeforeError = damagedRowsBeforeErrorA;
   if (columns < 1) {
     columns = 1;
   } else if (columns > INT_MAX - 2) {
diff --git a/poppler/Stream.h b/poppler/Stream.h
index 152dbb0..36988cc 100644
--- a/poppler/Stream.h
+++ b/poppler/Stream.h
@@ -794,7 +794,7 @@
 
   CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
 		 GBool byteAlignA, int columnsA, int rowsA,
-		 GBool endOfBlockA, GBool blackA);
+		 GBool endOfBlockA, GBool blackA, int damagedRowsBeforeErrorA);
   ~CCITTFaxStream();
   StreamKind getKind() override { return strCCITTFax; }
   void reset() override;
@@ -808,8 +808,11 @@
 
   int getEncoding() { return encoding; }
   GBool getEndOfLine() { return endOfLine; }
+  GBool getEncodedByteAlign() { return byteAlign; }
+  GBool getEndOfBlock() { return endOfBlock; }
   int getColumns() { return columns; }
   GBool getBlackIs1() { return black; }
+  int getDamagedRowsBeforeError() { return damagedRowsBeforeError; }
 
 private:
 
@@ -821,6 +824,7 @@
   int rows;			// 'Rows' parameter
   GBool endOfBlock;		// 'EndOfBlock' parameter
   GBool black;			// 'BlackIs1' parameter
+  int damagedRowsBeforeError;   // 'DamagedRowsBeforeError' parameter
   GBool eof;			// true if at eof
   GBool nextLine2D;		// true if next line uses 2D encoding
   int row;			// current row