XPS ttc handling.

R=reed@google.com

Author: bungeman@google.com

Review URL: https://chromiumcodereview.appspot.com/14873006

git-svn-id: http://skia.googlecode.com/svn/trunk@9037 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/device/xps/SkXPSDevice.cpp b/src/device/xps/SkXPSDevice.cpp
index ddd4a0d..d7a5598 100644
--- a/src/device/xps/SkXPSDevice.cpp
+++ b/src/device/xps/SkXPSDevice.cpp
@@ -22,6 +22,7 @@
 #include "SkData.h"
 #include "SkDraw.h"
 #include "SkDrawProcs.h"
+#include "SkEndian.h"
 #include "SkFontHost.h"
 #include "SkGlyphCache.h"
 #include "SkHRESULT.h"
@@ -31,12 +32,14 @@
 #include "SkPaint.h"
 #include "SkPoint.h"
 #include "SkRasterizer.h"
+#include "SkSFNTHeader.h"
 #include "SkShader.h"
 #include "SkSize.h"
 #include "SkStream.h"
 #include "SkTDArray.h"
 #include "SkTLazy.h"
 #include "SkTScopedComPtr.h"
+#include "SkTTCFHeader.h"
 #include "SkTypefacePriv.h"
 #include "SkUtils.h"
 #include "SkXPSDevice.h"
@@ -80,6 +83,7 @@
                       guid.Data4[6],
                       guid.Data4[7]);
 }
