[poppler] Toward to JBIG2 support in CairoOutputDev

Adrian Johnson ajohnson at redneon.com
Tue Jan 6 22:14:57 PST 2015


On 07/01/15 02:58, suzuki toshiya wrote:
> Hi all,
> 
> Here I submit a preliminary patch for git head,
> which enables JBIG2 embedding to PDF surface via Cairo.

+static cairo_status_t setJBIG2GlobalsIdByRefNums(int refNum, int refGen,
+                                                 cairo_surface_t *image)
+{
+  GooString *globalsStrId;
+  char *idBuffer;
+  cairo_status_t st;
+
+
+  globalsStrId = new GooString;
+  globalsStrId->appendf("{0:d}-{1:d}", refNum, refGen);
+  idBuffer = copyString(globalsStrId->getCString());
+  st = cairo_surface_set_mime_data (image, CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID,

The cairo JBIG2 mime types are new to 1.14.0 so will need to be inside a
#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 14, 0).

You could also factor out the CAIRO_MIME_TYPE_UNIQUE_ID code into this
function to avoid the duplication of code. eg name it
 setMimeIdFromRef(cairo_surface_t *surface,
                  const char *mime_type,
                  Ref ref);

and use it for setting UNIQUE_ID and JBIG2_GLOBAL_ID mime data.


+GBool CairoOutputDev::setMimeDataForJBIG2Globals(Stream  *str,
+                                                 cairo_surface_t *image)
+{
+  JBIG2Stream *jbig2Str = static_cast<JBIG2Stream *>(str);
+  Object* globalsStr = jbig2Str->getGlobalsStream();
+  char *globalsBuffer;
+  int globalsLength;
+
+  // According to PDF spec, Globals should be stream (or nothing)
+
+  if (globalsStr->isNull())
+    return gTrue;
+
+  if (!globalsStr->isStream())
+    return gFalse;
+
+  // See JBIG2Stream constructor for valid reference number
+  if (!jbig2Str->getGlobalsStreamRefNum())
+    return gFalse;

The globalsStr->isStream() check should be sufficient. The reference
number should always be valid of the global stream is valid.


+#define getMimeTypeFromStreamKind( k ) \
+        ( k == strDCT ? CAIRO_MIME_TYPE_JPEG : \
+          ( k == strJPX ? CAIRO_MIME_TYPE_JP2 : \
+            ( k == strJBIG2 ? CAIRO_MIME_TYPE_JBIG2 : \
+              "" \
+            ) \
+          ) \
+        )
+
     st = cairo_surface_set_mime_data (image,
-				      str->getKind() == strDCT ?
-				      CAIRO_MIME_TYPE_JPEG : CAIRO_MIME_TYPE_JP2,
+				      getMimeTypeFromStreamKind(strKind),
 				      (const unsigned char *)strBuffer,

I don't like the macro for converting stream type to mime type. I
suggest using a switch statement or lookup table instead.


-JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStreamA):
+JBIG2Stream::JBIG2Stream(Stream *strA, Object *globalsStreamA, Object
*globalsStreamRefA):
   FilterStream(strA)
 {
   pageBitmap = NULL;
@@ -1194,6 +1194,18 @@ JBIG2Stream::JBIG2Stream(Stream *strA, Object
*globalsStreamA):
   mmrDecoder = new JBIG2MMRDecoder();

   globalsStreamA->copy(&globalsStream);
+  if (globalsStreamRefA) {
+    globalsStreamRefNum = globalsStreamRefA->getRefNum();
+    globalsStreamRefGen = globalsStreamRefA->getRefGen();
+  } else {
+    // According to PDF spec, object reference number 0 is reserved
+    // to indicate the head & tail of xref, it should not be used
+    // for the valid reference. We use it to indicate the case that
+    // JBIG2Stream should be decoded without Globals stream.
+    globalsStreamRefNum = 0;
+    globalsStreamRefGen = 0;
+  }
+

globalStreamA is never null. I suggest storing the ref in a variable of
type Ref. Then the code can be:

if (globalsStreamA->isRef())
  globalsStreamRef = globalsStreamA->getRef();

Instead of using num,gen = 0 to indicate no global, I suggest checking
getGlobalsStream()->isStream().


+  virtual int getGlobalsStreamRefNum() { return globalsStreamRefNum; }
+  virtual int getGlobalsStreamRefGen() { return globalsStreamRefGen; }

You can use the Ref type to avoid having two functions to get the ref.
eg   virtual Ref getGlobalsStreamRef() { return globalsStreamRef; }

   Object globalsStream;
+  int globalsStreamRefNum;	// to make unique ID for JBIG2 Globals in
CairoOutputDev
+  int globalsStreamRefGen;	// to make unique ID for JBIG2 Globals in
CairoOutputDev

No need to mention what the variables are used for outside of this class.


@@ -336,11 +337,23 @@ Stream *Stream::makeFilter(char *name, Stream
*str, Object *params, int recursio
     }
     str = new FlateStream(str, pred, columns, colors, bits);
   } else if (!strcmp(name, "JBIG2Decode")) {
+    obj.free();

If obj is not null at this point it is a bug and the free belongs
somewhere else. If you just want to ensure obj is null, use obj.initNull().	

     if (params->isDict()) {
       params->dictLookup("JBIG2Globals", &globals, recursion);
+      if (globals.isStream()) {
+        params->dictLookupNF("JBIG2Globals", &obj);
+        XRef *xref = params->getDict()->getXRef();
+        Object rObj;
+        while (xref->fetch(obj.getRefNum(), obj.getRefGen(),
&rObj)->isRef()) {
+          obj.free();
+          rObj.copy(&obj);
+        }
+        rObj.free();
+      }
     }

This would be simpler and easier to understand when written as:

if (params->isDict()) {
  params->dictLookup("JBIG2Globals", &globals, recursion);
  while (globals.isRef()) {
    obj.free();
    globals.copy(&obj);
    globals.free();
    obj.fetch(xref, &globals);
  }
}
str = new JBIG2Stream(str, &globals, &refObj);




More information about the poppler mailing list