High level pdf classes and pdf specific interface.

The guts of the implementation will be in SkPDFDevice and below.  This is a first implementation of everything above that point.

Review URL: http://codereview.appspot.com/2342043

git-svn-id: http://skia.googlecode.com/svn/trunk@602 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp
index 1349da1..7979b70 100644
--- a/src/pdf/SkPDFCatalog.cpp
+++ b/src/pdf/SkPDFCatalog.cpp
@@ -17,23 +17,35 @@
 #include "SkPDFCatalog.h"
 #include "SkPDFTypes.h"
 #include "SkStream.h"
+#include "SkTypes.h"
 
 SkPDFCatalog::SkPDFCatalog()
-    : fNextObjNum(1),
-      fStartedAssigningObjNums(false),
-      fAssigningFirstPageObjNums(false) {
+    : fFirstPageCount(0),
+      fNextObjNum(1),
+      fNextFirstPageObjNum(0) {
 }
 
 SkPDFCatalog::~SkPDFCatalog() {}
 
 void SkPDFCatalog::addObject(SkPDFObject* obj, bool onFirstPage) {
     SkASSERT(findObjectIndex(obj) == -1);
-    SkASSERT(!fStartedAssigningObjNums);
+    SkASSERT(fNextFirstPageObjNum == 0);
+    if (onFirstPage)
+        fFirstPageCount++;
 
     struct Rec newEntry(obj, onFirstPage);
     fCatalog.append(1, &newEntry);
 }
 
+size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, size_t offset) {
+    int objIndex = assignObjNum(obj) - 1;
+    SkASSERT(fCatalog[objIndex].fObjNumAssigned);
+    SkASSERT(fCatalog[objIndex].fFileOffset == 0);
+    fCatalog[objIndex].fFileOffset = offset;
+
+    return obj->getOutputSize(this, true);
+}
+
 void SkPDFCatalog::emitObjectNumber(SkWStream* stream, SkPDFObject* obj) {
     stream->writeDecAsText(assignObjNum(obj));
     stream->writeText(" 0");  // Generation number is always 0.
@@ -60,21 +72,54 @@
     if (fCatalog[currentIndex].fObjNumAssigned)
         return currentIndex + 1;
 
-    fStartedAssigningObjNums = true;
+    // First assignment.
+    if (fNextFirstPageObjNum == 0)
+        fNextFirstPageObjNum = fCatalog.count() - fFirstPageCount + 1;
+
+    uint32_t objNum;
     if (fCatalog[currentIndex].fOnFirstPage) {
-        fAssigningFirstPageObjNums = true;
+        objNum = fNextFirstPageObjNum;
+        fNextFirstPageObjNum++;
     } else {
-        SkASSERT(!fAssigningFirstPageObjNums);
+        objNum = fNextObjNum;
+        fNextObjNum++;
     }
 
     // When we assign an object an object number, we put it in that array
     // offset (minus 1 because object number 0 is reserved).
-    if (fNextObjNum - 1 != currentIndex) {
-        Rec other = fCatalog[fNextObjNum - 1];
-        fCatalog[fNextObjNum - 1] = fCatalog[currentIndex];
-        fCatalog[currentIndex] = other;
+    SkASSERT(!fCatalog[objNum - 1].fObjNumAssigned);
+    if (objNum - 1 != currentIndex)
+        SkTSwap(fCatalog[objNum - 1], fCatalog[currentIndex]);
+    fCatalog[objNum - 1].fObjNumAssigned = true;
+    return objNum;
+}
+
+int32_t SkPDFCatalog::emitXrefTable(SkWStream* stream, bool firstPage) {
+    int first = -1;
+    int last = fCatalog.count() - 1;
+    // TODO(vandebo) support linearized format.
+    //int last = fCatalog.count() - fFirstPageCount - 1;
+    //if (firstPage) {
+    //    first = fCatalog.count() - fFirstPageCount;
+    //    last = fCatalog.count() - 1;
+    //}
+
+    stream->writeText("xref\n");
+    stream->writeDecAsText(first + 1);
+    stream->writeText(" ");
+    stream->writeDecAsText(last - first + 1);
+    stream->writeText("\n");
+
+    if (first == -1) {
+        stream->writeText("0000000000 65535 f \n");
+        first++;
     }
-    fCatalog[fNextObjNum - 1].fObjNumAssigned = true;
-    fNextObjNum++;
-    return fNextObjNum - 1;
+    for (int i = first; i <= last; i++) {
+        SkASSERT(fCatalog[i].fFileOffset > 0);
+        SkASSERT(fCatalog[i].fFileOffset <= 9999999999LL);
+        stream->writeBigDecAsText(fCatalog[i].fFileOffset, 10);
+        stream->writeText(" 00000 n \n");
+    }
+
+    return fCatalog.count() + 1;
 }