+
 /**
    Creates a GUID based id and places it into buffer.
    buffer should have space for at least GUID_ID_LEN wide characters.
@@ -179,10 +183,10 @@
         "Could not create thumbnail generator.");
 
     SkTScopedComPtr<IOpcPartUri> partUri;
-    static const size_t size = SK_MAX(
+    static const size_t size = SkTUMax<
         SK_ARRAY_COUNT(L"/Documents/1/Metadata/.png") + SK_DIGITS_IN(pageNum),
         SK_ARRAY_COUNT(L"/Metadata/" L_GUID_ID L".png")
-    );
+    >::value;
     wchar_t buffer[size];
     if (pageNum > 0) {
         swprintf_s(buffer, size, L"/Documents/1/Metadata/%u.png", pageNum);
@@ -334,18 +338,20 @@
     SkTDArray<unsigned short> keepList;
     current->glyphsUsed->exportTo(&keepList);
 
+    int ttcCount = (current->ttcIndex + 1);
+
     //The following are declared with the types required by CreateFontPackage.
-    unsigned char *puchFontPackageBuffer;
-    unsigned long pulFontPackageBufferSize;
-    unsigned long pulBytesWritten;
+    unsigned char *fontPackageBufferRaw = NULL;
+    unsigned long fontPackageBufferSize;
+    unsigned long bytesWritten;
     unsigned long result = CreateFontPackage(
         (unsigned char *) current->fontData->getMemoryBase(),
         (unsigned long) current->fontData->getLength(),
-        &puchFontPackageBuffer,
-        &pulFontPackageBufferSize,
-        &pulBytesWritten,
-        TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST,// | TTFCFP_FLAGS_TTC,
-        0,//TTC index
+        &fontPackageBufferRaw,
+        &fontPackageBufferSize,
+        &bytesWritten,
+        TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST | (ttcCount > 0 ? TTFCFP_FLAGS_TTC : 0),
+        current->ttcIndex,
         TTFCFP_SUBSET,
         0,
         0,
@@ -356,15 +362,51 @@
         sk_realloc_throw,
         sk_free,
         NULL);
+    SkAutoTMalloc<unsigned char> fontPackageBuffer(fontPackageBufferRaw);
     if (result != NO_ERROR) {
         SkDEBUGF(("CreateFontPackage Error %lu", result));
         return E_UNEXPECTED;
     }
 
-    SkMemoryStream* newStream = new SkMemoryStream;
-    newStream->setMemoryOwned(puchFontPackageBuffer, pulBytesWritten);
+    // If it was originally a ttc, keep it a ttc.
+    // CreateFontPackage over-allocates, realloc usually decreases the size substantially.
+    size_t extra;
+    if (ttcCount > 0) {
+        // Create space for a ttc header.
+        extra = sizeof(SkTTCFHeader) + (ttcCount * sizeof(SK_OT_ULONG));
+        fontPackageBuffer.realloc(bytesWritten + extra);
+        //overlap is certain, use memmove
+        memmove(fontPackageBuffer.get() + extra, fontPackageBuffer.get(), bytesWritten);
+
+        // Write the ttc header.
+        SkTTCFHeader* ttcfHeader = reinterpret_cast<SkTTCFHeader*>(fontPackageBuffer.get());
+        ttcfHeader->ttcTag = SkTTCFHeader::TAG;
+        ttcfHeader->version = SkTTCFHeader::version_1;
+        ttcfHeader->numOffsets = SkEndian_SwapBE32(ttcCount);
+        SK_OT_ULONG* offsetPtr = SkTAfter<SK_OT_ULONG>(ttcfHeader);
+        for (int i = 0; i < ttcCount; ++i, ++offsetPtr) {
+            *offsetPtr = SkEndian_SwapBE32(extra);
+        }
+
+        // Fix up offsets in sfnt table entries.
+        SkSFNTHeader* sfntHeader = SkTAddOffset<SkSFNTHeader>(fontPackageBuffer.get(), extra);
+        int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
+        SkSFNTHeader::TableDirectoryEntry* tableDirectory =
+            SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
+        for (int i = 0; i < numTables; ++i, ++tableDirectory) {
+            tableDirectory->offset = SkEndian_SwapBE32(
+                SkEndian_SwapBE32(tableDirectory->offset) + extra);
+        }
+    } else {
+        extra = 0;
+        fontPackageBuffer.realloc(bytesWritten);
+    }
+
+    SkAutoTUnref<SkMemoryStream> newStream(new SkMemoryStream());
+    newStream->setMemoryOwned(fontPackageBuffer.detach(), bytesWritten + extra);
+
     SkTScopedComPtr<IStream> newIStream;
-    SkIStream::CreateFromSkStream(newStream, true, &newIStream);
+    SkIStream::CreateFromSkStream(newStream.detach(), true, &newIStream);
 
     XPS_FONT_EMBEDDING embedding;
     HRM(current->xpsFont->GetEmbeddingOption(&embedding),
@@ -2034,7 +2076,8 @@
     XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED;
 
     SkTScopedComPtr<IStream> fontStream;
-    SkStream* fontData = typeface->openStream(NULL);
+    int ttcIndex;
+    SkStream* fontData = typeface->openStream(&ttcIndex);
     HRM(SkIStream::CreateFromSkStream(fontData, true, &fontStream),
         "Could not create font stream.");
 
@@ -2057,8 +2100,15 @@
                                               &xpsFontResource),
         "Could not create font resource.");
 
+    //TODO: change openStream to return -1 for non-ttc, get rid of this.
+    uint8_t* data = (uint8_t*)fontData->getMemoryBase();
+    bool isTTC = (data &&
+                  fontData->getLength() >= sizeof(SkTTCFHeader) &&
+                  ((SkTTCFHeader*)data)->ttcTag == SkTTCFHeader::TAG);
+
     TypefaceUse& newTypefaceUse = this->fTypefaces.push_back();
     newTypefaceUse.typefaceId = typefaceID;
+    newTypefaceUse.ttcIndex = isTTC ? ttcIndex : -1;
     newTypefaceUse.fontData = fontData;
     newTypefaceUse.xpsFont = xpsFontResource.release();
 
@@ -2074,7 +2124,7 @@
 HRESULT SkXPSDevice::AddGlyphs(const SkDraw& d,
                                IXpsOMObjectFactory* xpsFactory,
                                IXpsOMCanvas* canvas,
-                               IXpsOMFontResource* font,
+                               TypefaceUse* font,
                                LPCWSTR text,
                                XPS_GLYPH_INDEX* xpsGlyphs,
                                UINT32 xpsGlyphsLen,
@@ -2084,7 +2134,8 @@
                                const SkMatrix& transform,
                                const SkPaint& paint) {
     SkTScopedComPtr<IXpsOMGlyphs> glyphs;
-    HRM(xpsFactory->CreateGlyphs(font, &glyphs), "Could not create glyphs.");
+    HRM(xpsFactory->CreateGlyphs(font->xpsFont, &glyphs), "Could not create glyphs.");
+    HRM(glyphs->SetFontFaceIndex(font->ttcIndex), "Could not set glyph font face index.");
 
     //XPS uses affine transformations for everything...
     //...except positioning text.
@@ -2282,7 +2333,7 @@
     HRV(AddGlyphs(d,
                   this->fXpsFactory.get(),
                   this->fCurrentXpsCanvas.get(),
-                  typeface->xpsFont,
+                  typeface,
                   NULL,
                   procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
                   &origin,
@@ -2334,7 +2385,7 @@
     HRV(AddGlyphs(d,
                   this->fXpsFactory.get(),
                   this->fCurrentXpsCanvas.get(),
-                  typeface->xpsFont,
+                  typeface,
                   NULL,
                   procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
                   &origin,
diff --git a/src/sfnt/SkOTTable_name.cpp b/src/sfnt/SkOTTable_name.cpp
index 177cd8f..0b309cd 100644
--- a/src/sfnt/SkOTTable_name.cpp
+++ b/src/sfnt/SkOTTable_name.cpp
@@ -10,6 +10,7 @@
 #include "SkEndian.h"
 #include "SkString.h"
 #include "SkTSearch.h"
+#include "SkTemplates.h"
 #include "SkUtils.h"
 
 static SkUnichar SkUTF16BE_NextUnichar(const uint16_t** srcPtr) {
@@ -435,14 +436,6 @@
 }
 }
 
-template <typename D, typename S> static D* SkTAfter(S const * const ptr, size_t count = 1) {
-    return (D*)(ptr + count);
-}
-
-template <typename D, typename S> static D* SkTAddOffset(S const * const ptr, size_t byteOffset) {
-    return (D*)((char*)ptr + byteOffset);
-}
-
 bool SkOTTableName::Iterator::next(SkOTTableName::Iterator::Record& record) {
     const size_t nameRecordsCount = SkEndian_SwapBE16(fName.count);
     const SkOTTableName::Record* nameRecords = SkTAfter<const SkOTTableName::Record>(&fName);
@@ -459,12 +452,12 @@
     } while (fType != -1 && nameRecord->nameID.fontSpecific != fType);
 
     const uint16_t stringTableOffset = SkEndian_SwapBE16(fName.stringOffset);
-    const char* stringTable = SkTAddOffset<char>(&fName, stringTableOffset);
+    const char* stringTable = SkTAddOffset<const char>(&fName, stringTableOffset);
 
     // Decode the name into UTF-8.
     const uint16_t nameOffset = SkEndian_SwapBE16(nameRecord->offset);
     const uint16_t nameLength = SkEndian_SwapBE16(nameRecord->length);
-    const char* nameString = SkTAddOffset<char>(stringTable, nameOffset);
+    const char* nameString = SkTAddOffset<const char>(stringTable, nameOffset);
     switch (nameRecord->platformID.value) {
         case SkOTTableName::Record::PlatformID::Windows:
             SkASSERT(SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2
diff --git a/src/sfnt/SkTTCFHeader.h b/src/sfnt/SkTTCFHeader.h
new file mode 100644
index 0000000..ffe792b
--- /dev/null
+++ b/src/sfnt/SkTTCFHeader.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTTCFHeader_DEFINED
+#define SkTTCFHeader_DEFINED
+
+#include "SkOTTableTypes.h"
+
+#pragma pack(push, 1)
+
+struct SkTTCFHeader {
+    SK_SFNT_ULONG ttcTag;
+    static const SK_OT_CHAR TAG0 = 't';
+    static const SK_OT_CHAR TAG1 = 't';
+    static const SK_OT_CHAR TAG2 = 'c';
+    static const SK_OT_CHAR TAG3 = 'f';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkTTCFHeader>::value;
+
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed version_1 = SkTEndian_SwapBE32(1 << 16);
+    static const SK_OT_Fixed version_2 = SkTEndian_SwapBE32(2 << 16);
+    
+    SK_OT_ULONG numOffsets;
+    //SK_OT_ULONG offset[numOffsets]
+
+    struct Version2Ext {
+        SK_OT_ULONG dsigType;
+        struct dsigType_None {
+            static const SK_OT_CHAR TAG0 = 0;
+            static const SK_OT_CHAR TAG1 = 0;
+            static const SK_OT_CHAR TAG2 = 0;
+            static const SK_OT_CHAR TAG3 = 0;
+            static const SK_OT_ULONG TAG = SkOTTableTAG<dsigType_None>::value;
+        };
+        struct dsigType_Format1 {
+            static const SK_OT_CHAR TAG0 = 'D';
+            static const SK_OT_CHAR TAG1 = 'S';
+            static const SK_OT_CHAR TAG2 = 'I';
+            static const SK_OT_CHAR TAG3 = 'G';
+            static const SK_OT_ULONG TAG = SkOTTableTAG<dsigType_Format1>::value;
+        };
+        SK_OT_ULONG dsigLength; //Length of DSIG table (in bytes).
+        SK_OT_ULONG dsigOffset; //Offset of DSIG table from the beginning of file (in bytes).
+    };// version2ext (if version == version_2)
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkTTCFHeader) == 12, sizeof_SkTTCFHeader_not_12);
+
+#endif